From 24873930ea2b99dedfe09d9af46047bfc030b5d7 Mon Sep 17 00:00:00 2001 From: Matej Gera <38492574+matej-g@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:58:53 +0100 Subject: [PATCH] Sync fork (#2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [autoinstrumention] Bump dotnet (#1978) * Bump dotnet * changelog * [autoinstrumentation] Bump NodeJS Dependencies (#1977) * Bump nodejs dependencies * changelog * Expose the Prometheus exporter port when specified in the config (#1953) * Expose Prometheus exporter port in the OTEL Collector Signed-off-by: Israel Blancas * Format Signed-off-by: Israel Blancas * Allow the usage of multiple exporters Signed-off-by: Israel Blancas * Format Signed-off-by: Israel Blancas * Fix tests Signed-off-by: Israel Blancas * Apply changes requested in CR --------- Signed-off-by: Israel Blancas * Add managementState field to collector crd spec (#1888) * adds managementState field to collector crd spec * changes e2e test case * cover new case in e2e test case * fix typo * fix e2e test cases * fix e2e test case * add assertion on config map state * Remove legacy OTLP HTTP port (#1971) Signed-off-by: Pavol Loffay * Make sure port name in ingress and route match service (#1970) * Make sure port name in ingress and route match service Port name in service/ingress/route can be longer than 15 characters. The port name 15 characters is enforced in ports on pods. Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * [chore] Refactor Manifest Generation to internal package (#1965) * parent 2998ccc3963557acdbc0676d4775e70d32a3b8a2 author Jacob Aronoff 1690402542 -0400 committer Jacob Aronoff 1691001855 -0400 Merge upstream, squash to main * Fix missing import * fix whoopsie * Add dependabot grouping for common things (#1979) * Add groups * add chlog entry * remove chloggen * Bump the prometheus group with 1 update (#1985) Bumps the prometheus group with 1 update: [github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring](https://github.com/prometheus-operator/prometheus-operator). - [Release notes](https://github.com/prometheus-operator/prometheus-operator/releases) - [Changelog](https://github.com/prometheus-operator/prometheus-operator/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus-operator/prometheus-operator/compare/v0.66.0...v0.67.1) --- updated-dependencies: - dependency-name: github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/prometheus/prometheus from 0.45.0 to 0.46.0 (#1961) Bumps [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus) from 0.45.0 to 0.46.0. - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.45.0...v0.46.0) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Align management state constant with other constants (#1988) * Align management state constant with other constants Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * Set securityContext on injected initContainer (#1273) Co-authored-by: Jacob Aronoff * Bump the kubernetes group with 1 update (#1993) Bumps the kubernetes group with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.15.0...v0.15.1) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/operator-opamp-bridge with 1 update (#1991) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.15.0...v0.15.1) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * update prometheus deps (#1996) * Bump the kubernetes group in /cmd/otel-allocator with 1 update (#1992) Bumps the kubernetes group in /cmd/otel-allocator with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.15.0...v0.15.1) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Prepare changelog for release v0.82.0 (#1999) Signed-off-by: Benedikt Bongartz Co-authored-by: Mikołaj Świątek * Set the level 4 for the operator (#2003) * Set the level 4 for the operator Signed-off-by: Israel Blancas * Add missing changelog Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Bump golang from 1.20 to 1.21 (#2000) Bumps golang from 1.20 to 1.21. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Expose the pprof server in the operator (#1998) * Add changelog Signed-off-by: Israel Blancas * Expose pprof server Signed-off-by: Israel Blancas * Fix changelog Signed-off-by: Israel Blancas * Apply changes requested in CR Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Add dependabot rules for TA and opamp-bridge Dockerfiles (#2005) * Adding Additional Containers to the Collector spec (#1980) * Adding additional container specs to support sidecars and other containers in the collector pod Signed-off-by: Matt Christiansen * Addressing code review comments: Improving the godoc comment, adding changelog file * Update apis/v1alpha1/opentelemetrycollector_types.go Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> * Update apis/v1alpha1/opentelemetrycollector_types.go Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> * Adding in an assertion that the additional container equals input * Adding webhook validation and improving comments based on PR feedback --------- Signed-off-by: Matt Christiansen Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> * get servicename from daemonset if possible (#2013) * get servicename from daemonset if possible Signed-off-by: Angelo Poerio * added changelog Signed-off-by: Angelo Poerio --------- Signed-off-by: Angelo Poerio * Make sure OTLP exporter can report data to OTLP ingress/route without additional configuration (#1981) * Make sure OTLP export can report data to OTLP ingress/route without additional configuration. Signed-off-by: Pavol Loffay * Trim CRD description size Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * Use golang 1.21 (#2009) * Use golang 1.21 Signed-off-by: Pavol Loffay * Use golang 1.21 Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * Bump the kubernetes group in /cmd/operator-opamp-bridge with 2 updates (#2027) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 2 updates: [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/apimachinery` from 0.27.4 to 0.28.0 - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.27.4...v0.28.0) Updates `k8s.io/client-go` from 0.27.4 to 0.28.0 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.27.4...v0.28.0) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump k8s dependencies to 0.28.0 (#2031) * Fix getPrometheusExporterPorts incorrectly matching to prometheusremotewrite exporter (#2016) (#2017) * Fix getPrometheusExporterPorts incorrectly matching to prometheusremotewrite exporter * add unit test for collector prometheus rw exporter * standardized code format * Bump OpenTelemetry .NET Automatic Instrumentation to 1.0.0-rc.2 (#2030) * Update the OpenTelemetry Java agent version to 1.29.0 (#2034) * Use scratch as the base image for operator (#2014) * Bump the kubernetes group in /cmd/otel-allocator with 2 updates (#2025) Bumps the kubernetes group in /cmd/otel-allocator with 2 updates: [k8s.io/api](https://github.com/kubernetes/api) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/api` from 0.27.4 to 0.28.0 - [Commits](https://github.com/kubernetes/api/compare/v0.27.4...v0.28.0) Updates `k8s.io/client-go` from 0.27.4 to 0.28.0 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.27.4...v0.28.0) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Release v0.83.0 (#2035) * Bump versions * Release notes * version fix, components, etc. * Create Service Monitors for the Prometheus exporters (#1983) * Create Service Monitors for the Prometheus exporters * Remove testing changes Signed-off-by: Israel Blancas * Remove pull policy Signed-off-by: Israel Blancas * Apply changes requested in PR * Fix E2E test Signed-off-by: Israel Blancas * Update E2E test to use the enableMetrics flag Signed-off-by: Israel Blancas * Use the enable metrics to create the SM or not Signed-off-by: Israel Blancas * Update field description Signed-off-by: Israel Blancas * Add missing docs Signed-off-by: Israel Blancas * Remove config Signed-off-by: Israel Blancas * Ensue the ServiceMonitor matches a specific OpenTelemetry Collector instance Signed-off-by: Israel Blancas * Update generated files Signed-off-by: Israel Blancas * Fix comment Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Make OpenShift Route work with gRPC receivers (#2028) * Make OpenShift Route work with gRPC receivers Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * Bump the kubernetes group in /cmd/operator-opamp-bridge with 1 update (#2042) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.15.1...v0.16.0) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/otel-allocator with 1 update (#2041) Bumps the kubernetes group in /cmd/otel-allocator with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.15.1...v0.16.0) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump go.opentelemetry.io/collector/semconv from 0.82.0 to 0.83.0 (#2037) Bumps [go.opentelemetry.io/collector/semconv](https://github.com/open-telemetry/opentelemetry-collector) from 0.82.0 to 0.83.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-collector/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-collector/blob/main/CHANGELOG-API.md) - [Commits](https://github.com/open-telemetry/opentelemetry-collector/compare/v0.82.0...v0.83.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/collector/semconv dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update controller-runtime to 0.16 (#2046) * Bump the kubernetes group with 1 update Bumps the kubernetes group with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.15.1...v0.16.0) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes ... Signed-off-by: dependabot[bot] * Fixes Signed-off-by: Pavol Loffay --------- Signed-off-by: dependabot[bot] Signed-off-by: Pavol Loffay Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Run e2e tests on Kubernetes 1.28 (#2047) * Run e2e tests on Kubernetes 1.28 Signed-off-by: Pavol Loffay * Run e2e tests on Kubernetes 1.28 Signed-off-by: Pavol Loffay * Run e2e tests on Kubernetes 1.28 Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * Bump the kubernetes group in /cmd/operator-opamp-bridge with 2 updates (#2056) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 2 updates: [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/apimachinery` from 0.28.0 to 0.28.1 - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.28.0...v0.28.1) Updates `k8s.io/client-go` from 0.28.0 to 0.28.1 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.0...v0.28.1) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group with 3 updates (#2055) Bumps the kubernetes group with 3 updates: [k8s.io/api](https://github.com/kubernetes/api), [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) and [k8s.io/kubectl](https://github.com/kubernetes/kubectl). Updates `k8s.io/api` from 0.28.0 to 0.28.1 - [Commits](https://github.com/kubernetes/api/compare/v0.28.0...v0.28.1) Updates `k8s.io/apiextensions-apiserver` from 0.28.0 to 0.28.1 - [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases) - [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.28.0...v0.28.1) Updates `k8s.io/kubectl` from 0.28.0 to 0.28.1 - [Commits](https://github.com/kubernetes/kubectl/compare/v0.28.0...v0.28.1) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/apiextensions-apiserver dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/kubectl dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/otel-allocator with 2 updates (#2054) Bumps the kubernetes group in /cmd/otel-allocator with 2 updates: [k8s.io/api](https://github.com/kubernetes/api) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/api` from 0.28.0 to 0.28.1 - [Commits](https://github.com/kubernetes/api/compare/v0.28.0...v0.28.1) Updates `k8s.io/client-go` from 0.28.0 to 0.28.1 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.0...v0.28.1) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/prometheus/alertmanager in /cmd/otel-allocator (#2057) Bumps [github.com/prometheus/alertmanager](https://github.com/prometheus/alertmanager) from 0.25.0 to 0.25.1. - [Release notes](https://github.com/prometheus/alertmanager/releases) - [Changelog](https://github.com/prometheus/alertmanager/blob/v0.25.1/CHANGELOG.md) - [Commits](https://github.com/prometheus/alertmanager/compare/v0.25.0...v0.25.1) --- updated-dependencies: - dependency-name: github.com/prometheus/alertmanager dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix ci (#2052) * Bump go.opentelemetry.io/otel from 1.16.0 to 1.17.0 (#2067) Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.16.0 to 1.17.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.16.0...v1.17.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump go.opentelemetry.io/otel in /cmd/operator-opamp-bridge (#2065) Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.16.0 to 1.17.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.16.0...v1.17.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Avoid rerunning the pod mutator for the instrumentation injection more than one time (#2048) * Avoid running the instrumentation pod mutator for already instrumented pods Signed-off-by: Israel Blancas * Add missing changelog Signed-off-by: Israel Blancas * Fix E2E tests Signed-off-by: Israel Blancas * Apply changes requested in code review Signed-off-by: Israel Blancas * Add unit test for hte golang and env variable situations Signed-off-by: Israel Blancas * Fix lint Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Use Monitoring OLM category (#2062) Signed-off-by: Pavol Loffay Co-authored-by: Ben B * Limit auto-instrumentation volume size (#2045) * Make OpenShift Route work with gRPC receivers (#2028) * Make OpenShift Route work with gRPC receivers Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * Bump the kubernetes group in /cmd/operator-opamp-bridge with 1 update (#2077) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.16.0...v0.16.1) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group with 1 update (#2076) Bumps the kubernetes group with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.16.0...v0.16.1) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/otel-allocator with 1 update (#2075) Bumps the kubernetes group in /cmd/otel-allocator with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.16.0...v0.16.1) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Allow copy of files for non-root user (#2069) Signed-off-by: Israel Blancas * Bump go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp (#2066) Bumps [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp](https://github.com/open-telemetry/opentelemetry-go) from 0.39.0 to 0.40.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/sdk/metric/v0.39.0...sdk/metric/v0.40.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Make OpenShift routes work with missing hostname (#2074) * Make OpenShift routes work with missing hostname Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * Fix the changelog entry Signed-off-by: Pavol Loffay * Update .chloggen/route-use-defaults.yaml Co-authored-by: Israel Blancas --------- Signed-off-by: Pavol Loffay Co-authored-by: Ben B Co-authored-by: Israel Blancas * Move the instrumentation E2E tests to their own folder (#2084) Signed-off-by: Israel Blancas * Fixes reconciling Internal Traffic Policy changes and adds test (#2060) * Fixes reconciling Internal Traffic Policy changes and add tests * add changelog * issue in changelog * Bump actions/checkout from 3 to 4 (#2083) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update godoc for pod and container security context (#1279) * chore: update godoc for pod and container security context Add detailed description of SecurityContext and PodSecurityContext fields. * make bundle * re-run make api-docs * .NET auto-instrumentation include musl in docker image (#2087) * [.NET AutoInstrumentation] Docker image includes musl library * Replace ADD by get command * Remove redundant files from docker image * Execute all commands in one layer * Fix passing arguments * revert to ADD as wget is failing on CI * Typo fix Co-authored-by: Paulo Janotti --------- Co-authored-by: Paulo Janotti * [chore] Update go version used in workflows (#2088) * Update go version used in workflows * Use correct patch version * Bump the prometheus group with 1 update (#2090) Bumps the prometheus group with 1 update: [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus). - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.46.0...v0.47.0) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the prometheus group in /cmd/otel-allocator with 1 update (#2091) Bumps the prometheus group in /cmd/otel-allocator with 1 update: [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus). - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.46.0...v0.47.0) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [autoinstrumentation] Bump dotnet to 1.0.0 (#2096) * bump dotnet * Update .chloggen/upgrade-dotnet.yaml * Bump the prometheus group with 1 update (#2101) Bumps the prometheus group with 1 update: [github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring](https://github.com/prometheus-operator/prometheus-operator). - [Release notes](https://github.com/prometheus-operator/prometheus-operator/releases) - [Changelog](https://github.com/prometheus-operator/prometheus-operator/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus-operator/prometheus-operator/compare/v0.67.1...v0.68.0) --- updated-dependencies: - dependency-name: github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Remove TargetAllocator resource defaults (#2040) * Remove TargetAllocator resource defaults * Add changelog entry * Update e2e test for target allocator * trigger tests * Add heads up for change of behaviour * Bump go.opentelemetry.io/otel/metric in /cmd/operator-opamp-bridge (#2120) Bumps [go.opentelemetry.io/otel/metric](https://github.com/open-telemetry/opentelemetry-go) from 1.17.0 to 1.18.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.17.0...v1.18.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/metric dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Align version of ubuntu GHA runner (#2121) Signed-off-by: Pavol Loffay * Bump the kubernetes group in /cmd/otel-allocator with 1 update (#2111) Bumps the kubernetes group in /cmd/otel-allocator with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.16.1...v0.16.2) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group with 1 update (#2109) Bumps the kubernetes group with 1 update: [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.16.1...v0.16.2) --- updated-dependencies: - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Release 0.84.0 (#2092) * Release 0.84.0 Signed-off-by: Yuri Sa * .NET auto-instrumentation include musl in docker image (#2087) * [.NET AutoInstrumentation] Docker image includes musl library * Replace ADD by get command * Remove redundant files from docker image * Execute all commands in one layer * Fix passing arguments * revert to ADD as wget is failing on CI * Typo fix Co-authored-by: Paulo Janotti --------- Co-authored-by: Paulo Janotti * [chore] Update go version used in workflows (#2088) * Update go version used in workflows * Use correct patch version * Bump the prometheus group with 1 update (#2090) Bumps the prometheus group with 1 update: [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus). - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.46.0...v0.47.0) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the prometheus group in /cmd/otel-allocator with 1 update (#2091) Bumps the prometheus group in /cmd/otel-allocator with 1 update: [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus). - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.46.0...v0.47.0) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [autoinstrumentation] Bump dotnet to 1.0.0 (#2096) * bump dotnet * Update .chloggen/upgrade-dotnet.yaml * Bump the prometheus group with 1 update (#2101) Bumps the prometheus group with 1 update: [github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring](https://github.com/prometheus-operator/prometheus-operator). - [Release notes](https://github.com/prometheus-operator/prometheus-operator/releases) - [Changelog](https://github.com/prometheus-operator/prometheus-operator/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus-operator/prometheus-operator/compare/v0.67.1...v0.68.0) --- updated-dependencies: - dependency-name: github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Release 0.84.0 Signed-off-by: Yuri Sa * Release 0.84.0 Signed-off-by: Yuri Sa * Release 0.84.0 Signed-off-by: Yuri Sa * Release 0.84.0 Signed-off-by: Yuri Sa --------- Signed-off-by: Yuri Sa Signed-off-by: dependabot[bot] Co-authored-by: Piotr Kiełkowicz Co-authored-by: Paulo Janotti Co-authored-by: bryan-aguilar <46550959+bryan-aguilar@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> * Bump docker/setup-qemu-action from 2 to 3 (#2116) * Bump docker/metadata-action from 4 to 5 (#2114) * Bump docker/setup-buildx-action from 2 to 3 (#2115) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump docker/login-action from 2 to 3 (#2113) Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix publishing of bundle image to GHCR (#2122) * Fix publishing of bundle image to GHCR Signed-off-by: Pavol Loffay * Expand paths Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * [autoinstrumentation] bump go autoinstrumentation to 0.3.0-alpha (#2123) * bump go autoinstrumentation * changelog * Bump go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp (#2119) Bumps [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp](https://github.com/open-telemetry/opentelemetry-go) from 0.40.0 to 0.41.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/sdk/metric/v0.40.0...sdk/metric/v0.41.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [chore] typo fixes (#2099) Signed-off-by: Piotr Kiełkowicz * Bump the kubernetes group in /cmd/operator-opamp-bridge with 3 updates (#2126) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 3 updates: [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery), [k8s.io/client-go](https://github.com/kubernetes/client-go) and [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). Updates `k8s.io/apimachinery` from 0.28.1 to 0.28.2 - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.28.1...v0.28.2) Updates `k8s.io/client-go` from 0.28.1 to 0.28.2 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.1...v0.28.2) Updates `sigs.k8s.io/controller-runtime` from 0.16.1 to 0.16.2 - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.16.1...v0.16.2) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group with 3 updates (#2125) Bumps the kubernetes group with 3 updates: [k8s.io/api](https://github.com/kubernetes/api), [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver) and [k8s.io/kubectl](https://github.com/kubernetes/kubectl). Updates `k8s.io/api` from 0.28.1 to 0.28.2 - [Commits](https://github.com/kubernetes/api/compare/v0.28.1...v0.28.2) Updates `k8s.io/apiextensions-apiserver` from 0.28.1 to 0.28.2 - [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases) - [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.28.1...v0.28.2) Updates `k8s.io/kubectl` from 0.28.1 to 0.28.2 - [Commits](https://github.com/kubernetes/kubectl/compare/v0.28.1...v0.28.2) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/apiextensions-apiserver dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/kubectl dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/otel-allocator with 2 updates (#2124) Bumps the kubernetes group in /cmd/otel-allocator with 2 updates: [k8s.io/api](https://github.com/kubernetes/api) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/api` from 0.28.1 to 0.28.2 - [Commits](https://github.com/kubernetes/api/compare/v0.28.1...v0.28.2) Updates `k8s.io/client-go` from 0.28.1 to 0.28.2 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.1...v0.28.2) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump docker/build-push-action from 4 to 5 (#2112) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Run the upgrade of a collector instance after the status changes from unmanaged to managed (#2086) * Run the upgrade of a collector instance after the status changes from unmanaged to managed Signed-off-by: Israel Blancas * Add changelog Signed-off-by: Israel Blancas * Use the recorder Signed-off-by: Israel Blancas * Move import Signed-off-by: Israel Blancas * Fix imports Signed-off-by: Israel Blancas * Don't fail when the upgrade cannot be done as part of the reconciliation Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Enabling collector to mount multiple configMaps (#1989) * Enabling collector to mount multiple configMaps Signed-off-by: Yuri Sa * Fixed lints Signed-off-by: Yuri Sa * Fixed new copies Signed-off-by: Yuri Sa * Removed unnecessary kustomization Signed-off-by: Yuri Sa * Removed unnecessary kustomization Signed-off-by: Yuri Sa * Implemented path on ConfigMapsSpec Signed-off-by: Yuri Sa * Implemented path on ConfigMapsSpec Signed-off-by: Yuri Sa * Removed the name's append on VolumeMount Signed-off-by: Yuri Sa * Fix format Signed-off-by: Yuri Sa * Fix bundle Signed-off-by: Yuri Sa * Added e2e tests Signed-off-by: Yuri Sa * Added e2e tests Signed-off-by: Yuri Sa * Fixed comments regarding validations * Fixed comments regarding validations --------- Signed-off-by: Yuri Sa * Bump go.opentelemetry.io/otel from 1.17.0 to 1.18.0 (#2110) Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.17.0 to 1.18.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.17.0...v1.18.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update the OpenTelemetry Java agent version to 1.30.0 (#2127) * [dotnet-auto] Add support for musl based images (#2103) * [dotnet-auto] Add support for musl based images * PR feedback * scaffolding based on instrumentation-dotnet * adjust code to use musl annotation * include value in the error * add missing annotation * PR feedback - typo fix * PR feedback - use official .NET RID names * Refactor Reconciliation Flow (#1995) * save * bump * :shrug: * should b good * change delete logic * change error comparison * gomod * Working status * remove cruft * fix selector check for deepequals * add controller references * Fix lint and test * facepalm * Record field * Fix diffing bug * Add lots of tests * Remove old logic entirely * dont forget the kube builder * update comment, reusable builder * respond to feedback * update based on feedback * update comments, remove method that does nothing * naming * Prepare release 0.85.0 (#2137) Signed-off-by: Pavol Loffay * Fix publishing of bundle image (#2138) Signed-off-by: Pavol Loffay * Bump .NET AutoInstrumentation to 1.0.1 (#2142) * Refactor target allocator config handling (#1957) * Refactor target allocator config handling * Move TargetAllocator flags to a separate file * Fix yaml annotation on the TA config struct * Clearly separate loading config from CLI and file * Use an explicit flag set for target allocator flags * Add changelog entry * Pass config by value to PrometheusCRWatcher * Bump TA and bridge deps (#2144) * [chore] Build and publish application hosted in the repository for the Golang instrumentation E2E test (#2097) * Build and publish the Golang E2E image Signed-off-by: Israel Blancas * Fix the CI Signed-off-by: Israel Blancas * Fix the tags Signed-off-by: Israel Blancas * Add missing header Signed-off-by: Israel Blancas * Use alpine image for builder Signed-off-by: Israel Blancas * Change image name Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Bump docker/setup-buildx-action from 2 to 3 (#2152) Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Propagate proxy env vars (#2146) * Propagate proxy env vars Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * Bump docker/metadata-action from 4 to 5 (#2151) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5. - [Release notes](https://github.com/docker/metadata-action/releases) - [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md) - [Commits](https://github.com/docker/metadata-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump docker/login-action from 2 to 3 (#2150) Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump docker/build-push-action from 4 to 5 (#2149) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/v4...v5) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump actions/checkout from 3 to 4 (#2148) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Dependabot group for GHA docker actions (#2153) Signed-off-by: Pavol Loffay * Bump the gha-docker group with 1 update (#2156) Bumps the gha-docker group with 1 update: [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action). - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: gha-docker ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump OpenTelemetry .NET Automatic Instrumentation to 1.0.2 (#2168) * Bump github.com/prometheus/client_golang in /cmd/otel-allocator (#2167) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.16.0 to 1.17.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/v1.17.0/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.16.0...v1.17.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: Bump lowest supported k8s version to 1.23 (#2162) * feat: Bump lowest supported k8s version to 1.23 Signed-off-by: Jorge Turrado * u8ndo the format Signed-off-by: Jorge Turrado * undo wrong change Signed-off-by: Jorge Turrado * update kind-yaml Signed-off-by: Jorge Turrado * unify stykle Signed-off-by: Jorge Turrado * revert the change * apply feedback Signed-off-by: Jorge Turrado * apply feedback Signed-off-by: Jorge Turrado * apply feedback Signed-off-by: Jorge Turrado --------- Signed-off-by: Jorge Turrado * Bump go.opentelemetry.io/otel from 1.18.0 to 1.19.0 (#2173) Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.18.0 to 1.19.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.18.0...v1.19.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Enable discovery manager metrics in target allocator (#2169) * Fix failing test case. (#2178) * feat: add support for multi container instrumentation pod (#1901) * feat(annotations): add new instrumentation specific annotations * feat(dotnet): add instrumentation specific volume and initcont * feat(go): update containers names specific annotation * feat(injection): improve check for instrumentation status * feat(java): add instrumentation specific volume and initcont * feat(nodejs): add instrumentation specific volume and initcont * feat(python): add instrumentation specific volume and initcont * feat(mutator): add possibility to specify instrumentation specific containers * feat(sdkinjection): add possibility to instrument language specific containers * chore(e2e): update tests * chore: update go.sum * chore(e2e): bring back container-names annotation * chore(e2e): add multi-instrumentation tests * feat(instr): add multi-instrumentation support feature gate * chore(e2e): add multi-instrumentation tests to actions * chore(go): update container-names annotation check * chore(e2e): fix tests * chore: add changelog * chore(e2e): add and fix tests * chore(go): fix go tests * chore(mutator): add helpers and tests * feat(instr): improve multi instrumentation and specific cases handling * chore(helper): sort duplicates - fix tests * chore(feature): add from version * chore(instr): move funcs to languageInstrumentations struct * chore(instr): avoid reflection * feat(docs): add information about multi-instrumentation support * Update pkg/instrumentation/podmutator.go Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> * chore(instr): add new cases * chore(instr): log error for invalid instr conf * chore(instr): ret err for duplicates, improve conditions * chore(go): improve containers check condition * feat(sdk): fix initcontainer caps * feat(sdk): fix securitycontext injection * feat: update featuregate version * feat: update fg registration version * chore: update tests with vol sizelimit * feat: update readme * fix: dotnet tests * fix dotnet e2e test * fix: sort only if duplicates * chore: featuregate update fromversion --------- Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> * [chore] Fix tag for E2E Golang application (#2157) * Fix tag for E2E Golang application Signed-off-by: Israel Blancas * Fix tag Signed-off-by: Israel Blancas * Fix tag Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Bump go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp (#2175) Bumps [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp](https://github.com/open-telemetry/opentelemetry-go) from 0.41.0 to 0.42.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/sdk/metric/v0.41.0...example/view/v0.42.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [chore] Build and publish application hosted in the repository for the DotNet instrumentation E2E test (#2082) * Build and publish the DotNet E2E image Signed-off-by: Israel Blancas * Build the image for linux/arm64,linux/amd64,linux/s390x and linux/ppc64le Signed-off-by: Israel Blancas * Remove the application and generate it from the CLI Signed-off-by: Israel Blancas * Fix architectures Signed-off-by: Israel Blancas * Fix the tags Signed-off-by: Israel Blancas * Fix tag for E2E Golang application Signed-off-by: Israel Blancas * Fix tag Signed-off-by: Israel Blancas * Fix tag Signed-off-by: Israel Blancas * Make the workflow reusable Signed-off-by: Israel Blancas * Fix name Signed-off-by: Israel Blancas * Remove deps Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Rebuild targets on scrape config regex-only changes (#2171) * Fix depguard linter configuration (#2190) * refactor to be even more generic (#2185) * [chore] Build and publish the Apache HTTPD image for the E2E tests (#2085) * Build and publish the Apache HTTPD E2E image Signed-off-by: Israel Blancas * Reduce the number of files to commit Signed-off-by: Israel Blancas * Build the image using multiple architectures Signed-off-by: Israel Blancas * Fix the tags Signed-off-by: Israel Blancas * Fix the tags Signed-off-by: Israel Blancas * Use reusable workflow Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Fix .NET Automatic Instrumentation configured by namespace annotation (alpine based images) (#2186) * Failing test * Fix * Changelog * remove test line * Add support for Tolerations on target allocator (#2187) * Add support for Tolerations on target allocator * bundle * Fixing rebase breakage --------- Co-authored-by: Jacob Aronoff * [chore] Build and publish application hosted in the repository for the Java instrumentation E2E test (#2094) * Build and publish the Java E2E image Signed-off-by: Israel Blancas * Fix the tags Signed-off-by: Israel Blancas * Fix the tags Signed-off-by: Israel Blancas * Use reusable workflow Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * [chore] Build and publish application hosted in the repository for the Python instrumentation E2E test (#2095) * Build and publish the Python E2E image Signed-off-by: Israel Blancas * Fix archs Signed-off-by: Israel Blancas * Fix the tags Signed-off-by: Israel Blancas * Fix the tags Signed-off-by: Israel Blancas * Use reusable workflow Signed-off-by: Israel Blancas * Fix workflow name Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Bump the prometheus group with 1 update (#2194) Bumps the prometheus group with 1 update: [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus). - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.47.0...v0.47.1) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [autoinstrumentation/python] Bump python version to 1.20.0/0.41b0 (#2192) * Bump python version * Add PR num * remove ver bump * feat: Add support for PDBs on deployment and statefulset (#2141) * [chore] Build and publish the NodeJS E2E image (#2199) * Build and publish the NodeJS E2E image Signed-off-by: Israel Blancas * Fix workflow Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Ensures that we only skip reconciliation for unmanaged resources (#2197) * ensures that we only skip reconciliation for unmanaged resources * update tests * Bump the prometheus group in /cmd/otel-allocator with 1 update (#2195) Bumps the prometheus group in /cmd/otel-allocator with 1 update: [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus). - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.47.0...v0.47.1) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add e2e test case for multi-cluster by exposing OTEL collector. (#1956) * Nginx Autoinstrumentation Implementation (#2033) * all a17n logic implementation * nginx, podmutator tests * sdk and upgrade tests * refactor nginx + tests * Readme - added Nginx * chloggen for #PR 2033 * tests fixes * code cleanup * starting supported version, secContext * moved nginx e2e tests * unprivileged pods related fixes and tests * unprivileged test fix * unprivileged nginx test assert fix * typo fix * resource alloc for scaling tests * typo in assert * initContainer uses securityCtx of app container * test for container-level securityCtx * yaml formatting * nginx secCtx e2e test * typo * Update README.md Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> * Update pkg/featuregate/featuregate.go Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> * README.md - Nginx to stageAlpha * Nginx - to StageAlpha * not needed feature flag * Nginx alphaStage - upgrade test cases * Nginx stageAplha - reverted bundle change * revert of unintential change * unintended change revert * enable f-g operator.autoinstrumentation.nginx * revert * enable nginx featuregate * enable nginx featuregate * merge main branch - tests and typos * multicontainer nginx fixes * formatting * added Nginx version build arg --------- Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> * Release 0.86.0 (#2202) * Release 0.86.0 * update for review comments * Change GHCR bundle image to fix publish issues (#2203) Signed-off-by: Pavol Loffay * Remove autoscaling v2beta2 (#2204) Signed-off-by: Pavol Loffay * Specify Kube version in CSV (#2205) Signed-off-by: Pavol Loffay * [chore] Add issue templates (#2206) * add issue templates * add pr template * update usage of logging exporter (#2130) * update usage of logging exporter The logging exporter is being deprecated in favour of the debug exporter. This changes references to the logging exporter to use the new debug exporter instead. Note: this will remain in draft until v0.86.0 is released which will be the first release to include the debug exporter. Signed-off-by: Alex Boten * add changelog Signed-off-by: Alex Boten * update collector version Signed-off-by: Alex Boten * update expected data Signed-off-by: Alex Boten * fix test expectations Signed-off-by: Alex Boten --------- Signed-off-by: Alex Boten * Refactor OpAMP bridge logging and configuration (#2196) * [chore] Fix CI Tests (#2217) * Fix the tests * remove old one * fix applier * oops (#2218) * Bump golang.org/x/net in /cmd/operator-opamp-bridge (#2212) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.17.0. - [Commits](https://github.com/golang/net/compare/v0.15.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump golang.org/x/net from 0.15.0 to 0.17.0 in /cmd/otel-allocator (#2213) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.17.0. - [Commits](https://github.com/golang/net/compare/v0.15.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump golang.org/x/net from 0.15.0 to 0.17.0 (#2211) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.17.0. - [Commits](https://github.com/golang/net/compare/v0.15.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Publish operator images for IBM P/Z (#2215) * Publish operator images for IBM P/Z Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay Co-authored-by: Jacob Aronoff * Publish IBM P/Z images for those autoinstrumentation images where it is supported (#2224) Signed-off-by: Israel Blancas * Fix Nginx instrumentation tests to work with OpenShift (#2216) Co-authored-by: Jacob Aronoff * Fix CVE-2023-44487 for grpc-go (#2226) Signed-off-by: Pavol Loffay * Bump the prometheus group with 1 update (#2219) Bumps the prometheus group with 1 update: [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus). - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.47.1...v0.47.2) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump grpc-go to fix CVE-2023-44487 (#2228) Signed-off-by: Pavol Loffay * [chore] Change the base image to use the glibc version and not the musl version of test image (#2225) * Change the base image to use the glibc version and not the musl version Signed-off-by: Israel Blancas * Rerun E2E tests Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * [chore] Replace the images for the E2E tests with the ones built in this repository (#2222) * Replace the images by the ones hosted in the repository Signed-off-by: Israel Blancas * Revert change Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas Co-authored-by: Jacob Aronoff * Bump the prometheus group in /cmd/otel-allocator with 1 update (#2221) Bumps the prometheus group in /cmd/otel-allocator with 1 update: [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus). - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.47.1...v0.47.2) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Set the securityContext for the HTTPD Apache instrumentation (#2051) * Set the securityContext for the HTTPD Apache instrumentation Signed-off-by: Israel Blancas * Fix unit tests Signed-off-by: Israel Blancas * Add changelog Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas Co-authored-by: Jacob Aronoff * Replace tracegen by telemetrygen (#2233) Signed-off-by: Israel Blancas * [chore] Refactor Webhooks to their own packages (#2210) * lots of moving things around and simplifcation * moves webhook manifest * rename * generate * Add back annotations * Fix tests, simplify code * naming * fix borked test * FIX TESTS * oops * move things back * update manifests * fix a miss * fix tests * Rename * Add diagrams to further enhance Target Allocator documentation - #2229 (#2230) * Add diagrams to further enhance Target Allocator documentation - #2229 * Update cmd/otel-allocator/README.md Co-authored-by: Sebastian Poxhofer * Update cmd/otel-allocator/README.md Co-authored-by: Sebastian Poxhofer * Update TA diagram * Update TA diagram and description * Add mermaid diagrams * Add mermaid diagrams * Remove diagram images (using mermaid instead) --------- Co-authored-by: Sebastian Poxhofer * [chore] Revert change in NodeJS autoinstrumentation base image for 0.41.1 (#2239) * Revert change in NodeJS autoinstrumentation base image for 0.41.1 Signed-off-by: Israel Blancas * Fix platforms Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Enable Target Allocator Rewrite by default (#2231) * Enable Target Allocator Rewrite by default * Add test for target allocator rewrite override behaviour * Add rate limiting for scrape config updates (#2189) * Add rate limiting for scrape config updates * Rename constant to lowercase Co-authored-by: Ben B. --------- Co-authored-by: Ben B. * Prepare 0.87.0 release (#2242) * Prep 0.87.0 release * Fix test * regenerate changelog * Delete that annoying kustomization file * Revert "Delete that annoying kustomization file" This reverts commit cda45abd52d8d3d69b7e777d2bbb1556c60ab297. * Remove only unwanted changes * Update changelog * Bump the kubernetes group with 5 updates (#2249) Bumps the kubernetes group with 5 updates: | Package | From | To | | --- | --- | --- | | [k8s.io/api](https://github.com/kubernetes/api) | `0.28.2` | `0.28.3` | | [k8s.io/client-go](https://github.com/kubernetes/client-go) | `0.28.2` | `0.28.3` | | [k8s.io/component-base](https://github.com/kubernetes/component-base) | `0.28.2` | `0.28.3` | | [k8s.io/kubectl](https://github.com/kubernetes/kubectl) | `0.28.2` | `0.28.3` | | [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime) | `0.16.2` | `0.16.3` | Updates `k8s.io/api` from 0.28.2 to 0.28.3 - [Commits](https://github.com/kubernetes/api/compare/v0.28.2...v0.28.3) Updates `k8s.io/client-go` from 0.28.2 to 0.28.3 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.2...v0.28.3) Updates `k8s.io/component-base` from 0.28.2 to 0.28.3 - [Commits](https://github.com/kubernetes/component-base/compare/v0.28.2...v0.28.3) Updates `k8s.io/kubectl` from 0.28.2 to 0.28.3 - [Commits](https://github.com/kubernetes/kubectl/compare/v0.28.2...v0.28.3) Updates `sigs.k8s.io/controller-runtime` from 0.16.2 to 0.16.3 - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.16.2...v0.16.3) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/component-base dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/kubectl dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/prometheus/common in /cmd/otel-allocator (#2248) Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.44.0 to 0.45.0. - [Release notes](https://github.com/prometheus/common/releases) - [Commits](https://github.com/prometheus/common/compare/v0.44.0...v0.45.0) --- updated-dependencies: - dependency-name: github.com/prometheus/common dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/operator-opamp-bridge with 3 updates (#2245) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 3 updates: [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery), [k8s.io/client-go](https://github.com/kubernetes/client-go) and [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). Updates `k8s.io/apimachinery` from 0.28.2 to 0.28.3 - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.28.2...v0.28.3) Updates `k8s.io/client-go` from 0.28.2 to 0.28.3 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.2...v0.28.3) Updates `sigs.k8s.io/controller-runtime` from 0.16.2 to 0.16.3 - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.16.2...v0.16.3) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/otel-allocator with 3 updates (#2247) Bumps the kubernetes group in /cmd/otel-allocator with 3 updates: [k8s.io/api](https://github.com/kubernetes/api), [k8s.io/client-go](https://github.com/kubernetes/client-go) and [sigs.k8s.io/controller-runtime](https://github.com/kubernetes-sigs/controller-runtime). Updates `k8s.io/api` from 0.28.2 to 0.28.3 - [Commits](https://github.com/kubernetes/api/compare/v0.28.2...v0.28.3) Updates `k8s.io/client-go` from 0.28.2 to 0.28.3 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.2...v0.28.3) Updates `sigs.k8s.io/controller-runtime` from 0.16.2 to 0.16.3 - [Release notes](https://github.com/kubernetes-sigs/controller-runtime/releases) - [Changelog](https://github.com/kubernetes-sigs/controller-runtime/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/controller-runtime/compare/v0.16.2...v0.16.3) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: sigs.k8s.io/controller-runtime dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fixed Labels with sha256 image ref (#2238) * Fixed Labels with sha256 image ref Signed-off-by: Yuri Sa * Fixed Labels with sha256 image ref Signed-off-by: Yuri Sa * Added required unit tests Signed-off-by: Yuri Sa * Added required unit tests Signed-off-by: Yuri Sa * Changed unit tests Signed-off-by: Yuri Sa * Changed unit tests Signed-off-by: Yuri Sa * Changed unit tests Signed-off-by: Yuri Sa --------- Signed-off-by: Yuri Sa * Update the OpenTelemetry Java agent version to 1.31.0 (#2220) * make label functions generic (#2254) * Bump github.com/fsnotify/fsnotify in /cmd/otel-allocator (#2256) Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/fsnotify/fsnotify/releases) - [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md) - [Commits](https://github.com/fsnotify/fsnotify/compare/v1.6.0...v1.7.0) --- updated-dependencies: - dependency-name: github.com/fsnotify/fsnotify dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump NodeJS autoinstrumentation dependencies (#2241) * Revert change in NodeJS autoinstrumentation base image for 0.41.1 Signed-off-by: Israel Blancas * Bump NodeJS autoinstrumentation dependencies Signed-off-by: Israel Blancas * Increase the default volume size Signed-off-by: Israel Blancas * Add docs Signed-off-by: Israel Blancas * Fix test Signed-off-by: Israel Blancas * Add changelog Signed-off-by: Israel Blancas * Remove new architectures Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Bump OpenTelemetry .NET Automatic Instrumentation to 1.1.0 (#2252) * bump otel dotnet auto to 1.1.0 * set issue * reset kubeconfig to empty string when using in-cluster config (#2273) * creates CRD for OpAMPBridge resource (#1559) * creates CRD for OpAMPBridge resource * minor changes * fixes kustomization.yaml issues * adds age field to status * fixes lint issues * fixes lint issues * fixes lint issues Signed-off-by: Avadhut Pisal * fixes lint issues * fix ci issues * updates bundle * fixes webhook test issue * updates bundle * updates bundle * adds enum for opamp-bridge capabilities * fix opamp server endpoint in e2e test case * fix e2e test case * fix e2e test case * validate maximum replica count * add delete verb to webhook * code refactoring to move reconciliation implementation in separate package * resolves merge conflicts * using common reconciliation implementation for opampbridge * add test cases * resolves goimports lint issues * add validations for capabilities * removes validation on specific set of capabilities * change capabilities data type to map * defaulting required capabilities * add local e2e image entry for opampbridge in hack * add OPERATOROPAMPBRIDGE_IMG to prepare-e2e * fix review comments * fix e2e hack * add e2e test-suit to github action --------- Signed-off-by: Avadhut Pisal * Add E2E test for metrics collection using Prometheus CRs (#2275) * Add E2E test for metrics collection using Prometheus CRs * Check targetallocator jobs and scrape configs in E2E test * Update OpAMP Bridge to comply with its design (#2277) * Update bridge to be spec compliant * deep copy * Add more tests * Fix bork * update test to be less annoying * chore:refactor opampbridge webhook (#2288) * Bump the kubernetes group in /cmd/operator-opamp-bridge with 1 update (#2299) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 1 update: [k8s.io/klog/v2](https://github.com/kubernetes/klog). - [Release notes](https://github.com/kubernetes/klog/releases) - [Changelog](https://github.com/kubernetes/klog/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes/klog/compare/v2.100.1...v2.110.1) --- updated-dependencies: - dependency-name: k8s.io/klog/v2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/otel-allocator with 1 update (#2300) Bumps the kubernetes group in /cmd/otel-allocator with 1 update: [k8s.io/klog/v2](https://github.com/kubernetes/klog). - [Release notes](https://github.com/kubernetes/klog/releases) - [Changelog](https://github.com/kubernetes/klog/blob/main/RELEASE.md) - [Commits](https://github.com/kubernetes/klog/compare/v2.100.1...v2.110.1) --- updated-dependencies: - dependency-name: k8s.io/klog/v2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/go-logr/logr from 1.2.4 to 1.3.0 (#2286) Bumps [github.com/go-logr/logr](https://github.com/go-logr/logr) from 1.2.4 to 1.3.0. - [Release notes](https://github.com/go-logr/logr/releases) - [Changelog](https://github.com/go-logr/logr/blob/master/CHANGELOG.md) - [Commits](https://github.com/go-logr/logr/compare/v1.2.4...v1.3.0) --- updated-dependencies: - dependency-name: github.com/go-logr/logr dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/docker/docker (#2291) Bumps [github.com/docker/docker](https://github.com/docker/docker) from 24.0.4+incompatible to 24.0.7+incompatible. - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v24.0.4...v24.0.7) --- updated-dependencies: - dependency-name: github.com/docker/docker dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/docker/docker in /cmd/otel-allocator (#2292) Bumps [github.com/docker/docker](https://github.com/docker/docker) from 24.0.4+incompatible to 24.0.7+incompatible. - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v24.0.4...v24.0.7) --- updated-dependencies: - dependency-name: github.com/docker/docker dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Prepare 0.88.0 release (#2297) Co-authored-by: Jacob Aronoff * Add Heartbeat and managed annotation checker to the bridge (#2294) * add heartbeat, upgrade to latest * chlog * Fix tests * pipe the name through * set name in tests * add health test * Simplify * Fix tests * Declare ContainerPort for Target Allocator (#2313) * Declare ContainerPort for Target Allocator * Use ContainerPort in Target Allocator service * Changelog * Fix tests * Goimports * Goimports * Build manager on the host (#2289) * Build manager on the host Instead of building the manager binary during the docker image build, instead build it on the host and copy it to the image. This massively improves the performance of cross compilation, which is much faster natively vs emulation via QEMU. * Setup Go after checking our code in E2E tests This way we can cache based on the state of go.sum. * Minor improvements * Build ta on the host and copy it into the container image (#2293) * Build opamp-bridge on the host (#2296) * Bump the prometheus group with 1 update (#2316) Bumps the prometheus group with 1 update: [github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring](https://github.com/prometheus-operator/prometheus-operator). - [Release notes](https://github.com/prometheus-operator/prometheus-operator/releases) - [Changelog](https://github.com/prometheus-operator/prometheus-operator/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus-operator/prometheus-operator/compare/v0.68.0...v0.69.0) --- updated-dependencies: - dependency-name: github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/operator-opamp-bridge with 1 update (#2303) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 1 update: [sigs.k8s.io/yaml](https://github.com/kubernetes-sigs/yaml). - [Release notes](https://github.com/kubernetes-sigs/yaml/releases) - [Changelog](https://github.com/kubernetes-sigs/yaml/blob/master/RELEASE.md) - [Commits](https://github.com/kubernetes-sigs/yaml/compare/v1.3.0...v1.4.0) --- updated-dependencies: - dependency-name: sigs.k8s.io/yaml dependency-type: direct:production update-type: version-update:semver-minor dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Strip debug info from binaries (#2322) Strip the symbol table (-s) and DWARF debug information (-w) from published binaries. This is basically standard practice - it's how collector releases are built, for example. Note that the Go runtime will still retain enough information to annotate stack traces in panic logs. * fix(operator): truncate sidecar pod injected label to 63 chars (#2250) Refs: #1032 Co-authored-by: Jacob Aronoff * Add target allocator affinity configuration (#2323) * add affinity to api and manifest * crd and api with make * add test to target allocator affinity * add chlog * add test to deployment * update bundle * fix comment for ta deployment test * Support configuring images via RELATED_IMAGE_ environment variables (#2325) For disconnected environments, various tools expect the image location to be set in RELATED_IMAGE_ environment variables: https://redhat-connect.gitbook.io/certified-operator-guide/troubleshooting-and-resources/offline-enabled-operators#golang-operators https://docs.openshift.com/container-platform/4.14/operators/operator_sdk/osdk-generating-csvs.html#olm-enabling-operator-for-restricted-network_osdk-generating-csvs Resolves: #2326 Signed-off-by: Andreas Gerstmayr * Bump go.opentelemetry.io/otel from 1.19.0 to 1.20.0 (#2340) Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.19.0 to 1.20.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.19.0...v1.20.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add otel sdk group to dependabot config (#2342) * Bump go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp (#2339) Bumps [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp](https://github.com/open-telemetry/opentelemetry-go) from 0.42.0 to 0.43.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/example/view/v0.42.0...example/view/v0.43.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the prometheus group with 1 update (#2333) Bumps the prometheus group with 1 update: [github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring](https://github.com/prometheus-operator/prometheus-operator). - [Release notes](https://github.com/prometheus-operator/prometheus-operator/releases) - [Changelog](https://github.com/prometheus-operator/prometheus-operator/blob/v0.69.1/CHANGELOG.md) - [Commits](https://github.com/prometheus-operator/prometheus-operator/compare/v0.69.0...v0.69.1) --- updated-dependencies: - dependency-name: github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the prometheus group otel-allocator to 0.69.1 (#2347) prometheus-operator now requires go 1.21, so I needed to bump that version in otel-allocator as well. * Add logging for prometheus operator in TargetAllocator's config generator (#2329) * add logging for prometheus operator * adding yaml for changelog * Adding tracking issue * [chore] Add v1alpha2 types (#2283) * Add v1alpha2 types Signed-off-by: Israel Blancas * Fix lint Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Added service.instance.id into resource env (#2259) * Added service.instance.id into resource env Signed-off-by: Yuri Sa * Changed ServiceInstanceID following the RFC4122 Signed-off-by: Yuri Sa * Changed ServiceInstanceID following the RFC4122 Signed-off-by: Yuri Sa * Added semantic conventions Signed-off-by: Yuri Sa --------- Signed-off-by: Yuri Sa * Run the target allocator as non-root (#2352) * Run the target allocator as non-root Signed-off-by: David Leadbeater * Add issue to changelog Signed-off-by: David Leadbeater --------- Signed-off-by: David Leadbeater * Update file watcher to detect file write event (#2334) * adding file write event * adding issue link * Disable configuration hot reload (#2354) * implement livenessProbe endpoint to target allocator (#2355) * add liveness probe endpoint to TA * add chlog * reformat a bit * change liveness probe endpoint to /livez * Added updateStrategy to DaemonSet mode (#2305) * Added updateStrategy to DaemonSet mode Signed-off-by: Yuri Sa * Added updateStrategy to DaemonSet mode Signed-off-by: Yuri Sa * Added updateStrategy to DaemonSet mode Signed-off-by: Yuri Sa Added updateStrategy to DaemonSet mode Signed-off-by: Yuri Sa * Added webhooks test for DaemonSet Mode Signed-off-by: Yuri Sa --------- Signed-off-by: Yuri Sa * Bump the kubernetes group with 4 updates (#2360) Bumps the kubernetes group with 4 updates: [k8s.io/api](https://github.com/kubernetes/api), [k8s.io/client-go](https://github.com/kubernetes/client-go), [k8s.io/component-base](https://github.com/kubernetes/component-base) and [k8s.io/kubectl](https://github.com/kubernetes/kubectl). Updates `k8s.io/api` from 0.28.3 to 0.28.4 - [Commits](https://github.com/kubernetes/api/compare/v0.28.3...v0.28.4) Updates `k8s.io/client-go` from 0.28.3 to 0.28.4 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.3...v0.28.4) Updates `k8s.io/component-base` from 0.28.3 to 0.28.4 - [Commits](https://github.com/kubernetes/component-base/compare/v0.28.3...v0.28.4) Updates `k8s.io/kubectl` from 0.28.3 to 0.28.4 - [Commits](https://github.com/kubernetes/kubectl/compare/v0.28.3...v0.28.4) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/component-base dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/kubectl dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/operator-opamp-bridge with 2 updates (#2363) Bumps the kubernetes group in /cmd/operator-opamp-bridge with 2 updates: [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/apimachinery` from 0.28.3 to 0.28.4 - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.28.3...v0.28.4) Updates `k8s.io/client-go` from 0.28.3 to 0.28.4 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.3...v0.28.4) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the kubernetes group in /cmd/otel-allocator with 2 updates (#2362) Bumps the kubernetes group in /cmd/otel-allocator with 2 updates: [k8s.io/api](https://github.com/kubernetes/api) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/api` from 0.28.3 to 0.28.4 - [Commits](https://github.com/kubernetes/api/compare/v0.28.3...v0.28.4) Updates `k8s.io/client-go` from 0.28.3 to 0.28.4 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.28.3...v0.28.4) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [autoinstrumentation] Bump go auto instrumentation to 0.8.0-alpha (#2358) * Bump go auto instrumentation to 0.8.0-alpha * Changelog * Update readme * Fix test instrumentation-apache-httpd to work on OpenShift. (#2310) * Bump go.opentelemetry.io/otel from 1.20.0 to 1.21.0 (#2367) Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.20.0 to 1.21.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.20.0...v1.21.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump the otel group in /cmd/operator-opamp-bridge with 2 updates (#2365) Bumps the otel group in /cmd/operator-opamp-bridge with 2 updates: [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) and [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp](https://github.com/open-telemetry/opentelemetry-go). Updates `go.opentelemetry.io/otel` from 1.20.0 to 1.21.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.20.0...v1.21.0) Updates `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` from 0.43.0 to 0.44.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/example/view/v0.43.0...bridge/opencensus/v0.44.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-type: direct:production update-type: version-update:semver-minor dependency-group: otel - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp dependency-type: direct:production update-type: version-update:semver-minor dependency-group: otel ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * prepare changelog for release v0.89.0 (#2371) Signed-off-by: Benedikt Bongartz * Fix for Target Allocator not saving targets when collector instances take time to come up (#2351) * change to account for targets discoverd before instances * adding change log * adding instance changed to leat weighted as well * adding tests * adding unit tests * updaintg tests * fixing lint error and updating tests and fixing bug in least weighted algo * fix indent * Update the OpenTelemetry Java agent version to 1.32.0 (#2373) * implement readinessProbe endpoint to target allocator (#2368) * add readyz endpoint to TA * add chlog * Add livenessProbe to target allocator deployment for operator (#2356) * add liveness probe to ta manifest generation * add livenessProbe to e2e-ta-features * add chlog * clean up oopsies * add liveness e2e test to target allocator * change healthz to livez * Bump the prometheus group with 1 update (#2359) Bumps the prometheus group with 1 update: [github.com/prometheus/prometheus](https://github.com/prometheus/prometheus). - [Release notes](https://github.com/prometheus/prometheus/releases) - [Changelog](https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/prometheus/compare/v0.47.2...v0.48.0) --- updated-dependencies: - dependency-name: github.com/prometheus/prometheus dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add readinessProbe to target allocator deployment for operator (#2374) * add readiness probe to ta deployment * add readiness probe to ta e2e * add readiness probe to ta e2e * add chlog --------- Co-authored-by: Jacob Aronoff * Bump the kubernetes group with 1 update (#2379) Bumps the kubernetes group with 1 update: [k8s.io/apiextensions-apiserver](https://github.com/kubernetes/apiextensions-apiserver). - [Release notes](https://github.com/kubernetes/apiextensions-apiserver/releases) - [Commits](https://github.com/kubernetes/apiextensions-apiserver/compare/v0.28.3...v0.28.4) --- updated-dependencies: - dependency-name: k8s.io/apiextensions-apiserver dependency-type: direct:production update-type: version-update:semver-patch dependency-group: kubernetes ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump github.com/prometheus/prometheus to v0.48.0 (#2381) * Bump OpenTelemetry .NET Automatic Instrumentation to 1.2.0 (#2382) * Fix service account is created even when one is specified in collector spec (#2378) * add additional check before creating sa for collector * update builder test for collector * change check collector sa specified in sa manifest * add specified account test case to collector building * Add OpAMP example to CSV (#2386) Resolves the following warning from the scorecard tests: ``` Warning: Value opentelemetry.io/v1alpha1, Kind=OpAMPBridge: provided API should have an example annotation ``` Signed-off-by: Andreas Gerstmayr * add missing pod in the rbac (#2327) * add missing pod in the RBAC Signed-off-by: Husni Alhamdani * add missing pod in the rbac Signed-off-by: Husni Alhamdani * add missing pod in the rbac Signed-off-by: Husni Alhamdani --------- Signed-off-by: Husni Alhamdani * fix for #2366 (#2385) * fix for #2366 * added cloggen * typo * typo * e2e test for lifecycle removal from init container * e2e test for lifecycle removal in init container * Upgrade dario.cat/mergo v1.0.0 (#2388) Signed-off-by: Lam Tran * Fix cascading delete, retry on conflict (#2383) * fix cascading delete problem w/ checking for deletion timestamp, also retry on conflict automatically * Add a test * Fix test * [chore] Create a single interface for the Receiver and Exporter parsers (#2287) * Create a single interface for the Receiver and Exporter parsers Signed-off-by: Israel Blancas * Fix lint Signed-off-by: Israel Blancas * Use ComponentType Signed-off-by: Israel Blancas * Apply changes requested in code review Signed-off-by: Israel Blancas * Simplify getting the enabled components Signed-off-by: Israel Blancas * Fix lint Signed-off-by: Israel Blancas --------- Signed-off-by: Israel Blancas * Fix example command for enabling feature gates (#2391) Signed-off-by: Artem Nefedov * Enhanced Reconciliation Errors (#2193) * adding error messages * propagated errors * added changelog * working on updating tests * propagated errors and added test * Merge conflicts * changing to debug exporter and adding additional error checks * changing to debug exporter * clean up code * fixed linting errors * config_to_ports error type * fixing sha * [Target Allocator] Minor docs fix (#2400) * minor docs fix * remove chlog * Use only target address for allocation in consistent-hashing strategy (#2290) Co-authored-by: Jacob Aronoff * [target-allocator] Exit on invalid config (#1767) * add docker prerequisites (#2402) * Remove old autodetection for openshift routes, cleanup (#2398) * remove old autodetection, cleanup * remove unused * Add e2e test case for OpenTelemetry monitoring. (#2246) * Bump the prometheus group with 1 update (#2406) Bumps the prometheus group with 1 update: [github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring](https://github.com/prometheus-operator/prometheus-operator). - [Release notes](https://github.com/prometheus-operator/prometheus-operator/releases) - [Changelog](https://github.com/prometheus-operator/prometheus-operator/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus-operator/prometheus-operator/compare/v0.69.1...v0.70.0) --- updated-dependencies: - dependency-name: github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prometheus ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix smoke-daemonset to work on multi-node cluster. (#2412) * Add the ability to set headers in the opamp bridge config (#2413) * add headers to bridge config and include test for parsing config from yaml * headers to own type and make translation method a method on the headers type * rename file to headers.go, s/ToHttpHeader/ToHTTPHeader * add changelog * add copyright header * Added PodMonitor when using sidecar mode (#2404) * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa * Added PodMonitor when using sidecar mode Signed-off-by: Yuri Sa --------- Signed-off-by: Yuri Sa * Include opamp bridge headers in configmap generation (#2414) * add headers to opamp bridge spec and pass through to configmap generation * run make generate * add changelog * run make bundle and make api-docs * run 'VERSION="0.89.0" IMG="ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator" make bundle' from jacobs advice * put these versions back * rm another autogenned change * rm this too * rerun make bundle after pulling in main * fix error logging in collector container creation (#2420) * fix logging err in collector container manifest * add chlog * Add targetallocator securityContext configuration (#2419) * Add targetallocator securitycontext Signed-off-by: Frances Barcelos * Add targetallocator securitycontext Signed-off-by: Frances Barcelos * Adding perms for servicemonitors * Update 00-assert.yaml --------- Signed-off-by: Frances Barcelos Co-authored-by: Jacob Aronoff * Prep release 0.90 (#2423) * bundlings * Chlog * forgot some stuff * Bump actions/setup-go from 3 to 5 (#2424) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 5. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Improve developer experience by minimizing local changes (#2421) * Improve developer experience Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay * handle e2e tests Signed-off-by: Pavol Loffay * Fix Signed-off-by: Pavol Loffay --------- Signed-off-by: Pavol Loffay * Remove configuration hot reloading from target allocator (#2425) * Bump alpine from 3.18 to 3.19 in /cmd/otel-allocator (#2429) Bumps alpine from 3.18 to 3.19. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add e2e test case for OTEL Kafka receiver and exporter. (#2274) * Bump alpine from 3.18 to 3.19 in /cmd/operator-opamp-bridge (#2428) Bumps alpine from 3.18 to 3.19. --- updated-dependencies: - dependency-name: alpine dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: Israel Blancas Signed-off-by: Pavol Loffay Signed-off-by: dependabot[bot] Signed-off-by: Benedikt Bongartz Signed-off-by: Matt Christiansen Signed-off-by: Angelo Poerio Signed-off-by: Yuri Sa Signed-off-by: Piotr Kiełkowicz Signed-off-by: Jorge Turrado Signed-off-by: Alex Boten Signed-off-by: Avadhut Pisal Signed-off-by: Andreas Gerstmayr Signed-off-by: David Leadbeater Signed-off-by: Husni Alhamdani Signed-off-by: Lam Tran Signed-off-by: Artem Nefedov Signed-off-by: Frances Barcelos Co-authored-by: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> Co-authored-by: Israel Blancas Co-authored-by: Avadhut Pisal Co-authored-by: Pavol Loffay Co-authored-by: Jacob Aronoff Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Andreas Mårtensson Co-authored-by: Ben B Co-authored-by: Mikołaj Świątek Co-authored-by: Matt Christiansen Co-authored-by: Angelo Poerio Co-authored-by: hzhhong Co-authored-by: Piotr Kiełkowicz Co-authored-by: OpenTelemetry Bot <107717825+opentelemetrybot@users.noreply.github.com> Co-authored-by: Christian Mergenthaler <377953+cmergenthaler@users.noreply.github.com> Co-authored-by: Jonathan Yu Co-authored-by: Paulo Janotti Co-authored-by: bryan-aguilar <46550959+bryan-aguilar@users.noreply.github.com> Co-authored-by: PeterSzegedi Co-authored-by: Yuri Sa <48062171+yuriolisa@users.noreply.github.com> Co-authored-by: Jorge Turrado Ferrero Co-authored-by: Ishwar Kanse Co-authored-by: Mateusz "mat" Rumian <58699800+mat-rumian@users.noreply.github.com> Co-authored-by: Aravindhan K Co-authored-by: Jacob Aronoff Co-authored-by: Jorge Turrado Ferrero Co-authored-by: Martin Divis Co-authored-by: Vineeth Pothulapati Co-authored-by: Alex Boten Co-authored-by: Adriana Villela <50256412+avillela@users.noreply.github.com> Co-authored-by: Sebastian Poxhofer Co-authored-by: Mateusz Łach Co-authored-by: Darren Wang <68258717+changexd@users.noreply.github.com> Co-authored-by: Rohan Vernekar Co-authored-by: Andreas Gerstmayr Co-authored-by: rashmichandrashekar Co-authored-by: David Leadbeater Co-authored-by: Husni Alhamdani Co-authored-by: Lam Tran Co-authored-by: artem-nefedov Co-authored-by: Tania Pham Co-authored-by: Jason Crouse Co-authored-by: Jason Crouse Co-authored-by: frances-barcelos --- .chloggen/TEMPLATE.yaml | 16 + .chloggen/ta_removehotreload.yaml | 16 + .github/ISSUE_TEMPLATE/bug_report.yaml | 73 + .github/ISSUE_TEMPLATE/feature_request.yaml | 36 + .github/ISSUE_TEMPLATE/other.yaml | 22 + .github/dependabot.yml | 77 +- .github/pull_request_template.md | 9 + .github/workflows/auto-update-java-agent.yaml | 81 + .github/workflows/changelog.yaml | 80 + .github/workflows/check_links_config.json | 17 + .github/workflows/continuous-integration.yaml | 49 +- .github/workflows/e2e.yaml | 55 +- ...lish-autoinstrumentation-apache-httpd.yaml | 69 + .../publish-autoinstrumentation-dotnet.yaml | 30 +- ...ublish-autoinstrumentation-e2e-images.yaml | 50 + .../publish-autoinstrumentation-java.yaml | 33 +- .../publish-autoinstrumentation-nodejs.yaml | 31 +- .../publish-autoinstrumentation-python.yaml | 33 +- .github/workflows/publish-images.yaml | 59 +- .../workflows/publish-operator-bundle.yaml | 79 + .../publish-operator-opamp-bridge.yaml | 94 + .../workflows/publish-target-allocator.yaml | 55 +- .github/workflows/release.yaml | 32 +- .../reusable-operator-hub-release.yaml | 88 + ...ublish-autoinstrumentation-e2e-images.yaml | 58 + .github/workflows/scorecard.yaml | 38 +- .gitignore | 1 + .golangci.yaml | 49 +- CHANGELOG.md | 748 +- CONTRIBUTING.md | 56 +- Dockerfile | 53 +- Makefile | 254 +- PROJECT | 12 + README.md | 461 +- RELEASE.md | 52 +- apis/v1alpha1/allocation_strategy.go | 29 + apis/v1alpha1/collector_webhook.go | 358 + apis/v1alpha1/collector_webhook_test.go | 814 + apis/v1alpha1/ingress_type.go | 39 +- apis/v1alpha1/instrumentation_types.go | 140 + apis/v1alpha1/instrumentation_webhook.go | 326 +- apis/v1alpha1/instrumentation_webhook_test.go | 131 +- apis/v1alpha1/opampbridge_capabilities.go | 35 + apis/v1alpha1/opampbridge_types.go | 145 + apis/v1alpha1/opampbridge_webhook.go | 149 + apis/v1alpha1/opampbridge_webhook_test.go | 323 + apis/v1alpha1/opentelemetrycollector_types.go | 319 +- .../opentelemetrycollector_webhook.go | 181 - .../opentelemetrycollector_webhook_test.go | 357 - apis/v1alpha1/samplers.go | 2 + apis/v1alpha1/zz_generated.deepcopy.go | 621 +- apis/v1alpha2/groupversion_info.go | 34 + apis/v1alpha2/instrumentation_types.go | 325 + apis/v1alpha2/opentelemetrycollector_types.go | 255 + apis/v1alpha2/zz_generated.deepcopy.go | 639 + autoinstrumentation/apache-httpd/Dockerfile | 26 + autoinstrumentation/apache-httpd/README.md | 13 + autoinstrumentation/apache-httpd/version.txt | 1 + autoinstrumentation/dotnet/Dockerfile | 17 +- autoinstrumentation/dotnet/version.txt | 2 +- autoinstrumentation/java/Dockerfile | 2 + autoinstrumentation/java/version.txt | 2 +- autoinstrumentation/nodejs/Dockerfile | 4 +- autoinstrumentation/nodejs/package.json | 16 +- .../nodejs/src/autoinstrumentation.ts | 47 + autoinstrumentation/python/Dockerfile | 5 +- autoinstrumentation/python/requirements.txt | 89 +- bundle.Dockerfile | 2 +- ...emetry-operator.clusterserviceversion.yaml | 242 +- .../opentelemetry.io_instrumentations.yaml | 1269 +- .../opentelemetry.io_opampbridges.yaml | 2839 +++ ...ntelemetry.io_opentelemetrycollectors.yaml | 6559 +++++- bundle/metadata/annotations.yaml | 2 +- bundle/tests/scorecard/config.yaml | 8 +- cmd/operator-opamp-bridge/Dockerfile | 21 + cmd/operator-opamp-bridge/agent/agent.go | 366 + cmd/operator-opamp-bridge/agent/agent_test.go | 768 + .../agent/collector_key.go | 43 + .../agent/collector_key_test.go | 98 + .../agent/testdata/agent.yaml | 14 + .../testdata/agentbasiccomponentsallowed.yaml | 22 + .../agent/testdata/agentbatchnotallowed.yaml | 21 + .../agent/testdata/agenthttpbasic.yaml | 14 + .../testdata/agentnoprocessorsallowed.yaml | 19 + .../agent/testdata/basic.yaml | 30 + .../agent/testdata/invalid.yaml | 30 + .../agent/testdata/updated.yaml | 31 + cmd/operator-opamp-bridge/config/config.go | 276 + .../config/config_test.go | 182 + cmd/operator-opamp-bridge/config/flags.go | 73 + .../config/flags_test.go | 97 + cmd/operator-opamp-bridge/config/headers.go | 27 + .../config/testdata/agent.yaml | 14 + .../config/testdata/agentbadconf.yaml | 14 + .../testdata/agentbasiccomponentsallowed.yaml | 22 + .../config/testdata/agenthttpbasic.yaml | 16 + .../config/testdata/agentwithheaders.yaml | 17 + cmd/operator-opamp-bridge/go.mod | 95 + cmd/operator-opamp-bridge/go.sum | 275 + cmd/operator-opamp-bridge/header.txt | 13 + cmd/operator-opamp-bridge/logger/logger.go | 40 + cmd/operator-opamp-bridge/main.go | 63 + cmd/operator-opamp-bridge/metrics/reporter.go | 168 + cmd/operator-opamp-bridge/operator/client.go | 250 + .../operator/client_test.go | 245 + .../operator/testdata/collector.yaml | 31 + .../operator/testdata/invalid-collector.yaml | 28 + .../testdata/reporting-collector.yaml | 31 + .../testdata/unmanaged-collector.yaml | 31 + .../operator/testdata/updated-collector.yaml | 31 + cmd/otel-allocator/Dockerfile | 29 +- cmd/otel-allocator/README.md | 194 +- .../allocation/allocatortest.go | 71 + .../allocation/consistent_hashing.go | 154 +- .../allocation/consistent_hashing_test.go | 62 +- cmd/otel-allocator/allocation/http.go | 80 - cmd/otel-allocator/allocation/http_test.go | 222 - .../allocation/least_weighted.go | 160 +- .../allocation/least_weighted_test.go | 119 +- cmd/otel-allocator/allocation/strategy.go | 70 +- .../allocation/strategy_test.go | 136 + cmd/otel-allocator/collector/collector.go | 57 +- .../collector/collector_test.go | 214 +- cmd/otel-allocator/config/config.go | 145 +- cmd/otel-allocator/config/config_test.go | 213 +- cmd/otel-allocator/config/flags.go | 64 + cmd/otel-allocator/config/flags_test.go | 91 + .../config/testdata/config_test.yaml | 4 +- .../config/testdata/no_config.yaml | 0 .../testdata/pod_service_selector_test.yaml | 14 + cmd/otel-allocator/diff/diff.go | 27 +- cmd/otel-allocator/diff/diff_test.go | 73 +- cmd/otel-allocator/discovery/discovery.go | 122 - .../discovery/discovery_test.go | 100 - cmd/otel-allocator/go.mod | 297 +- cmd/otel-allocator/go.sum | 2647 +-- cmd/otel-allocator/main.go | 374 +- cmd/otel-allocator/prehook/prehook.go | 64 + cmd/otel-allocator/prehook/relabel.go | 110 + cmd/otel-allocator/prehook/relabel_test.go | 270 + cmd/otel-allocator/server/bench_test.go | 270 + cmd/otel-allocator/server/mocks_test.go | 38 + cmd/otel-allocator/server/server.go | 240 + cmd/otel-allocator/server/server_test.go | 617 + cmd/otel-allocator/target/discovery.go | 156 + cmd/otel-allocator/target/discovery_test.go | 413 + cmd/otel-allocator/target/target.go | 55 + .../{discovery => target}/testdata/test.yaml | 10 +- .../target/testdata/test_update.yaml | 14 + cmd/otel-allocator/watcher/file.go | 75 - cmd/otel-allocator/watcher/main.go | 118 - cmd/otel-allocator/watcher/promOperator.go | 279 +- .../watcher/promOperator_test.go | 427 + cmd/otel-allocator/watcher/watcher.go | 51 + .../opentelemetry.io_instrumentations.yaml | 1270 +- .../bases/opentelemetry.io_opampbridges.yaml | 2830 +++ ...ntelemetry.io_opentelemetrycollectors.yaml | 6560 +++++- config/crd/kustomization.yaml | 7 +- .../patches/cainjection_in_opampbridges.yaml | 7 + .../crd/patches/webhook_in_opampbridges.yaml | 16 + config/default/manager_auth_proxy_patch.yaml | 5 +- config/manager/kustomization.yaml | 2 +- config/manager/manager.yaml | 3 - ...emetry-operator.clusterserviceversion.yaml | 36 +- config/rbac/_opampbridge_editor_role.yaml | 24 + config/rbac/_opampbridge_viewer_role.yaml | 20 + config/rbac/role.yaml | 94 +- config/samples/_v1alpha1_opampbridge.yaml | 25 + .../core_v1alpha1_opentelemetrycollector.yaml | 4 +- config/samples/kustomization.yaml | 4 +- config/samples/sidecar.yaml | 4 +- config/scorecard/patches/basic.config.yaml | 2 +- config/scorecard/patches/olm.config.yaml | 6 +- config/webhook/manifests.yaml | 61 +- controllers/builder_test.go | 1058 + controllers/common.go | 124 + controllers/opampbridge_controller.go | 119 + controllers/opampbridge_controller_test.go | 173 + .../opentelemetrycollector_controller.go | 151 +- .../opentelemetrycollector_controller_test.go | 368 - controllers/reconcile_test.go | 799 + controllers/suite_test.go | 361 +- controllers/testdata/ingress_testdata.yaml | 17 + controllers/testdata/test.yaml | 22 + controllers/testdata/test_ta_update.yaml | 22 + docs/api.md | 19002 +++++++++++++++- go.mod | 227 +- go.sum | 1391 +- hack/check-operator-ready.go | 135 + hack/ignore-createdAt-bundle.sh | 13 + hack/install-kubebuilder.sh | 7 - hack/install-kustomize.sh | 4 - hack/install-kuttl.sh | 2 +- hack/install-metrics-server.sh | 17 +- hack/install-prometheus-operator.sh | 7 + ...install-targetallocator-prometheus-crds.sh | 8 + hack/modify-test-images.sh | 16 + internal/autodetect/main.go | 66 + {pkg => internal}/autodetect/main_test.go | 43 +- .../autodetect/openshift/routes.go | 22 +- internal/config/main.go | 173 +- internal/config/main_test.go | 67 +- internal/config/options.go | 88 +- internal/manifests/builder.go | 46 + .../collector/adapters/config_from.go | 0 .../collector/adapters/config_from_test.go | 2 +- .../collector/adapters/config_to_ports.go | 178 + .../adapters/config_to_ports_test.go | 105 +- .../collector/adapters/config_to_probe.go | 24 +- .../adapters/config_to_probe_test.go | 4 +- .../collector/adapters/config_validate.go | 59 +- .../adapters/config_validate_test.go | 16 +- .../manifests}/collector/annotations.go | 7 + .../manifests}/collector/annotations_test.go | 5 + internal/manifests/collector/collector.go | 78 + .../manifests/collector/config_replace.go | 101 + .../collector/config_replace_test.go | 203 + internal/manifests/collector/configmap.go | 47 + .../manifests/collector/configmap_test.go | 219 + .../manifests}/collector/container.go | 161 +- .../manifests}/collector/container_test.go | 382 +- internal/manifests/collector/daemonset.go | 67 + .../manifests/collector/daemonset_test.go | 491 + internal/manifests/collector/deployment.go | 70 + .../manifests/collector/deployment_test.go | 556 + .../collector/horizontalpodautoscaler.go | 120 + .../collector/horizontalpodautoscaler_test.go | 103 + internal/manifests/collector/ingress.go | 170 + internal/manifests/collector/ingress_test.go | 289 + .../collector/parser/exporter/exporter.go | 128 + .../parser/exporter/exporter_prometheus.go | 77 + internal/manifests/collector/parser/parser.go | 31 + .../collector/parser/receiver}/receiver.go | 107 +- .../parser/receiver}/receiver_aws-xray.go | 10 +- .../receiver}/receiver_aws-xray_test.go | 2 +- .../parser/receiver}/receiver_carbon.go | 10 +- .../parser/receiver}/receiver_carbon_test.go | 2 +- .../parser/receiver}/receiver_collectd.go | 10 +- .../receiver}/receiver_collectd_test.go | 2 +- .../receiver}/receiver_fluent-forward.go | 10 +- .../receiver}/receiver_fluent-forward_test.go | 2 +- .../parser/receiver}/receiver_generic.go | 11 +- .../parser/receiver}/receiver_generic_test.go | 37 +- .../parser/receiver}/receiver_influxdb.go | 10 +- .../receiver}/receiver_influxdb_test.go | 2 +- .../parser/receiver}/receiver_jaeger.go | 11 +- .../parser/receiver}/receiver_jaeger_test.go | 13 +- .../collector/parser/receiver}/receiver_oc.go | 10 +- .../parser/receiver}/receiver_oc_test.go | 2 +- .../parser/receiver}/receiver_otlp.go | 24 +- .../parser/receiver}/receiver_otlp_test.go | 10 +- .../parser/receiver}/receiver_sapm.go | 10 +- .../parser/receiver}/receiver_sapm_test.go | 2 +- .../parser/receiver}/receiver_signalfx.go | 10 +- .../receiver}/receiver_signalfx_test.go | 2 +- .../parser/receiver/receiver_skywalking.go | 131 + .../receiver/receiver_skywalking_test.go | 109 + .../parser/receiver}/receiver_splunk-hec.go | 10 +- .../receiver}/receiver_splunk-hec_test.go | 2 +- .../parser/receiver}/receiver_statsd.go | 22 +- .../parser/receiver}/receiver_statsd_test.go | 2 +- .../parser/receiver}/receiver_test.go | 25 +- .../parser/receiver}/receiver_wavefront.go | 10 +- .../receiver}/receiver_wavefront_test.go | 2 +- .../receiver}/receiver_zipkin-scribe.go | 10 +- .../receiver}/receiver_zipkin-scribe_test.go | 2 +- .../parser/receiver}/receiver_zipkin.go | 6 +- .../parser/receiver}/receiver_zipkin_test.go | 2 +- .../collector/poddisruptionbudget.go | 55 + .../collector/poddisruptionbudget_test.go | 99 + internal/manifests/collector/podmonitor.go | 101 + .../manifests/collector/podmonitor_test.go | 59 + internal/manifests/collector/route.go | 101 + internal/manifests/collector/route_test.go | 212 + internal/manifests/collector/service.go | 200 + .../manifests/collector}/service_test.go | 173 +- .../manifests}/collector/serviceaccount.go | 23 +- .../collector/serviceaccount_test.go | 2 +- .../manifests/collector/servicemonitor.go | 100 + .../collector/servicemonitor_test.go | 50 + internal/manifests/collector/statefulset.go | 72 + .../manifests}/collector/statefulset_test.go | 274 +- internal/manifests/collector/suite_test.go | 140 + .../config_expected_targetallocator.yaml | 22 + .../http_sd_config_servicemonitor_test.yaml | 4 +- ..._sd_config_servicemonitor_test_ta_set.yaml | 31 + .../testdata/http_sd_config_ta_test.yaml | 25 + .../testdata/http_sd_config_test.yaml | 4 +- .../collector/testdata/ingress_testdata.yaml | 3 + .../testdata/prometheus-exporter.yaml | 19 + ...elabel_config_expected_with_sd_config.yaml | 57 + .../testdata/relabel_config_original.yaml | 51 + .../manifests/collector/testdata/route_crd.go | 74 + .../manifests/collector/testdata/sm_crd.go | 38 + .../manifests}/collector/testdata/test.yaml | 4 +- .../manifests}/collector/utils.go | 2 +- .../manifests}/collector/volume.go | 19 +- .../manifests}/collector/volume_test.go | 30 +- internal/manifests/collector/volumeclaim.go | 33 + .../manifests}/collector/volumeclaim_test.go | 36 +- .../manifests/manifestutils}/labels.go | 41 +- .../manifests/manifestutils}/labels_test.go | 80 +- internal/manifests/mutate.go | 366 + internal/manifests/opampbridge/configmap.go | 75 + .../manifests/opampbridge/configmap_test.go | 107 + internal/manifests/opampbridge/container.go | 77 + .../manifests/opampbridge}/container_test.go | 35 +- internal/manifests/opampbridge/deployment.go | 65 + .../manifests/opampbridge/deployment_test.go | 425 + internal/manifests/opampbridge/opampbridge.go | 45 + internal/manifests/opampbridge/service.go | 51 + .../manifests/opampbridge/serviceaccount.go | 48 + .../opampbridge/serviceaccount_test.go | 57 + internal/manifests/opampbridge/utils.go | 29 + internal/manifests/opampbridge/volume.go | 43 + .../manifests/opampbridge/volume_test.go | 31 +- .../manifests}/params.go | 15 +- .../adapters/config_to_prom_config.go | 331 + .../adapters/config_to_prom_config_test.go | 450 + .../manifests/targetallocator/annotations.go | 54 + .../targetallocator/annotations_test.go | 63 + .../manifests/targetallocator/configmap.go | 103 + .../targetallocator/configmap_test.go | 160 + .../manifests/targetallocator/container.go | 102 + .../targetallocator/container_test.go | 370 + .../manifests/targetallocator/deployment.go | 67 + .../targetallocator/deployment_test.go | 372 + .../manifests}/targetallocator/labels.go | 8 +- .../manifests}/targetallocator/labels_test.go | 22 +- internal/manifests/targetallocator/service.go | 47 + .../manifests/targetallocator/service_test.go | 45 + .../targetallocator/serviceaccount.go | 20 +- .../targetallocator/serviceaccount_test.go | 0 .../targetallocator/targetallocator.go | 44 + .../targetallocator/testdata}/test.yaml | 8 +- .../manifests}/targetallocator/volume.go | 4 +- .../manifests}/targetallocator/volume_test.go | 4 +- {pkg => internal}/naming/dns.go | 0 {pkg => internal}/naming/dns_test.go | 0 internal/naming/main.go | 161 + internal/naming/port.go | 44 + internal/naming/port_test.go | 68 + {pkg => internal}/naming/triming.go | 0 {pkg => internal}/naming/triming_test.go | 0 .../status/collector/collector.go | 62 +- internal/status/collector/handle.go | 72 + internal/status/opampbridge/handle.go | 58 + internal/status/opampbridge/opampbridge.go | 31 + internal/version/main.go | 102 +- .../podmutation}/webhookhandler.go | 21 +- .../podmutation}/webhookhandler_suite_test.go | 46 +- .../podmutation}/webhookhandler_test.go | 42 +- kind-1.19.yaml | 5 - kind-1.20.yaml | 5 - kind-1.21.yaml | 5 - kind-1.22.yaml | 5 - kind-1.23.yaml | 17 +- kind-1.24.yaml | 15 +- kind-1.25.yaml | 15 +- kind-1.26.yaml | 18 + kind-1.27.yaml | 18 + kind-1.28.yaml | 18 + kuttl-test-autoscale.yaml | 6 + kuttl-test-instrumentation.yaml | 5 + kuttl-test-multi-instr.yaml | 9 + kuttl-test-opampbridge.yaml | 6 + kuttl-test-openshift.yaml | 6 + kuttl-test-pdb.yaml | 6 + kuttl-test-prometheuscr.yaml | 20 + kuttl-test-upgrade.yaml | 11 +- kuttl-test.yaml | 11 +- main.go | 209 +- pkg/autodetect/main.go | 128 - pkg/collector/adapters/config_to_ports.go | 99 - pkg/collector/daemonset.go | 65 - pkg/collector/daemonset_test.go | 238 - pkg/collector/deployment.go | 67 - pkg/collector/deployment_test.go | 260 - pkg/collector/horizontalpodautoscaler.go | 196 - pkg/collector/horizontalpodautoscaler_test.go | 167 - pkg/collector/reconcile/config_replace.go | 86 - .../reconcile/config_replace_test.go | 98 - pkg/collector/reconcile/configmap.go | 243 - pkg/collector/reconcile/configmap_test.go | 357 - pkg/collector/reconcile/daemonset.go | 146 - pkg/collector/reconcile/daemonset_test.go | 163 - pkg/collector/reconcile/deployment.go | 166 - pkg/collector/reconcile/deployment_test.go | 393 - .../reconcile/horizontalpodautoscaler.go | 197 - .../reconcile/horizontalpodautoscaler_test.go | 178 - pkg/collector/reconcile/ingress.go | 242 - pkg/collector/reconcile/ingress_test.go | 268 - pkg/collector/reconcile/service.go | 315 - pkg/collector/reconcile/serviceaccount.go | 139 - .../reconcile/serviceaccount_test.go | 119 - pkg/collector/reconcile/statefulset.go | 147 - pkg/collector/reconcile/statefulset_test.go | 165 - pkg/collector/reconcile/suite_test.go | 277 - pkg/collector/statefulset.go | 70 - pkg/collector/upgrade/suite_test.go | 41 +- pkg/collector/upgrade/upgrade.go | 24 +- pkg/collector/upgrade/upgrade_test.go | 65 +- pkg/collector/upgrade/v0_19_0.go | 2 +- pkg/collector/upgrade/v0_19_0_test.go | 2 +- pkg/collector/upgrade/v0_24_0.go | 2 +- pkg/collector/upgrade/v0_31_0.go | 2 +- pkg/collector/upgrade/v0_36_0.go | 22 +- pkg/collector/upgrade/v0_38_0.go | 2 +- pkg/collector/upgrade/v0_39_0.go | 2 +- pkg/collector/upgrade/v0_41_0.go | 2 +- pkg/collector/upgrade/v0_43_0.go | 2 +- pkg/collector/upgrade/v0_56_0.go | 2 +- pkg/collector/upgrade/v0_57_2.go | 2 +- pkg/collector/upgrade/v0_61_0.go | 2 +- pkg/collector/upgrade/v0_9_0.go | 2 +- pkg/collector/volumeclaim.go | 56 - pkg/constants/env.go | 9 + pkg/featuregate/featuregate.go | 101 + pkg/featuregate/featuregate_test.go | 89 + pkg/instrumentation/annotation.go | 25 +- pkg/instrumentation/apachehttpd.go | 287 + pkg/instrumentation/apachehttpd_test.go | 586 + pkg/instrumentation/dotnet.go | 63 +- pkg/instrumentation/dotnet_test.go | 226 +- pkg/instrumentation/golang.go | 101 + pkg/instrumentation/golang_test.go | 298 + pkg/instrumentation/helper.go | 106 +- pkg/instrumentation/helper_test.go | 121 +- .../instrumentation_suite_test.go | 15 +- pkg/instrumentation/javaagent.go | 30 +- pkg/instrumentation/javaagent_test.go | 39 +- pkg/instrumentation/nginx.go | 354 + pkg/instrumentation/nginx_test.go | 659 + pkg/instrumentation/nodejs.go | 30 +- pkg/instrumentation/nodejs_test.go | 43 +- pkg/instrumentation/podmutator.go | 269 +- pkg/instrumentation/podmutator_test.go | 4815 +++- pkg/instrumentation/python.go | 65 +- pkg/instrumentation/python_test.go | 127 +- pkg/instrumentation/sdk.go | 254 +- pkg/instrumentation/sdk_test.go | 893 +- pkg/instrumentation/upgrade/upgrade.go | 116 +- .../upgrade/upgrade_suite_test.go | 7 +- pkg/instrumentation/upgrade/upgrade_test.go | 75 +- pkg/naming/main.go | 110 - pkg/sidecar/pod.go | 24 +- pkg/sidecar/pod_test.go | 63 +- pkg/sidecar/podmutator.go | 15 +- .../adapters/config_to_prom_config.go | 69 - .../adapters/config_to_prom_config_test.go | 78 - pkg/targetallocator/container.go | 60 - pkg/targetallocator/deployment.go | 57 - pkg/targetallocator/deployment_test.go | 71 - tests/e2e-autoscale/autoscale/00-assert.yaml | 86 + .../autoscale/00-install.yaml | 24 +- tests/e2e-autoscale/autoscale/01-assert.yaml | 36 + tests/e2e-autoscale/autoscale/01-install.yaml | 44 + tests/e2e-autoscale/autoscale/02-assert.yaml | 7 + tests/e2e-autoscale/autoscale/02-install.yaml | 22 + .../autoscale/03-assert.yaml} | 3 +- tests/e2e-autoscale/autoscale/03-delete.yaml | 8 + .../00-install-collector.yaml | 31 + .../00-install-instrumentation.yaml | 17 + .../01-assert.yaml | 51 + .../01-install-app.yaml | 37 + .../00-install-collector.yaml | 22 + .../00-install-instrumentation.yaml | 18 + .../01-assert.yaml | 71 + .../01-install-app.yaml | 36 + .../02-assert.yaml | 68 + .../02-install-app.yaml | 36 + .../00-install-collector.yaml | 4 +- .../00-install-instrumentation.yaml | 0 .../01-assert.yaml | 28 +- .../01-install-app.yaml | 4 +- .../02-assert.yaml | 14 +- .../02-install-app.yaml | 4 +- .../00-install-collector.yaml | 32 + .../00-install-instrumentation.yaml | 0 .../01-assert.yaml | 66 + .../01-install-app.yaml | 29 + .../00-install-collector.yaml | 32 + .../00-install-instrumentation.yaml | 26 + .../instrumentation-dotnet/01-assert.yaml | 27 +- .../01-install-app.yaml | 6 +- .../00-install-collector.yaml | 4 +- .../00-install-instrumentation.yaml | 26 + .../instrumentation-go/01-add-scc.yaml | 16 + .../instrumentation-go/02-assert.yaml | 51 + .../instrumentation-go/02-install-app.yaml | 26 + .../instrumentation-go/add-scc.sh | 6 + .../instrumentation-go/scc.yaml | 17 + .../00-install-collector.yaml | 4 +- .../00-install-instrumentation.yaml | 0 .../01-assert.yaml | 12 +- .../01-install-app.yaml | 4 +- .../02-assert.yaml | 6 +- .../02-install-app.yaml | 4 +- .../01-cleanup-other-ns.yaml} | 0 .../02-install-collector.yaml | 32 + .../02-install-instrumentation.yaml} | 0 .../03-assert.yaml} | 12 +- .../03-install-app.yaml} | 6 +- .../04-cleanup-other-ns.yaml | 6 + .../00-install-collector.yaml | 32 + .../00-install-instrumentation.yaml | 0 .../instrumentation-java/01-assert.yaml | 19 +- .../instrumentation-java/01-install-app.yaml | 6 +- .../00-install-collector.yaml | 31 + .../00-install-instrumentation.yaml | 17 + .../01-assert.yaml | 59 + .../01-install-app.yaml | 94 + .../00-install-collector.yaml | 32 + .../00-install-instrumentation.yaml | 19 + .../01-assert.yaml | 77 + .../01-install-app.yaml | 119 + .../02-assert.yaml | 74 + .../02-install-app.yaml | 118 + .../00-install-collector.yaml | 31 + .../00-install-instrumentation.yaml | 17 + .../instrumentation-nginx/01-assert.yaml | 57 + .../instrumentation-nginx/01-install-app.yaml | 104 + .../00-install-collector.yaml | 4 +- .../00-install-instrumentation.yaml | 0 .../01-assert.yaml | 12 +- .../01-install-app.yaml | 4 +- .../02-assert.yaml | 6 +- .../02-install-app.yaml | 4 +- .../00-install-collector.yaml | 32 + .../00-install-instrumentation.yaml | 2 + .../instrumentation-nodejs/01-assert.yaml | 22 +- .../01-install-app.yaml | 6 +- .../00-install-collector.yaml | 23 + .../00-install-instrumentation.yaml | 0 .../01-assert.yaml | 28 +- .../01-install-app.yaml | 4 +- .../02-assert.yaml | 14 +- .../02-install-app.yaml | 4 +- .../00-install-collector.yaml | 32 + .../00-install-instrumentation.yaml | 4 +- .../instrumentation-python/01-assert.yaml | 29 +- .../01-install-app.yaml | 6 +- .../00-install-collector.yaml | 32 + .../00-install-instrumentation.yaml | 0 .../instrumentation-sdk/01-assert.yaml | 0 .../instrumentation-sdk/01-install-app.yaml | 2 +- .../00-install-collector.yaml | 0 .../00-install-instrumentation.yaml | 40 + .../01-assert.yaml | 129 + .../01-install-app.yaml | 39 + .../00-install-collector.yaml | 0 .../00-install-instrumentation.yaml | 40 + .../01-assert.yaml | 22 + .../01-install-app.yaml | 29 + .../00-install-collector.yaml | 0 .../00-install-instrumentation.yaml | 40 + .../01-assert.yaml | 38 + .../01-install-app.yaml | 25 + .../manager_deployment_feature_gate.yaml | 63 + .../opampbridge/00-assert.yaml | 70 + .../opampbridge/00-install.yaml | 49 + tests/e2e-openshift/Dockerfile | 35 + tests/e2e-openshift/kafka/00-assert.yaml | 180 + .../kafka/00-create-kafka-instance.yaml | 60 + tests/e2e-openshift/kafka/01-assert.yaml | 13 + .../kafka/01-create-kafka-topics.yaml | 13 + tests/e2e-openshift/kafka/02-assert.yaml | 39 + .../kafka/02-otel-kakfa-receiver.yaml | 22 + tests/e2e-openshift/kafka/03-assert.yaml | 75 + .../kafka/03-otel-kakfa-exporter.yaml | 25 + tests/e2e-openshift/kafka/04-assert.yaml | 9 + .../kafka/04-generate-traces.yaml | 26 + tests/e2e-openshift/kafka/05-assert.yaml | 6 + tests/e2e-openshift/kafka/check_traces.sh | 39 + tests/e2e-openshift/monitoring/00-assert.yaml | 4 + .../monitoring/00-workload-monitoring.yaml | 13 + tests/e2e-openshift/monitoring/01-assert.yaml | 111 + .../monitoring/01-otel-collector.yaml | 24 + tests/e2e-openshift/monitoring/02-assert.yaml | 6 + .../monitoring/02-generate-traces.yaml | 27 + tests/e2e-openshift/monitoring/03-assert.yaml | 5 + .../e2e-openshift/monitoring/check_metrics.sh | 23 + .../check_user_workload_monitoring.sh | 22 + .../multi-cluster/00-assert.yaml | 13 + .../multi-cluster/00-create-namespaces.yaml | 10 + .../multi-cluster/01-assert.yaml | 126 + .../multi-cluster/01-create-jaeger.yaml | 9 + .../multi-cluster/02-assert.yaml | 147 + .../multi-cluster/02-otlp-receiver.yaml | 49 + .../multi-cluster/03-assert.yaml | 107 + .../multi-cluster/03-otlp-sender.yaml | 46 + .../multi-cluster/04-assert.yaml | 20 + .../multi-cluster/04-generate-traces.yaml | 41 + .../multi-cluster/05-assert.yaml | 6 + .../multi-cluster/check_traces.sh | 26 + .../multi-cluster/create_otlp_sender.sh | 62 + .../multi-cluster/generate_certs.sh | 59 + .../otlp-metrics-traces/00-assert.yaml | 10 + .../00-install-jaeger.yaml | 18 + .../otlp-metrics-traces/01-assert.yaml | 4 + .../01-workload-monitoring.yaml | 11 + .../otlp-metrics-traces/02-assert.yaml | 31 + .../02-otel-metrics-collector.yaml | 38 + .../otlp-metrics-traces/03-assert.yaml | 22 + .../03-metrics-traces-gen.yaml | 52 + .../otlp-metrics-traces/04-assert.yaml | 6 + .../otlp-metrics-traces/05-assert.yaml | 5 + .../otlp-metrics-traces/check_metrics.sh | 15 + .../otlp-metrics-traces/check_traces.sh | 12 + .../check_user_workload_monitoring.sh | 22 + tests/e2e-openshift/route/00-assert.yaml | 53 + tests/e2e-openshift/route/00-install.yaml | 30 + .../route/01-report-empty-otlphttp-spans.yaml | 9 + tests/e2e-pdb/pdb/00-install.yaml | 4 + tests/e2e-pdb/pdb/01-assert.yaml | 63 + tests/e2e-pdb/pdb/01-install.yaml | 141 + .../00-install.yaml | 35 + .../01-assert.yaml | 44 + .../01-install-app.yaml | 20 + .../00-install.yaml | 4 + .../01-assert.yaml | 65 + .../01-install.yaml | 29 + .../02-assert.yaml | 42 + .../02-install.yaml | 26 + .../03-assert.yaml | 21 + .../03-install.yaml | 26 + .../04-error.yaml | 19 + .../04-install.yaml | 26 + .../05-assert.yaml | 21 + .../05-error.yaml | 22 + .../05-install.yaml | 32 + .../06-assert.yaml | 20 + .../06-install.yaml | 29 + .../07-delete.yaml | 8 + .../07-error.yaml | 5 + tests/e2e-upgrade/upgrade-test/00-assert.yaml | 9 +- .../e2e-upgrade/upgrade-test/00-install.yaml | 6 +- .../upgrade-test/01-upgrade-operator.yaml | 4 +- .../upgrade-test/02-upgrade-collector.yaml | 4 +- .../opentelemetry-operator-v0.49.0.yaml | 2732 --- .../opentelemetry-operator-v0.86.0.yaml | 8558 +++++++ tests/e2e/autoscale/00-assert.yaml | 32 - tests/e2e/autoscale/01-assert.yaml | 18 - tests/e2e/autoscale/01-install.yaml | 19 - tests/e2e/daemonset-features/00-add-scc.yaml | 4 + .../{00-install.yaml => 01-install.yaml} | 8 +- .../{00-assert.yaml => 02-assert.yaml} | 2 - .../03-add-sa-collector.yaml | 4 + tests/e2e/daemonset-features/03-assert.yaml | 8 + .../daemonset-features/add-sa-collector.sh | 5 + .../daemonset-features/add-scc-openshift.sh | 10 + tests/e2e/daemonset-features/scc.yaml | 22 + tests/e2e/ingress-subdomains/00-assert.yaml | 45 + tests/e2e/ingress-subdomains/00-install.yaml | 29 + .../01-report-empty-otlphttp-spans.yaml | 8 + tests/e2e/ingress/00-assert.yaml | 9 +- tests/e2e/ingress/00-install.yaml | 4 +- .../00-install-collector.yaml | 23 - .../00-install-collector.yaml | 23 - .../00-install-collector.yaml | 23 - tests/e2e/managed-reconcile/00-assert.yaml | 48 + tests/e2e/managed-reconcile/00-install.yaml | 22 + tests/e2e/managed-reconcile/01-assert.yaml | 72 + .../01-disable-reconciliation.yaml | 51 + tests/e2e/managed-reconcile/02-assert.yaml | 73 + .../02-enable-reconciliation.yaml | 23 + tests/e2e/multiple-configmaps/00-assert.yaml | 51 + tests/e2e/multiple-configmaps/00-install.yaml | 43 + .../00-assert.yaml | 25 + .../00-promreceiver-allocatorconfig.yaml | 69 + .../01-assert.yaml | 28 + .../01-promreceiver-labeldrop.yaml | 49 + .../02-assert.yaml | 25 + ...02-promreceiver-allocatorconfig-extra.yaml | 53 + .../03-assert.yaml | 25 + .../03-promreceiver-nopromconfig.yaml | 48 + tests/e2e/smoke-daemonset/00-assert.yaml | 20 + tests/e2e/smoke-daemonset/00-install.yaml | 24 + tests/e2e/smoke-daemonset/check-daemonset.sh | 15 + .../e2e/smoke-init-containers/00-assert.yaml | 59 + .../e2e/smoke-init-containers/00-install.yaml | 29 + .../e2e/smoke-pod-annotations/00-install.yaml | 4 +- tests/e2e/smoke-pod-labels/00-assert.yaml | 12 + tests/e2e/smoke-pod-labels/00-install.yaml | 30 + .../00-install.yaml | 4 +- .../01-install-second-config.yaml | 4 +- .../00-install.yaml | 29 + .../01-assert.yaml | 21 + .../01-install-app.yaml | 20 + tests/e2e/smoke-sidecar/00-install.yaml | 4 +- tests/e2e/smoke-sidecar/01-assert.yaml | 1 + tests/e2e/smoke-sidecar/01-install-app.yaml | 2 +- tests/e2e/smoke-simplest/00-assert.yaml | 10 - tests/e2e/smoke-simplest/00-install.yaml | 4 +- tests/e2e/smoke-statefulset/00-install.yaml | 4 +- .../e2e/smoke-targetallocator/00-assert.yaml | 28 +- .../e2e/smoke-targetallocator/00-install.yaml | 10 +- .../e2e/smoke-targetallocator/01-assert.yaml | 8 + .../01-change-ta-config.yaml | 39 + .../e2e/statefulset-features/00-install.yaml | 4 +- tests/e2e/statefulset-features/01-assert.yaml | 41 + .../01-update-volume-claim-templates.yaml | 34 + .../targetallocator-features/00-assert.yaml | 27 + .../targetallocator-features/00-install.yaml | 38 +- .../targetallocator-features/01-assert.yaml | 14 + .../targetallocator-features/01-liveness.yaml | 5 + .../00-assert.yaml | 53 + .../00-install.yaml | 137 + .../01-assert.yaml | 20 + .../01-install.yaml | 48 + .../apache-httpd/Dockerfile | 5 + .../dotnet/Dockerfile | 18 + .../golang/Dockerfile | 8 + tests/instrumentation-e2e-apps/golang/main.go | 37 + .../java/DemoApplication.java | 23 + .../instrumentation-e2e-apps/java/Dockerfile | 23 + .../java/build.gradle | 25 + .../nodejs/Dockerfile | 5 + .../instrumentation-e2e-apps/nodejs/index.js | 11 + .../python/Dockerfile | 7 + tests/instrumentation-e2e-apps/python/app.py | 10 + .../python/requirements.txt | 1 + versions.txt | 28 +- 723 files changed, 92933 insertions(+), 20126 deletions(-) create mode 100644 .chloggen/TEMPLATE.yaml create mode 100755 .chloggen/ta_removehotreload.yaml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yaml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yaml create mode 100644 .github/ISSUE_TEMPLATE/other.yaml create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/auto-update-java-agent.yaml create mode 100644 .github/workflows/changelog.yaml create mode 100644 .github/workflows/check_links_config.json create mode 100644 .github/workflows/publish-autoinstrumentation-apache-httpd.yaml create mode 100644 .github/workflows/publish-autoinstrumentation-e2e-images.yaml create mode 100644 .github/workflows/publish-operator-bundle.yaml create mode 100644 .github/workflows/publish-operator-opamp-bridge.yaml create mode 100644 .github/workflows/reusable-operator-hub-release.yaml create mode 100644 .github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml create mode 100644 apis/v1alpha1/allocation_strategy.go create mode 100644 apis/v1alpha1/collector_webhook.go create mode 100644 apis/v1alpha1/collector_webhook_test.go create mode 100644 apis/v1alpha1/opampbridge_capabilities.go create mode 100644 apis/v1alpha1/opampbridge_types.go create mode 100644 apis/v1alpha1/opampbridge_webhook.go create mode 100644 apis/v1alpha1/opampbridge_webhook_test.go delete mode 100644 apis/v1alpha1/opentelemetrycollector_webhook.go delete mode 100644 apis/v1alpha1/opentelemetrycollector_webhook_test.go create mode 100644 apis/v1alpha2/groupversion_info.go create mode 100644 apis/v1alpha2/instrumentation_types.go create mode 100644 apis/v1alpha2/opentelemetrycollector_types.go create mode 100644 apis/v1alpha2/zz_generated.deepcopy.go create mode 100644 autoinstrumentation/apache-httpd/Dockerfile create mode 100644 autoinstrumentation/apache-httpd/README.md create mode 100644 autoinstrumentation/apache-httpd/version.txt create mode 100644 bundle/manifests/opentelemetry.io_opampbridges.yaml create mode 100644 cmd/operator-opamp-bridge/Dockerfile create mode 100644 cmd/operator-opamp-bridge/agent/agent.go create mode 100644 cmd/operator-opamp-bridge/agent/agent_test.go create mode 100644 cmd/operator-opamp-bridge/agent/collector_key.go create mode 100644 cmd/operator-opamp-bridge/agent/collector_key_test.go create mode 100644 cmd/operator-opamp-bridge/agent/testdata/agent.yaml create mode 100644 cmd/operator-opamp-bridge/agent/testdata/agentbasiccomponentsallowed.yaml create mode 100644 cmd/operator-opamp-bridge/agent/testdata/agentbatchnotallowed.yaml create mode 100644 cmd/operator-opamp-bridge/agent/testdata/agenthttpbasic.yaml create mode 100644 cmd/operator-opamp-bridge/agent/testdata/agentnoprocessorsallowed.yaml create mode 100644 cmd/operator-opamp-bridge/agent/testdata/basic.yaml create mode 100644 cmd/operator-opamp-bridge/agent/testdata/invalid.yaml create mode 100644 cmd/operator-opamp-bridge/agent/testdata/updated.yaml create mode 100644 cmd/operator-opamp-bridge/config/config.go create mode 100644 cmd/operator-opamp-bridge/config/config_test.go create mode 100644 cmd/operator-opamp-bridge/config/flags.go create mode 100644 cmd/operator-opamp-bridge/config/flags_test.go create mode 100644 cmd/operator-opamp-bridge/config/headers.go create mode 100644 cmd/operator-opamp-bridge/config/testdata/agent.yaml create mode 100644 cmd/operator-opamp-bridge/config/testdata/agentbadconf.yaml create mode 100644 cmd/operator-opamp-bridge/config/testdata/agentbasiccomponentsallowed.yaml create mode 100644 cmd/operator-opamp-bridge/config/testdata/agenthttpbasic.yaml create mode 100644 cmd/operator-opamp-bridge/config/testdata/agentwithheaders.yaml create mode 100644 cmd/operator-opamp-bridge/go.mod create mode 100644 cmd/operator-opamp-bridge/go.sum create mode 100644 cmd/operator-opamp-bridge/header.txt create mode 100644 cmd/operator-opamp-bridge/logger/logger.go create mode 100644 cmd/operator-opamp-bridge/main.go create mode 100644 cmd/operator-opamp-bridge/metrics/reporter.go create mode 100644 cmd/operator-opamp-bridge/operator/client.go create mode 100644 cmd/operator-opamp-bridge/operator/client_test.go create mode 100644 cmd/operator-opamp-bridge/operator/testdata/collector.yaml create mode 100644 cmd/operator-opamp-bridge/operator/testdata/invalid-collector.yaml create mode 100644 cmd/operator-opamp-bridge/operator/testdata/reporting-collector.yaml create mode 100644 cmd/operator-opamp-bridge/operator/testdata/unmanaged-collector.yaml create mode 100644 cmd/operator-opamp-bridge/operator/testdata/updated-collector.yaml create mode 100644 cmd/otel-allocator/allocation/allocatortest.go delete mode 100644 cmd/otel-allocator/allocation/http.go delete mode 100644 cmd/otel-allocator/allocation/http_test.go create mode 100644 cmd/otel-allocator/allocation/strategy_test.go create mode 100644 cmd/otel-allocator/config/flags.go create mode 100644 cmd/otel-allocator/config/flags_test.go create mode 100644 cmd/otel-allocator/config/testdata/no_config.yaml create mode 100644 cmd/otel-allocator/config/testdata/pod_service_selector_test.yaml delete mode 100644 cmd/otel-allocator/discovery/discovery.go delete mode 100644 cmd/otel-allocator/discovery/discovery_test.go create mode 100644 cmd/otel-allocator/prehook/prehook.go create mode 100644 cmd/otel-allocator/prehook/relabel.go create mode 100644 cmd/otel-allocator/prehook/relabel_test.go create mode 100644 cmd/otel-allocator/server/bench_test.go create mode 100644 cmd/otel-allocator/server/mocks_test.go create mode 100644 cmd/otel-allocator/server/server.go create mode 100644 cmd/otel-allocator/server/server_test.go create mode 100644 cmd/otel-allocator/target/discovery.go create mode 100644 cmd/otel-allocator/target/discovery_test.go create mode 100644 cmd/otel-allocator/target/target.go rename cmd/otel-allocator/{discovery => target}/testdata/test.yaml (68%) create mode 100644 cmd/otel-allocator/target/testdata/test_update.yaml delete mode 100644 cmd/otel-allocator/watcher/file.go delete mode 100644 cmd/otel-allocator/watcher/main.go create mode 100644 cmd/otel-allocator/watcher/promOperator_test.go create mode 100644 cmd/otel-allocator/watcher/watcher.go create mode 100644 config/crd/bases/opentelemetry.io_opampbridges.yaml create mode 100644 config/crd/patches/cainjection_in_opampbridges.yaml create mode 100644 config/crd/patches/webhook_in_opampbridges.yaml create mode 100644 config/rbac/_opampbridge_editor_role.yaml create mode 100644 config/rbac/_opampbridge_viewer_role.yaml create mode 100644 config/samples/_v1alpha1_opampbridge.yaml create mode 100644 controllers/builder_test.go create mode 100644 controllers/common.go create mode 100644 controllers/opampbridge_controller.go create mode 100644 controllers/opampbridge_controller_test.go delete mode 100644 controllers/opentelemetrycollector_controller_test.go create mode 100644 controllers/reconcile_test.go create mode 100644 controllers/testdata/ingress_testdata.yaml create mode 100644 controllers/testdata/test.yaml create mode 100644 controllers/testdata/test_ta_update.yaml create mode 100644 hack/check-operator-ready.go create mode 100755 hack/ignore-createdAt-bundle.sh delete mode 100755 hack/install-kubebuilder.sh delete mode 100755 hack/install-kustomize.sh create mode 100755 hack/install-prometheus-operator.sh create mode 100755 hack/install-targetallocator-prometheus-crds.sh create mode 100755 hack/modify-test-images.sh create mode 100644 internal/autodetect/main.go rename {pkg => internal}/autodetect/main_test.go (55%) rename pkg/platform/main.go => internal/autodetect/openshift/routes.go (55%) create mode 100644 internal/manifests/builder.go rename {pkg => internal/manifests}/collector/adapters/config_from.go (100%) rename {pkg => internal/manifests}/collector/adapters/config_from_test.go (91%) create mode 100644 internal/manifests/collector/adapters/config_to_ports.go rename {pkg => internal/manifests}/collector/adapters/config_to_ports_test.go (63%) rename {pkg => internal/manifests}/collector/adapters/config_to_probe.go (88%) rename {pkg => internal/manifests}/collector/adapters/config_to_probe_test.go (98%) rename {pkg => internal/manifests}/collector/adapters/config_validate.go (56%) rename {pkg => internal/manifests}/collector/adapters/config_validate_test.go (87%) rename {pkg => internal/manifests}/collector/annotations.go (91%) rename {pkg => internal/manifests}/collector/annotations_test.go (90%) create mode 100644 internal/manifests/collector/collector.go create mode 100644 internal/manifests/collector/config_replace.go create mode 100644 internal/manifests/collector/config_replace_test.go create mode 100644 internal/manifests/collector/configmap.go create mode 100644 internal/manifests/collector/configmap_test.go rename {pkg => internal/manifests}/collector/container.go (52%) rename {pkg => internal/manifests}/collector/container_test.go (50%) create mode 100644 internal/manifests/collector/daemonset.go create mode 100644 internal/manifests/collector/daemonset_test.go create mode 100644 internal/manifests/collector/deployment.go create mode 100644 internal/manifests/collector/deployment_test.go create mode 100644 internal/manifests/collector/horizontalpodautoscaler.go create mode 100644 internal/manifests/collector/horizontalpodautoscaler_test.go create mode 100644 internal/manifests/collector/ingress.go create mode 100644 internal/manifests/collector/ingress_test.go create mode 100644 internal/manifests/collector/parser/exporter/exporter.go create mode 100644 internal/manifests/collector/parser/exporter/exporter_prometheus.go create mode 100644 internal/manifests/collector/parser/parser.go rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver.go (65%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_aws-xray.go (83%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_aws-xray_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_carbon.go (83%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_carbon_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_collectd.go (83%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_collectd_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_fluent-forward.go (83%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_fluent-forward_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_generic.go (86%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_generic_test.go (64%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_influxdb.go (83%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_influxdb_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_jaeger.go (91%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_jaeger_test.go (87%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_oc.go (82%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_oc_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_otlp.go (83%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_otlp_test.go (92%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_sapm.go (82%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_sapm_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_signalfx.go (83%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_signalfx_test.go (97%) create mode 100644 internal/manifests/collector/parser/receiver/receiver_skywalking.go create mode 100644 internal/manifests/collector/parser/receiver/receiver_skywalking_test.go rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_splunk-hec.go (83%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_splunk-hec_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_statsd.go (68%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_statsd_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_test.go (82%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_wavefront.go (83%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_wavefront_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_zipkin-scribe.go (82%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_zipkin-scribe_test.go (97%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_zipkin.go (87%) rename {pkg/collector/parser => internal/manifests/collector/parser/receiver}/receiver_zipkin_test.go (97%) create mode 100644 internal/manifests/collector/poddisruptionbudget.go create mode 100644 internal/manifests/collector/poddisruptionbudget_test.go create mode 100644 internal/manifests/collector/podmonitor.go create mode 100644 internal/manifests/collector/podmonitor_test.go create mode 100644 internal/manifests/collector/route.go create mode 100644 internal/manifests/collector/route_test.go create mode 100644 internal/manifests/collector/service.go rename {pkg/collector/reconcile => internal/manifests/collector}/service_test.go (53%) rename {pkg => internal/manifests}/collector/serviceaccount.go (62%) rename {pkg => internal/manifests}/collector/serviceaccount_test.go (94%) create mode 100644 internal/manifests/collector/servicemonitor.go create mode 100644 internal/manifests/collector/servicemonitor_test.go create mode 100644 internal/manifests/collector/statefulset.go rename {pkg => internal/manifests}/collector/statefulset_test.go (57%) create mode 100644 internal/manifests/collector/suite_test.go create mode 100644 internal/manifests/collector/testdata/config_expected_targetallocator.yaml rename {pkg => internal/manifests}/collector/testdata/http_sd_config_servicemonitor_test.yaml (91%) create mode 100644 internal/manifests/collector/testdata/http_sd_config_servicemonitor_test_ta_set.yaml create mode 100644 internal/manifests/collector/testdata/http_sd_config_ta_test.yaml rename {pkg => internal/manifests}/collector/testdata/http_sd_config_test.yaml (94%) rename {pkg => internal/manifests}/collector/testdata/ingress_testdata.yaml (85%) create mode 100644 internal/manifests/collector/testdata/prometheus-exporter.yaml create mode 100644 internal/manifests/collector/testdata/relabel_config_expected_with_sd_config.yaml create mode 100644 internal/manifests/collector/testdata/relabel_config_original.yaml create mode 100644 internal/manifests/collector/testdata/route_crd.go create mode 100644 internal/manifests/collector/testdata/sm_crd.go rename {pkg => internal/manifests}/collector/testdata/test.yaml (90%) rename {pkg => internal/manifests}/collector/utils.go (93%) rename {pkg => internal/manifests}/collector/volume.go (73%) rename {pkg => internal/manifests}/collector/volume_test.go (66%) create mode 100644 internal/manifests/collector/volumeclaim.go rename {pkg => internal/manifests}/collector/volumeclaim_test.go (71%) rename {pkg/collector => internal/manifests/manifestutils}/labels.go (61%) rename {pkg/collector => internal/manifests/manifestutils}/labels_test.go (51%) create mode 100644 internal/manifests/mutate.go create mode 100644 internal/manifests/opampbridge/configmap.go create mode 100644 internal/manifests/opampbridge/configmap_test.go create mode 100644 internal/manifests/opampbridge/container.go rename {pkg/targetallocator => internal/manifests/opampbridge}/container_test.go (61%) create mode 100644 internal/manifests/opampbridge/deployment.go create mode 100644 internal/manifests/opampbridge/deployment_test.go create mode 100644 internal/manifests/opampbridge/opampbridge.go create mode 100644 internal/manifests/opampbridge/service.go create mode 100644 internal/manifests/opampbridge/serviceaccount.go create mode 100644 internal/manifests/opampbridge/serviceaccount_test.go create mode 100644 internal/manifests/opampbridge/utils.go create mode 100644 internal/manifests/opampbridge/volume.go rename pkg/collector/reconcile/opentelemetry_test.go => internal/manifests/opampbridge/volume_test.go (53%) rename {pkg/collector/reconcile => internal/manifests}/params.go (80%) create mode 100644 internal/manifests/targetallocator/adapters/config_to_prom_config.go create mode 100644 internal/manifests/targetallocator/adapters/config_to_prom_config_test.go create mode 100644 internal/manifests/targetallocator/annotations.go create mode 100644 internal/manifests/targetallocator/annotations_test.go create mode 100644 internal/manifests/targetallocator/configmap.go create mode 100644 internal/manifests/targetallocator/configmap_test.go create mode 100644 internal/manifests/targetallocator/container.go create mode 100644 internal/manifests/targetallocator/container_test.go create mode 100644 internal/manifests/targetallocator/deployment.go create mode 100644 internal/manifests/targetallocator/deployment_test.go rename {pkg => internal/manifests}/targetallocator/labels.go (83%) rename {pkg => internal/manifests}/targetallocator/labels_test.go (77%) create mode 100644 internal/manifests/targetallocator/service.go create mode 100644 internal/manifests/targetallocator/service_test.go rename {pkg => internal/manifests}/targetallocator/serviceaccount.go (71%) rename {pkg => internal/manifests}/targetallocator/serviceaccount_test.go (100%) create mode 100644 internal/manifests/targetallocator/targetallocator.go rename {pkg/collector/reconcile => internal/manifests/targetallocator/testdata}/test.yaml (73%) rename {pkg => internal/manifests}/targetallocator/volume.go (93%) rename {pkg => internal/manifests}/targetallocator/volume_test.go (89%) rename {pkg => internal}/naming/dns.go (100%) rename {pkg => internal}/naming/dns_test.go (100%) create mode 100644 internal/naming/main.go create mode 100644 internal/naming/port.go create mode 100644 internal/naming/port_test.go rename {pkg => internal}/naming/triming.go (100%) rename {pkg => internal}/naming/triming_test.go (100%) rename pkg/collector/reconcile/opentelemetry.go => internal/status/collector/collector.go (58%) create mode 100644 internal/status/collector/handle.go create mode 100644 internal/status/opampbridge/handle.go create mode 100644 internal/status/opampbridge/opampbridge.go rename internal/{webhookhandler => webhook/podmutation}/webhookhandler.go (86%) rename internal/{webhookhandler => webhook/podmutation}/webhookhandler_suite_test.go (71%) rename internal/{webhookhandler => webhook/podmutation}/webhookhandler_test.go (90%) delete mode 100644 kind-1.19.yaml delete mode 100644 kind-1.20.yaml delete mode 100644 kind-1.21.yaml delete mode 100644 kind-1.22.yaml create mode 100644 kind-1.26.yaml create mode 100644 kind-1.27.yaml create mode 100644 kind-1.28.yaml create mode 100644 kuttl-test-autoscale.yaml create mode 100644 kuttl-test-instrumentation.yaml create mode 100644 kuttl-test-multi-instr.yaml create mode 100644 kuttl-test-opampbridge.yaml create mode 100644 kuttl-test-openshift.yaml create mode 100644 kuttl-test-pdb.yaml create mode 100644 kuttl-test-prometheuscr.yaml delete mode 100644 pkg/autodetect/main.go delete mode 100644 pkg/collector/adapters/config_to_ports.go delete mode 100644 pkg/collector/daemonset.go delete mode 100644 pkg/collector/daemonset_test.go delete mode 100644 pkg/collector/deployment.go delete mode 100644 pkg/collector/deployment_test.go delete mode 100644 pkg/collector/horizontalpodautoscaler.go delete mode 100644 pkg/collector/horizontalpodautoscaler_test.go delete mode 100644 pkg/collector/reconcile/config_replace.go delete mode 100644 pkg/collector/reconcile/config_replace_test.go delete mode 100644 pkg/collector/reconcile/configmap.go delete mode 100644 pkg/collector/reconcile/configmap_test.go delete mode 100644 pkg/collector/reconcile/daemonset.go delete mode 100644 pkg/collector/reconcile/daemonset_test.go delete mode 100644 pkg/collector/reconcile/deployment.go delete mode 100644 pkg/collector/reconcile/deployment_test.go delete mode 100644 pkg/collector/reconcile/horizontalpodautoscaler.go delete mode 100644 pkg/collector/reconcile/horizontalpodautoscaler_test.go delete mode 100644 pkg/collector/reconcile/ingress.go delete mode 100644 pkg/collector/reconcile/ingress_test.go delete mode 100644 pkg/collector/reconcile/service.go delete mode 100644 pkg/collector/reconcile/serviceaccount.go delete mode 100644 pkg/collector/reconcile/serviceaccount_test.go delete mode 100644 pkg/collector/reconcile/statefulset.go delete mode 100644 pkg/collector/reconcile/statefulset_test.go delete mode 100644 pkg/collector/reconcile/suite_test.go delete mode 100644 pkg/collector/statefulset.go delete mode 100644 pkg/collector/volumeclaim.go create mode 100644 pkg/featuregate/featuregate.go create mode 100644 pkg/featuregate/featuregate_test.go create mode 100644 pkg/instrumentation/apachehttpd.go create mode 100644 pkg/instrumentation/apachehttpd_test.go create mode 100644 pkg/instrumentation/golang.go create mode 100644 pkg/instrumentation/golang_test.go create mode 100644 pkg/instrumentation/nginx.go create mode 100644 pkg/instrumentation/nginx_test.go delete mode 100644 pkg/naming/main.go delete mode 100644 pkg/targetallocator/adapters/config_to_prom_config.go delete mode 100644 pkg/targetallocator/adapters/config_to_prom_config_test.go delete mode 100644 pkg/targetallocator/container.go delete mode 100644 pkg/targetallocator/deployment.go delete mode 100644 pkg/targetallocator/deployment_test.go create mode 100644 tests/e2e-autoscale/autoscale/00-assert.yaml rename tests/{e2e => e2e-autoscale}/autoscale/00-install.yaml (61%) create mode 100644 tests/e2e-autoscale/autoscale/01-assert.yaml create mode 100644 tests/e2e-autoscale/autoscale/01-install.yaml create mode 100644 tests/e2e-autoscale/autoscale/02-assert.yaml create mode 100644 tests/e2e-autoscale/autoscale/02-install.yaml rename tests/{e2e/autoscale/02-assert.yaml => e2e-autoscale/autoscale/03-assert.yaml} (76%) create mode 100644 tests/e2e-autoscale/autoscale/03-delete.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-httpd/00-install-collector.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-httpd/00-install-instrumentation.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-httpd/01-assert.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-httpd/01-install-app.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-multicontainer/00-install-collector.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-multicontainer/00-install-instrumentation.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-multicontainer/01-assert.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-multicontainer/01-install-app.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-multicontainer/02-assert.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-apache-multicontainer/02-install-app.yaml rename tests/{e2e/instrumentation-java => e2e-instrumentation/instrumentation-dotnet-multicontainer}/00-install-collector.yaml (87%) rename tests/{e2e => e2e-instrumentation}/instrumentation-dotnet-multicontainer/00-install-instrumentation.yaml (100%) rename tests/{e2e => e2e-instrumentation}/instrumentation-dotnet-multicontainer/01-assert.yaml (72%) rename tests/{e2e => e2e-instrumentation}/instrumentation-dotnet-multicontainer/01-install-app.yaml (73%) rename tests/{e2e => e2e-instrumentation}/instrumentation-dotnet-multicontainer/02-assert.yaml (76%) rename tests/{e2e => e2e-instrumentation}/instrumentation-dotnet-multicontainer/02-install-app.yaml (73%) create mode 100644 tests/e2e-instrumentation/instrumentation-dotnet-musl/00-install-collector.yaml rename tests/{e2e/instrumentation-dotnet => e2e-instrumentation/instrumentation-dotnet-musl}/00-install-instrumentation.yaml (100%) create mode 100644 tests/e2e-instrumentation/instrumentation-dotnet-musl/01-assert.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-dotnet-musl/01-install-app.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-dotnet/00-install-collector.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-dotnet/00-install-instrumentation.yaml rename tests/{e2e => e2e-instrumentation}/instrumentation-dotnet/01-assert.yaml (63%) rename tests/{e2e => e2e-instrumentation}/instrumentation-dotnet/01-install-app.yaml (71%) rename tests/{e2e/instrumentation-nodejs-multicontainer => e2e-instrumentation/instrumentation-go}/00-install-collector.yaml (87%) create mode 100644 tests/e2e-instrumentation/instrumentation-go/00-install-instrumentation.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-go/01-add-scc.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-go/02-assert.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-go/02-install-app.yaml create mode 100755 tests/e2e-instrumentation/instrumentation-go/add-scc.sh create mode 100644 tests/e2e-instrumentation/instrumentation-go/scc.yaml rename tests/{e2e/instrumentation-nodejs => e2e-instrumentation/instrumentation-java-multicontainer}/00-install-collector.yaml (87%) rename tests/{e2e => e2e-instrumentation}/instrumentation-java-multicontainer/00-install-instrumentation.yaml (100%) rename tests/{e2e => e2e-instrumentation}/instrumentation-java-multicontainer/01-assert.yaml (86%) rename tests/{e2e => e2e-instrumentation}/instrumentation-java-multicontainer/01-install-app.yaml (82%) rename tests/{e2e => e2e-instrumentation}/instrumentation-java-multicontainer/02-assert.yaml (88%) rename tests/{e2e => e2e-instrumentation}/instrumentation-java-multicontainer/02-install-app.yaml (82%) rename tests/{e2e/instrumentation-java-other-ns/00-cleanup-other-ns.yaml => e2e-instrumentation/instrumentation-java-other-ns/01-cleanup-other-ns.yaml} (100%) create mode 100644 tests/e2e-instrumentation/instrumentation-java-other-ns/02-install-collector.yaml rename tests/{e2e/instrumentation-java-other-ns/00-install-instrumentation.yaml => e2e-instrumentation/instrumentation-java-other-ns/02-install-instrumentation.yaml} (100%) rename tests/{e2e/instrumentation-java-other-ns/01-assert.yaml => e2e-instrumentation/instrumentation-java-other-ns/03-assert.yaml} (79%) rename tests/{e2e/instrumentation-java-other-ns/01-install-app.yaml => e2e-instrumentation/instrumentation-java-other-ns/03-install-app.yaml} (72%) create mode 100644 tests/e2e-instrumentation/instrumentation-java-other-ns/04-cleanup-other-ns.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-java/00-install-collector.yaml rename tests/{e2e => e2e-instrumentation}/instrumentation-java/00-install-instrumentation.yaml (100%) rename tests/{e2e => e2e-instrumentation}/instrumentation-java/01-assert.yaml (72%) rename tests/{e2e => e2e-instrumentation}/instrumentation-java/01-install-app.yaml (71%) create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/00-install-collector.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/00-install-instrumentation.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/01-assert.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/01-install-app.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-multicontainer/00-install-collector.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-multicontainer/00-install-instrumentation.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-multicontainer/01-assert.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-multicontainer/01-install-app.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-multicontainer/02-assert.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx-multicontainer/02-install-app.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx/00-install-collector.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx/00-install-instrumentation.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx/01-assert.yaml create mode 100644 tests/e2e-instrumentation/instrumentation-nginx/01-install-app.yaml rename tests/{e2e/instrumentation-java-other-ns => e2e-instrumentation/instrumentation-nodejs-multicontainer}/00-install-collector.yaml (87%) rename tests/{e2e => e2e-instrumentation}/instrumentation-nodejs-multicontainer/00-install-instrumentation.yaml (100%) rename tests/{e2e => e2e-instrumentation}/instrumentation-nodejs-multicontainer/01-assert.yaml (83%) rename tests/{e2e => e2e-instrumentation}/instrumentation-nodejs-multicontainer/01-install-app.yaml (82%) rename tests/{e2e => e2e-instrumentation}/instrumentation-nodejs-multicontainer/02-assert.yaml (86%) rename tests/{e2e => e2e-instrumentation}/instrumentation-nodejs-multicontainer/02-install-app.yaml (81%) create mode 100644 tests/e2e-instrumentation/instrumentation-nodejs/00-install-collector.yaml rename tests/{e2e => e2e-instrumentation}/instrumentation-nodejs/00-install-instrumentation.yaml (92%) rename tests/{e2e => e2e-instrumentation}/instrumentation-nodejs/01-assert.yaml (67%) rename tests/{e2e => e2e-instrumentation}/instrumentation-nodejs/01-install-app.yaml (71%) create mode 100644 tests/e2e-instrumentation/instrumentation-python-multicontainer/00-install-collector.yaml rename tests/{e2e => e2e-instrumentation}/instrumentation-python-multicontainer/00-install-instrumentation.yaml (100%) rename tests/{e2e => e2e-instrumentation}/instrumentation-python-multicontainer/01-assert.yaml (69%) rename tests/{e2e => e2e-instrumentation}/instrumentation-python-multicontainer/01-install-app.yaml (82%) rename tests/{e2e => e2e-instrumentation}/instrumentation-python-multicontainer/02-assert.yaml (73%) rename tests/{e2e => e2e-instrumentation}/instrumentation-python-multicontainer/02-install-app.yaml (81%) create mode 100644 tests/e2e-instrumentation/instrumentation-python/00-install-collector.yaml rename tests/{e2e => e2e-instrumentation}/instrumentation-python/00-install-instrumentation.yaml (90%) rename tests/{e2e => e2e-instrumentation}/instrumentation-python/01-assert.yaml (58%) rename tests/{e2e => e2e-instrumentation}/instrumentation-python/01-install-app.yaml (71%) create mode 100644 tests/e2e-instrumentation/instrumentation-sdk/00-install-collector.yaml rename tests/{e2e => e2e-instrumentation}/instrumentation-sdk/00-install-instrumentation.yaml (100%) rename tests/{e2e => e2e-instrumentation}/instrumentation-sdk/01-assert.yaml (100%) rename tests/{e2e => e2e-instrumentation}/instrumentation-sdk/01-install-app.yaml (85%) rename tests/{e2e/instrumentation-dotnet-multicontainer => e2e-multi-instrumentation/instrumentation-multi-multicontainer}/00-install-collector.yaml (100%) create mode 100644 tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/00-install-instrumentation.yaml create mode 100644 tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml create mode 100644 tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml rename tests/{e2e/instrumentation-dotnet => e2e-multi-instrumentation/instrumentation-multi-no-containers}/00-install-collector.yaml (100%) create mode 100644 tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/00-install-instrumentation.yaml create mode 100644 tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-assert.yaml create mode 100644 tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-install-app.yaml rename tests/{e2e/instrumentation-java-multicontainer => e2e-multi-instrumentation/instrumentation-single-instr-first-container}/00-install-collector.yaml (100%) create mode 100644 tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/00-install-instrumentation.yaml create mode 100644 tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-assert.yaml create mode 100644 tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-install-app.yaml create mode 100644 tests/e2e-multi-instrumentation/manager_deployment_feature_gate.yaml create mode 100644 tests/e2e-opampbridge/opampbridge/00-assert.yaml create mode 100644 tests/e2e-opampbridge/opampbridge/00-install.yaml create mode 100644 tests/e2e-openshift/Dockerfile create mode 100644 tests/e2e-openshift/kafka/00-assert.yaml create mode 100644 tests/e2e-openshift/kafka/00-create-kafka-instance.yaml create mode 100644 tests/e2e-openshift/kafka/01-assert.yaml create mode 100644 tests/e2e-openshift/kafka/01-create-kafka-topics.yaml create mode 100644 tests/e2e-openshift/kafka/02-assert.yaml create mode 100644 tests/e2e-openshift/kafka/02-otel-kakfa-receiver.yaml create mode 100644 tests/e2e-openshift/kafka/03-assert.yaml create mode 100644 tests/e2e-openshift/kafka/03-otel-kakfa-exporter.yaml create mode 100644 tests/e2e-openshift/kafka/04-assert.yaml create mode 100644 tests/e2e-openshift/kafka/04-generate-traces.yaml create mode 100644 tests/e2e-openshift/kafka/05-assert.yaml create mode 100755 tests/e2e-openshift/kafka/check_traces.sh create mode 100644 tests/e2e-openshift/monitoring/00-assert.yaml create mode 100644 tests/e2e-openshift/monitoring/00-workload-monitoring.yaml create mode 100644 tests/e2e-openshift/monitoring/01-assert.yaml create mode 100644 tests/e2e-openshift/monitoring/01-otel-collector.yaml create mode 100644 tests/e2e-openshift/monitoring/02-assert.yaml create mode 100644 tests/e2e-openshift/monitoring/02-generate-traces.yaml create mode 100644 tests/e2e-openshift/monitoring/03-assert.yaml create mode 100755 tests/e2e-openshift/monitoring/check_metrics.sh create mode 100755 tests/e2e-openshift/monitoring/check_user_workload_monitoring.sh create mode 100644 tests/e2e-openshift/multi-cluster/00-assert.yaml create mode 100644 tests/e2e-openshift/multi-cluster/00-create-namespaces.yaml create mode 100644 tests/e2e-openshift/multi-cluster/01-assert.yaml create mode 100644 tests/e2e-openshift/multi-cluster/01-create-jaeger.yaml create mode 100644 tests/e2e-openshift/multi-cluster/02-assert.yaml create mode 100644 tests/e2e-openshift/multi-cluster/02-otlp-receiver.yaml create mode 100644 tests/e2e-openshift/multi-cluster/03-assert.yaml create mode 100644 tests/e2e-openshift/multi-cluster/03-otlp-sender.yaml create mode 100644 tests/e2e-openshift/multi-cluster/04-assert.yaml create mode 100644 tests/e2e-openshift/multi-cluster/04-generate-traces.yaml create mode 100644 tests/e2e-openshift/multi-cluster/05-assert.yaml create mode 100755 tests/e2e-openshift/multi-cluster/check_traces.sh create mode 100755 tests/e2e-openshift/multi-cluster/create_otlp_sender.sh create mode 100755 tests/e2e-openshift/multi-cluster/generate_certs.sh create mode 100644 tests/e2e-openshift/otlp-metrics-traces/00-assert.yaml create mode 100644 tests/e2e-openshift/otlp-metrics-traces/00-install-jaeger.yaml create mode 100644 tests/e2e-openshift/otlp-metrics-traces/01-assert.yaml create mode 100644 tests/e2e-openshift/otlp-metrics-traces/01-workload-monitoring.yaml create mode 100644 tests/e2e-openshift/otlp-metrics-traces/02-assert.yaml create mode 100644 tests/e2e-openshift/otlp-metrics-traces/02-otel-metrics-collector.yaml create mode 100644 tests/e2e-openshift/otlp-metrics-traces/03-assert.yaml create mode 100644 tests/e2e-openshift/otlp-metrics-traces/03-metrics-traces-gen.yaml create mode 100644 tests/e2e-openshift/otlp-metrics-traces/04-assert.yaml create mode 100644 tests/e2e-openshift/otlp-metrics-traces/05-assert.yaml create mode 100755 tests/e2e-openshift/otlp-metrics-traces/check_metrics.sh create mode 100755 tests/e2e-openshift/otlp-metrics-traces/check_traces.sh create mode 100755 tests/e2e-openshift/otlp-metrics-traces/check_user_workload_monitoring.sh create mode 100644 tests/e2e-openshift/route/00-assert.yaml create mode 100644 tests/e2e-openshift/route/00-install.yaml create mode 100644 tests/e2e-openshift/route/01-report-empty-otlphttp-spans.yaml create mode 100644 tests/e2e-pdb/pdb/00-install.yaml create mode 100644 tests/e2e-pdb/pdb/01-assert.yaml create mode 100644 tests/e2e-pdb/pdb/01-install.yaml create mode 100644 tests/e2e-prometheuscr/create-pm-prometheus-exporters/00-install.yaml create mode 100644 tests/e2e-prometheuscr/create-pm-prometheus-exporters/01-assert.yaml create mode 100644 tests/e2e-prometheuscr/create-pm-prometheus-exporters/01-install-app.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/00-install.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-assert.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-install.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-assert.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-install.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/03-assert.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/03-install.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-error.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-install.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-assert.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-error.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-install.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-assert.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-install.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/07-delete.yaml create mode 100644 tests/e2e-prometheuscr/create-sm-prometheus-exporters/07-error.yaml delete mode 100644 tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.49.0.yaml create mode 100644 tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.86.0.yaml delete mode 100644 tests/e2e/autoscale/00-assert.yaml delete mode 100644 tests/e2e/autoscale/01-assert.yaml delete mode 100644 tests/e2e/autoscale/01-install.yaml create mode 100644 tests/e2e/daemonset-features/00-add-scc.yaml rename tests/e2e/daemonset-features/{00-install.yaml => 01-install.yaml} (78%) rename tests/e2e/daemonset-features/{00-assert.yaml => 02-assert.yaml} (90%) create mode 100644 tests/e2e/daemonset-features/03-add-sa-collector.yaml create mode 100644 tests/e2e/daemonset-features/03-assert.yaml create mode 100755 tests/e2e/daemonset-features/add-sa-collector.sh create mode 100755 tests/e2e/daemonset-features/add-scc-openshift.sh create mode 100644 tests/e2e/daemonset-features/scc.yaml create mode 100644 tests/e2e/ingress-subdomains/00-assert.yaml create mode 100644 tests/e2e/ingress-subdomains/00-install.yaml create mode 100644 tests/e2e/ingress-subdomains/01-report-empty-otlphttp-spans.yaml delete mode 100644 tests/e2e/instrumentation-python-multicontainer/00-install-collector.yaml delete mode 100644 tests/e2e/instrumentation-python/00-install-collector.yaml delete mode 100644 tests/e2e/instrumentation-sdk/00-install-collector.yaml create mode 100644 tests/e2e/managed-reconcile/00-assert.yaml create mode 100644 tests/e2e/managed-reconcile/00-install.yaml create mode 100644 tests/e2e/managed-reconcile/01-assert.yaml create mode 100644 tests/e2e/managed-reconcile/01-disable-reconciliation.yaml create mode 100644 tests/e2e/managed-reconcile/02-assert.yaml create mode 100644 tests/e2e/managed-reconcile/02-enable-reconciliation.yaml create mode 100644 tests/e2e/multiple-configmaps/00-assert.yaml create mode 100644 tests/e2e/multiple-configmaps/00-install.yaml create mode 100644 tests/e2e/prometheus-config-validation/00-assert.yaml create mode 100644 tests/e2e/prometheus-config-validation/00-promreceiver-allocatorconfig.yaml create mode 100644 tests/e2e/prometheus-config-validation/01-assert.yaml create mode 100644 tests/e2e/prometheus-config-validation/01-promreceiver-labeldrop.yaml create mode 100644 tests/e2e/prometheus-config-validation/02-assert.yaml create mode 100644 tests/e2e/prometheus-config-validation/02-promreceiver-allocatorconfig-extra.yaml create mode 100644 tests/e2e/prometheus-config-validation/03-assert.yaml create mode 100644 tests/e2e/prometheus-config-validation/03-promreceiver-nopromconfig.yaml create mode 100644 tests/e2e/smoke-daemonset/00-assert.yaml create mode 100644 tests/e2e/smoke-daemonset/00-install.yaml create mode 100755 tests/e2e/smoke-daemonset/check-daemonset.sh create mode 100644 tests/e2e/smoke-init-containers/00-assert.yaml create mode 100644 tests/e2e/smoke-init-containers/00-install.yaml create mode 100644 tests/e2e/smoke-pod-labels/00-assert.yaml create mode 100644 tests/e2e/smoke-pod-labels/00-install.yaml create mode 100644 tests/e2e/smoke-sidecar-other-namespace/00-install.yaml create mode 100644 tests/e2e/smoke-sidecar-other-namespace/01-assert.yaml create mode 100644 tests/e2e/smoke-sidecar-other-namespace/01-install-app.yaml create mode 100644 tests/e2e/smoke-targetallocator/01-assert.yaml create mode 100644 tests/e2e/smoke-targetallocator/01-change-ta-config.yaml create mode 100644 tests/e2e/statefulset-features/01-assert.yaml create mode 100644 tests/e2e/statefulset-features/01-update-volume-claim-templates.yaml create mode 100644 tests/e2e/targetallocator-features/01-assert.yaml create mode 100644 tests/e2e/targetallocator-features/01-liveness.yaml create mode 100644 tests/e2e/targetallocator-prometheuscr/00-assert.yaml create mode 100644 tests/e2e/targetallocator-prometheuscr/00-install.yaml create mode 100644 tests/e2e/targetallocator-prometheuscr/01-assert.yaml create mode 100644 tests/e2e/targetallocator-prometheuscr/01-install.yaml create mode 100644 tests/instrumentation-e2e-apps/apache-httpd/Dockerfile create mode 100644 tests/instrumentation-e2e-apps/dotnet/Dockerfile create mode 100644 tests/instrumentation-e2e-apps/golang/Dockerfile create mode 100644 tests/instrumentation-e2e-apps/golang/main.go create mode 100644 tests/instrumentation-e2e-apps/java/DemoApplication.java create mode 100644 tests/instrumentation-e2e-apps/java/Dockerfile create mode 100644 tests/instrumentation-e2e-apps/java/build.gradle create mode 100644 tests/instrumentation-e2e-apps/nodejs/Dockerfile create mode 100644 tests/instrumentation-e2e-apps/nodejs/index.js create mode 100644 tests/instrumentation-e2e-apps/python/Dockerfile create mode 100644 tests/instrumentation-e2e-apps/python/app.py create mode 100644 tests/instrumentation-e2e-apps/python/requirements.txt diff --git a/.chloggen/TEMPLATE.yaml b/.chloggen/TEMPLATE.yaml new file mode 100644 index 0000000000..ca13515f1f --- /dev/null +++ b/.chloggen/TEMPLATE.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: + +# The name of the component, or a single word describing the area of concern, (e.g. operator, target allocator, github action) +component: + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: + +# One or more tracking issues related to the change +issues: [] + +# (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/ta_removehotreload.yaml b/.chloggen/ta_removehotreload.yaml new file mode 100755 index 0000000000..442d27dc3c --- /dev/null +++ b/.chloggen/ta_removehotreload.yaml @@ -0,0 +1,16 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. operator, target allocator, 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: Remove configuration hot reloading from target allocator + +# One or more tracking issues related to the change +issues: [2032] + +# (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: This only affects use of target allocator without the operator. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000000..50eae65547 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,73 @@ +name: Bug report +description: Create a report to help us improve +labels: ["bug", "needs triage"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! Please make sure to fill out the entire form below, providing as much context as you can in order to help us triage and track down your bug as quickly as possible. + + Before filing a bug, please be sure you have searched through [existing bugs](https://github.com/open-telemetry/opentelemetry-operator/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Abug) to see if an existing issue covers your bug. + - type: dropdown + id: component + attributes: + label: Component(s) + description: Which component(s) does your bug report concern? + multiple: true + options: + - operator + - collector + - instrumentation + - target allocator + - opamp bridge + - type: textarea + attributes: + label: What happened? + description: Please provide as much detail as you reasonably can. + value: | + ## Description + + ## Steps to Reproduce + + ## Expected Result + + ## Actual Result + + validations: + required: true + - type: input + attributes: + label: Kubernetes Version + description: What version did you use? (e.g., `1.23.0`) + validations: + required: true + - type: input + attributes: + label: Operator version + description: What version did you use? (e.g., `v0.4.0`, `1eb551b`, etc) + validations: + required: true + - type: input + attributes: + label: Collector version + description: What version did you use? (e.g., `v0.4.0`, `1eb551b`, etc) + validations: + required: true + - type: textarea + attributes: + label: Environment information + description: Please provide any additional information about your installation. + value: | + ## Environment + OS: (e.g., "Ubuntu 20.04") + Compiler(if manually compiled): (e.g., "go 14.2") + - type: textarea + attributes: + label: Log output + description: | + Please copy and paste any relevant log output. + render: shell + - type: textarea + attributes: + label: Additional context + description: Any additional information you think may be relevant to this issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml new file mode 100644 index 0000000000..30de7c742a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -0,0 +1,36 @@ +name: Feature request +description: Suggest an idea for this project +labels: ["enhancement", "needs triage"] +body: + - type: dropdown + id: component + attributes: + label: Component(s) + description: Which component(s) does your feature request concern? + multiple: true + options: + - operator + - collector + - instrumentation + - target allocator + - opamp bridge + - type: textarea + attributes: + label: Is your feature request related to a problem? Please describe. + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + validations: + required: true + - type: textarea + attributes: + label: Describe the solution you'd like + description: A clear and concise description of what you want to happen. + validations: + required: true + - type: textarea + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + - type: textarea + attributes: + label: Additional context + description: Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/other.yaml b/.github/ISSUE_TEMPLATE/other.yaml new file mode 100644 index 0000000000..d7be8e0b0f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other.yaml @@ -0,0 +1,22 @@ +name: Other issue +description: Create a new issue to help us improve the operator +labels: ["needs triage"] +body: + - type: dropdown + id: component + attributes: + label: Component(s) + description: Which component(s) does your issue concern? + multiple: true + options: + - operator + - collector + - instrumentation + - target allocator + - opamp bridge + - type: textarea + attributes: + label: Describe the issue you're reporting + description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + validations: + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 04278f7ed7..fa1ed9e3f4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,13 +4,88 @@ updates: directory: / schedule: interval: daily + # Create a group of dependencies to be updated together in one pull request + groups: + golang-org-x: + patterns: + - "golang.org/x/*" + prometheus: + patterns: + - "github.com/prometheus-operator/prometheus-operator" + - "github.com/prometheus-operator/prometheus-operator/*" + - "github.com/prometheus/prometheus" + - "github.com/prometheus/prometheus/*" + - "github.com/prometheus/client_go" + - "github.com/prometheus/client_go/*" + kubernetes: + patterns: + - "k8s.io/*" + - "sigs.k8s.io/*" + - package-ecosystem: gomod + directory: /cmd/operator-opamp-bridge + schedule: + interval: daily + groups: + golang-org-x: + patterns: + - "golang.org/x/*" + prometheus: + patterns: + - "github.com/prometheus-operator/prometheus-operator" + - "github.com/prometheus-operator/prometheus-operator/*" + - "github.com/prometheus/prometheus" + - "github.com/prometheus/prometheus/*" + - "github.com/prometheus/client_go" + - "github.com/prometheus/client_go/*" + kubernetes: + patterns: + - "k8s.io/*" + - "sigs.k8s.io/*" + otel: + patterns: + - go.opentelemetry.io/otel + - go.opentelemetry.io/otel/* + - package-ecosystem: gomod + directory: /cmd/otel-allocator + schedule: + interval: daily + groups: + golang-org-x: + patterns: + - "golang.org/x/*" + prometheus: + patterns: + - "github.com/prometheus-operator/prometheus-operator" + - "github.com/prometheus-operator/prometheus-operator/*" + - "github.com/prometheus/prometheus" + - "github.com/prometheus/prometheus/*" + - "github.com/prometheus/client_go" + - "github.com/prometheus/client_go/*" + kubernetes: + patterns: + - "k8s.io/*" + - "sigs.k8s.io/*" - package-ecosystem: github-actions directory: / schedule: interval: daily + groups: + gha-docker: + patterns: + - "docker/*" - package-ecosystem: docker directory: / schedule: - interval: daily \ No newline at end of file + interval: daily + + - package-ecosystem: docker + directory: /cmd/otel-allocator + schedule: + interval: daily + + - package-ecosystem: docker + directory: /cmd/operator-opamp-bridge + schedule: + interval: daily diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..23b91eeeed --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,9 @@ +**Description:** + + +**Link to tracking Issue:** + +**Testing:** + +**Documentation:** diff --git a/.github/workflows/auto-update-java-agent.yaml b/.github/workflows/auto-update-java-agent.yaml new file mode 100644 index 0000000000..c6df7768e0 --- /dev/null +++ b/.github/workflows/auto-update-java-agent.yaml @@ -0,0 +1,81 @@ +name: Auto-update Java agent + +on: + schedule: + # Daily at 01:30 (UTC) + - cron: '30 1 * * *' + workflow_dispatch: + +jobs: + check-versions: + runs-on: ubuntu-latest + outputs: + current-version: ${{ steps.check-versions.outputs.current-version }} + latest-version: ${{ steps.check-versions.outputs.latest-version }} + already-opened: ${{ steps.check-versions.outputs.already-opened }} + steps: + - uses: actions/checkout@v4 + + - id: check-versions + name: Check versions + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + current_version=$(grep -Po "[0-9]+.[0-9]+.[0-9]+" autoinstrumentation/java/version.txt) + latest_version=$(gh release view \ + --repo open-telemetry/opentelemetry-java-instrumentation \ + --json tagName \ + --jq .tagName \ + | sed 's/^v//') + + matches=$(gh pr list \ + --author opentelemetrybot \ + --state open \ + --search "in:title \"Update the OpenTelemetry Java agent version to $latest_version\"") + if [ ! -z "$matches" ] + then + already_opened=true + fi + + echo "current-version=$current_version" >> $GITHUB_OUTPUT + echo "latest-version=$latest_version" >> $GITHUB_OUTPUT + echo "already-opened=$already_opened" >> $GITHUB_OUTPUT + + update-java-agent: + runs-on: ubuntu-latest + if: | + needs.check-versions.outputs.current-version != needs.check-versions.outputs.latest-version && + needs.check-versions.outputs.already-opened != 'true' + needs: + - check-versions + steps: + - uses: actions/checkout@v4 + + - name: Update version + env: + VERSION: ${{ needs.check-versions.outputs.latest-version }} + run: | + echo $VERSION > autoinstrumentation/java/version.txt + + - name: Use CLA approved github bot + run: | + git config user.name opentelemetrybot + git config user.email 107717825+opentelemetrybot@users.noreply.github.com + + - name: Create pull request against main + if: success() || failure() + env: + VERSION: ${{ needs.check-versions.outputs.latest-version }} + # not using secrets.GITHUB_TOKEN since pull requests from that token do not run workflows + GH_TOKEN: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN }} + run: | + message="Update the OpenTelemetry Java agent version to $VERSION" + body="Update the OpenTelemetry Java agent version to \`$VERSION\`." + branch="opentelemetrybot/update-opentelemetry-java-agent-to-${VERSION}" + + git checkout -b $branch + git commit -a -m "$message" + git push --set-upstream origin $branch + gh pr create --title "$message" \ + --body "$body" \ + --base main diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml new file mode 100644 index 0000000000..844fb1f177 --- /dev/null +++ b/.github/workflows/changelog.yaml @@ -0,0 +1,80 @@ +# This action requires that any PR targeting the main branch should add a +# yaml file to the ./.chloggen/ directory. If a CHANGELOG entry is not required, +# or if performing maintance on the Changelog, add either \"[chore]\" to the title of +# the pull request or add the \"Skip Changelog\" label to disable this action. + +name: changelog + +on: + pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] + branches: + - main + +env: + # See: https://github.com/actions/cache/issues/810#issuecomment-1222550359 + # Cache downloads for this workflow consistently run in under 1 minute + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 5 + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + changelog: + runs-on: ubuntu-latest + if: ${{ !contains(github.event.pull_request.labels.*.name, 'dependencies') && !contains(github.event.pull_request.labels.*.name, 'Skip Changelog') && !contains(github.event.pull_request.title, '[chore]')}} + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: "1.20" + + - name: Ensure no changes to the CHANGELOG + run: | + if [[ $(git diff --name-only $(git merge-base origin/main ${{ github.event.pull_request.head.sha }}) ${{ github.event.pull_request.head.sha }} ./CHANGELOG.md) ]] + then + echo "The CHANGELOG should not be directly modified." + echo "Please add a .yaml file to the ./.chloggen/ directory." + echo "See CONTRIBUTING.md for more details." + echo "Alternately, add either \"[chore]\" to the title of the pull request or add the \"Skip Changelog\" label if this job should be skipped." + false + else + echo "The CHANGELOG was not modified." + fi + + - name: Ensure ./.chloggen/*.yaml addition(s) + run: | + if [[ 1 -gt $(git diff --diff-filter=A --name-only $(git merge-base origin/main ${{ github.event.pull_request.head.sha }}) ${{ github.event.pull_request.head.sha }} ./.chloggen | grep -c \\.yaml) ]] + then + echo "No changelog entry was added to the ./.chloggen/ directory." + echo "Please add a .yaml file to the ./.chloggen/ directory." + echo "See CONTRIBUTING.md for more details." + echo "Alternately, add either \"[chore]\" to the title of the pull request or add the \"Skip Changelog\" label if this job should be skipped." + false + else + echo "A changelog entry was added to the ./.chloggen/ directory." + fi + + - name: Validate ./.chloggen/*.yaml changes + run: | + make chlog-validate \ + || { echo "New ./.chloggen/*.yaml file failed validation."; exit 1; } + + # In order to validate any links in the yaml file, render the config to markdown + - name: Render .chloggen changelog entries + run: make chlog-preview > changelog_preview.md + - name: Install markdown-link-check + run: npm install -g markdown-link-check + - name: Run markdown-link-check + run: | + markdown-link-check \ + --verbose \ + --config .github/workflows/check_links_config.json \ + changelog_preview.md \ + || { echo "Check that anchor links are lowercase"; exit 1; } diff --git a/.github/workflows/check_links_config.json b/.github/workflows/check_links_config.json new file mode 100644 index 0000000000..83b164ead4 --- /dev/null +++ b/.github/workflows/check_links_config.json @@ -0,0 +1,17 @@ +{ + "ignorePatterns": [ + { + "pattern": "http(s)?://\\d+\\.\\d+\\.\\d+\\.\\d+" + }, + { + "pattern": "http(s)?://localhost" + }, + { + "pattern": "http(s)?://example.com" + }, + { + "pattern": "^#" + } + ], + "aliveStatusCodes": [429, 200] +} \ No newline at end of file diff --git a/.github/workflows/continuous-integration.yaml b/.github/workflows/continuous-integration.yaml index 2b5a9437b0..c5259c0c0a 100644 --- a/.github/workflows/continuous-integration.yaml +++ b/.github/workflows/continuous-integration.yaml @@ -6,55 +6,56 @@ on: pull_request: branches: [ main ] +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: unit-tests: name: Unit tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: "~1.21.1" - name: Check out code into the Go module directory - uses: actions/checkout@v3 - - - name: "install kubebuilder" - run: ./hack/install-kubebuilder.sh - - - name: "install kustomize" - run: ./hack/install-kustomize.sh + uses: actions/checkout@v4 - name: "basic checks" run: make ci lint: name: Code standards (linting) - runs-on: ubuntu-20.04 - strategy: - matrix: - workdir: [".", "./cmd/otel-allocator"] + runs-on: ubuntu-22.04 steps: - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: "~1.21.1" - name: Check out code into the Go module directory - uses: actions/checkout@v3 + uses: actions/checkout@v4 - - name: Lint - uses: golangci/golangci-lint-action@v3 + - uses: actions/cache@v3 with: - args: -v - version: v1.48 - working-directory: ${{ matrix.workdir }} + path: | + /home/runner/.cache/golangci-lint + /home/runner/go/pkg/mod + ./bin + key: golangcilint-${{ hashFiles('**/go.sum') }} + restore-keys: | + golangcilint- + + - name: Lint + run: make lint security: name: Security - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Check out code into the Go module directory - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 9ac9ffc4e7..0ab9811ad4 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -6,28 +6,50 @@ on: pull_request: branches: [ main ] +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: e2e-tests: name: End-to-end tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: + fail-fast: false matrix: # The e2e tests are run on the lowest and highest supported k8s version. # All Kubernetes version in between expose the same APIs, hence the operator # should be compatible with them. kube-version: - - "1.19" - - "1.25" + - "1.23" + - "1.28" + group: + - e2e + - e2e-instrumentation + - e2e-upgrade + - e2e-prometheuscr + - e2e-autoscale + - e2e-multi-instrumentation + - e2e-pdb + - e2e-opampbridge steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: "~1.21.3" + cache-dependency-path: | + go.sum + cmd/otel-allocator/go.sum + cmd/operator-opamp-bridge/go.sum - - name: Check out code into the Go module directory - uses: actions/checkout@v3 + - name: Setup kind + env: + KIND_VERSION: "0.20.0" + run: go install sigs.k8s.io/kind@v${KIND_VERSION} - name: "install kuttl" run: ./hack/install-kuttl.sh @@ -35,10 +57,27 @@ jobs: - name: "run tests" env: KUBE_VERSION: ${{ matrix.kube-version }} - run: make prepare-e2e e2e e2e-upgrade KUBE_VERSION=$KUBE_VERSION + run: make prepare-e2e ${{ matrix.group }} KUBE_VERSION=$KUBE_VERSION VERSION=e2e - name: "log operator if failed" if: ${{ failure() }} env: KUBE_VERSION: ${{ matrix.kube-version }} run: make e2e-log-operator KUBE_VERSION=$KUBE_VERSION + + e2e-tests-check: + runs-on: ubuntu-22.04 + if: always() + needs: [e2e-tests] + steps: + - name: Print result + run: echo ${{ needs.e2e-tests.result }} + - name: Interpret result + run: | + if [[ success == ${{ needs.e2e-tests.result }} ]] + then + echo "All matrix jobs passed!" + else + echo "One or more matrix jobs failed." + false + fi diff --git a/.github/workflows/publish-autoinstrumentation-apache-httpd.yaml b/.github/workflows/publish-autoinstrumentation-apache-httpd.yaml new file mode 100644 index 0000000000..53de1e159a --- /dev/null +++ b/.github/workflows/publish-autoinstrumentation-apache-httpd.yaml @@ -0,0 +1,69 @@ +name: "Publish Apache HTTPD Auto-Instrumentation" + +on: + push: + paths: + - 'autoinstrumentation/apache-httpd/**' + - '.github/workflows/publish-autoinstrumentation-apache-httpd.yaml' + branches: + - main + pull_request: + paths: + - 'autoinstrumentation/apache-httpd/**' + - '.github/workflows/publish-autoinstrumentation-apache-httpd.yaml' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + publish: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v4 + + - name: Read version + run: echo "VERSION=$(cat autoinstrumentation/apache-httpd/version.txt)" >> $GITHUB_ENV + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-apache-httpd + tags: | + type=match,pattern=v(.*),group=1,value=v${{ env.VERSION }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Login to GitHub Package Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: autoinstrumentation/apache-httpd + platforms: linux/amd64 + push: ${{ github.event_name == 'push' }} + build-args: version=${{ env.VERSION }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache diff --git a/.github/workflows/publish-autoinstrumentation-dotnet.yaml b/.github/workflows/publish-autoinstrumentation-dotnet.yaml index 6d56f375c0..27c194937c 100644 --- a/.github/workflows/publish-autoinstrumentation-dotnet.yaml +++ b/.github/workflows/publish-autoinstrumentation-dotnet.yaml @@ -13,29 +13,35 @@ on: - '.github/workflows/publish-autoinstrumentation-dotnet.yaml' workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: publish: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Read version run: echo "VERSION=$(cat autoinstrumentation/dotnet/version.txt)" >> $GITHUB_ENV - name: Docker meta id: meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v5 with: - images: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-dotnet + images: | + otel/autoinstrumentation-dotnet + ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-dotnet tags: | type=match,pattern=v(.*),group=1,value=v${{ env.VERSION }} - name: Set up QEMU - uses: docker/setup-qemu-action@v2.1.0 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2.2.0 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers uses: actions/cache@v3 @@ -45,15 +51,23 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- + - name: Log into Docker.io + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to GitHub Package Registry - uses: docker/login-action@v2.1.0 + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v5 with: context: autoinstrumentation/dotnet platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/publish-autoinstrumentation-e2e-images.yaml b/.github/workflows/publish-autoinstrumentation-e2e-images.yaml new file mode 100644 index 0000000000..b911d44ab2 --- /dev/null +++ b/.github/workflows/publish-autoinstrumentation-e2e-images.yaml @@ -0,0 +1,50 @@ +name: "Publish instrumentation E2E images" + +on: + push: + paths: + - 'tests/instrumentation-e2e-apps/**' + - '.github/workflows/publish-autoinstrumentation-e2e-images.yaml' + branches: + - main + pull_request: + paths: + - 'tests/instrumentation-e2e-apps/**' + - '.github/workflows/publish-autoinstrumentation-e2e-images.yaml' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + golang: + uses: ./.github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml + with: + language: golang + platforms: linux/arm64,linux/amd64,linux/s390x,linux/ppc64le + python: + uses: ./.github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml + with: + language: python + platforms: linux/arm64,linux/amd64,linux/s390x,linux/ppc64le + java: + uses: ./.github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml + with: + language: java + platforms: linux/arm64,linux/amd64,linux/s390x,linux/ppc64le + apache-httpd: + uses: ./.github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml + with: + language: apache-httpd + platforms: linux/arm64,linux/amd64,linux/s390x,linux/ppc64le + dotnet: + uses: ./.github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml + with: + language: dotnet + platforms: linux/arm64,linux/amd64 + nodejs: + uses: ./.github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml + with: + language: nodejs + platforms: linux/arm64,linux/amd64,linux/s390x,linux/ppc64le diff --git a/.github/workflows/publish-autoinstrumentation-java.yaml b/.github/workflows/publish-autoinstrumentation-java.yaml index a9fcc4ba06..10a129d02d 100644 --- a/.github/workflows/publish-autoinstrumentation-java.yaml +++ b/.github/workflows/publish-autoinstrumentation-java.yaml @@ -13,29 +13,36 @@ on: - '.github/workflows/publish-autoinstrumentation-java.yaml' workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + + jobs: publish: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Read version run: echo "VERSION=$(cat autoinstrumentation/java/version.txt)" >> $GITHUB_ENV - name: Docker meta id: meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v5 with: - images: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java + images: | + otel/autoinstrumentation-java + ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java tags: | type=match,pattern=v(.*),group=1,value=v${{ env.VERSION }} - name: Set up QEMU - uses: docker/setup-qemu-action@v2.1.0 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2.2.0 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers uses: actions/cache@v3 @@ -45,18 +52,26 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- + - name: Log into Docker.io + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to GitHub Package Registry - uses: docker/login-action@v2.1.0 + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v5 with: context: autoinstrumentation/java - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le push: ${{ github.event_name == 'push' }} build-args: version=${{ env.VERSION }} tags: ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/publish-autoinstrumentation-nodejs.yaml b/.github/workflows/publish-autoinstrumentation-nodejs.yaml index 89b1cc7cbc..1fea43e542 100644 --- a/.github/workflows/publish-autoinstrumentation-nodejs.yaml +++ b/.github/workflows/publish-autoinstrumentation-nodejs.yaml @@ -13,29 +13,36 @@ on: - '.github/workflows/publish-autoinstrumentation-nodejs.yaml' workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + + jobs: publish: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Read version run: echo VERSION=$(cat autoinstrumentation/nodejs/package.json | jq -r '.dependencies."@opentelemetry/sdk-node"') >> $GITHUB_ENV - name: Docker meta id: meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v5 with: - images: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs + images: | + otel/autoinstrumentation-nodejs + ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs tags: | type=match,pattern=v(.*),group=1,value=v${{ env.VERSION }} - name: Set up QEMU - uses: docker/setup-qemu-action@v2.1.0 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2.2.0 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers uses: actions/cache@v3 @@ -45,15 +52,23 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- + - name: Log into Docker.io + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to GitHub Package Registry - uses: docker/login-action@v2.1.0 + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v5 with: context: autoinstrumentation/nodejs platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/publish-autoinstrumentation-python.yaml b/.github/workflows/publish-autoinstrumentation-python.yaml index 9d134c954c..0239e37fbc 100644 --- a/.github/workflows/publish-autoinstrumentation-python.yaml +++ b/.github/workflows/publish-autoinstrumentation-python.yaml @@ -13,29 +13,36 @@ on: - '.github/workflows/publish-autoinstrumentation-python.yaml' workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + + jobs: publish: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Read version run: echo VERSION=$(head -n 1 autoinstrumentation/python/requirements.txt | cut -d '=' -f3) >> $GITHUB_ENV - name: Docker meta id: meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v5 with: - images: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python + images: | + otel/autoinstrumentation-python + ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python tags: | type=match,pattern=v(.*),group=1,value=v${{ env.VERSION }} - name: Set up QEMU - uses: docker/setup-qemu-action@v2.1.0 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2.2.0 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers uses: actions/cache@v3 @@ -45,18 +52,26 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- + - name: Log into Docker.io + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to GitHub Package Registry - uses: docker/login-action@v2.1.0 + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v5 with: context: autoinstrumentation/python - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le push: ${{ github.event_name == 'push' }} build-args: version=${{ env.VERSION }} tags: ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/publish-images.yaml b/.github/workflows/publish-images.yaml index 35b4255c39..9c82f1c7be 100644 --- a/.github/workflows/publish-images.yaml +++ b/.github/workflows/publish-images.yaml @@ -7,12 +7,19 @@ on: workflow_dispatch: +env: + PLATFORMS: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le + jobs: publish: name: Publish container images - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: '~1.21.3' - name: Unshallow run: git fetch --prune --unshallow @@ -24,18 +31,32 @@ jobs: run: | grep -v '\#' versions.txt | grep opentelemetry-collector | awk -F= '{print "OTELCOL_VERSION="$2}' >> $GITHUB_ENV grep -v '\#' versions.txt | grep targetallocator | awk -F= '{print "TARGETALLOCATOR_VERSION="$2}' >> $GITHUB_ENV + grep -v '\#' versions.txt | grep operator-opamp-bridge | awk -F= '{print "OPERATOR_OPAMP_BRIDGE_VERSION="$2}' >> $GITHUB_ENV grep -v '\#' versions.txt | grep autoinstrumentation-java | awk -F= '{print "AUTO_INSTRUMENTATION_JAVA_VERSION="$2}' >> $GITHUB_ENV grep -v '\#' versions.txt | grep autoinstrumentation-nodejs | awk -F= '{print "AUTO_INSTRUMENTATION_NODEJS_VERSION="$2}' >> $GITHUB_ENV grep -v '\#' versions.txt | grep autoinstrumentation-python | awk -F= '{print "AUTO_INSTRUMENTATION_PYTHON_VERSION="$2}' >> $GITHUB_ENV grep -v '\#' versions.txt | grep autoinstrumentation-dotnet | awk -F= '{print "AUTO_INSTRUMENTATION_DOTNET_VERSION="$2}' >> $GITHUB_ENV + grep -v '\#' versions.txt | grep autoinstrumentation-go | awk -F= '{print "AUTO_INSTRUMENTATION_GO_VERSION="$2}' >> $GITHUB_ENV + grep -v '\#' versions.txt | grep autoinstrumentation-apache-httpd | awk -F= '{print "AUTO_INSTRUMENTATION_APACHE_HTTPD_VERSION="$2}' >> $GITHUB_ENV + grep -v '\#' versions.txt | grep autoinstrumentation-apache-httpd | awk -F= '{print "AUTO_INSTRUMENTATION_NGINX_VERSION="$2}' >> $GITHUB_ENV echo "VERSION_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV echo "VERSION=$(git describe --tags | sed 's/^v//')" >> $GITHUB_ENV - + + - name: Build the binary for each supported architecture + run: | + for platform in $(echo $PLATFORMS | tr "," "\n"); do + arch=${platform#*/} + echo "Building manager for $arch" + make manager ARCH=$arch + done + - name: Docker meta id: docker_meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v5 with: - images: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator + images: | + otel/opentelemetry-operator + ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} @@ -43,10 +64,10 @@ jobs: type=ref,event=branch - name: Set up QEMU - uses: docker/setup-qemu-action@v2.1.0 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2.2.0 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers uses: actions/cache@v3 @@ -56,31 +77,29 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- + - name: Log into Docker.io + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to GitHub Package Registry - uses: docker/login-action@v2.1.0 + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Operator image - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile - platforms: linux/amd64,linux/arm64 + platforms: ${{ env.PLATFORMS }} push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.docker_meta.outputs.tags }} labels: ${{ steps.docker_meta.outputs.labels }} - build-args: | - VERSION_PKG=github.com/open-telemetry/opentelemetry-operator/internal/version - VERSION=${{ env.VERSION }} - VERSION_DATE=${{ env.VERSION_DATE }} - OTELCOL_VERSION=${{ env.OTELCOL_VERSION }} - TARGETALLOCATOR_VERSION=${{ env.TARGETALLOCATOR_VERSION }} - AUTO_INSTRUMENTATION_JAVA_VERSION=${{ env.AUTO_INSTRUMENTATION_JAVA_VERSION }} - AUTO_INSTRUMENTATION_NODEJS_VERSION=${{ env.AUTO_INSTRUMENTATION_NODEJS_VERSION }} - AUTO_INSTRUMENTATION_PYTHON_VERSION=${{ env.AUTO_INSTRUMENTATION_PYTHON_VERSION }} - AUTO_INSTRUMENTATION_DOTNET_VERSION=${{ env.AUTO_INSTRUMENTATION_DOTNET_VERSION }} cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache diff --git a/.github/workflows/publish-operator-bundle.yaml b/.github/workflows/publish-operator-bundle.yaml new file mode 100644 index 0000000000..27896351a0 --- /dev/null +++ b/.github/workflows/publish-operator-bundle.yaml @@ -0,0 +1,79 @@ +name: "Publish operator bundle" + +on: + push: + paths: + - '.github/workflows/publish-operator-bundle.yaml' + - 'bundle.Dockerfile' + - 'bundle/**' + branches: + - main + tags: + - 'v*' + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + publish: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v4 + + - name: Docker meta + id: docker_meta + uses: docker/metadata-action@v5 + with: + images: | + otel/opentelemetry-operator-bundle + ghcr.io/open-telemetry/opentelemetry-operator/operator-bundle + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{raw}} + type=ref,event=branch + + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Log into Docker.io + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to GitHub Package Registry + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Operator image + uses: docker/build-push-action@v5 + with: + context: . + file: ./bundle.Dockerfile + platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache diff --git a/.github/workflows/publish-operator-opamp-bridge.yaml b/.github/workflows/publish-operator-opamp-bridge.yaml new file mode 100644 index 0000000000..f8ff72f01f --- /dev/null +++ b/.github/workflows/publish-operator-opamp-bridge.yaml @@ -0,0 +1,94 @@ +name: "Publish Operator OpAMP Bridge" + +on: + push: + paths: + - 'cmd/operator-opamp-bridge/**' + - '.github/workflows/publish-operator-opamp-bridge.yaml' + branches: + - main + tags: + - 'v*' + workflow_dispatch: + +env: + PLATFORMS: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le + +jobs: + publish: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: '~1.21.3' + cache-dependency-path: 'cmd/operator-opamp-bridge/go.sum' + + # TODO: We're currently not using this. Should we? + - name: Read version + run: | + echo "VERSION=$(git describe --tags | sed 's/^v//')" >> $GITHUB_ENV + echo "VERSION_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV + + - name: Build the binary for each supported architecture + run: | + for platform in $(echo $PLATFORMS | tr "," "\n"); do + arch=${platform#*/} + echo "Building operator-opamp-bridge for $arch" + make operator-opamp-bridge ARCH=$arch + done + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + otel/operator-opamp-bridge + ghcr.io/open-telemetry/opentelemetry-operator/operator-opamp-bridge + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{raw}} + type=ref,event=branch + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Log into Docker.io + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to GitHub Package Registry + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: cmd/operator-opamp-bridge + platforms: ${{ env.PLATFORMS }} + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache diff --git a/.github/workflows/publish-target-allocator.yaml b/.github/workflows/publish-target-allocator.yaml index cc6e4a148d..99a7aa0da6 100644 --- a/.github/workflows/publish-target-allocator.yaml +++ b/.github/workflows/publish-target-allocator.yaml @@ -11,29 +11,53 @@ on: - 'v*' workflow_dispatch: +env: + PLATFORMS: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le + jobs: publish: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: '~1.21.3' + cache-dependency-path: 'cmd/otel-allocator/go.sum' + # TODO: We're currently not using this. Should we? - name: Read version - run: grep -v '\#' versions.txt | grep targetallocator | awk -F= '{print "VERSION="$2}' >> $GITHUB_ENV + run: | + echo "VERSION=$(git describe --tags | sed 's/^v//')" >> $GITHUB_ENV + echo "VERSION_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV + + - name: Build the binary for each supported architecture + run: | + for platform in $(echo $PLATFORMS | tr "," "\n"); do + arch=${platform#*/} + echo "Building target allocator for $arch" + make targetallocator ARCH=$arch + done - name: Docker meta id: meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v5 with: - images: ghcr.io/open-telemetry/opentelemetry-operator/target-allocator + images: | + otel/target-allocator + ghcr.io/open-telemetry/opentelemetry-operator/target-allocator tags: | - type=match,pattern=v(.*),group=1,value=v${{ env.VERSION }} + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{raw}} + type=ref,event=branch - name: Set up QEMU - uses: docker/setup-qemu-action@v2.1.0 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2.2.0 + uses: docker/setup-buildx-action@v3 - name: Cache Docker layers uses: actions/cache@v3 @@ -43,20 +67,27 @@ jobs: restore-keys: | ${{ runner.os }}-buildx- + - name: Log into Docker.io + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to GitHub Package Registry - uses: docker/login-action@v2.1.0 + uses: docker/login-action@v3 + if: ${{ github.event_name == 'push' }} with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v5 with: context: cmd/otel-allocator - platforms: linux/amd64,linux/arm64 + platforms: ${{ env.PLATFORMS }} push: true - build-args: version=${{ env.VERSION }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=local,src=/tmp/.buildx-cache diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d84b0272d6..735c6bf4be 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -5,20 +5,14 @@ on: jobs: release: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: "~1.21.1" - - uses: actions/checkout@v3 - - - name: "install kubebuilder" - run: ./hack/install-kubebuilder.sh - - - name: "install kustomize" - run: ./hack/install-kustomize.sh + - uses: actions/checkout@v4 - name: "generate release resources" run: make release-artifacts IMG_PREFIX="ghcr.io/open-telemetry/opentelemetry-operator" @@ -32,3 +26,21 @@ jobs: run: | OPERATOR_VERSION=$(git describe --tags) curl https://proxy.golang.org/github.com/open-telemetry/opentelemetry-operator/@v/${OPERATOR_VERSION}.info + + operator-hub-prod-release: + needs: release + uses: ./.github/workflows/reusable-operator-hub-release.yaml + with: + org: redhat-openshift-ecosystem + repo: community-operators-prod + secrets: + OPENTELEMETRYBOT_GITHUB_TOKEN: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN }} + + operator-hub-community-release: + needs: release + uses: ./.github/workflows/reusable-operator-hub-release.yaml + with: + org: k8s-operatorhub + repo: community-operators + secrets: + OPENTELEMETRYBOT_GITHUB_TOKEN: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN }} diff --git a/.github/workflows/reusable-operator-hub-release.yaml b/.github/workflows/reusable-operator-hub-release.yaml new file mode 100644 index 0000000000..e6e90f7445 --- /dev/null +++ b/.github/workflows/reusable-operator-hub-release.yaml @@ -0,0 +1,88 @@ +name: Reusable - Create operator hub pull request + +on: + workflow_call: + inputs: + org: + type: string + required: true + repo: + type: string + required: true + secrets: + OPENTELEMETRYBOT_GITHUB_TOKEN: + required: true + +jobs: + create-operator-pull-request: + runs-on: ubuntu-latest + steps: + - name: Set version as env variable + env: + TAG: ${{ github.ref_name }} + run: | + echo $TAG + TAG=${TAG:1} # remove v (prefix) + echo version=${TAG} >> $GITHUB_ENV # update GitHub ENV vars + + - name: Sync fork + env: + GH_TOKEN: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN }} + run: | + # synchronizing the fork is fast, and avoids the need to fetch the full upstream repo + # (fetching the upstream repo with "--depth 1" would lead to "shallow update not allowed" + # error when pushing back to the origin repo) + gh repo sync opentelemetrybot/${{ inputs.repo }} \ + --source ${{ inputs.org }}/${{ inputs.repo }} \ + --force + + - name: Checkout operatorhub repo + uses: actions/checkout@v4 + with: + repository: opentelemetrybot/${{ inputs.repo }} + token: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN }} + + - name: Checkout opentelemetry-operator to tmp/ directory + uses: actions/checkout@v4 + with: + repository: open-telemetry/opentelemetry-operator + token: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN }} + path: tmp/ + + - name: Update version + env: + VERSION: ${{ env.version }} + run: | + mkdir operators/opentelemetry-operator/${VERSION} + cp -R ./tmp/bundle/* operators/opentelemetry-operator/${VERSION} + rm -rf ./tmp + + - name: Use CLA approved github bot + run: | + git config user.name opentelemetrybot + git config user.email 107717825+opentelemetrybot@users.noreply.github.com + + - name: Create pull request against ${{ inputs.org }}/${{ inputs.repo }} + env: + VERSION: ${{ env.version }} + GH_TOKEN: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN }} + run: | + message="Update the opentelemetry to $VERSION" + body="Release opentelemetry-operator \`$VERSION\`. + + cc @pavolloffay @frzifus @yuriolisa @jaronoff97 @VineethReddy02 @TylerHelmuth @swiatekm-sumo + " + branch="update-opentelemetry-operator-to-${VERSION}" + + # gh pr create doesn't have a way to explicitly specify different head and base + # repositories currently, but it will implicitly pick up the head from a different + # repository if you set up a tracking branch + + git checkout -b $branch + git add -A + git commit -s -m "$message" + git push -f --set-upstream origin $branch + gh pr create --title "$message" \ + --body "$body" \ + --repo ${{ inputs.org }}/${{ inputs.repo }} \ + --base main diff --git a/.github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml b/.github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml new file mode 100644 index 0000000000..cfe6268864 --- /dev/null +++ b/.github/workflows/reusable-publish-autoinstrumentation-e2e-images.yaml @@ -0,0 +1,58 @@ +name: Reusable - Publish autoinstrumentation E2E images + +on: + workflow_call: + inputs: + language: + type: string + required: true + platforms: + type: string + required: true + +jobs: + publish-e2e-image: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-${{ inputs.language }} + tags: | + type=ref,event=branch + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Login to GitHub Package Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + tags: ${{ steps.meta.outputs.tags }} + context: tests/instrumentation-e2e-apps/${{ inputs.language }} + platforms: ${{ inputs.platforms }} + push: ${{ github.event_name == 'push' }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index fa011fe7f3..95339d67e9 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -6,25 +6,34 @@ on: pull_request: branches: [ main ] +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + jobs: scorecard-tests: name: test on k8s - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: kube-version: - - "1.19" - - "1.25" + - "1.23" + - "1.28" steps: - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.19 + go-version: "~1.21.1" + + - name: Setup kind + env: + KIND_VERSION: "0.20.0" + run: go install sigs.k8s.io/kind@v${KIND_VERSION} - name: Check out code into the Go module directory - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: "install kuttl and kind" run: ./hack/install-kuttl.sh @@ -39,3 +48,20 @@ jobs: - name: "run scorecard test" run: make scorecard-tests + + scorecard-tests-check: + runs-on: ubuntu-22.04 + if: always() + needs: [scorecard-tests] + steps: + - name: Print result + run: echo ${{ needs.scorecard-tests.result }} + - name: Interpret result + run: | + if [[ success == ${{ needs.scorecard-tests.result }} ]] + then + echo "All matrix jobs passed!" + else + echo "One or more matrix jobs failed." + false + fi diff --git a/.gitignore b/.gitignore index 6b11e10253..88abe862c0 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.dylib bin vendor +.DS_Store # Test binary, build with `go test -c` *.test diff --git a/.golangci.yaml b/.golangci.yaml index f1a7317c86..b644373c03 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,5 +1,8 @@ run: + concurrency: 4 timeout: 5m + issues-exit-code: 1 + tests: true # all available settings of specific linters linters-settings: @@ -11,8 +14,43 @@ linters-settings: suggest-new: true misspell: locale: US + ignore-words: + - cancelled + - metre + - meter + - metres + - kilometre + - kilometres govet: - disable-all: true + # report about shadowed variables + check-shadowing: true + + # settings per analyzer + settings: + printf: # analyzer name, run `go tool vet help` to see all analyzers + funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf + - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf + + enable-all: true + # TODO: Enable this and fix the alignment issues. + disable: + - fieldalignment + gofmt: + simplify: true + revive: + min-confidence: 0.8 + + depguard: + rules: + main: + deny: + - pkg: sync/atomic + desc: "Use go.uber.org/atomic instead of sync/atomic" + - pkg: github.com/pkg/errors + desc: "Use 'errors' or 'fmt' instead of github.com/pkg/errors" linters: enable: @@ -24,3 +62,12 @@ linters: - exhaustive - godot - unparam + - gosimple + - unused + - staticcheck + - ineffassign + - typecheck + - unparam + - depguard + - errcheck + - errorlint diff --git a/CHANGELOG.md b/CHANGELOG.md index 315101cdd3..d8eeda0bbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,745 @@ Changes by Version ================== + + +## 0.90.0 + +### 💡 Enhancements 💡 + +- `autoinstrumentation`: Bump OpenTelemetry .NET Automatic Instrumentation to 1.2.0 (#2382) +- `operator`: add liveness probe to target allocator deployment generation (#2258) +- `operator`: added reconciliation errors for CRD events (#1972) +- `operator`: removes the old way of running autodetection for openshift routes being available (#2108) +- `bridge`: adds request headers to the opamp bridge config (#2410) +- `bridge`: adds Headers to opamp bridge spec and configmap generation (#2410) +- `operator`: Create PodMonitor when deploying collector in sidecar mode and Prometheus exporters are used. (#2306) +- `operator`: add readiness probe to target allocator deployment generation (#2258) +- `target allocator`: add readyz endpoint to TA (#2258) +- `target allocator`: add target allocator securityContext configuration (#2397) +- `target allocator`: Use only target address for allocation in consistent-hashing strategy (#2280) + +### 🧰 Bug fixes 🧰 + +- `operator`: fixes ability to do a foreground cascading delete (#2364) +- `operator`: fix error logging in collector container creation (#2420) +- `operator`: lifecycle spec removed from cloned initContainer (#2366) +- `operator`: add missing pod in the rbac (#1679) +- `operator`: check if service account specified in otelcol before creating service account resource for collectors (#2372) +- `target allocator`: Save targets discovered before collector instances come up (#2350) + +### Components + +* [OpenTelemetry Collector - v0.90.1](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.90.1) +* [OpenTelemetry Contrib - v0.90.1](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.90.1) +* [Java auto-instrumentation - 1.32.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.32.0) +* [.NET auto-instrumentation - 1.2.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/1.2.0) +* [Node.JS - 0.44.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-0.44.0) +* [Python - 0.41b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/0.41b0) +* [Go - v0.8.0-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.8.0-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) +* [Nginx - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3)] + +## 0.89.0 + +### 🛑 Breaking changes 🛑 + +- `autoinstrumentation`: Bump Go auto instrumentation version to v0.8.0-alpha (#2358) + The default export protocol was switched from `grpc` to `http/proto` +- `target allocator`: Disable configuration hot reload (#2032) + This feature can be re-enabled by passing the --reload-config flag to the target allocator. + However, this is deprecated and will be removed in an upcoming release. + +### 💡 Enhancements 💡 + +- `target allocator`: add healthcheck endpoint to TA (#2258) +- `OpAMP Bridge`: Sends a heartbeat from the bridge and brings the annotation to spec. (#2132) +- `operator`: Added updateStrategy for DaemonSet mode. (#2107) +- `operator`: add target allocator affinity configuration (#2263) +- `Operator`: Added the service.instance.id as the pod.UID into the traces resource Env. (#1921) +- `operator`: Support configuring images via RELATED_IMAGE_ environment variables (#2326) +- `target allocator`: Declare and use ContainerPort for Target Allocator (#2312) +- `target allocator`: Add logging for prometheus operator in TargetAllocator's config generator (#2348) + +### 🧰 Bug fixes 🧰 + +- `target allocator`: Update file watcher to detect file write events (#2349) +- `target allocator`: Run the target allocator as a non-root user (#738) + Some Kubernetes configurations do not allow running images as root, so + provide a non-zero UID in the Docker image. + +- `operator`: Truncate `sidecar.opentelemetry.io/injected` sidecar pod label to 63 characters (#1031) + +### Components + +* [OpenTelemetry Collector - v0.89.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.89.0) +* [OpenTelemetry Contrib - v0.89.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.89.0) +* [Java auto-instrumentation - 1.31.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.31.0) +* [.NET auto-instrumentation - 1.1.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/1.1.0) +* [Node.JS - 0.44.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-0.44.0) +* [Python - 0.41b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/0.41b0) +* [Go - v0.8.0-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.8.0-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) +* [Nginx - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3)] + + +## 0.88.0 + +### 🛑 Breaking changes 🛑 + +- `OpAMP Bridge`: Currently, the bridge doesn't adhere to the spec for the naming structure. This changes the bridge to use the / structure as described. (#2131) + * Updates the bridge to get collectors using the reporting annotation + * Fixes a bug where we were using the incorrect structure for the collectors + + +### 💡 Enhancements 💡 + +- `operator-opamp-bridge`: Creates the CRD for the OpAMPBridge resource (#1368) +- `autoinstrumentation`: Bump OpenTelemetry .NET Automatic Instrumentation to 1.1.0 (#2252) +- `operator`: Bump NodeJS dependencies. Also, increase the size of the default size for the volume used to copy the autoinstrumentation libraries from 150M to 200M (#2240, #2237) + +### 🧰 Bug fixes 🧰 + +- `Operator`: Fixed the labeling process which was broken at the moment to capture the current image tag when the users set the sha256 reference. (#1982) +- `target allocator`: reset kubeconfig to empty string when using in-cluster config (#2262) + +### Components + +* [OpenTelemetry Collector - v0.88.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.88.0) +* [OpenTelemetry Contrib - v0.88.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.88.0) +* [Java auto-instrumentation - 1.31.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.31.0) +* [.NET auto-instrumentation - 1.1.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/1.1.0) +* [Node.JS - 0.44.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-0.44.0) +* [Python - 0.41b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/0.41b0) +* [Go - v0.7.0-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.7.0-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) +* [Nginx - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3)] + +## 0.87.0 + +### 🛑 Breaking changes 🛑 + +- `OpAMP Bridge`: This PR simplifies the bridge's configuration and logging by renaming and removing fields. (#1368) + `components_allowed` => `componentsAllowed` + :x: `protocol` which is now inferred from endpoint + capabilities `[]string` => `map[Capability]bool` for enhanced configuration validation +- `operator`: Enable Target Allocator Rewrite by default (#2208) + See [the documentation](/README.md#target-allocator) for details. + Use the `--feature-gates=-operator.collector.rewritetargetallocator` command line option to switch back to the old behaviour. + + +### 💡 Enhancements 💡 + +- `operator`: updating the operator to use the Collector's debug exporter in replacement of the deprecated logging exporter (#2130) +- `operator`: Publish operator images for I IBM P/Z (linux/s390x,linux/ppc64le) architectures. (#2215) +- `Documentation`: Add diagrams to Target Allocator Readme. (#2229) +- `target allocator`: Add rate limiting for scrape config updates (#1544) + +### 🧰 Bug fixes 🧰 + +- `operator`: Set the security context for the init containers of the Apache HTTPD instrumentation (#2050) + +### Components + +* [OpenTelemetry Collector - v0.87.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.87.0) +* [OpenTelemetry Contrib - v0.87.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.87.0) +* [Java auto-instrumentation - 1.30.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.30.0) +* [.NET auto-instrumentation - 1.0.2](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/1.0.2) +* [Node.JS - 0.41.1](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-0.41.1) +* [Python - 0.41b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/0.41b0) +* [Go - v0.7.0-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.7.0-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) +* [Nginx - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3)] + +## 0.86.0 + +### 🛑 Breaking changes 🛑 + +- `operator`: Get rid of autoscaling/v2beta2 (#2145) + Kubernetes 1.23 is the minimum available version everywhere after 1.22 deprecation, + due to it, the minimum required version has been updated to it, dropping support for + autoscaling/v2beta2 + + +### 💡 Enhancements 💡 + +- `operator`: Add support for multi instrumentation (#1717) +- `operator`: Implementation of new Nginx autoinstrumentation. (#2033) +- `operator`: Add PDB support for OpenTelemetryCollector (#2136) + This PR adds support for PodDisruptionBudgets when OpenTelemetryCollector is deployed + as `deployment` or `statefulset`. +- `operator`: Add support for Tolerations on target allocator (#2172) +- `autoinstrumentation`: Bump OpenTelemetry .NET Automatic Instrumentation to 1.0.2 (#2168) +- `target allocator`: Enable discovery manager metrics in target allocator (#2170) +- `target allocator`: Allow target allocator to be completely configured via the config file (#2129) +- `operator`: Propagate proxy environment variables to operands. (#2146) +- `autoinstrumentation`: Bump python autoinstrumentation version to 1.20.0/0.41b0 (#2192) + +### 🧰 Bug fixes 🧰 + +- `autoinstrumentation`: Fix .NET Automatic Instrumentation for alpine based images configured by namespace annotations (#2179) +- `operator`: fixes scenario where an old CRD would cause the operator to default to an unmanaged state (#2039) +- `target allocator`: Rebuild targets on scrape config regex-only changes (#1358, #1926) + +### Components + +* [OpenTelemetry Collector - v0.86.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.86.0) +* [OpenTelemetry Contrib - v0.86.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.86.0) +* [Java auto-instrumentation - 1.30.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.30.0) +* [.NET auto-instrumentation - 1.0.2](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/1.0.2) +* [Node.JS - 0.41.1](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-0.41.1) +* [Python - 0.41b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/0.41b0) +* [Go - v0.3.0-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.3.0-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) +* [Nginx - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3)] + +## 0.85.0 + +### 💡 Enhancements 💡 + +- `autoinstrumentation`: .NET Automatic Instrumentation support for Alpine-based images (#1849) +- `operator`: Allow the collector CRD to specify a list of configmaps to mount (#1819) +- `autoinstrumentation`: Bump Go auto-instrumentation support to v0.3.0-alpha. (#2123) +- `operator`: Introduces a new method of reconciliation to reduce duplication and complexity (#1959) + +### 🧰 Bug fixes 🧰 + +- `operator`: Run the upgrade mechanism when there is a change in an instance to ensure it is upgraded. This is useful for cases where the instance uses the unmanaged state, the operator is upgraded and the instance changes to use a managed state. (#1890) + +### Components + +* [OpenTelemetry Collector - v0.85.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.85.0) +* [OpenTelemetry Contrib - v0.85.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.85.0) +* [Java auto-instrumentation - 1.30.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.30.0) +* [.NET auto-instrumentation - 1.0.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/1.0.0) +* [Node.JS - 0.41.1](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-0.41.1) +* [Python - 0.40b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/0.40b0) +* [Go - v0.3.0-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.3.0-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) + +## 0.84.0 + +### 💡 Enhancements 💡 + +- `autoinstrumentation`: Bump dotnet instrumentation version to 1.0.0 (#2096) +- `operator`: Remove default cpu and mem requests and limits from target allocator to match otel-collector behaviour (#1914) + To preserve the old behaviour for the case when the requests/limits were not explicitely set during the deployment, make sure to set the requests/limits of 100m/200m for CPU and 250Mi/500Mi for memory. +- `operator`: Create ServiceMonitors when the Prometheus exporters are used. (#1963) +- `operator`: Run end-to-end tests on Kubernetes 1.28 (#2047) +- `operator`: Limit auto-instrumentation emptydir volume size (#2044) +- `operator`: Make OpenShift routes work with missing hostname (#2074) + If the Ingress hostname is not specified OpenShift route hostname is set to `--route--basedomain`. + +### 🧰 Bug fixes 🧰 + +- `operator`: Avoid running the auto-instrumentation pod mutator for pods already auto-instrumented (#1366) +- `autoinstrumentation`: Allow the usage of the Apache HTTPD autoinstrumentation to be run as non-root user. Change the files permission to allow their copy from a non-root user. (#2068) +- `operator`: Fixes reconciling otel-collector service's internal traffic policy changes. (#2061) +- `operator`: Make OpenShift Route work with gRPC receivers by using h2c appProtocol (#1969) + +### Components + +* [OpenTelemetry Collector - v0.84.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.84.0) +* [OpenTelemetry Contrib - v0.84.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.84.0) +* [Java auto-instrumentation - 1.29.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.29.0) +* [.NET auto-instrumentation - 1.0.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/1.0.0) +* [Node.JS - 0.41.1](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-0.41.1) +* [Python - 0.40b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/0.40b0) +* [Go - v0.2.2-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.2.2-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) +## 0.83.0 + +### 🛑 Breaking changes 🛑 + +- `operator`: Make sure OTLP export can report data to OTLP ingress/route without additional configuration (#1967) + The ingress can be configured to create a single host with multiple paths or + multiple hosts with subdomains (one per receiver port). + The path from OpenShift route was removed. + The port names are truncate to 15 characters. Users with custom receivers + which create ports with longer name might need to update their configuration. + + +### 💡 Enhancements 💡 + +- `operator`: Add `AdditionalContainers` to the collector spec allowing to configure sidecar containers. This only applies to Deployment/StatefulSet/DeamonSet deployment modes of the collector. (#1987) +- `operator`: Add flag to enable support for the pprof server in the operator. (#1997) +- `operator`: Set the level 4 of capabilities in the CSV for the OpenTelemetry Operator. (#2002) +- `autoinstrumentation`: Bump OpenTelemetry .NET Automatic Instrumentation to 1.0.0-rc.2 (#2030) +- `operator`: Use scratch as the base image for operator (#2011) +- `operator`: Bump Golang to 1.21 (#2009) +- `operator`: Daemonsets can be instrumented so the generated servicename should use their name for better discoverability (#2015) + +### 🧰 Bug fixes 🧰 + +- `operator`: fixes bug introduced in v0.82.0 where Prometheus exporters weren't being generated correctly (#2016) + +### Components + +* [OpenTelemetry Collector - v0.83.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.83.0) +* [OpenTelemetry Contrib - v0.83.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.83.0) +* [Java auto-instrumentation - 1.29.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.29.0) +* [.NET auto-instrumentation - 1.0.0-rc.2](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/1.0.0-rc.2) +* [Node.JS - 0.41.1](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-0.41.1) +* [Python - 0.40b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/0.40b0) +* [Go - v0.2.2-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.2.2-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) + +## 0.82.0 + +### 🛑 Breaking changes 🛑 + +- `operator`: Remove legacy OTLP HTTP port (#1954) + +### 💡 Enhancements 💡 + +- `operator`: Expose the Prometheus exporter port in the OpenTelemetry Collector container when it is used in the configuration. (#1689) +- `operator`: Add the ability to the operator to create Service Monitors for the OpenTelemetry Collectors in order to gather the metrics they are generating (#1768) +- `target allocator`: Add support for environment variables in target allocator config. + (#1773) +- `operator`: Add a GitHub Actions Workflow to build and publish the operator bundle images (#1879) +- `operator`: Add a new field called `managementState` in the OpenTelemetry Collector CRD. (#1881) +- `operator`: When an user specifies the monitoring port for their collector in the configuration, the monitoring service uses that port. (#1931) +- `operator`: Add support for TopologySpreadConstraints & nodeSelector on collector and target allocator (#1899) +- `autoinstrumentation`: Bump dotnet dependency to 1.0.0-rc.1 (#1978) +- `autoinstrumentation`: Bump opentelemetry-go-instrumentation image to v0.2.2-alpha (#1915) +- `autoinstrumentation`: Bumps java autoinstrumentation version to 1.28.0 (#1918) +- `autoinstrumentaion`: Bump NodeJS dependencies to 1.15.1/0.41.1 (#1977) +- `autoinstrumentation`: Bump python packages to 1.19.0/0.40b0 (#1930) +- `target allocator`: Restart target allocator when its configuration changes (#1882) +- `target allocator`: Make the Target Allocator default scrape interval for Prometheus CRs configurable (#1925) + Note that this only works for Prometheus CRs, raw Prometheus configuration from the receiver uses its own settings. +- `operator`: Set securityContext on injected initContainer based on existing containers. (#1084, #1058) +- `Documentation`: Update OTel Operator and Target Allocator readmes. (#1952) + +### 🧰 Bug fixes 🧰 + +- `operator`: Fix port name matching between ingress/route and service. All ports are truncated to 15 characters. If the port name is longer it is changed to port-%d pattern. (#1954) +- `operator`: Fix for issue #1893 (#1905) + +### Components + +* [OpenTelemetry Collector - v0.82.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.82.0) +* [OpenTelemetry Contrib - v0.82.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.82.0) +* [Java auto-instrumentation - 1.28.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.28.0) +* [.NET auto-instrumentation - 1.0.0-rc.1](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/1.0.0-rc.1) +* [Node.JS - 0.41.1](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-0.41.1) +* [Python - 0.40b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/0.40b0) +* [Go - v0.2.2-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.2.2-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) + +## 0.81.0 + +### 💡 Enhancements 💡 + +- `operator`: Create index image to be used as a Catalog. (#1823) + +### 🧰 Bug fixes 🧰 + +- `operator`: Fix `.sampler.type` being incorrectly required for Instrumentation (#1886) +- `receivers`: Skip service port for scraper receivers (#1866) + +### Components + +* [OpenTelemetry Collector - v0.81.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.81.0) +* [OpenTelemetry Contrib - v0.81.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.81.0) +* [Java auto-instrumentation - 1.26.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.26.0) +* [.NET auto-instrumentation - 0.7.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.7.0) +* [Node.JS - 0.40.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.40.0) +* [Python - 0.39b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.39b0) +* [Go - 0.2.1-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.2.1-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) + +## 0.80.0 + +### 💡 Enhancements 💡 + +- `collector`: Add Skywalking parser to extract skywalking service port from config (#1634) +- `target allocator`: Only admit configurations where Target Allocator actually has targets (#1859) +- `target allocator`: Populate credentials for Prometheus CR (service and pod monitor) scrape configs. (#1669) +- `collector`: Adds ability to set init containers for collector (#1684) +- `operator`: Adding more tests to validate existence of init containers. (#1826) +- `operator`: For Apache HTTPD instrumentation, use latest instrumentation library v1.0.3. (#1827) +- `autoinstrumentation/nodejs`: Bump python packages to 1.14.0/0.40.0 (#1790) +- `samplers`: Add ParentBasedJaegerRemote sampler & validate argument (#1801) +- `operator`: Operator-sdk upgrade to v1.29.0 (#1755) + +### 🧰 Bug fixes 🧰 + +- `operator`: Fix for #1820 and #1821 plus added covering unit tests. (#1847) +- `operator`: Fix the upgrade mechanism to not crash when one OTEL Collector instance uses the old approach to set the autoscaler. (#1799) +- `target allocator`: Fix the empty global scrape interval in Prometheus CR watcher, which causes configuration unmarshalling to fail. (#1811) + +### 🚀 New components 🚀 + +- `operator`: Instrumentation crd for Nginx auto-instrumentation. (#1853) + +### Components + +* [OpenTelemetry Collector - v0.80.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.80.0) +* [OpenTelemetry Contrib - v0.80.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.80.0) +* [Java auto-instrumentation - 1.26.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.26.0) +* [.NET auto-instrumentation - 0.7.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.7.0) +* [Node.JS - 0.40.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.40.0) +* [Python - 0.39b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.39b0) +* [Go - 0.2.1-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.2.1-alpha) +* [ApacheHTTPD - 1.0.3](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.3) + +## 0.79.0 + +### 💡 Enhancements 💡 + +- `nodejs autoinstrumentation`: Prometheus metric exporter support for nodejs autoinstrumentation (#1798) +- `operator`: Add service version injection (#1670) + Adds the ability to inject the service version into the environment of the instrumented application. +- `operator`: Added readyReplicas field to the status section and added Current,Desired and Image to the get operation. (#1355) + +### 🧰 Bug fixes 🧰 + +- `operator`: The OpenTelemetry Collector version is not shown properly in the status field if no upgrade routines are performed. (#1802) + +### Components + +* [OpenTelemetry Collector - v0.79.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.79.0) +* [OpenTelemetry Contrib - v0.79.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.79.0) +* [Java auto-instrumentation - 1.26.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.26.0) +* [.NET auto-instrumentation - 0.7.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.7.0) +* [Node.JS - 0.39.1](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.39.1) +* [Python - 0.39b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.39b0) +* [Go - 0.2.1-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.2.1-alpha) +* [ApacheHTTPD - 1.0.2](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.2) + +## 0.78.0 + +### 💡 Enhancements 💡 + +- `autoinstrumentaiton/nodejs`: Bump js packages to latest versions (#1791) +- `autoinstrumentation/python`: Bump python packages to 1.18.0/0.39b0 (#1790) +- `operator`: Added all webhook instrumentation logic, e2e tests, readme (#1444) +- `Autoscaler`: Support scaling on Pod custom metrics. (#1560) +- `targetallocator`: Set resource requests/limits for TargetAllocator (#1103) +- `operator`: provide default resource limits for go sidecar container (#1732) +- `operator`: Propagate Metadata.Annotations to PodSpec.Annotations (#900) +- `operator`: Improve config validation for prometheus receiver and target allocator (#1581) + +### 🧰 Bug fixes 🧰 + +- `operator`: fixes a previously undocumented behavior that a collector could not override the collector's app name (#1777) +- `operator`: Fix issue where the operator's released image did not correctly set the default go auto-instrumentation version (#1757) +- `pkg/collector, pkg/targetallocator`: fix issues related to prometheus relabel configs when target allocator is enabled (#958, #1622, #1623) + +### Components + +* [OpenTelemetry Collector - v0.78.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.78.0) +* [OpenTelemetry Contrib - v0.78.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.78.0) +* [Java auto-instrumentation - 1.26.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.26.0) +* [.NET auto-instrumentation - 0.7.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.7.0) +* [Node.JS - 0.39.1](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.39.1) +* [Python - 0.39b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.39b0) +* [Go - 0.2.1-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.2.1-alpha) +* [ApacheHTTPD - 1.0.2](https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/tag/webserver%2Fv1.0.2) + +## 0.77.0 + +### 💡 Enhancements 💡 + +- `operator`: Add support for Go auto instrumentation (#1555) +- `operator`: Add liveness probe configs (#760) +- `operator`: set default resource limits for instrumentation init containers (#1407) +- `github actions`: Publish image to dockerhub too (#1708) +- `instrumentation`: Bump Go Instrumentation image from `v0.2.0-alpha` to `v0.2.1-alpha` (#1740) + +### 🧰 Bug fixes 🧰 + +- `operator`: fixes a bug where setting the http_sd_config would crash the configmap replacement. (#1742) +### Components + +* [OpenTelemetry Collector - v0.77.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.77.0) +* [OpenTelemetry Contrib - v0.77.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.77.0) +* [Java auto-instrumentation - 1.25.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.25.0) +* [.NET auto-instrumentation - 0.7.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.7.0) +* [Node.JS - 0.38.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.38.0) +* [Python - 0.38b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.38b0) +* [Go - 0.2.1-alpha](https://github.com/open-telemetry/opentelemetry-go-instrumentation/releases/tag/v0.2.1-alpha) + +## 0.76.1 + +### 💡 Enhancements 💡 + +- `operator`: add support for `lifecycle` hooks and `terminationGracePeriodSeconds` in collector spec. (#1618) +- `autoinstrumentation`: Bump OpenTelemetry .NET Automatic Instrumentation to 0.7.0 (#1672) +- `autoinstrumentation`: Bump nodejs dependencies to latest versions (#1682) +- `pkg/instrumentation`: Add dotnet instrumentation capability behind a feature gate which is enabled by default. (#1629) +- `operator`: Add ability to use feature gates in the operator (#1619) +- `autoinstrumentation`: Add metrics exporter to Node.JS autoinstrumentation (#1627) +- `autoinstrumentation`: Bump nodejs dependencies to latest versions (#1626) +- `pkg/instrumentation`: Add java instrumentation capability behind a feature gate which is enabled by default. (#1695) +- `pkg/instrumentation`: Add nodejs instrumentation capability behind a feature gate which is enabled by default. (#1697) +- `operator`: Introduces a new feature flag "`operator.collector.rewritetargetallocator`" that allows an operator to add the target_allocator configuration to the collector configuration (#1581) + Note that the ConfigToPromConfig function in pkg/targetallocator/adapters now correctly returns the prometheus receiver config + in accordance with its docstring. It used to erroneously return the actual Prometheus config from a level lower. + +- `pkg/instrumentation`: Add python instrumentation capability behind a feature gate which is enabled by default. (#1696) + +### 🧰 Bug fixes 🧰 + +- `target allocator`: fix updating scrape configs (#1415) + +### Components + +* [OpenTelemetry Collector - v0.76.1](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.76.1) +* [OpenTelemetry Contrib - v0.76.1](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.76.1) +* [Java auto-instrumentation - 1.25.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.25.0) +* [.NET auto-instrumentation - 0.7.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.7.0) +* [Node.JS - 0.38.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.38.0) +* [Python - 0.38b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.38b0) + +## 0.75.0 + +### 💡 Enhancements 💡 + +- `operator`: Add ability to use feature gates in the operator (#1619) +- `autoinstrumentation`: Add metrics exporter to Node.JS autoinstrumentation (#1627) +- `autoinstrumentation`: Bump nodejs dependencies to latest versions (#1626) +- `autoinstrumentation`: Bump python dependencies to latest versions (#1640) + +### Components + +* [OpenTelemetry Collector - v0.75.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.75.0) +* [OpenTelemetry Contrib - v0.75.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.75.0) +* [Java auto-instrumentation - 1.24.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.24.0) +* [.NET auto-instrumentation - 0.6.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.6.0) +* [Node.JS - 0.37.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.37.0) +* [Python - 0.38b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.38b0) + +## 0.74.0 + +### Components + +* [OpenTelemetry Collector - v0.74.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.74.0) +* [OpenTelemetry Contrib - v0.74.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.74.0) +* [Java auto-instrumentation - 1.23.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.23.0) +* [.NET auto-instrumentation - 0.6.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.6.0) +* [Node.JS - 0.34.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.34.0) +* [Python - 0.36b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.36b0) + +## 0.73.0 + +### 💡 Enhancements 💡 + +- `target allocator`: Use jsoniter to marshal json (#1336) + +### Components + +* [OpenTelemetry Collector - v0.73.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.73.0) +* [OpenTelemetry Contrib - v0.73.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.73.0) +* [Java auto-instrumentation - 1.23.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.23.0) +* [.NET auto-instrumentation - 0.6.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.6.0) +* [Node.JS - 0.34.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.34.0) +* [Python - 0.36b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.36b0) + + +## 0.72.0 + +### 🛑 Breaking changes 🛑 + +- `operator`: Fixes inability of the operator to reconcile in stateful set mode when the immutable field `volumeClaimTemplates` is changed. If such change is detected, the operator will recreate the stateful set. (#1491) + +### 💡 Enhancements 💡 + +- `operator`: Bump OpenTelemetry .NET Automatic Instrumentation to 0.6.0 (#1538) +- `operator`: Bump Kubernetes golang dependencies to 1.26.x (#1385) +- `operator`: Build operator, target-allocator and opAMP bridge with golang 1.20. (#1566) + +### 🧰 Bug fixes 🧰 + +- `Autoscaler`: Fix the issue where HPA fails to update when an additional metric is added to the spec. (#1439) +- `operator`: The args created for corev1.container object is not ordered and creates a situation where there is a diff detected during reconcile. Forces an ordered args. (#1460) +- `Autoscaler`: Fix the issue where HPA fails to update autoscaler behavior. (#1516) +- `operator`: Set `ServiceInternalTrafficPolicy`` to `Local` when using daemonset mode. (#1401) + +### Components + +* [OpenTelemetry Collector - v0.72.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.72.0) +* [OpenTelemetry Contrib - v0.72.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.72.0) +* [Java auto-instrumentation - 1.23.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.23.0) +* [.NET auto-instrumentation - 0.6.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.6.0) +* [Node.JS - 0.34.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.34.0) +* [Python - 0.36b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.36b0) + +0.71.0 +------------------ + +### 🛑 Breaking changes 🛑 + +- `target allocator`: Updates versions of many dependencies, sets defaults for prometheus operator to work. The breaking change introduced is the new RBAC requirement for "endpointslices" in the "discovery.k8s.io" api group. (#1464) + +### 🧰 Bug fixes 🧰 + +- `target allocator`: Properly handle all types of profiles in the pprof endpoint. Previously, some profiles where unavailable, leading to 404 response. (#1478) + +0.70.0 +------------------ +### 💡 Enhancements 💡 + +- `target allocator`: Save the scrape config response in the HTTP server upon relevant config change, instead of building it on every handler call. At the same time, this avoids data race when accessing the scrape configs map. (#1359) +- `target allocator`: Configure `gin` router to be used in release mode and do not use the default logging middleware which is noisy and not formatted properly. (#1352) +- `github action`: This PR adds github action for publishing the `Operator OpAMP Bridge` container image to Github Container Registry. (#1369) +- `operator`: Add `Operator-OpAMP-Bridge` version info to Operator (#1455) + +### 🧰 Bug fixes 🧰 + +- `statsd-receiver`: Switched the protocol of statsd-receiver to UDP from TCP (#1476) + +### Components + +* [OpenTelemetry Collector - v0.70.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.70.0) +* [OpenTelemetry Contrib - v0.70.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.70.0) +* [Java auto-instrumentation - 1.23.0](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.23.0) +* [.NET auto-instrumentation - 0.5.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.5.0) +* [Node.JS - 0.34.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.34.0) +* [Python - 0.36b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.36b0) + +0.69.0 +------------------ +### 🚩 Deprecations 🚩 +* `target allocator`: Replace deprecated `gorilla/mux` dependency with `gin` ([#1383](https://github.com/open-telemetry/opentelemetry-operator/pull/1383), [@matej-g](https://github.com/matej-g)) +### 💡 Enhancements 💡 +* `operator`: CRD defs for Apache HTTPD Autoinstrumentation ([#1305](https://github.com/open-telemetry/opentelemetry-operator/pull/1305), [@chrlic](https://github.com/chrlic)) +* `operator`: Inject otelcol sidecar into any namespace ([#1395](https://github.com/open-telemetry/opentelemetry-operator/pull/1395), [@pavolloffay](https://github.com/pavolloffay)) +* `operator`: Update bridge and allocator dependencies ([#1450](https://github.com/open-telemetry/opentelemetry-operator/pull/1450), [@jaronoff97](https://github.com/jaronoff97)) +* `target allocator`: register pprof endpoints for allocator ([#1408](https://github.com/open-telemetry/opentelemetry-operator/pull/1408), [@seankhliao](https://github.com/seankhliao)) +* `target allocator`: Addtl server unit tests ([#1357](https://github.com/open-telemetry/opentelemetry-operator/pull/1357), [@kristinapathak](https://github.com/kristinapathak)) +* `target-allocator`: Use `gin` in release mode and without default logger middleware ([#1414](https://github.com/open-telemetry/opentelemetry-operator/pull/1414), [@matej-g](https://github.com/matej-g)) +* `operator`: Update README.md document Kubernetes Operator Introduction ([#1440](https://github.com/open-telemetry/opentelemetry-operator/pull/1440), [@fengshunli](https://github.com/fengshunli)) +* `operator`: Update package dependencies ([#1441](https://github.com/open-telemetry/opentelemetry-operator/pull/1441), [@fengshunli](https://github.com/fengshunli)) +### 🧰 Bug fixes 🧰 +* `operator`: Fix daemonset-features E2E test for OpenShift ([#1354](https://github.com/open-telemetry/opentelemetry-operator/pull/1354), [@iblancasa](https://github.com/iblancasa)) +* `operator`: Fix E2E autoscale test for OpenShift ([#1365](https://github.com/open-telemetry/opentelemetry-operator/pull/1365), [@iblancasa](https://github.com/iblancasa)) +* `target allocator`: Fix Target Allocator tests ([#1403](https://github.com/open-telemetry/opentelemetry-operator/pull/1403), [@jaronoff97](https://github.com/jaronoff97)) +### Components +* [OpenTelemetry Collector - v0.69.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.69.0) +* [OpenTelemetry Contrib - v0.69.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.69.0) +* [Java auto-instrumentation - 1.22.1](https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/tag/v1.22.1) +* [.NET auto-instrumentation - 0.5.0](https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/tag/v0.5.0) +* [Node.JS - 0.34.0](https://github.com/open-telemetry/opentelemetry-js-contrib/releases/tag/auto-instrumentations-node-v0.34.0) +* [Python - 0.36b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.36b0) + +0.68.0 +------------------ +### 🚩 Deprecations 🚩 +* `HPA`: Move maxReplicas and minReplicas to AutoscalerSpec.([#1302](https://github.com/open-telemetry/opentelemetry-operator/pull/1302), [@moh-osman3](https://github.com/moh-osman3)) +### 🚀 New components 🚀 +* `Operator OpAMP Bridge`: Operator OpAMP Bridge Service. ([#1339](https://github.com/open-telemetry/opentelemetry-operator/pull/1339), [@jaronoff97](https://github.com/jaronoff97)) +### 💡 Enhancements 💡 +* `instrumentation/python`: Update default python exporters to use OTLP. ([#1328](https://github.com/open-telemetry/opentelemetry-operator/pull/1328), [@TylerHelmuth](https://github.com/TylerHelmuth)) +* `target-allocator`: Change the github action to match the operator. ([#1347](https://github.com/open-telemetry/opentelemetry-operator/pull/1347), [@jaronoff97](https://github.com/jaronoff97)) +### 🧰 Bug fixes 🧰 +* `operator`: Missing resource from OpenShift Routes prevents them to be deployed in OpenShift clusters.([#1337](https://github.com/open-telemetry/opentelemetry-operator/pull/1337), [@iblancasa](https://github.com/iblancasa)) +* `target allocator`: Refactor the target allocator build to not run it as root. ([#1345](https://github.com/open-telemetry/opentelemetry-operator/pull/1345), [@iblancasa](https://github.com/iblancasa)) +#### OpenTelemetry Collector and Contrib +* [OpenTelemetry Collector - v0.68.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.68.0) +* [OpenTelemetry Contrib - v0.68.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.68.0) + +0.67.0 +------------------ +### 🚀 New components 🚀 +* Support openshift routes ([#1206](https://github.com/open-telemetry/opentelemetry-operator/pull/1206), [@frzifus](https://github.com/frzifus)) +* Add TargetMemoryUtilization metric for AutoScaling ([#1223](https://github.com/open-telemetry/opentelemetry-operator/pull/1223), [@kevinearls](https://github.com/kevinearls)) +### 💡 Enhancements 💡 +* Update the javaagent version to 1.21.0 ([#1324](https://github.com/open-telemetry/opentelemetry-operator/pull/1324)) +* Update default python exporters to use OTLP ([#1328](https://github.com/open-telemetry/opentelemetry-operator/pull/1328), [@TylerHelmuth](https://github.com/TylerHelmuth)) +* Update default Node.JS instrumentation to 0.34.0 ([#1334](https://github.com/open-telemetry/opentelemetry-operator/pull/1334), [@mat-rumian](https://github.com/mat-rumian)) +* Update default Python instrumentation to 0.36b0 ([#1333](https://github.com/open-telemetry/opentelemetry-operator/pull/1333), [@mat-rumian](https://github.com/mat-rumian)) +* [HPA] Move maxReplicas and minReplicas to AutoscalerSpec ([#1333](https://github.com/open-telemetry/opentelemetry-operator/pull/1302), [@moh-osman3](https://github.com/moh-osman3)) +* Memory improvements first pass ([#1293](https://github.com/open-telemetry/opentelemetry-operator/pull/1293), [@jaronoff97](https://github.com/jaronoff97)) +* Add change handler to register callbacks ([#1292](https://github.com/open-telemetry/opentelemetry-operator/pull/1292), [@frzifus](https://github.com/frzifus)) +* Ignore reconcile errors that occur because a pod is being terminated ([#1233](https://github.com/open-telemetry/opentelemetry-operator/pull/1233), [@kevinearls](https://github.com/kevinearls)) +* remove unused onChange function from config ([#1290](https://github.com/open-telemetry/opentelemetry-operator/pull/1290), [@frzifus](https://github.com/frzifus)) +* Remove default claims - fixes #1281 ([#1282](https://github.com/open-telemetry/opentelemetry-operator/pull/1282), [@ekarlso](https://github.com/ekarlso)) +#### OpenTelemetry Collector and Contrib +* [OpenTelemetry Collector - v0.67.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.67.0) +* [OpenTelemetry Contrib - v0.67.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.67.0) + +0.66.0 +------------------ +### 🚀 New components 🚀 +* Add ingressClassName field to collector spec ([#1269](https://github.com/open-telemetry/opentelemetry-operator/pull/1269), [@avadhut123pisal](https://github.com/avadhut123pisal)) +* Add secure ciphersuites for TLS config ([#1244](https://github.com/open-telemetry/opentelemetry-operator/pull/1244), [@kangsheng89](https://github.com/kangsheng89)) +* Add Apache-httpd instrumentation v1.0 (part-1) ([#1236](https://github.com/open-telemetry/opentelemetry-operator/pull/1236), [@chrlic](https://github.com/chrlic)) +### 💡 Enhancements 💡 +* Update the javaagent version to 1.20.2 ([#1212](https://github.com/open-telemetry/opentelemetry-operator/pull/1270)) +* Bump OTel .NET AutoInstrumentation to 0.5.0 ([#1276](https://github.com/open-telemetry/opentelemetry-operator/pull/1276), [@pellared](https://github.com/pellared)) + +### 🧰 Bug fixes 🧰 +* Fix bug found when using relabel-config filterStrategy with serviceMonitors ([#1232](https://github.com/open-telemetry/opentelemetry-operator/pull/1232), [@moh-osman3](https://github.com/moh-osman3)) + +#### OpenTelemetry Collector and Contrib +* [OpenTelemetry Collector - v0.66.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.66.0) +* [OpenTelemetry Contrib - v0.66.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.66.0) + +0.64.1 +------------------ +### 🚀 New components 🚀 +* add headless label ([#1088](https://github.com/open-telemetry/opentelemetry-operator/pull/1088), [@kristinapathak](https://github.com/kristinapathak)) +* Add new selector for pod and service monitor ([#1256](https://github.com/open-telemetry/opentelemetry-operator/pull/1256), [@jaronoff97](https://github.com/jaronoff97)) +* [target-allocator] Add a pre-hook to the allocator to filter out dropped targets ([#1127](https://github.com/open-telemetry/opentelemetry-operator/pull/1127), [@moh-osman3](https://github.com/moh-osman3)) +* [target-allocator] create new target package ([#1214](https://github.com/open-telemetry/opentelemetry-operator/pull/1214), [@moh-osman3](https://github.com/moh-osman3)) +### 💡 Enhancements 💡 +* Only create ServiceAccounts if existing ServiceAccount is not specified ([#1246](https://github.com/open-telemetry/opentelemetry-operator/pull/1246), [@csquire](https://github.com/csquire)) +* feat(otel-allocator): use type for AllocationStrategy ([#1220](https://github.com/open-telemetry/opentelemetry-operator/pull/1220), [@secustor](https://github.com/secustor)) +* fix min tls setting for webhook server (#1225) ([#1230](https://github.com/open-telemetry/opentelemetry-operator/pull/1230), [@kangsheng89](https://github.com/kangsheng89)) +* Bump OTel python versions to 1.14.0 and 0.35b0 ([#1227](https://github.com/open-telemetry/opentelemetry-operator/pull/1227), [@vainikkaj](https://github.com/vainikkaj)) +* Trim unnecessary otelcol operator verbs ([#1222](https://github.com/open-telemetry/opentelemetry-operator/pull/1222), [@Allex1](https://github.com/Allex1)) +* decrease autoscaling version detection log verbosity ([#1212](https://github.com/open-telemetry/opentelemetry-operator/pull/1212), [@frzifus](https://github.com/frzifus)) + +### 🧰 Bug fixes 🧰 +* None + +#### OpenTelemetry Collector and Contrib +* [OpenTelemetry Collector - v0.64.1](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.64.1) +* [OpenTelemetry Contrib - v0.64.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.64.0) +* [OpenTelemetry Collector - v0.64.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.64.0) + +0.63.1 +------------------ +### 🚀 New components 🚀 +* None + +### 💡 Enhancements 💡 +* None + +### 🧰 Bug fixes 🧰 +* None + +#### OpenTelemetry Collector and Contrib +* [OpenTelemetry Collector - v0.63.1](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.63.1) +* [OpenTelemetry Contrib - v0.63.1](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.63.1) +* [OpenTelemetry Collector - v0.63.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.63.0) +* [OpenTelemetry Contrib - v0.63.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.63.0) + +0.62.1 +------------------ +### 🚀 New components 🚀 +* Adds support of affinity in collector spec ([#1204](https://github.com/open-telemetry/opentelemetry-operator/pull/1204), [@avadhut123pisal](https://github.com/avadhut123pisal)) + +### 💡 Enhancements 💡 + +* Make logging easier to configure ([#1193](https://github.com/open-telemetry/opentelemetry-operator/pull/1193), [@pavolloffay](https://github.com/pavolloffay)) +* Using immutable labels as service selectors ([#1152](https://github.com/open-telemetry/opentelemetry-operator/pull/1152), [@angelokurtis](https://github.com/angelokurtis)) +* Avoid OOM of the operator ([#1194](https://github.com/open-telemetry/opentelemetry-operator/pull/1194), [@pavolloffay](https://github.com/pavolloffay)) +* Update the javaagent version to 1.19.1 ([#1188](https://github.com/open-telemetry/opentelemetry-operator/pull/1188), [@opentelemetrybot](https://github.com/opentelemetrybot)) +* Bump OTel .NET AutoInstrumentation to 0.4.0-beta.1 ([#1209](https://github.com/open-telemetry/opentelemetry-operator/pull/1209), [@pellared](https://github.com/pellared)) +* Skip .NET auto-instrumentation if OTEL_DOTNET_AUTO_HOME env var is already set ([#1177](https://github.com/open-telemetry/opentelemetry-operator/pull/1177), [@avadhut123pisal](https://github.com/avadhut123pisal)) + +### 🧰 Bug fixes 🧰 +* Fix panic if maxreplicas is set but autoscale is not defined in the CR ([#1201](https://github.com/open-telemetry/opentelemetry-operator/pull/1201), [@kevinearls](https://github.com/kevinearls)) + +#### OpenTelemetry Collector and Contrib +* [OpenTelemetry Collector - v0.62.1](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.62.1) +* [OpenTelemetry Contrib - v0.62.1](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.62.1) +* [OpenTelemetry Collector - v0.62.0](https://github.com/open-telemetry/opentelemetry-collector/releases/tag/v0.62.0) +* [OpenTelemetry Contrib - v0.62.0](https://github.com/open-telemetry/opentelemetry-collector-contrib/releases/tag/v0.62.0) + + 0.61.0 ------------------- #### :x: Breaking Changes :x: @@ -79,7 +819,7 @@ Changes by Version 0.57.2 ------------------- ### 🚀 New components 🚀 -* Support DotNet auto-instrumentation ([#976](https://github.com/open-telemetry/opentelemetry-operator/pull/976), [@avadhut123pisal](https://github.com/avadhut123pisal)) +* Support .NET auto-instrumentation ([#976](https://github.com/open-telemetry/opentelemetry-operator/pull/976), [@avadhut123pisal](https://github.com/avadhut123pisal)) * Enable instrumentation injecting only core SDK config ([#1000](https://github.com/open-telemetry/opentelemetry-operator/pull/1000), [@bilbof](https://github.com/bilbof)) * Instrument TA with prometheus ([#1030](https://github.com/open-telemetry/opentelemetry-operator/pull/1030), [@jaronoff97](https://github.com/jaronoff97)) ### 💡 Enhancements 💡 @@ -437,18 +1177,18 @@ _Note: The default port for the OTLP receiver has been changed from 55680 to 431 0.15.0 (2020-11-27) ------------------- -* Bumped OpenTelemetry Collector to v0.15.0 ([#131](https://github.com/open-telemetry/opentelemetry-operator/pull/131), [@jpkrohling](https://github.com/jpkrohling)) +* Bumped OpenTelemetry Collector to v0.15.0 ([#131](https://github.com/open-telemetry/opentelemetry-operator/pull/131), [@jpkrohling](https://github.com/jpkrohling)) 0.14.0 (2020-11-09) ------------------- -* Bumped OpenTelemetry Collector to v0.14.0 ([#112](https://github.com/open-telemetry/opentelemetry-operator/pull/112), [@jpkrohling](https://github.com/jpkrohling)) +* Bumped OpenTelemetry Collector to v0.14.0 ([#112](https://github.com/open-telemetry/opentelemetry-operator/pull/112), [@jpkrohling](https://github.com/jpkrohling)) _Note: The `tailsampling` processor was moved to the contrib repository, requiring a manual intervention in case this processor is being used: either replace the image with the contrib one (v0.14.0, which includes this processor), or remove the processor._ 0.13.0 (2020-10-22) ------------------- -* Bumped OpenTelemetry Collector to v0.13.0 ([#101](https://github.com/open-telemetry/opentelemetry-operator/pull/101), [@dengliming](https://github.com/dengliming)) +* Bumped OpenTelemetry Collector to v0.13.0 ([#101](https://github.com/open-telemetry/opentelemetry-operator/pull/101), [@dengliming](https://github.com/dengliming)) * Allow for spec.Env to be set on the OTEL Collector Spec ([#94](https://github.com/open-telemetry/opentelemetry-operator/pull/94), [@ekarlso](https://github.com/ekarlso)) _Note: The `groupbytrace` processor was moved to the contrib repository, requiring a manual intervention in case this processor is being used: either replace the image with the contrib one (v0.13.1, which includes this processor), or remove the processor._ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bce58b96fc..495c20fa6e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,11 +20,28 @@ git commit -sam "Add feature X" gh pr create ``` +#### Make changes to the project manifests + +The following command should be run to make sure the project manifests are up-to-date: + +```bash +make generate manifests bundle api-docs reset +``` + +The local changes after running the command should be added to the pull request: + +The following `make` target is run on CI to verify the project structure: + +```bash +make ensure-generate-is-noop +``` + ### Pre-requisites * Install [Go](https://golang.org/doc/install). * Install [Kustomize](https://kubectl.docs.kubernetes.io/installation/kustomize/). * Install [Operator SDK](https://sdk.operatorframework.io/docs/installation/). * Have a Kubernetes cluster ready for development. We recommend `minikube` or `kind`. +* Docker version 23.0.0 or greater. ### Adding new components - webhook, API @@ -36,7 +53,7 @@ Refer to the [Operator SDK documentation](https://sdk.operatorframework.io/docs/ Build the manifests, install the CRD and run the operator as a local process: ```bash -make bundle install run +make install run ``` ### Deployment with webhooks @@ -57,16 +74,16 @@ The environment variable `CERTMANAGER_VERSION` can be used to override the cert- CERTMANAGER_VERSION=1.60 make cert-manager ``` -When deploying the operator into the cluster using `make deploy`, an image in the format `ghcr.io/${USER}/opentelemetry-operator` is generated. If this format isn't suitable, it can be overridden by: +When deploying the operator into the cluster using `make deploy`, an image in the format `ghcr.io/${DOCKER_USER}/opentelemetry-operator` is generated. If this format isn't suitable, it can be overridden by: * `IMG_PREFIX`, to override the registry, namespace and image name -* `USER`, to override the namespace +* `DOCKER_USER`, to override the namespace * `IMG_REPO`, to override the repository (`opentelemetry-operator`) * `VERSION`, to override only the version part * `IMG`, to override the entire image specification ```bash -IMG=docker.io/${USER}/opentelemetry-operator:latest make generate bundle container container-push deploy +IMG=docker.io/${DOCKER_USER}/opentelemetry-operator:dev-$(git rev-parse --short HEAD)-$(date +%s) make generate container container-push deploy ``` Your operator will be available in the `opentelemetry-operator-system` namespace. @@ -107,6 +124,17 @@ Once they are installed, the tests can be executed with `make prepare-e2e`, whic The tests are located under `tests/e2e` and are written to be used with `kuttl`. Refer to their documentation to understand how tests are written. +To evert the changes made by the `make prepare-e2e` run `make reset`. + +### OpenShift End to End tests +To run the end-to-end tests written for OpenShift, you'll need a OpenShift cluster. + +To install the OpenTelemetry operator, please follow the instructions in [Operator Lifecycle Manager (OLM)](https://github.com/open-telemetry/opentelemetry-operator/blob/main/CONTRIBUTING.md#operator-lifecycle-manager-olm) + +Once the operator is installed, the tests can be executed using `make e2e-openshift`, which will call to the `e2e-openshift` target. Note that `kind` is disabled for the TestSuite as the requirement is to use an OpenShift cluster for these test cases. + +The tests are located under `tests/e2e-openshift` and are written to be used with `kuttl`. + ### Undeploying the operator from the local cluster ```bash @@ -135,6 +163,24 @@ Every bug fix should be accompanied with a unit test, so that we can prevent reg They are mostly welcome! +### Adding a Changelog Entry + +The [CHANGELOG.md](./CHANGELOG.md) file in this repo is autogenerated from `.yaml` files in the `./.chloggen` directory. + +Your pull-request should add a new `.yaml` file to this directory. The name of your file must be unique since the last release. + +During the collector release process, all `./.chloggen/*.yaml` files are transcribed into `CHANGELOG.md` and then deleted. + +If a changelog entry is not required, add either `[chore]` to the title of the pull request or add the `"Skip Changelog"` label to disable this action. + +**Recommended Steps** +1. Create an entry file using `make chlog-new`. This generates a file based on your current branch (e.g. `./.chloggen/my-branch.yaml`) +2. Fill in all fields in the new file +3. Run `make chlog-validate` to ensure the new file is valid +4. Commit and push the file + +Alternately, copy `./.chloggen/TEMPLATE.yaml`, or just create your file from scratch. + ## Operator Lifecycle Manager (OLM) For production environments, it is recommended to use the [Operator Lifecycle Manager (OLM)](https://github.com/operator-framework/operator-lifecycle-manager) to provision and update the OpenTelemetry Operator. Our operator is available in the [Operator Hub](https://operatorhub.io/operator/opentelemetry-operator), and when making changes involving those manifests the following steps can be used for testing. Refer to the [OLM documentation](https://sdk.operatorframework.io/docs/olm-integration/quickstart-bundle/) for more complete information. @@ -160,7 +206,7 @@ BUNDLE_IMG=docker.io/${USER}/opentelemetry-operator-bundle:latest IMG=docker.io/ ### Install the operator ```bash -operator-sdk run bundle docker.io/${USER}/opentelemetry-operator-bundle:latest +operator-sdk run bundle docker.io/${DOCKER_USER}/opentelemetry-operator-bundle:latest ``` ### Uninstall the operator diff --git a/Dockerfile b/Dockerfile index 89a589723e..e1efea1afc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,40 +1,21 @@ -# Build the manager binary -FROM golang:1.19 as builder - -WORKDIR /workspace -# Copy the Go Modules manifests -COPY go.mod go.mod -COPY go.sum go.sum -# cache deps before building and copying source so that we don't need to re-download as much -# and so that source changes don't invalidate our downloaded layer -RUN go mod download - -# Copy the go source -COPY main.go main.go -COPY apis/ apis/ -COPY controllers/ controllers/ -COPY internal/ internal/ -COPY pkg/ pkg/ -COPY versions.txt versions.txt - -ARG VERSION_PKG -ARG VERSION -ARG VERSION_DATE -ARG OTELCOL_VERSION -ARG TARGETALLOCATOR_VERSION -ARG AUTO_INSTRUMENTATION_JAVA_VERSION -ARG AUTO_INSTRUMENTATION_NODEJS_VERSION -ARG AUTO_INSTRUMENTATION_PYTHON_VERSION -ARG AUTO_INSTRUMENTATION_DOTNET_VERSION - -# Build -RUN CGO_ENABLED=0 GOOS=linux GO111MODULE=on go build -ldflags="-X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -X ${VERSION_PKG}.otelCol=${OTELCOL_VERSION} -X ${VERSION_PKG}.targetAllocator=${TARGETALLOCATOR_VERSION} -X ${VERSION_PKG}.autoInstrumentationJava=${AUTO_INSTRUMENTATION_JAVA_VERSION} -X ${VERSION_PKG}.autoInstrumentationNodeJS=${AUTO_INSTRUMENTATION_NODEJS_VERSION} -X ${VERSION_PKG}.autoInstrumentationPython=${AUTO_INSTRUMENTATION_PYTHON_VERSION} -X ${VERSION_PKG}.autoInstrumentationDotNet=${AUTO_INSTRUMENTATION_DOTNET_VERSION}" -a -o manager main.go - -# Use distroless as minimal base image to package the manager binary -# Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM gcr.io/distroless/static:nonroot +# Get CA certificates from alpine package repo +FROM alpine:3.18 as certificates + +RUN apk --no-cache add ca-certificates + +######## Start a new stage from scratch ####### +FROM scratch + +ARG TARGETARCH + WORKDIR / -COPY --from=builder /workspace/manager . + +# Copy the certs from Alpine +COPY --from=certificates /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +# Copy binary built on the host +COPY bin/manager_${TARGETARCH} manager + USER 65532:65532 ENTRYPOINT ["/manager"] diff --git a/Makefile b/Makefile index 1f90583a08..d14311a893 100644 --- a/Makefile +++ b/Makefile @@ -3,17 +3,23 @@ VERSION ?= "$(shell git describe --tags | sed 's/^v//')" VERSION_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') VERSION_PKG ?= "github.com/open-telemetry/opentelemetry-operator/internal/version" OTELCOL_VERSION ?= "$(shell grep -v '\#' versions.txt | grep opentelemetry-collector | awk -F= '{print $$2}')" -OPERATOR_VERSION ?= "$(shell grep -v '\#' versions.txt | grep operator | awk -F= '{print $$2}')" +OPERATOR_VERSION ?= "$(shell grep -v '\#' versions.txt | grep operator= | awk -F= '{print $$2}')" TARGETALLOCATOR_VERSION ?= "$(shell grep -v '\#' versions.txt | grep targetallocator | awk -F= '{print $$2}')" +OPERATOR_OPAMP_BRIDGE_VERSION ?= "$(shell grep -v '\#' versions.txt | grep operator-opamp-bridge | awk -F= '{print $$2}')" AUTO_INSTRUMENTATION_JAVA_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-java | awk -F= '{print $$2}')" AUTO_INSTRUMENTATION_NODEJS_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-nodejs | awk -F= '{print $$2}')" AUTO_INSTRUMENTATION_PYTHON_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-python | awk -F= '{print $$2}')" AUTO_INSTRUMENTATION_DOTNET_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-dotnet | awk -F= '{print $$2}')" -LD_FLAGS ?= "-X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -X ${VERSION_PKG}.otelCol=${OTELCOL_VERSION} -X ${VERSION_PKG}.targetAllocator=${TARGETALLOCATOR_VERSION} -X ${VERSION_PKG}.autoInstrumentationJava=${AUTO_INSTRUMENTATION_JAVA_VERSION} -X ${VERSION_PKG}.autoInstrumentationNodeJS=${AUTO_INSTRUMENTATION_NODEJS_VERSION} -X ${VERSION_PKG}.autoInstrumentationPython=${AUTO_INSTRUMENTATION_PYTHON_VERSION} -X ${VERSION_PKG}.autoInstrumentationDotNet=${AUTO_INSTRUMENTATION_DOTNET_VERSION}" +AUTO_INSTRUMENTATION_GO_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-go | awk -F= '{print $$2}')" +AUTO_INSTRUMENTATION_APACHE_HTTPD_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-apache-httpd | awk -F= '{print $$2}')" +AUTO_INSTRUMENTATION_NGINX_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-nginx | awk -F= '{print $$2}')" +COMMON_LDFLAGS ?= -s -w +OPERATOR_LDFLAGS ?= -X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -X ${VERSION_PKG}.otelCol=${OTELCOL_VERSION} -X ${VERSION_PKG}.targetAllocator=${TARGETALLOCATOR_VERSION} -X ${VERSION_PKG}.operatorOpAMPBridge=${OPERATOR_OPAMP_BRIDGE_VERSION} -X ${VERSION_PKG}.autoInstrumentationJava=${AUTO_INSTRUMENTATION_JAVA_VERSION} -X ${VERSION_PKG}.autoInstrumentationNodeJS=${AUTO_INSTRUMENTATION_NODEJS_VERSION} -X ${VERSION_PKG}.autoInstrumentationPython=${AUTO_INSTRUMENTATION_PYTHON_VERSION} -X ${VERSION_PKG}.autoInstrumentationDotNet=${AUTO_INSTRUMENTATION_DOTNET_VERSION} -X ${VERSION_PKG}.autoInstrumentationGo=${AUTO_INSTRUMENTATION_GO_VERSION} -X ${VERSION_PKG}.autoInstrumentationApacheHttpd=${AUTO_INSTRUMENTATION_APACHE_HTTPD_VERSION} -X ${VERSION_PKG}.autoInstrumentationNginx=${AUTO_INSTRUMENTATION_NGINX_VERSION} ARCH ?= $(shell go env GOARCH) # Image URL to use all building/pushing image targets -IMG_PREFIX ?= ghcr.io/${USER}/opentelemetry-operator +DOCKER_USER ?= open-telemetry +IMG_PREFIX ?= ghcr.io/${DOCKER_USER}/opentelemetry-operator IMG_REPO ?= opentelemetry-operator IMG ?= ${IMG_PREFIX}/${IMG_REPO}:${VERSION} BUNDLE_IMG ?= ${IMG_PREFIX}/${IMG_REPO}-bundle:${VERSION} @@ -21,6 +27,9 @@ BUNDLE_IMG ?= ${IMG_PREFIX}/${IMG_REPO}-bundle:${VERSION} TARGETALLOCATOR_IMG_REPO ?= target-allocator TARGETALLOCATOR_IMG ?= ${IMG_PREFIX}/${TARGETALLOCATOR_IMG_REPO}:$(addprefix v,${VERSION}) +OPERATOROPAMPBRIDGE_IMG_REPO ?= operator-opamp-bridge +OPERATOROPAMPBRIDGE_IMG ?= ${IMG_PREFIX}/${OPERATOROPAMPBRIDGE_IMG_REPO}:$(addprefix v,${VERSION}) + # Options for 'bundle-build' ifneq ($(origin CHANNELS), undefined) BUNDLE_CHANNELS := --channels=$(CHANNELS) @@ -30,7 +39,7 @@ BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL) endif BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) -CRD_OPTIONS ?= "crd:generateEmbeddedObjectMeta=true" +CRD_OPTIONS ?= "crd:generateEmbeddedObjectMeta=true,maxDescLen=200" # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -49,12 +58,15 @@ else GOTEST_OPTS=-race -v endif +START_KIND_CLUSTER ?= true + KUBE_VERSION ?= 1.24 KIND_CONFIG ?= kind-$(KUBE_VERSION).yaml +KIND_CLUSTER_NAME ?= "otel-operator" -OPERATOR_SDK_VERSION ?= 1.23.0 +OPERATOR_SDK_VERSION ?= 1.29.0 -CERTMANAGER_VERSION ?= 1.8.0 +CERTMANAGER_VERSION ?= 1.10.0 ifndef ignore-not-found ignore-not-found = false @@ -65,9 +77,13 @@ LOCALBIN ?= $(shell pwd)/bin $(LOCALBIN): mkdir -p $(LOCALBIN) +## On MacOS, use gsed instead of sed, to make sed behavior +## consistent with Linux. +SED ?= $(shell which gsed 2>/dev/null || which sed) + .PHONY: ensure-generate-is-noop ensure-generate-is-noop: VERSION=$(OPERATOR_VERSION) -ensure-generate-is-noop: USER=open-telemetry +ensure-generate-is-noop: DOCKER_USER=open-telemetry ensure-generate-is-noop: set-image-controller generate bundle @# on make bundle config/manager/kustomization.yaml includes changes, which should be ignored for the below check @git restore config/manager/kustomization.yaml @@ -86,16 +102,26 @@ ci: test .PHONY: test test: generate fmt vet ensure-generate-is-noop envtest KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(KUBE_VERSION) -p path)" go test ${GOTEST_OPTS} ./... + cd cmd/otel-allocator && KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(KUBE_VERSION) -p path)" go test ${GOTEST_OPTS} ./... + cd cmd/operator-opamp-bridge && KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(KUBE_VERSION) -p path)" go test ${GOTEST_OPTS} ./... # Build manager binary .PHONY: manager manager: generate fmt vet - go build -o bin/manager main.go + CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(ARCH) go build -o bin/manager_${ARCH} -ldflags "${COMMON_LDFLAGS} ${OPERATOR_LDFLAGS}" main.go + +# Build target allocator binary +targetallocator: + cd cmd/otel-allocator && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(ARCH) go build -a -installsuffix cgo -o bin/targetallocator_${ARCH} -ldflags "${COMMON_LDFLAGS}" . + +# Build opamp bridge binary +operator-opamp-bridge: + cd cmd/operator-opamp-bridge && CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(ARCH) go build -a -installsuffix cgo -o bin/opampbridge_${ARCH} -ldflags "${COMMON_LDFLAGS}" . # Run against the configured Kubernetes cluster in ~/.kube/config .PHONY: run run: generate fmt vet manifests - ENABLE_WEBHOOKS=$(ENABLE_WEBHOOKS) go run -ldflags ${LD_FLAGS} ./main.go --zap-devel + ENABLE_WEBHOOKS=$(ENABLE_WEBHOOKS) go run -ldflags ${OPERATOR_LDFLAGS} ./main.go --zap-devel # Install CRDs into a cluster .PHONY: install @@ -116,6 +142,7 @@ set-image-controller: manifests kustomize .PHONY: deploy deploy: set-image-controller $(KUSTOMIZE) build config/default | kubectl apply -f - + go run hack/check-operator-ready.go 300 # Undeploy controller in the current Kubernetes context, configured in ~/.kube/config .PHONY: undeploy @@ -145,13 +172,14 @@ vet: # Run go lint against code .PHONY: lint -lint: - golangci-lint run - cd cmd/otel-allocator && golangci-lint run +lint: golangci-lint + $(GOLANGCI_LINT) run + cd cmd/otel-allocator && $(GOLANGCI_LINT) run + cd cmd/operator-opamp-bridge && $(GOLANGCI_LINT) run # Generate code .PHONY: generate -generate: controller-gen api-docs +generate: controller-gen $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." # end-to-tests @@ -159,64 +187,135 @@ generate: controller-gen api-docs e2e: $(KUTTL) test + +# instrumentation end-to-tests +.PHONY: e2e-instrumentation +e2e-instrumentation: + $(KUTTL) test --config kuttl-test-instrumentation.yaml + +# end-to-end-test for PrometheusCR E2E tests +.PHONY: e2e-prometheuscr +e2e-prometheuscr: + $(KUTTL) test --config kuttl-test-prometheuscr.yaml + # end-to-end-test for testing upgrading .PHONY: e2e-upgrade -e2e-upgrade: +e2e-upgrade: undeploy $(KUTTL) test --config kuttl-test-upgrade.yaml +# end-to-end-test for testing autoscale +.PHONY: e2e-autoscale +e2e-autoscale: + $(KUTTL) test --config kuttl-test-autoscale.yaml + +# end-to-end-test for testing pdb support +.PHONY: e2e-pdb +e2e-pdb: + $(KUTTL) test --config kuttl-test-pdb.yaml + +# end-to-end-test for testing OpenShift cases +.PHONY: e2e-openshift +e2e-openshift: + $(KUTTL) test --config kuttl-test-openshift.yaml + .PHONY: e2e-log-operator e2e-log-operator: kubectl get pod -n opentelemetry-operator-system | grep "opentelemetry-operator" | awk '{print $$1}' | xargs -I {} kubectl logs -n opentelemetry-operator-system {} manager kubectl get deploy -A +# end-to-tests for multi-instrumentation +.PHONY: e2e-multi-instrumentation +e2e-multi-instrumentation: + $(KUTTL) test --config kuttl-test-multi-instr.yaml + +# OpAMPBridge CR end-to-tests +.PHONY: e2e-opampbridge +e2e-opampbridge: + $(KUTTL) test --config kuttl-test-opampbridge.yaml + .PHONY: prepare-e2e -prepare-e2e: kuttl set-test-image-vars set-image-controller container container-target-allocator start-kind install-metrics-server load-image-all - mkdir -p tests/_build/crds tests/_build/manifests - $(KUSTOMIZE) build config/default -o tests/_build/manifests/01-opentelemetry-operator.yaml - $(KUSTOMIZE) build config/crd -o tests/_build/crds/ +prepare-e2e: kuttl set-image-controller container container-target-allocator container-operator-opamp-bridge start-kind cert-manager install-metrics-server install-targetallocator-prometheus-crds load-image-all deploy + TARGETALLOCATOR_IMG=$(TARGETALLOCATOR_IMG) OPERATOROPAMPBRIDGE_IMG=$(OPERATOROPAMPBRIDGE_IMG) OPERATOR_IMG=$(IMG) SED_BIN="$(SED)" ./hack/modify-test-images.sh + +.PHONY: enable-prometheus-feature-flag +enable-prometheus-feature-flag: + $(SED) -i "s#--feature-gates=+operator.autoinstrumentation.go#--feature-gates=+operator.autoinstrumentation.go,+operator.observability.prometheus#g" config/default/manager_auth_proxy_patch.yaml + .PHONY: scorecard-tests scorecard-tests: operator-sdk $(OPERATOR_SDK) scorecard -w=5m bundle || (echo "scorecard test failed" && exit 1) -.PHONY: set-test-image-vars -set-test-image-vars: - $(eval IMG=local/opentelemetry-operator:e2e) - $(eval TARGETALLOCATOR_IMG=local/opentelemetry-operator-targetallocator:e2e) # Build the container image, used only for local dev purposes # buildx is used to ensure same results for arm based systems (m1/2 chips) .PHONY: container -container: - docker buildx build --platform linux/${ARCH} -t ${IMG} --build-arg VERSION_PKG=${VERSION_PKG} --build-arg VERSION=${VERSION} --build-arg VERSION_DATE=${VERSION_DATE} --build-arg OTELCOL_VERSION=${OTELCOL_VERSION} --build-arg TARGETALLOCATOR_VERSION=${TARGETALLOCATOR_VERSION} --build-arg AUTO_INSTRUMENTATION_JAVA_VERSION=${AUTO_INSTRUMENTATION_JAVA_VERSION} --build-arg AUTO_INSTRUMENTATION_NODEJS_VERSION=${AUTO_INSTRUMENTATION_NODEJS_VERSION} --build-arg AUTO_INSTRUMENTATION_PYTHON_VERSION=${AUTO_INSTRUMENTATION_PYTHON_VERSION} --build-arg AUTO_INSTRUMENTATION_DOTNET_VERSION=${AUTO_INSTRUMENTATION_DOTNET_VERSION} . +container: GOOS = linux +container: manager + docker build -t ${IMG} . # Push the container image, used only for local dev purposes .PHONY: container-push container-push: docker push ${IMG} +.PHONY: container-target-allocator-push +container-target-allocator-push: + docker push ${TARGETALLOCATOR_IMG} + .PHONY: container-target-allocator -container-target-allocator: - docker buildx build --platform linux/${ARCH} -t ${TARGETALLOCATOR_IMG} cmd/otel-allocator +container-target-allocator: GOOS = linux +container-target-allocator: targetallocator + docker build -t ${TARGETALLOCATOR_IMG} cmd/otel-allocator + +.PHONY: container-operator-opamp-bridge +container-operator-opamp-bridge: GOOS = linux +container-operator-opamp-bridge: operator-opamp-bridge + docker build -t ${OPERATOROPAMPBRIDGE_IMG} cmd/operator-opamp-bridge .PHONY: start-kind start-kind: - kind create cluster --config $(KIND_CONFIG) +ifeq (true,$(START_KIND_CLUSTER)) + kind create cluster --name $(KIND_CLUSTER_NAME) --config $(KIND_CONFIG) +endif .PHONY: install-metrics-server install-metrics-server: ./hack/install-metrics-server.sh +.PHONY: install-prometheus-operator +install-prometheus-operator: + ./hack/install-prometheus-operator.sh + +# This only installs the CRDs Target Allocator supports +.PHONY: install-targetallocator-prometheus-crds +install-targetallocator-prometheus-crds: + ./hack/install-targetallocator-prometheus-crds.sh + .PHONY: load-image-all -load-image-all: load-image-operator load-image-target-allocator +load-image-all: load-image-operator load-image-target-allocator load-image-operator-opamp-bridge .PHONY: load-image-operator -load-image-operator: - kind load docker-image local/opentelemetry-operator:e2e +load-image-operator: container +ifeq (true,$(START_KIND_CLUSTER)) + kind load --name $(KIND_CLUSTER_NAME) docker-image $(IMG) +else + $(MAKE) container-push +endif + .PHONY: load-image-target-allocator -load-image-target-allocator: - kind load docker-image ${TARGETALLOCATOR_IMG} +load-image-target-allocator: container-target-allocator +ifeq (true,$(START_KIND_CLUSTER)) + kind load --name $(KIND_CLUSTER_NAME) docker-image $(TARGETALLOCATOR_IMG) +else + $(MAKE) container-target-allocator-push +endif + + +.PHONY: load-image-operator-opamp-bridge +load-image-operator-opamp-bridge: + kind load --name $(KIND_CLUSTER_NAME) docker-image ${OPERATOROPAMPBRIDGE_IMG} .PHONY: cert-manager cert-manager: cmctl @@ -244,20 +343,25 @@ cmctl: KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest +CHLOGGEN ?= $(LOCALBIN)/chloggen +GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint -## Tool Versions -KUSTOMIZE_VERSION ?= v4.5.5 -CONTROLLER_TOOLS_VERSION ?= v0.9.2 +KUSTOMIZE_VERSION ?= v5.0.3 +CONTROLLER_TOOLS_VERSION ?= v0.12.0 +GOLANGCI_LINT_VERSION ?= v1.54.0 .PHONY: kustomize kustomize: ## Download kustomize locally if necessary. - $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v4,$(KUSTOMIZE_VERSION)) + $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) + +golangci-lint: ## Download golangci-lint locally if necessary. + $(call go-get-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) .PHONY: controller-gen controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. $(CONTROLLER_GEN): $(LOCALBIN) - test -s $(LOCALBIN)/controller-gen || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) .PHONY: envtest envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. @@ -337,6 +441,17 @@ bundle: kustomize operator-sdk manifests set-image-controller $(OPERATOR_SDK) generate kustomize manifests -q $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS) $(OPERATOR_SDK) bundle validate ./bundle + ./hack/ignore-createdAt-bundle.sh + +.PHONY: reset +reset: kustomize operator-sdk manifests + $(MAKE) VERSION=${OPERATOR_VERSION} set-image-controller + $(OPERATOR_SDK) generate kustomize manifests -q + $(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle -q --overwrite --version ${OPERATOR_VERSION} $(BUNDLE_METADATA_OPTS) + $(OPERATOR_SDK) bundle validate ./bundle + ./hack/ignore-createdAt-bundle.sh + git checkout config/manager/kustomization.yaml + OPERATOR_IMG=local/opentelemetry-operator:e2e TARGETALLOCATOR_IMG=local/opentelemetry-operator-targetallocator:e2e OPERATOROPAMPBRIDGE_IMG=local/opentelemetry-operator-opamp-bridge:e2e DEFAULT_OPERATOR_IMG=$(IMG) DEFAULT_TARGETALLOCATOR_IMG=$(TARGETALLOCATOR_IMG) DEFAULT_OPERATOROPAMPBRIDGE_IMG=$(OPERATOROPAMPBRIDGE_IMG) SED_BIN="$(SED)" ./hack/modify-test-images.sh # Build the bundle image, used only for local dev purposes .PHONY: bundle-build @@ -355,3 +470,68 @@ api-docs: crdoc kustomize $(KUSTOMIZE) build config/crd -o $$TMP_DIR/crd-output.yaml ;\ $(CRDOC) --resources $$TMP_DIR/crd-output.yaml --output docs/api.md ;\ } + + +.PHONY: chlog-install +chlog-install: $(CHLOGGEN) +$(CHLOGGEN): $(LOCALBIN) + GOBIN=$(LOCALBIN) go install go.opentelemetry.io/build-tools/chloggen@v0.11.0 + +FILENAME?=$(shell git branch --show-current) +.PHONY: chlog-new +chlog-new: chlog-install + $(CHLOGGEN) new --filename $(FILENAME) + +.PHONY: chlog-validate +chlog-validate: chlog-install + $(CHLOGGEN) validate + +.PHONY: chlog-preview +chlog-preview: chlog-install + $(CHLOGGEN) update --dry + +.PHONY: chlog-update +chlog-update: chlog-install + $(CHLOGGEN) update --version $(VERSION) + + +.PHONY: opm +OPM = ./bin/opm +opm: ## Download opm locally if necessary. +ifeq (,$(wildcard $(OPM))) +ifeq (,$(shell which opm 2>/dev/null)) + @{ \ + set -e ;\ + mkdir -p $(dir $(OPM)) ;\ + OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ + curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.28.0/$${OS}-$${ARCH}-opm ;\ + chmod +x $(OPM) ;\ + } +else +OPM = $(shell which opm) +endif +endif + +# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0). +# These images MUST exist in a registry and be pull-able. +BUNDLE_IMGS ?= $(BUNDLE_IMG) + +# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0). +CATALOG_IMG ?= ${IMG_PREFIX}/${IMG_REPO}-catalog:${VERSION} + +# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image. +ifneq ($(origin CATALOG_BASE_IMG), undefined) +FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG) +endif + +# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'. +# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see: +# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator +.PHONY: catalog-build +catalog-build: opm bundle-build bundle-push ## Build a catalog image. + $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) + +# Push the catalog image. +.PHONY: catalog-push +catalog-push: ## Push a catalog image. + docker push $(CATALOG_IMG) diff --git a/PROJECT b/PROJECT index bd15742a02..610235b46f 100644 --- a/PROJECT +++ b/PROJECT @@ -30,4 +30,16 @@ resources: webhooks: defaulting: true webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: opentelemetry.io + kind: OpAMPBridge + path: github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1 + version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 version: "3" diff --git a/README.md b/README.md index ffcf40168c..fc240c38b3 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,20 @@ # OpenTelemetry Operator for Kubernetes -The OpenTelemetry Operator is an implementation of a [Kubernetes Operator](https://coreos.com/operators/). +The OpenTelemetry Operator is an implementation of a [Kubernetes Operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). The operator manages: * [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector) -* auto-instrumentation of the workloads using OpenTelemetry instrumentation libraries +* [auto-instrumentation](https://opentelemetry.io/docs/concepts/instrumentation/automatic/) of the workloads using OpenTelemetry instrumentation libraries ## Documentation * [API docs](./docs/api.md) +## Helm Charts + +You can install Opentelemetry Operator via [Helm Chart](https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-operator) from the opentelemetry-helm-charts repository. More information is available in [here](https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-operator). + ## Getting started To install the operator in an existing cluster, make sure you have [`cert-manager` installed](https://cert-manager.io/docs/installation/) and run: @@ -44,29 +48,28 @@ spec: timeout: 10s exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] processors: [] - exporters: [logging] + exporters: [debug] EOF ``` **_WARNING:_** Until the OpenTelemetry Collector format is stable, changes may be required in the above example to remain compatible with the latest version of the OpenTelemetry Collector image being referenced. -This will create an OpenTelemetry Collector instance named `simplest`, exposing a `jaeger-grpc` port to consume spans from your instrumented applications and exporting those spans via `logging`, which writes the spans to the console (`stdout`) of the OpenTelemetry Collector instance that receives the span. +This will create an OpenTelemetry Collector instance named `simplest`, exposing a `jaeger-grpc` port to consume spans from your instrumented applications and exporting those spans via `debug`, which writes the spans to the console (`stdout`) of the OpenTelemetry Collector instance that receives the span. The `config` node holds the `YAML` that should be passed down as-is to the underlying OpenTelemetry Collector instances. Refer to the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector) documentation for a reference of the possible entries. -At this point, the Operator does *not* validate the contents of the configuration file: if the configuration is invalid, the instance will still be created but the underlying OpenTelemetry Collector might crash. +> 🚨 **NOTE:** At this point, the Operator does *not* validate the contents of the configuration file: if the configuration is invalid, the instance will still be created but the underlying OpenTelemetry Collector might crash. The Operator does examine the configuration file to discover configured receivers and their ports. If it finds receivers with ports, it creates a pair of kubernetes services, one headless, exposing those ports within the cluster. The headless service contains a `service.beta.openshift.io/serving-cert-secret-name` annotation that will cause OpenShift to create a secret containing a certificate and key. This secret can be mounted as a volume and the certificate and key used in those receivers' TLS configurations. - ### Upgrades As noted above, the OpenTelemetry Collector format is continuing to evolve. However, a best-effort attempt is made to upgrade all managed `OpenTelemetryCollector` resources. @@ -80,11 +83,17 @@ The default and only other acceptable value for `.Spec.UpgradeStrategy` is `auto ### Deployment modes -The `CustomResource` for the `OpenTelemetryCollector` exposes a property named `.Spec.Mode`, which can be used to specify whether the collector should run as a `DaemonSet`, `Sidecar`, or `Deployment` (default). Look at [this sample](https://github.com/open-telemetry/opentelemetry-operator/blob/main/tests/e2e/daemonset-features/00-install.yaml) for reference. +The `CustomResource` for the `OpenTelemetryCollector` exposes a property named `.Spec.Mode`, which can be used to specify whether the Collector should run as a [`DaemonSet`](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/), [`Sidecar`](https://kubernetes.io/docs/concepts/workloads/pods/#workload-resources-for-managing-pods), [`StatefulSet`](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) or [`Deployment`](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) (default). + +See below for examples of each deployment mode: +- [`Deployment`](https://github.com/open-telemetry/opentelemetry-operator/blob/main/tests/e2e/ingress/00-install.yaml) +- [`DaemonSet`](https://github.com/open-telemetry/opentelemetry-operator/blob/main/tests/e2e/daemonset-features/01-install.yaml) +- [`StatefulSet`](https://github.com/open-telemetry/opentelemetry-operator/blob/main/tests/e2e/smoke-statefulset/00-install.yaml) +- [`Sidecar`](https://github.com/open-telemetry/opentelemetry-operator/blob/main/tests/e2e/instrumentation-python/00-install-collector.yaml) #### Sidecar injection -A sidecar with the OpenTelemetry Collector can be injected into pod-based workloads by setting the pod annotation `sidecar.opentelemetry.io/inject` to either `"true"`, or to the name of a concrete `OpenTelemetryCollector` from the same namespace, like in the following example: +A sidecar with the OpenTelemetry Collector can be injected into pod-based workloads by setting the pod annotation `sidecar.opentelemetry.io/inject` to either `"true"`, or to the name of a concrete `OpenTelemetryCollector`, like in the following example: ```yaml kubectl apply -f - < 🚨 **NOTE**: Go auto-instrumentation **does not** support multicontainer pods. When injecting Go auto-instrumentation the first pod should be the only pod you want instrumented. + +#### Multi-container pods with multiple instrumentations + +Works only when `operator.autoinstrumentation.multi-instrumentation` feature is `enabled`. + +Annotations defining which language instrumentation will be injected are required. When feature is enabled, specific for Instrumentation language containers annotations are used: + +Java: +```bash +instrumentation.opentelemetry.io/java-container-names: "java1,java2" +``` + +NodeJS: +```bash +instrumentation.opentelemetry.io/nodejs-container-names: "nodejs1,nodejs2" +``` + +Python: +```bash +instrumentation.opentelemetry.io/python-container-names: "python1,python3" +``` + +DotNet: +```bash +instrumentation.opentelemetry.io/dotnet-container-names: "dotnet1,dotnet2" +``` + +Go: +```bash +instrumentation.opentelemetry.io/go-container-names: "go1" +``` + +ApacheHttpD: +```bash +instrumentation.opentelemetry.io/apache-httpd-container-names: "apache1,apache2" +``` + +SDK: +```bash +instrumentation.opentelemetry.io/sdk-container-names: "app1,app2" +``` + +If language instrumentation specific container names are not specified, instrumentation is performed on the first container available in the pod spec (only if single instrumentation injection is configured). + +In some cases containers in the pod are using different technologies. It becomes necessary to specify language instrumentation for container(s) on which this injection must be performed. + +For this, we will use language instrumentation specific container names annotation for which we will indicate one or more container names (`.spec.containers.name`) on which the injection must be made: + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment-with-multi-containers-multi-instrumentations +spec: + selector: + matchLabels: + app: my-pod-with-multi-containers-multi-instrumentations + replicas: 1 + template: + metadata: + labels: + app: my-pod-with-multi-containers-multi-instrumentations + annotations: + instrumentation.opentelemetry.io/inject-java: "true" + instrumentation.opentelemetry.io/java-container-names: "myapp,myapp2" + instrumentation.opentelemetry.io/inject-python: "true" + instrumentation.opentelemetry.io/python-container-names: "myapp3" + spec: + containers: + - name: myapp + image: myImage1 + - name: myapp2 + image: myImage2 + - name: myapp3 + image: myImage3 +``` + +In the above case, `myapp` and `myapp2` containers will be instrumented using Java and `myapp3` using Python instrumentation. + +**NOTE**: Go auto-instrumentation **does not** support multicontainer pods. When injecting Go auto-instrumentation the first container should be the only you want to instrument. + +**NOTE**: This type of instrumentation **does not** allow to instrument a container with multiple language instrumentations. + +**NOTE**: `instrumentation.opentelemetry.io/container-names` annotation is not used for this feature. + #### Use customized or vendor instrumentation By default, the operator uses upstream auto-instrumentation libraries. Custom auto-instrumentation can be configured by -overriding the image fields in a CR. +overriding the `image` fields in a CR. ```yaml apiVersion: opentelemetry.io/v1alpha1 @@ -290,19 +454,217 @@ spec: image: your-customized-auto-instrumentation-image:python dotnet: image: your-customized-auto-instrumentation-image:dotnet + go: + image: your-customized-auto-instrumentation-image:go + apacheHttpd: + image: your-customized-auto-instrumentation-image:apache-httpd + nginx: + image: your-customized-auto-instrumentation-image:nginx ``` The Dockerfiles for auto-instrumentation can be found in [autoinstrumentation directory](./autoinstrumentation). Follow the instructions in the Dockerfiles on how to build a custom container image. +#### Using Apache HTTPD autoinstrumentation + +For `Apache HTTPD` autoinstrumentation, by default, instrumentation assumes httpd version 2.4 and httpd configuration directory `/usr/local/apache2/conf` as it is in the official `Apache HTTPD` image (f.e. docker.io/httpd:latest). If you need to use version 2.2, or your HTTPD configuration directory is different, and or you need to adjust agent attributes, customize the instrumentation specification per following example: +```yaml +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: my-instrumentation + apache: + image: your-customized-auto-instrumentation-image:apache-httpd + version: 2.2 + configPath: /your-custom-config-path + attrs: + - name: ApacheModuleOtelMaxQueueSize + value: "4096" + - name: ... + value: ... +``` +List of all available attributes can be found at [otel-webserver-module](https://github.com/open-telemetry/opentelemetry-cpp-contrib/tree/main/instrumentation/otel-webserver-module) + +#### Using Nginx autoinstrumentation + +For `Nginx` autoinstrumentation, Nginx versions 1.22.0, 1.23.0, and 1.23.1 are supported at this time. The Nginx configuration file is expected to be `/etc/nginx/nginx.conf` by default, if it's different, see following example on how to change it. Instrumentation at this time also expects, that `conf.d` directory is present in the directory, where configuration file resides and that there is a `include /conf.d/*.conf;` directive in the `http { ... }` section of Nginx configuration file (like it is in the default configuration file of Nginx). You can also adjust OpenTelemetry SDK attributes. Example: + +```yaml +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: my-instrumentation + nginx: + image: your-customized-auto-instrumentation-image:nginx # if custom instrumentation image is needed + configFile: /my/custom-dir/custom-nginx.conf + attrs: + - name: NginxModuleOtelMaxQueueSize + value: "4096" + - name: ... + value: ... +``` +List of all available attributes can be found at [otel-webserver-module](https://github.com/open-telemetry/opentelemetry-cpp-contrib/tree/main/instrumentation/otel-webserver-module) + #### Inject OpenTelemetry SDK environment variables only -You can configure the OpenTelemetry SDK for applications which can't currently be autoinstrumented by using `inject-sdk` in place of (e.g.) `inject-python` or `inject-java`. This will inject environment variables like `OTEL_RESOURCE_ATTRIBUTES`, `OTEL_TRACES_SAMPLER`, and `OTEL_EXPORTER_OTLP_ENDPOINT`, that you can configure in the `Instrumentation`, but will not actually provide the SDK. +You can configure the OpenTelemetry SDK for applications which can't currently be autoinstrumented by using `inject-sdk` in place of `inject-python` or `inject-java`, for example. This will inject environment variables like `OTEL_RESOURCE_ATTRIBUTES`, `OTEL_TRACES_SAMPLER`, and `OTEL_EXPORTER_OTLP_ENDPOINT`, that you can configure in the `Instrumentation`, but will not actually provide the SDK. ```bash instrumentation.opentelemetry.io/inject-sdk: "true" ``` +#### Controlling Instrumentation Capabilities + +The operator allows specifying, via the feature gates, which languages the Instrumentation resource may instrument. +These feature gates must be passed to the operator via the `--feature-gates` flag. +The flag allows for a comma-delimited list of feature gate identifiers. +Prefix a gate with '-' to disable support for the corresponding language or multi instrumentation feature. +Prefixing a gate with '+' or no prefix will enable support for the corresponding language or multi instrumentation feature. +If a language is enabled by default its gate only needs to be supplied when disabling the gate. + +| Language | Gate | Default Value | +|---------------|---------------------------------------------|---------------| +| Java | `operator.autoinstrumentation.java` | enabled | +| NodeJS | `operator.autoinstrumentation.nodejs` | enabled | +| Python | `operator.autoinstrumentation.python` | enabled | +| DotNet | `operator.autoinstrumentation.dotnet` | enabled | +| ApacheHttpD | `operator.autoinstrumentation.apache-httpd` | enabled | +| Go | `operator.autoinstrumentation.go` | disabled | +| Nginx | `operator.autoinstrumentation.nginx` | disabled | + +Language not specified in the table are always supported and cannot be disabled. + +OpenTelemetry Operator allows to instrument multiple containers using multiple language specific instrumentations. +These features can be enabled using `operator.autoinstrumentation.multi-instrumentation` flag when installing the Operator via Helm. By default flag is `disabled`. For example: + +```sh +helm install opentelemetry-operator open-telemetry/opentelemetry-operator --set manager.featureGates=operator.autoinstrumentation.multi-instrumentation +``` + +For more information about multi-instrumentation feature capabilities please see [Multi-container pods with multiple instrumentations](#Multi-container-pods-with-multiple-instrumentations). + +### Target Allocator + +The OpenTelemetry Operator comes with an optional component, the [Target Allocator](/cmd/otel-allocator/README.md) (TA). When creating an OpenTelemetryCollector Custom Resource (CR) and setting the TA as enabled, the Operator will create a new deployment and service to serve specific `http_sd_config` directives for each Collector pod as part of that CR. It will also rewrite the Prometheus receiver configuration in the CR, so that it uses the deployed target allocator. The following example shows how to get started with the Target Allocator: + +```yaml +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: collector-with-ta +spec: + mode: statefulset + targetAllocator: + enabled: true + config: | + receivers: + prometheus: + config: + scrape_configs: + - job_name: 'otel-collector' + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888' ] + metric_relabel_configs: + - action: labeldrop + regex: (id|name) + replacement: $$1 + - action: labelmap + regex: label_(.+) + replacement: $$1 + + exporters: + debug: + + service: + pipelines: + metrics: + receivers: [prometheus] + processors: [] + exporters: [debug] +``` + +The usage of `$$` in the replacement keys in the example above is based on the information provided in the Prometheus receiver [README](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/prometheusreceiver/README.md) documentation, which states: +`Note: Since the collector configuration supports env variable substitution $ characters in your prometheus configuration are interpreted as environment variables. If you want to use $ characters in your prometheus configuration, you must escape them using $$.` + +Behind the scenes, the OpenTelemetry Operator will convert the Collector’s configuration after the reconciliation into the following: + +```yaml + receivers: + prometheus: + target_allocator: + endpoint: http://collector-with-ta-targetallocator:80 + interval: 30s + collector_id: $POD_NAME + + exporters: + debug: + + service: + pipelines: + metrics: + receivers: [prometheus] + processors: [] + exporters: [debug] +``` + +The OpenTelemetry Operator will also convert the Target Allocator's Prometheus configuration after the reconciliation into the following: + +```yaml + config: + scrape_configs: + - job_name: otel-collector + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888' ] + metric_relabel_configs: + - action: labeldrop + regex: (id|name) + replacement: $1 + - action: labelmap + regex: label_(.+) + replacement: $1 +``` + +Note that in this case, the Operator replaces "$$" with a single "$" in the replacement keys. This is because the collector supports environment variable substitution, whereas the TA (Target Allocator) does not. Therefore, to ensure compatibility, the TA configuration should only contain a single "$" symbol. + +More info on the TargetAllocator can be found [here](cmd/otel-allocator/README.md). + +#### Using Prometheus Custom Resources for service discovery + +The target allocator can use Custom Resources from the prometheus-operator ecosystem, like ServiceMonitors and PodMonitors, for service discovery, performing +a function analogous to that of prometheus-operator itself. This is enabled via the `prometheusCR` section in the Collector CR. + +See below for a minimal example: + +```yaml +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: collector-with-ta-prometheus-cr +spec: + mode: statefulset + targetAllocator: + enabled: true + serviceAccount: everything-prometheus-operator-needs + prometheusCR: + enabled: true + config: | + receivers: + prometheus: + config: + + exporters: + debug: + + service: + pipelines: + metrics: + receivers: [prometheus] + processors: [] + exporters: [debug] +``` + ## Compatibility matrix ### OpenTelemetry Operator vs. OpenTelemetry Collector @@ -322,38 +684,44 @@ We use `cert-manager` for some features of this operator and the third column sh The OpenTelemetry Operator *might* work on versions outside of the given range, but when opening new issues, please make sure to test your scenario on a supported version. -| OpenTelemetry Operator | Kubernetes | Cert-Manager | -|------------------------|----------------------|----------------------| -| v0.61.0 | v1.19 to v1.25 | v1 | -| v0.60.0 | v1.19 to v1.25 | v1 | -| v0.59.0 | v1.19 to v1.24 | v1 | -| v0.58.0 | v1.19 to v1.24 | v1 | -| v0.57.2 | v1.19 to v1.24 | v1 | -| v0.56.0 | v1.19 to v1.24 | v1 | -| v0.55.0 | v1.19 to v1.24 | v1 | -| v0.54.0 | v1.19 to v1.24 | v1 | -| v0.53.0 | v1.19 to v1.24 | v1 | -| v0.52.0 | v1.19 to v1.23 | v1 | -| v0.51.0 | v1.19 to v1.23 | v1alpha2 | -| v0.50.0 | v1.19 to v1.23 | v1alpha2 | -| v0.49.0 | v1.19 to v1.23 | v1alpha2 | -| v0.48.0 | v1.19 to v1.23 | v1alpha2 | -| v0.47.0 | v1.19 to v1.23 | v1alpha2 | -| v0.46.0 | v1.19 to v1.23 | v1alpha2 | -| v0.45.0 | v1.21 to v1.23 | v1alpha2 | -| v0.44.0 | v1.21 to v1.23 | v1alpha2 | -| v0.43.0 | v1.21 to v1.23 | v1alpha2 | -| v0.42.0 | v1.21 to v1.23 | v1alpha2 | -| v0.41.1 | v1.21 to v1.23 | v1alpha2 | - +| OpenTelemetry Operator | Kubernetes | Cert-Manager | +|------------------------|----------------------|---------------------| +| v0.90.0 | v1.23 to v1.28 | v1 | +| v0.89.0 | v1.23 to v1.28 | v1 | +| v0.88.0 | v1.23 to v1.28 | v1 | +| v0.87.0 | v1.23 to v1.28 | v1 | +| v0.86.0 | v1.23 to v1.28 | v1 | +| v0.85.0 | v1.19 to v1.28 | v1 | +| v0.84.0 | v1.19 to v1.28 | v1 | +| v0.83.0 | v1.19 to v1.27 | v1 | +| v0.82.0 | v1.19 to v1.27 | v1 | +| v0.81.0 | v1.19 to v1.27 | v1 | +| 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 | +| v0.76.1 | v1.19 to v1.26 | v1 | +| v0.75.0 | v1.19 to v1.26 | v1 | +| v0.74.0 | v1.19 to v1.26 | v1 | +| v0.73.0 | v1.19 to v1.26 | v1 | +| v0.72.0 | v1.19 to v1.26 | v1 | +| v0.71.0 | v1.19 to v1.25 | v1 | +| v0.70.0 | v1.19 to v1.25 | v1 | +| v0.69.0 | v1.19 to v1.25 | v1 | +| v0.68.0 | v1.19 to v1.25 | v1 | ## Contributing and Developing Please see [CONTRIBUTING.md](CONTRIBUTING.md). +In addition to the [core responsibilities](https://github.com/open-telemetry/community/blob/main/community-membership.md) the operator project requires approvers and maintainers to be responsible for releasing the project. See [RELEASE.md](./RELEASE.md) for more information and release schedule. + Approvers ([@open-telemetry/operator-approvers](https://github.com/orgs/open-telemetry/teams/operator-approvers)): +- [Benedikt Bongartz](https://github.com/frzifus), Red Hat +- [Tyler Helmuth](https://github.com/TylerHelmuth), Honeycomb - [Yuri Oliveira Sa](https://github.com/yuriolisa), Red Hat +- [Mikołaj Świątek](https://github.com/swiatekm-sumo), Sumo Logic Emeritus Approvers: @@ -364,9 +732,15 @@ Emeritus Approvers: - [Owais Lone](https://github.com/owais), Splunk - [Pablo Baeyens](https://github.com/mx-psi), DataDog +Target Allocator Maintainers ([@open-telemetry/operator-ta-maintainers](https://github.com/orgs/open-telemetry/teams/operator-ta-maintainers)): + +- [Anthony Mirabella](https://github.com/Aneurysm9), AWS +- [Kristina Pathak](https://github.com/kristinapathak), Lightstep +- [Sebastian Poxhofer](https://github.com/secustor) + Maintainers ([@open-telemetry/operator-maintainers](https://github.com/orgs/open-telemetry/teams/operator-maintainers)): -- [Juraci Paixão Kröhling](https://github.com/jpkrohling), Grafana Labs +- [Jacob Aronoff](https://github.com/jaronoff97), Lightstep - [Pavol Loffay](https://github.com/pavolloffay), Red Hat - [Vineeth Pothulapati](https://github.com/VineethReddy02), Timescale @@ -374,6 +748,7 @@ Emeritus Maintainers - [Alex Boten](https://github.com/codeboten), Lightstep - [Bogdan Drutu](https://github.com/BogdanDrutu), Splunk +- [Juraci Paixão Kröhling](https://github.com/jpkrohling), Grafana Labs - [Tigran Najaryan](https://github.com/tigrannajaryan), Splunk Learn more about roles in the [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md). @@ -383,7 +758,7 @@ Thanks to all the people who already contributed! [![Contributors][contributors-img]][contributors] ## License - + [Apache 2.0 License](./LICENSE). [github-workflow]: https://github.com/open-telemetry/opentelemetry-operator/actions diff --git a/RELEASE.md b/RELEASE.md index 21095ac66a..7d67c27039 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -3,25 +3,45 @@ Steps to release a new version of the OpenTelemetry Operator: 1. Change the `versions.txt`, so that it lists the target version of the OpenTelemetry Collector (operand), and the desired version for the target allocator and the operator. The `major.minor` should typically match, with the patch portion being possibly different. -1. Run `make bundle USER=open-telemetry VERSION=0.38.0`, using the version that will be released. -1. Change the compatibility matrix in the readme file, using the OpenTelemetry Operator version to be released and the current latest Kubernetes version as the latest supported version, with N-2 being the lower bound. Make sure that the CI is currently testing the latest Kubernetes version! -1. Add the changes to the changelog -1. Check the OpenTelemetry Collector's changelog and ensure migration steps are present in `pkg/collector/upgrade` -1. Once the changes above are merged and available in `main`, tag it with the desired version, prefixed with `v`: `v0.38.0` -1. The GitHub Workflow will take it from here, creating a GitHub release with the generated artifacts (manifests) and publishing the images -1. After the release, generate a new OLM bundle (`make bundle`) and create two PRs against the `Community Operators repositories`: - 1. one for the `community-operators-prod`, used by OLM on Kubernetes. Example: [`operator-framework/community-operators-prod`](https://github.com/redhat-openshift-ecosystem/community-operators-prod/pull/494) - 1. one for the `community-operators` directory, used by Operatorhub.io. Example: [`operator-framework/community-operators`](https://github.com/k8s-operatorhub/community-operators/pull/461) +2. Change the `autoinstrumentation-*` versions in `versions.txt` as per the latest supported versions in `autoinstrumentation/`. +3. Run `make bundle DOCKER_USER=open-telemetry VERSION=0.38.0`, using the version that will be released. +4. Change the compatibility matrix in the [readme](./README.md) file, using the OpenTelemetry Operator version to be released and the current latest Kubernetes version as the latest supported version. Remove the oldest entry. +5. Add the changes to the changelog. Manually add versions of all operator components. +6. Check the OpenTelemetry Collector's changelog and ensure migration steps are present in `pkg/collector/upgrade` +7. Once the changes above are merged and available in `main`, tag it with the desired version, prefixed with `v`: `v0.38.0` +8. The GitHub Workflow will take it from here, creating a GitHub release with the generated artifacts (manifests) and publishing the images +9. The GitHub Workflow, submits two pull requests to the Operator hub repositories. Make sure the pull requests are approved and merged. + 1. `community-operators-prod` is used by OLM on OpenShift. Example: [`operator-framework/community-operators-prod`](https://github.com/redhat-openshift-ecosystem/community-operators-prod/pull/494) + 1. `community-operators` is used by Operatorhub.io. Example: [`operator-framework/community-operators`](https://github.com/k8s-operatorhub/community-operators/pull/461) +10. Update release schedule table, by moving the current release manager to the end of the table with updated release version. ## Generating the changelog -Run this generator: +We now use the chloggen to generate the changelog, simply run the following to generate the Changelog: + ```bash -docker run --rm -v "${PWD}:/app" pavolloffay/gch:latest --oauth-token ${GH_WRITE_TOKEN} --owner open-telemetry --repo opentelemetry-operator --branch main +make chlog-update ``` -* Note that you must generate a Personal Access Token (PAT) and set `GH_WRITE_TOKEN` as that. -Remove the commits that are not relevant to users, like: -* CI or testing-specific commits (e2e, unit test, ...) -* bug fixes for problems that are not part of a release yet -* version bumps for internal dependencies +This will delete all entries (other than the template) in the `.chloggen` directory and create a populated Changelog.md entry. Make sure that the PR you are generating for the release has the `[chore]` prefix, otherwise CI will not pass. + + +## Release managers + +A release manager is the person responsible for a specific release. While the manager might request help from other folks, they are ultimately responsible for the success of a release. + +In order to have more people comfortable with the release process, and in order to decrease the burden on a small number of volunteers, all approvers and maintainers are release managers from time to time, listed under the Release Schedule section. That table is updated at every release, with the current manager adding themselves to the bottom of the table, removing themselves from the top of the table. + +## Release schedule + +The operator should be released within a week after the [OpenTelemetry collector release](https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/release.md#release-schedule). + +| Version | Release manager | +|---------|-----------------| +| v0.91.0 | @yuriolisa | +| v0.92.0 | @pavolloffay | +| v0.93.0 | @VineethReddy02 | +| v0.94.0 | @TylerHelmuth | +| v0.95.0 | @swiatekm-sumo | +| v0.96.0 | @frzifus | +| v0.97.0 | @jaronoff97 | diff --git a/apis/v1alpha1/allocation_strategy.go b/apis/v1alpha1/allocation_strategy.go new file mode 100644 index 0000000000..49c7945171 --- /dev/null +++ b/apis/v1alpha1/allocation_strategy.go @@ -0,0 +1,29 @@ +// 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 v1alpha1 + +type ( + // OpenTelemetryTargetAllocatorAllocationStrategy represent which strategy to distribute target to each collector + // +kubebuilder:validation:Enum=least-weighted;consistent-hashing + OpenTelemetryTargetAllocatorAllocationStrategy string +) + +const ( + // OpenTelemetryTargetAllocatorAllocationStrategyLeastWeighted targets will be distributed to collector with fewer targets currently assigned. + OpenTelemetryTargetAllocatorAllocationStrategyLeastWeighted OpenTelemetryTargetAllocatorAllocationStrategy = "least-weighted" + + // OpenTelemetryTargetAllocatorAllocationStrategyConsistentHashing targets will be consistently added to collectors, which allows a high-availability setup. + OpenTelemetryTargetAllocatorAllocationStrategyConsistentHashing OpenTelemetryTargetAllocatorAllocationStrategy = "consistent-hashing" +) diff --git a/apis/v1alpha1/collector_webhook.go b/apis/v1alpha1/collector_webhook.go new file mode 100644 index 0000000000..7b0cefddfa --- /dev/null +++ b/apis/v1alpha1/collector_webhook.go @@ -0,0 +1,358 @@ +// 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 v1alpha1 + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + autoscalingv2 "k8s.io/api/autoscaling/v2" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/validation" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/open-telemetry/opentelemetry-operator/internal/config" + ta "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" +) + +var ( + _ admission.CustomValidator = &CollectorWebhook{} + _ admission.CustomDefaulter = &CollectorWebhook{} +) + +// +kubebuilder:webhook:path=/mutate-opentelemetry-io-v1alpha1-opentelemetrycollector,mutating=true,failurePolicy=fail,groups=opentelemetry.io,resources=opentelemetrycollectors,verbs=create;update,versions=v1alpha1,name=mopentelemetrycollector.kb.io,sideEffects=none,admissionReviewVersions=v1 +// +kubebuilder:webhook:verbs=create;update,path=/validate-opentelemetry-io-v1alpha1-opentelemetrycollector,mutating=false,failurePolicy=fail,groups=opentelemetry.io,resources=opentelemetrycollectors,versions=v1alpha1,name=vopentelemetrycollectorcreateupdate.kb.io,sideEffects=none,admissionReviewVersions=v1 +// +kubebuilder:webhook:verbs=delete,path=/validate-opentelemetry-io-v1alpha1-opentelemetrycollector,mutating=false,failurePolicy=ignore,groups=opentelemetry.io,resources=opentelemetrycollectors,versions=v1alpha1,name=vopentelemetrycollectordelete.kb.io,sideEffects=none,admissionReviewVersions=v1 +// +kubebuilder:object:generate=false + +type CollectorWebhook struct { + logger logr.Logger + cfg config.Config + scheme *runtime.Scheme +} + +func (c CollectorWebhook) Default(ctx context.Context, obj runtime.Object) error { + otelcol, ok := obj.(*OpenTelemetryCollector) + if !ok { + return fmt.Errorf("expected an OpenTelemetryCollector, received %T", obj) + } + return c.defaulter(otelcol) +} + +func (c CollectorWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + otelcol, ok := obj.(*OpenTelemetryCollector) + if !ok { + return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", obj) + } + return c.validate(otelcol) +} + +func (c CollectorWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + otelcol, ok := newObj.(*OpenTelemetryCollector) + if !ok { + return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", newObj) + } + return c.validate(otelcol) +} + +func (c CollectorWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + otelcol, ok := obj.(*OpenTelemetryCollector) + if !ok || otelcol == nil { + return nil, fmt.Errorf("expected an OpenTelemetryCollector, received %T", obj) + } + return c.validate(otelcol) +} + +func (c CollectorWebhook) defaulter(r *OpenTelemetryCollector) error { + if len(r.Spec.Mode) == 0 { + r.Spec.Mode = ModeDeployment + } + if len(r.Spec.UpgradeStrategy) == 0 { + r.Spec.UpgradeStrategy = UpgradeStrategyAutomatic + } + + if r.Labels == nil { + r.Labels = map[string]string{} + } + if r.Labels["app.kubernetes.io/managed-by"] == "" { + r.Labels["app.kubernetes.io/managed-by"] = "opentelemetry-operator" + } + + // We can default to one because dependent objects Deployment and HorizontalPodAutoScaler + // default to 1 as well. + one := int32(1) + if r.Spec.Replicas == nil { + r.Spec.Replicas = &one + } + if r.Spec.TargetAllocator.Enabled && r.Spec.TargetAllocator.Replicas == nil { + r.Spec.TargetAllocator.Replicas = &one + } + + if r.Spec.MaxReplicas != nil || (r.Spec.Autoscaler != nil && r.Spec.Autoscaler.MaxReplicas != nil) { + if r.Spec.Autoscaler == nil { + r.Spec.Autoscaler = &AutoscalerSpec{} + } + + if r.Spec.Autoscaler.MaxReplicas == nil { + r.Spec.Autoscaler.MaxReplicas = r.Spec.MaxReplicas + } + if r.Spec.Autoscaler.MinReplicas == nil { + if r.Spec.MinReplicas != nil { + r.Spec.Autoscaler.MinReplicas = r.Spec.MinReplicas + } else { + r.Spec.Autoscaler.MinReplicas = r.Spec.Replicas + } + } + + if r.Spec.Autoscaler.TargetMemoryUtilization == nil && r.Spec.Autoscaler.TargetCPUUtilization == nil { + defaultCPUTarget := int32(90) + r.Spec.Autoscaler.TargetCPUUtilization = &defaultCPUTarget + } + } + + // if pod isn't provided, we set MaxUnavailable 1, + // which will work even if there is just one replica, + // not blocking node drains but preventing out-of-the-box + // from disruption generated by them with replicas > 1 + if r.Spec.PodDisruptionBudget == nil { + r.Spec.PodDisruptionBudget = &PodDisruptionBudgetSpec{ + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: 1, + }, + } + } + + if r.Spec.Ingress.Type == IngressTypeRoute && r.Spec.Ingress.Route.Termination == "" { + r.Spec.Ingress.Route.Termination = TLSRouteTerminationTypeEdge + } + if r.Spec.Ingress.Type == IngressTypeNginx && r.Spec.Ingress.RuleType == "" { + r.Spec.Ingress.RuleType = IngressRuleTypePath + } + // If someone upgrades to a later version without upgrading their CRD they will not have a management state set. + // This results in a default state of unmanaged preventing reconciliation from continuing. + if len(r.Spec.ManagementState) == 0 { + r.Spec.ManagementState = ManagementStateManaged + } + return nil +} + +func (c CollectorWebhook) validate(r *OpenTelemetryCollector) (admission.Warnings, error) { + warnings := admission.Warnings{} + // validate volumeClaimTemplates + if r.Spec.Mode != ModeStatefulSet && len(r.Spec.VolumeClaimTemplates) > 0 { + return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'volumeClaimTemplates'", r.Spec.Mode) + } + + // validate tolerations + if r.Spec.Mode == ModeSidecar && len(r.Spec.Tolerations) > 0 { + return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'tolerations'", r.Spec.Mode) + } + + // validate priorityClassName + if r.Spec.Mode == ModeSidecar && r.Spec.PriorityClassName != "" { + return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'priorityClassName'", r.Spec.Mode) + } + + // validate affinity + if r.Spec.Mode == ModeSidecar && r.Spec.Affinity != nil { + return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'affinity'", r.Spec.Mode) + } + + if r.Spec.Mode == ModeSidecar && len(r.Spec.AdditionalContainers) > 0 { + return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'AdditionalContainers'", r.Spec.Mode) + } + + // validate target allocation + if r.Spec.TargetAllocator.Enabled && r.Spec.Mode != ModeStatefulSet { + return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the target allocation deployment", r.Spec.Mode) + } + + // validate Prometheus config for target allocation + if r.Spec.TargetAllocator.Enabled { + promCfg, err := ta.ConfigToPromConfig(r.Spec.Config) + if err != nil { + return warnings, fmt.Errorf("the OpenTelemetry Spec Prometheus configuration is incorrect, %w", err) + } + err = ta.ValidatePromConfig(promCfg, r.Spec.TargetAllocator.Enabled, featuregate.EnableTargetAllocatorRewrite.IsEnabled()) + if err != nil { + return warnings, fmt.Errorf("the OpenTelemetry Spec Prometheus configuration is incorrect, %w", err) + } + err = ta.ValidateTargetAllocatorConfig(r.Spec.TargetAllocator.PrometheusCR.Enabled, promCfg) + if err != nil { + return warnings, fmt.Errorf("the OpenTelemetry Spec Prometheus configuration is incorrect, %w", err) + } + } + + // validator port config + for _, p := range r.Spec.Ports { + nameErrs := validation.IsValidPortName(p.Name) + numErrs := validation.IsValidPortNum(int(p.Port)) + if len(nameErrs) > 0 || len(numErrs) > 0 { + return warnings, fmt.Errorf("the OpenTelemetry Spec Ports configuration is incorrect, port name '%s' errors: %s, num '%d' errors: %s", + p.Name, nameErrs, p.Port, numErrs) + } + } + + var maxReplicas *int32 + if r.Spec.Autoscaler != nil && r.Spec.Autoscaler.MaxReplicas != nil { + maxReplicas = r.Spec.Autoscaler.MaxReplicas + } + + // check deprecated .Spec.MaxReplicas if maxReplicas is not set + if maxReplicas == nil && r.Spec.MaxReplicas != nil { + warnings = append(warnings, "MaxReplicas is deprecated") + maxReplicas = r.Spec.MaxReplicas + } + + var minReplicas *int32 + if r.Spec.Autoscaler != nil && r.Spec.Autoscaler.MinReplicas != nil { + minReplicas = r.Spec.Autoscaler.MinReplicas + } + + // check deprecated .Spec.MinReplicas if minReplicas is not set + if minReplicas == nil { + if r.Spec.MinReplicas != nil { + warnings = append(warnings, "MinReplicas is deprecated") + minReplicas = r.Spec.MinReplicas + } else { + minReplicas = r.Spec.Replicas + } + } + + if r.Spec.Ingress.Type == IngressTypeNginx && r.Spec.Mode == ModeSidecar { + return warnings, fmt.Errorf("the OpenTelemetry Spec Ingress configuration is incorrect. Ingress can only be used in combination with the modes: %s, %s, %s", + ModeDeployment, ModeDaemonSet, ModeStatefulSet, + ) + } + + // validate autoscale with horizontal pod autoscaler + if maxReplicas != nil { + if *maxReplicas < int32(1) { + return warnings, fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, maxReplicas should be defined and one or more") + } + + if r.Spec.Replicas != nil && *r.Spec.Replicas > *maxReplicas { + return warnings, fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, replicas must not be greater than maxReplicas") + } + + if minReplicas != nil && *minReplicas > *maxReplicas { + return warnings, fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, minReplicas must not be greater than maxReplicas") + } + + if minReplicas != nil && *minReplicas < int32(1) { + return warnings, fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, minReplicas should be one or more") + } + + if r.Spec.Autoscaler != nil { + return warnings, checkAutoscalerSpec(r.Spec.Autoscaler) + } + } + + if r.Spec.Ingress.Type == IngressTypeNginx && r.Spec.Mode == ModeSidecar { + return warnings, fmt.Errorf("the OpenTelemetry Spec Ingress configuiration is incorrect. Ingress can only be used in combination with the modes: %s, %s, %s", + ModeDeployment, ModeDaemonSet, ModeStatefulSet, + ) + } + if r.Spec.Ingress.RuleType == IngressRuleTypeSubdomain && (r.Spec.Ingress.Hostname == "" || r.Spec.Ingress.Hostname == "*") { + return warnings, fmt.Errorf("a valid Ingress hostname has to be defined for subdomain ruleType") + } + + if r.Spec.LivenessProbe != nil { + if r.Spec.LivenessProbe.InitialDelaySeconds != nil && *r.Spec.LivenessProbe.InitialDelaySeconds < 0 { + return warnings, fmt.Errorf("the OpenTelemetry Spec LivenessProbe InitialDelaySeconds configuration is incorrect. InitialDelaySeconds should be greater than or equal to 0") + } + if r.Spec.LivenessProbe.PeriodSeconds != nil && *r.Spec.LivenessProbe.PeriodSeconds < 1 { + return warnings, fmt.Errorf("the OpenTelemetry Spec LivenessProbe PeriodSeconds configuration is incorrect. PeriodSeconds should be greater than or equal to 1") + } + if r.Spec.LivenessProbe.TimeoutSeconds != nil && *r.Spec.LivenessProbe.TimeoutSeconds < 1 { + return warnings, fmt.Errorf("the OpenTelemetry Spec LivenessProbe TimeoutSeconds configuration is incorrect. TimeoutSeconds should be greater than or equal to 1") + } + if r.Spec.LivenessProbe.SuccessThreshold != nil && *r.Spec.LivenessProbe.SuccessThreshold < 1 { + return warnings, fmt.Errorf("the OpenTelemetry Spec LivenessProbe SuccessThreshold configuration is incorrect. SuccessThreshold should be greater than or equal to 1") + } + if r.Spec.LivenessProbe.FailureThreshold != nil && *r.Spec.LivenessProbe.FailureThreshold < 1 { + return warnings, fmt.Errorf("the OpenTelemetry Spec LivenessProbe FailureThreshold configuration is incorrect. FailureThreshold should be greater than or equal to 1") + } + if r.Spec.LivenessProbe.TerminationGracePeriodSeconds != nil && *r.Spec.LivenessProbe.TerminationGracePeriodSeconds < 1 { + return warnings, fmt.Errorf("the OpenTelemetry Spec LivenessProbe TerminationGracePeriodSeconds configuration is incorrect. TerminationGracePeriodSeconds should be greater than or equal to 1") + } + } + + // validate updateStrategy for DaemonSet + if r.Spec.Mode != ModeDaemonSet && len(r.Spec.UpdateStrategy.Type) > 0 { + return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'updateStrategy'", r.Spec.Mode) + } + + return warnings, nil +} + +func checkAutoscalerSpec(autoscaler *AutoscalerSpec) error { + if autoscaler.Behavior != nil { + if autoscaler.Behavior.ScaleDown != nil && autoscaler.Behavior.ScaleDown.StabilizationWindowSeconds != nil && + *autoscaler.Behavior.ScaleDown.StabilizationWindowSeconds < int32(1) { + return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, scaleDown should be one or more") + } + + if autoscaler.Behavior.ScaleUp != nil && autoscaler.Behavior.ScaleUp.StabilizationWindowSeconds != nil && + *autoscaler.Behavior.ScaleUp.StabilizationWindowSeconds < int32(1) { + return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, scaleUp should be one or more") + } + } + if autoscaler.TargetCPUUtilization != nil && (*autoscaler.TargetCPUUtilization < int32(1) || *autoscaler.TargetCPUUtilization > int32(99)) { + return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, targetCPUUtilization should be greater than 0 and less than 100") + } + if autoscaler.TargetMemoryUtilization != nil && (*autoscaler.TargetMemoryUtilization < int32(1) || *autoscaler.TargetMemoryUtilization > int32(99)) { + return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, targetMemoryUtilization should be greater than 0 and less than 100") + } + + for _, metric := range autoscaler.Metrics { + if metric.Type != autoscalingv2.PodsMetricSourceType { + return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, metric type unsupported. Expected metric of source type Pod") + } + + // pod metrics target only support value and averageValue. + if metric.Pods.Target.Type == autoscalingv2.AverageValueMetricType { + if val, ok := metric.Pods.Target.AverageValue.AsInt64(); !ok || val < int64(1) { + return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, average value should be greater than 0") + } + } else if metric.Pods.Target.Type == autoscalingv2.ValueMetricType { + if val, ok := metric.Pods.Target.Value.AsInt64(); !ok || val < int64(1) { + return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, value should be greater than 0") + } + } else { + return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, invalid pods target type") + } + } + + return nil +} + +func SetupCollectorWebhook(mgr ctrl.Manager, cfg config.Config) error { + cvw := &CollectorWebhook{ + logger: mgr.GetLogger().WithValues("handler", "CollectorWebhook"), + scheme: mgr.GetScheme(), + cfg: cfg, + } + return ctrl.NewWebhookManagedBy(mgr). + For(&OpenTelemetryCollector{}). + WithValidator(cvw). + WithDefaulter(cvw). + Complete() +} diff --git a/apis/v1alpha1/collector_webhook_test.go b/apis/v1alpha1/collector_webhook_test.go new file mode 100644 index 0000000000..fd8763ebd5 --- /dev/null +++ b/apis/v1alpha1/collector_webhook_test.go @@ -0,0 +1,814 @@ +// 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 v1alpha1 + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes/scheme" + + "github.com/open-telemetry/opentelemetry-operator/internal/config" +) + +var ( + testScheme *runtime.Scheme = scheme.Scheme +) + +func TestOTELColDefaultingWebhook(t *testing.T) { + one := int32(1) + five := int32(5) + defaultCPUTarget := int32(90) + + if err := AddToScheme(testScheme); err != nil { + fmt.Printf("failed to register scheme: %v", err) + os.Exit(1) + } + + tests := []struct { + name string + otelcol OpenTelemetryCollector + expected OpenTelemetryCollector + }{ + { + name: "all fields default", + otelcol: OpenTelemetryCollector{}, + expected: OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeDeployment, + Replicas: &one, + UpgradeStrategy: UpgradeStrategyAutomatic, + ManagementState: ManagementStateManaged, + PodDisruptionBudget: &PodDisruptionBudgetSpec{ + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: 1, + }, + }, + }, + }, + }, + { + name: "provided values in spec", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeSidecar, + Replicas: &five, + UpgradeStrategy: "adhoc", + }, + }, + expected: OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeSidecar, + Replicas: &five, + UpgradeStrategy: "adhoc", + ManagementState: ManagementStateManaged, + PodDisruptionBudget: &PodDisruptionBudgetSpec{ + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: 1, + }, + }, + }, + }, + }, + { + name: "doesn't override unmanaged", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + ManagementState: ManagementStateUnmanaged, + Mode: ModeSidecar, + Replicas: &five, + UpgradeStrategy: "adhoc", + }, + }, + expected: OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeSidecar, + Replicas: &five, + UpgradeStrategy: "adhoc", + ManagementState: ManagementStateUnmanaged, + PodDisruptionBudget: &PodDisruptionBudgetSpec{ + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: 1, + }, + }, + }, + }, + }, + { + name: "Setting Autoscaler MaxReplicas", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Autoscaler: &AutoscalerSpec{ + MaxReplicas: &five, + MinReplicas: &one, + }, + }, + }, + expected: OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeDeployment, + Replicas: &one, + UpgradeStrategy: UpgradeStrategyAutomatic, + ManagementState: ManagementStateManaged, + Autoscaler: &AutoscalerSpec{ + TargetCPUUtilization: &defaultCPUTarget, + MaxReplicas: &five, + MinReplicas: &one, + }, + PodDisruptionBudget: &PodDisruptionBudgetSpec{ + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: 1, + }, + }, + }, + }, + }, + { + name: "MaxReplicas but no Autoscale", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &five, + }, + }, + expected: OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeDeployment, + Replicas: &one, + UpgradeStrategy: UpgradeStrategyAutomatic, + ManagementState: ManagementStateManaged, + Autoscaler: &AutoscalerSpec{ + TargetCPUUtilization: &defaultCPUTarget, + // webhook Default adds MaxReplicas to Autoscaler because + // OpenTelemetryCollector.Spec.MaxReplicas is deprecated. + MaxReplicas: &five, + MinReplicas: &one, + }, + MaxReplicas: &five, + PodDisruptionBudget: &PodDisruptionBudgetSpec{ + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: 1, + }, + }, + }, + }, + }, + { + name: "Missing route termination", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeDeployment, + Ingress: Ingress{ + Type: IngressTypeRoute, + }, + }, + }, + expected: OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeDeployment, + ManagementState: ManagementStateManaged, + Ingress: Ingress{ + Type: IngressTypeRoute, + Route: OpenShiftRoute{ + Termination: TLSRouteTerminationTypeEdge, + }, + }, + Replicas: &one, + UpgradeStrategy: UpgradeStrategyAutomatic, + PodDisruptionBudget: &PodDisruptionBudgetSpec{ + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: 1, + }, + }, + }, + }, + }, + { + name: "Defined PDB", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeDeployment, + PodDisruptionBudget: &PodDisruptionBudgetSpec{ + MinAvailable: &intstr.IntOrString{ + Type: intstr.String, + StrVal: "10%", + }, + }, + }, + }, + expected: OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeDeployment, + Replicas: &one, + UpgradeStrategy: UpgradeStrategyAutomatic, + ManagementState: ManagementStateManaged, + PodDisruptionBudget: &PodDisruptionBudgetSpec{ + MinAvailable: &intstr.IntOrString{ + Type: intstr.String, + StrVal: "10%", + }, + }, + }, + }, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + cvw := &CollectorWebhook{ + logger: logr.Discard(), + scheme: testScheme, + cfg: config.New( + config.WithCollectorImage("collector:v0.0.0"), + config.WithTargetAllocatorImage("ta:v0.0.0"), + ), + } + ctx := context.Background() + err := cvw.Default(ctx, &test.otelcol) + assert.NoError(t, err) + assert.Equal(t, test.expected, test.otelcol) + }) + } +} + +// TODO: a lot of these tests use .Spec.MaxReplicas and .Spec.MinReplicas. These fields are +// deprecated and moved to .Spec.Autoscaler. Fine to use these fields to test that old CRD is +// still supported but should eventually be updated. +func TestOTELColValidatingWebhook(t *testing.T) { + minusOne := int32(-1) + zero := int32(0) + zero64 := int64(0) + one := int32(1) + three := int32(3) + five := int32(5) + + tests := []struct { //nolint:govet + name string + otelcol OpenTelemetryCollector + expectedErr string + expectedWarnings []string + }{ + { + name: "valid empty spec", + otelcol: OpenTelemetryCollector{}, + }, + { + name: "valid full spec", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeStatefulSet, + MinReplicas: &one, + Replicas: &three, + MaxReplicas: &five, + UpgradeStrategy: "adhoc", + TargetAllocator: OpenTelemetryTargetAllocator{ + Enabled: true, + }, + Config: `receivers: + examplereceiver: + endpoint: "0.0.0.0:12345" + examplereceiver/settings: + endpoint: "0.0.0.0:12346" + prometheus: + config: + scrape_configs: + - job_name: otel-collector + scrape_interval: 10s + jaeger/custom: + protocols: + thrift_http: + endpoint: 0.0.0.0:15268 +`, + Ports: []v1.ServicePort{ + { + Name: "port1", + Port: 5555, + }, + { + Name: "port2", + Port: 5554, + Protocol: v1.ProtocolUDP, + }, + }, + Autoscaler: &AutoscalerSpec{ + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &three, + }, + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &five, + }, + }, + TargetCPUUtilization: &five, + }, + }, + }, + }, + { + name: "invalid mode with volume claim templates", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeSidecar, + VolumeClaimTemplates: []v1.PersistentVolumeClaim{{}, {}}, + }, + }, + expectedErr: "does not support the attribute 'volumeClaimTemplates'", + }, + { + name: "invalid mode with tolerations", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeSidecar, + Tolerations: []v1.Toleration{{}, {}}, + }, + }, + expectedErr: "does not support the attribute 'tolerations'", + }, + { + name: "invalid mode with target allocator", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeDeployment, + TargetAllocator: OpenTelemetryTargetAllocator{ + Enabled: true, + }, + }, + }, + expectedErr: "does not support the target allocation deployment", + }, + { + name: "invalid target allocator config", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeStatefulSet, + TargetAllocator: OpenTelemetryTargetAllocator{ + Enabled: true, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec Prometheus configuration is incorrect", + }, + { + name: "invalid port name", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Ports: []v1.ServicePort{ + { + // this port name contains a non alphanumeric character, which is invalid. + Name: "-test🦄port", + Port: 12345, + Protocol: v1.ProtocolTCP, + }, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec Ports configuration is incorrect", + }, + { + name: "invalid port name, too long", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Ports: []v1.ServicePort{ + { + Name: "aaaabbbbccccdddd", // len: 16, too long + Port: 5555, + }, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec Ports configuration is incorrect", + }, + { + name: "invalid port num", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Ports: []v1.ServicePort{ + { + Name: "aaaabbbbccccddd", // len: 15 + // no port set means it's 0, which is invalid + }, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec Ports configuration is incorrect", + }, + { + name: "invalid max replicas", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &zero, + }, + }, + expectedErr: "maxReplicas should be defined and one or more", + expectedWarnings: []string{"MaxReplicas is deprecated"}, + }, + { + name: "invalid replicas, greater than max", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &three, + Replicas: &five, + }, + }, + expectedErr: "replicas must not be greater than maxReplicas", + expectedWarnings: []string{"MaxReplicas is deprecated"}, + }, + { + name: "invalid min replicas, greater than max", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &three, + MinReplicas: &five, + }, + }, + expectedErr: "minReplicas must not be greater than maxReplicas", + expectedWarnings: []string{"MaxReplicas is deprecated", "MinReplicas is deprecated"}, + }, + { + name: "invalid min replicas, lesser than 1", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &three, + MinReplicas: &zero, + }, + }, + expectedErr: "minReplicas should be one or more", + expectedWarnings: []string{"MaxReplicas is deprecated", "MinReplicas is deprecated"}, + }, + { + name: "invalid autoscaler scale down", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &three, + Autoscaler: &AutoscalerSpec{ + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleDown: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &zero, + }, + }, + }, + }, + }, + expectedErr: "scaleDown should be one or more", + expectedWarnings: []string{"MaxReplicas is deprecated"}, + }, + { + name: "invalid autoscaler scale up", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &three, + Autoscaler: &AutoscalerSpec{ + Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ + ScaleUp: &autoscalingv2.HPAScalingRules{ + StabilizationWindowSeconds: &zero, + }, + }, + }, + }, + }, + expectedErr: "scaleUp should be one or more", + expectedWarnings: []string{"MaxReplicas is deprecated"}, + }, + { + name: "invalid autoscaler target cpu utilization", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &three, + Autoscaler: &AutoscalerSpec{ + TargetCPUUtilization: &zero, + }, + }, + }, + expectedErr: "targetCPUUtilization should be greater than 0 and less than 100", + expectedWarnings: []string{"MaxReplicas is deprecated"}, + }, + { + name: "autoscaler minReplicas is less than maxReplicas", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Autoscaler: &AutoscalerSpec{ + MaxReplicas: &one, + MinReplicas: &five, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec autoscale configuration is incorrect, minReplicas must not be greater than maxReplicas", + }, + { + name: "invalid autoscaler metric type", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &three, + Autoscaler: &AutoscalerSpec{ + Metrics: []MetricSpec{ + { + Type: autoscalingv2.ResourceMetricSourceType, + }, + }, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec autoscale configuration is incorrect, metric type unsupported. Expected metric of source type Pod", + expectedWarnings: []string{"MaxReplicas is deprecated"}, + }, + { + name: "invalid pod metric average value", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &three, + Autoscaler: &AutoscalerSpec{ + Metrics: []MetricSpec{ + { + Type: autoscalingv2.PodsMetricSourceType, + Pods: &autoscalingv2.PodsMetricSource{ + Metric: autoscalingv2.MetricIdentifier{ + Name: "custom1", + }, + Target: autoscalingv2.MetricTarget{ + Type: autoscalingv2.AverageValueMetricType, + AverageValue: resource.NewQuantity(int64(0), resource.DecimalSI), + }, + }, + }, + }, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec autoscale configuration is incorrect, average value should be greater than 0", + expectedWarnings: []string{"MaxReplicas is deprecated"}, + }, + { + name: "utilization target is not valid with pod metrics", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + MaxReplicas: &three, + Autoscaler: &AutoscalerSpec{ + Metrics: []MetricSpec{ + { + Type: autoscalingv2.PodsMetricSourceType, + Pods: &autoscalingv2.PodsMetricSource{ + Metric: autoscalingv2.MetricIdentifier{ + Name: "custom1", + }, + Target: autoscalingv2.MetricTarget{ + Type: autoscalingv2.UtilizationMetricType, + AverageUtilization: &one, + }, + }, + }, + }, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec autoscale configuration is incorrect, invalid pods target type", + expectedWarnings: []string{"MaxReplicas is deprecated"}, + }, + { + name: "invalid deployment mode incompabible with ingress settings", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeSidecar, + Ingress: Ingress{ + Type: IngressTypeNginx, + }, + }, + }, + expectedErr: fmt.Sprintf("Ingress can only be used in combination with the modes: %s, %s, %s", + ModeDeployment, ModeDaemonSet, ModeStatefulSet, + ), + }, + { + name: "invalid mode with priorityClassName", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeSidecar, + PriorityClassName: "test-class", + }, + }, + expectedErr: "does not support the attribute 'priorityClassName'", + }, + { + name: "invalid mode with affinity", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeSidecar, + Affinity: &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ + NodeSelectorTerms: []v1.NodeSelectorTerm{ + { + MatchExpressions: []v1.NodeSelectorRequirement{ + { + Key: "node", + Operator: v1.NodeSelectorOpIn, + Values: []string{"test-node"}, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectedErr: "does not support the attribute 'affinity'", + }, + { + name: "invalid InitialDelaySeconds", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + LivenessProbe: &Probe{ + InitialDelaySeconds: &minusOne, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec LivenessProbe InitialDelaySeconds configuration is incorrect", + }, + { + name: "invalid PeriodSeconds", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + LivenessProbe: &Probe{ + PeriodSeconds: &zero, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec LivenessProbe PeriodSeconds configuration is incorrect", + }, + { + name: "invalid TimeoutSeconds", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + LivenessProbe: &Probe{ + TimeoutSeconds: &zero, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec LivenessProbe TimeoutSeconds configuration is incorrect", + }, + { + name: "invalid SuccessThreshold", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + LivenessProbe: &Probe{ + SuccessThreshold: &zero, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec LivenessProbe SuccessThreshold configuration is incorrect", + }, + { + name: "invalid FailureThreshold", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + LivenessProbe: &Probe{ + FailureThreshold: &zero, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec LivenessProbe FailureThreshold configuration is incorrect", + }, + { + name: "invalid TerminationGracePeriodSeconds", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + LivenessProbe: &Probe{ + TerminationGracePeriodSeconds: &zero64, + }, + }, + }, + expectedErr: "the OpenTelemetry Spec LivenessProbe TerminationGracePeriodSeconds configuration is incorrect", + }, + { + name: "invalid AdditionalContainers", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeSidecar, + AdditionalContainers: []v1.Container{ + { + Name: "test", + }, + }, + }, + }, + expectedErr: "the OpenTelemetry Collector mode is set to sidecar, which does not support the attribute 'AdditionalContainers'", + }, + { + name: "missing ingress hostname for subdomain ruleType", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Ingress: Ingress{ + RuleType: IngressRuleTypeSubdomain, + }, + }, + }, + expectedErr: "a valid Ingress hostname has to be defined for subdomain ruleType", + }, + { + name: "invalid updateStrategy for Deployment mode", + otelcol: OpenTelemetryCollector{ + Spec: OpenTelemetryCollectorSpec{ + Mode: ModeDeployment, + UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDaemonSet{ + MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + }, + }, + }, + }, + expectedErr: "the OpenTelemetry Collector mode is set to deployment, which does not support the attribute 'updateStrategy'", + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + cvw := &CollectorWebhook{ + logger: logr.Discard(), + scheme: testScheme, + cfg: config.New( + config.WithCollectorImage("collector:v0.0.0"), + config.WithTargetAllocatorImage("ta:v0.0.0"), + ), + } + ctx := context.Background() + warnings, err := cvw.ValidateCreate(ctx, &test.otelcol) + if test.expectedErr == "" { + assert.NoError(t, err) + return + } + if len(test.expectedWarnings) == 0 { + assert.Empty(t, warnings, test.expectedWarnings) + } else { + assert.ElementsMatch(t, warnings, test.expectedWarnings) + } + assert.ErrorContains(t, err, test.expectedErr) + }) + } +} diff --git a/apis/v1alpha1/ingress_type.go b/apis/v1alpha1/ingress_type.go index 5ef8528b04..63ff858843 100644 --- a/apis/v1alpha1/ingress_type.go +++ b/apis/v1alpha1/ingress_type.go @@ -16,11 +16,48 @@ package v1alpha1 type ( // IngressType represents how a collector should be exposed (ingress vs route). - // +kubebuilder:validation:Enum=ingress + // +kubebuilder:validation:Enum=ingress;route IngressType string ) const ( // IngressTypeNginx specifies that an ingress entry should be created. IngressTypeNginx IngressType = "ingress" + // IngressTypeOpenshiftRoute specifies that an route entry should be created. + IngressTypeRoute IngressType = "route" +) + +type ( + // TLSRouteTerminationType is used to indicate which tls settings should be used. + // +kubebuilder:validation:Enum=insecure;edge;passthrough;reencrypt + TLSRouteTerminationType string +) + +const ( + // TLSRouteTerminationTypeInsecure indicates that insecure connections are allowed. + TLSRouteTerminationTypeInsecure TLSRouteTerminationType = "insecure" + // TLSRouteTerminationTypeEdge indicates that encryption should be terminated + // at the edge router. + TLSRouteTerminationTypeEdge TLSRouteTerminationType = "edge" + // TLSTerminationPassthrough indicates that the destination service is + // responsible for decrypting traffic. + TLSRouteTerminationTypePassthrough TLSRouteTerminationType = "passthrough" + // TLSTerminationReencrypt indicates that traffic will be decrypted on the edge + // and re-encrypt using a new certificate. + TLSRouteTerminationTypeReencrypt TLSRouteTerminationType = "reencrypt" +) + +// IngressRuleType defines how the collector receivers will be exposed in the Ingress. +// +// +kubebuilder:validation:Enum=path;subdomain +type IngressRuleType string + +const ( + // IngressRuleTypePath configures Ingress to use single host with multiple paths. + // This configuration might require additional ingress setting to rewrite paths. + IngressRuleTypePath IngressRuleType = "path" + + // IngressRuleTypeSubdomain configures Ingress to use multiple hosts - one for each exposed + // receiver port. The port name is used as a subdomain for the host defined in the Ingress e.g. otlp-http.example.com. + IngressRuleTypeSubdomain IngressRuleType = "subdomain" ) diff --git a/apis/v1alpha1/instrumentation_types.go b/apis/v1alpha1/instrumentation_types.go index d4d00bece9..0f3abf4203 100644 --- a/apis/v1alpha1/instrumentation_types.go +++ b/apis/v1alpha1/instrumentation_types.go @@ -16,6 +16,7 @@ package v1alpha1 import ( corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -30,6 +31,8 @@ type InstrumentationSpec struct { Resource Resource `json:"resource,omitempty"` // Propagators defines inter-process context propagation configuration. + // Values in this list will be set in the OTEL_PROPAGATORS env var. + // Enum=tracecontext;baggage;b3;b3multi;jaeger;xray;ottrace;none // +optional Propagators []Propagator `json:"propagators,omitempty"` @@ -58,6 +61,21 @@ type InstrumentationSpec struct { // DotNet defines configuration for DotNet auto-instrumentation. // +optional DotNet DotNet `json:"dotnet,omitempty"` + + // Go defines configuration for Go auto-instrumentation. + // When using Go auto-instrumentation you must provide a value for the OTEL_GO_AUTO_TARGET_EXE env var via the + // Instrumentation env vars or via the instrumentation.opentelemetry.io/otel-go-auto-target-exe pod annotation. + // Failure to set this value causes instrumentation injection to abort, leaving the original pod unchanged. + // +optional + Go Go `json:"go,omitempty"` + + // ApacheHttpd defines configuration for Apache HTTPD auto-instrumentation. + // +optional + ApacheHttpd ApacheHttpd `json:"apacheHttpd,omitempty"` + + // Nginx defines configuration for Nginx auto-instrumentation. + // +optional + Nginx Nginx `json:"nginx,omitempty"` } // Resource defines the configuration for the resource attributes, as defined by the OpenTelemetry specification. @@ -83,6 +101,7 @@ type Exporter struct { // Sampler defines sampling configuration. type Sampler struct { // Type defines sampler type. + // The value will be set in the OTEL_TRACES_SAMPLER env var. // The value can be for instance parentbased_always_on, parentbased_always_off, parentbased_traceidratio... // +optional Type SamplerType `json:"type,omitempty"` @@ -90,6 +109,7 @@ type Sampler struct { // Argument defines sampler argument. // The value depends on the sampler type. // For instance for parentbased_traceidratio sampler type it is a number in range [0..1] e.g. 0.25. + // The value will be set in the OTEL_TRACES_SAMPLER_ARG env var. // +optional Argument string `json:"argument,omitempty"` } @@ -100,11 +120,19 @@ type Java struct { // +optional Image string `json:"image,omitempty"` + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + // Env defines java specific env vars. There are four layers for env vars' definitions and // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. // If the former var had been defined, then the other vars would be ignored. // +optional Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resources,omitempty"` } // NodeJS defines NodeJS SDK and instrumentation configuration. @@ -113,11 +141,19 @@ type NodeJS struct { // +optional Image string `json:"image,omitempty"` + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + // Env defines nodejs specific env vars. There are four layers for env vars' definitions and // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. // If the former var had been defined, then the other vars would be ignored. // +optional Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` } // Python defines Python SDK and instrumentation configuration. @@ -126,23 +162,127 @@ type Python struct { // +optional Image string `json:"image,omitempty"` + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + // Env defines python specific env vars. There are four layers for env vars' definitions and // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. // If the former var had been defined, then the other vars would be ignored. // +optional Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` } +// DotNet defines DotNet SDK and instrumentation configuration. type DotNet struct { // Image is a container image with DotNet SDK and auto-instrumentation. // +optional Image string `json:"image,omitempty"` + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + // Env defines DotNet specific env vars. There are four layers for env vars' definitions and // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. // If the former var had been defined, then the other vars would be ignored. // +optional Env []corev1.EnvVar `json:"env,omitempty"` + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` +} + +type Go struct { + // Image is a container image with Go SDK and auto-instrumentation. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines Go specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` +} + +// ApacheHttpd defines Apache SDK and instrumentation configuration. +type ApacheHttpd struct { + // Image is a container image with Apache SDK and auto-instrumentation. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines Apache HTTPD specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Attrs defines Apache HTTPD agent specific attributes. The precedence is: + // `agent default attributes` > `instrument spec attributes` . + // Attributes are documented at https://github.com/open-telemetry/opentelemetry-cpp-contrib/tree/main/instrumentation/otel-webserver-module + // +optional + Attrs []corev1.EnvVar `json:"attrs,omitempty"` + + // Apache HTTPD server version. One of 2.4 or 2.2. Default is 2.4 + // +optional + Version string `json:"version,omitempty"` + + // Location of Apache HTTPD server configuration. + // Needed only if different from default "/usr/local/apache2/conf" + // +optional + ConfigPath string `json:"configPath,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` +} + +// Nginx defines Nginx SDK and instrumentation configuration. +type Nginx struct { + // Image is a container image with Nginx SDK and auto-instrumentation. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines Nginx specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Attrs defines Nginx agent specific attributes. The precedence order is: + // `agent default attributes` > `instrument spec attributes` . + // Attributes are documented at https://github.com/open-telemetry/opentelemetry-cpp-contrib/tree/main/instrumentation/otel-webserver-module + // +optional + Attrs []corev1.EnvVar `json:"attrs,omitempty"` + + // Location of Nginx configuration file. + // Needed only if different from default "/etx/nginx/nginx.conf" + // +optional + ConfigFile string `json:"configFile,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` } // InstrumentationStatus defines status of the instrumentation. diff --git a/apis/v1alpha1/instrumentation_webhook.go b/apis/v1alpha1/instrumentation_webhook.go index 0a19653ce8..beeaedf362 100644 --- a/apis/v1alpha1/instrumentation_webhook.go +++ b/apis/v1alpha1/instrumentation_webhook.go @@ -15,42 +15,84 @@ package v1alpha1 import ( + "context" "fmt" "strconv" "strings" + "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" ) const ( - AnnotationDefaultAutoInstrumentationJava = "instrumentation.opentelemetry.io/default-auto-instrumentation-java-image" - AnnotationDefaultAutoInstrumentationNodeJS = "instrumentation.opentelemetry.io/default-auto-instrumentation-nodejs-image" - AnnotationDefaultAutoInstrumentationPython = "instrumentation.opentelemetry.io/default-auto-instrumentation-python-image" - AnnotationDefaultAutoInstrumentationDotNet = "instrumentation.opentelemetry.io/default-auto-instrumentation-dotnet-image" - envPrefix = "OTEL_" - envSplunkPrefix = "SPLUNK_" + envPrefix = "OTEL_" + envSplunkPrefix = "SPLUNK_" ) -// log is for logging in this package. -var instrumentationlog = logf.Log.WithName("instrumentation-resource") +var ( + _ admission.CustomValidator = &InstrumentationWebhook{} + _ admission.CustomDefaulter = &InstrumentationWebhook{} + initContainerDefaultLimitResources = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + } + initContainerDefaultRequestedResources = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + } +) -func (r *Instrumentation) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() +// +kubebuilder:webhook:path=/mutate-opentelemetry-io-v1alpha1-instrumentation,mutating=true,failurePolicy=fail,sideEffects=None,groups=opentelemetry.io,resources=instrumentations,verbs=create;update,versions=v1alpha1,name=minstrumentation.kb.io,admissionReviewVersions=v1 +// +kubebuilder:webhook:verbs=create;update,path=/validate-opentelemetry-io-v1alpha1-instrumentation,mutating=false,failurePolicy=fail,groups=opentelemetry.io,resources=instrumentations,versions=v1alpha1,name=vinstrumentationcreateupdate.kb.io,sideEffects=none,admissionReviewVersions=v1 +// +kubebuilder:webhook:verbs=delete,path=/validate-opentelemetry-io-v1alpha1-instrumentation,mutating=false,failurePolicy=ignore,groups=opentelemetry.io,resources=instrumentations,versions=v1alpha1,name=vinstrumentationdelete.kb.io,sideEffects=none,admissionReviewVersions=v1 +// +kubebuilder:object:generate=false + +type InstrumentationWebhook struct { + logger logr.Logger + cfg config.Config + scheme *runtime.Scheme +} + +func (w InstrumentationWebhook) Default(ctx context.Context, obj runtime.Object) error { + instrumentation, ok := obj.(*Instrumentation) + if !ok { + return fmt.Errorf("expected an Instrumentation, received %T", obj) + } + return w.defaulter(instrumentation) } -//+kubebuilder:webhook:path=/mutate-opentelemetry-io-v1alpha1-instrumentation,mutating=true,failurePolicy=fail,sideEffects=None,groups=opentelemetry.io,resources=instrumentations,verbs=create;update,versions=v1alpha1,name=minstrumentation.kb.io,admissionReviewVersions=v1 +func (w InstrumentationWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + inst, ok := obj.(*Instrumentation) + if !ok { + return nil, fmt.Errorf("expected an Instrumentation, received %T", obj) + } + return w.validate(inst) +} -var _ webhook.Defaulter = &Instrumentation{} +func (w InstrumentationWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + inst, ok := newObj.(*Instrumentation) + if !ok { + return nil, fmt.Errorf("expected an Instrumentation, received %T", newObj) + } + return w.validate(inst) +} -// Default implements webhook.Defaulter so a webhook will be registered for the type. -func (r *Instrumentation) Default() { - instrumentationlog.Info("default", "name", r.Name) +func (w InstrumentationWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + inst, ok := obj.(*Instrumentation) + if !ok || inst == nil { + return nil, fmt.Errorf("expected an Instrumentation, received %T", obj) + } + return w.validate(inst) +} + +func (w InstrumentationWebhook) defaulter(r *Instrumentation) error { if r.Labels == nil { r.Labels = map[string]string{} } @@ -59,86 +101,180 @@ func (r *Instrumentation) Default() { } if r.Spec.Java.Image == "" { - if val, ok := r.Annotations[AnnotationDefaultAutoInstrumentationJava]; ok { - r.Spec.Java.Image = val + r.Spec.Java.Image = w.cfg.AutoInstrumentationJavaImage() + } + if r.Spec.Java.Resources.Limits == nil { + r.Spec.Java.Resources.Limits = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("64Mi"), + } + } + if r.Spec.Java.Resources.Requests == nil { + r.Spec.Java.Resources.Requests = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("50m"), + corev1.ResourceMemory: resource.MustParse("64Mi"), } } if r.Spec.NodeJS.Image == "" { - if val, ok := r.Annotations[AnnotationDefaultAutoInstrumentationNodeJS]; ok { - r.Spec.NodeJS.Image = val + r.Spec.NodeJS.Image = w.cfg.AutoInstrumentationNodeJSImage() + } + if r.Spec.NodeJS.Resources.Limits == nil { + r.Spec.NodeJS.Resources.Limits = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + } + } + if r.Spec.NodeJS.Resources.Requests == nil { + r.Spec.NodeJS.Resources.Requests = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("50m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), } } if r.Spec.Python.Image == "" { - if val, ok := r.Annotations[AnnotationDefaultAutoInstrumentationPython]; ok { - r.Spec.Python.Image = val + r.Spec.Python.Image = w.cfg.AutoInstrumentationPythonImage() + } + if r.Spec.Python.Resources.Limits == nil { + r.Spec.Python.Resources.Limits = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("32Mi"), + } + } + if r.Spec.Python.Resources.Requests == nil { + r.Spec.Python.Resources.Requests = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("50m"), + corev1.ResourceMemory: resource.MustParse("32Mi"), } } if r.Spec.DotNet.Image == "" { - if val, ok := r.Annotations[AnnotationDefaultAutoInstrumentationDotNet]; ok { - r.Spec.DotNet.Image = val + r.Spec.DotNet.Image = w.cfg.AutoInstrumentationDotNetImage() + } + if r.Spec.DotNet.Resources.Limits == nil { + r.Spec.DotNet.Resources.Limits = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), } } -} - -// +kubebuilder:webhook:verbs=create;update,path=/validate-opentelemetry-io-v1alpha1-instrumentation,mutating=false,failurePolicy=fail,groups=opentelemetry.io,resources=instrumentations,versions=v1alpha1,name=vinstrumentationcreateupdate.kb.io,sideEffects=none,admissionReviewVersions=v1 -// +kubebuilder:webhook:verbs=delete,path=/validate-opentelemetry-io-v1alpha1-instrumentation,mutating=false,failurePolicy=ignore,groups=opentelemetry.io,resources=instrumentations,versions=v1alpha1,name=vinstrumentationdelete.kb.io,sideEffects=none,admissionReviewVersions=v1 - -var _ webhook.Validator = &Instrumentation{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (in *Instrumentation) ValidateCreate() error { - instrumentationlog.Info("validate create", "name", in.Name) - return in.validate() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (in *Instrumentation) ValidateUpdate(old runtime.Object) error { - instrumentationlog.Info("validate update", "name", in.Name) - return in.validate() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (in *Instrumentation) ValidateDelete() error { - instrumentationlog.Info("validate delete", "name", in.Name) + if r.Spec.DotNet.Resources.Requests == nil { + r.Spec.DotNet.Resources.Requests = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("50m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + } + } + if r.Spec.Go.Image == "" { + r.Spec.Go.Image = w.cfg.AutoInstrumentationGoImage() + } + if r.Spec.Go.Resources.Limits == nil { + r.Spec.Go.Resources.Limits = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("32Mi"), + } + } + if r.Spec.Go.Resources.Requests == nil { + r.Spec.Go.Resources.Requests = corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("50m"), + corev1.ResourceMemory: resource.MustParse("32Mi"), + } + } + if r.Spec.ApacheHttpd.Image == "" { + r.Spec.ApacheHttpd.Image = w.cfg.AutoInstrumentationApacheHttpdImage() + } + if r.Spec.ApacheHttpd.Resources.Limits == nil { + r.Spec.ApacheHttpd.Resources.Limits = initContainerDefaultLimitResources + } + if r.Spec.ApacheHttpd.Resources.Requests == nil { + r.Spec.ApacheHttpd.Resources.Requests = initContainerDefaultRequestedResources + } + if r.Spec.ApacheHttpd.Version == "" { + r.Spec.ApacheHttpd.Version = "2.4" + } + if r.Spec.ApacheHttpd.ConfigPath == "" { + r.Spec.ApacheHttpd.ConfigPath = "/usr/local/apache2/conf" + } + if r.Spec.Nginx.Image == "" { + r.Spec.Nginx.Image = w.cfg.AutoInstrumentationNginxImage() + } + if r.Spec.Nginx.Resources.Limits == nil { + r.Spec.Nginx.Resources.Limits = initContainerDefaultLimitResources + } + if r.Spec.Nginx.Resources.Requests == nil { + r.Spec.Nginx.Resources.Requests = initContainerDefaultRequestedResources + } + if r.Spec.Nginx.ConfigFile == "" { + r.Spec.Nginx.ConfigFile = "/etc/nginx/nginx.conf" + } + // Set the defaulting annotations + if r.Annotations == nil { + r.Annotations = map[string]string{} + } + r.Annotations[constants.AnnotationDefaultAutoInstrumentationJava] = w.cfg.AutoInstrumentationJavaImage() + r.Annotations[constants.AnnotationDefaultAutoInstrumentationNodeJS] = w.cfg.AutoInstrumentationNodeJSImage() + r.Annotations[constants.AnnotationDefaultAutoInstrumentationPython] = w.cfg.AutoInstrumentationPythonImage() + r.Annotations[constants.AnnotationDefaultAutoInstrumentationDotNet] = w.cfg.AutoInstrumentationDotNetImage() + r.Annotations[constants.AnnotationDefaultAutoInstrumentationGo] = w.cfg.AutoInstrumentationGoImage() + r.Annotations[constants.AnnotationDefaultAutoInstrumentationApacheHttpd] = w.cfg.AutoInstrumentationApacheHttpdImage() + r.Annotations[constants.AnnotationDefaultAutoInstrumentationNginx] = w.cfg.AutoInstrumentationNginxImage() return nil } -func (in *Instrumentation) validate() error { - switch in.Spec.Sampler.Type { +func (w InstrumentationWebhook) validate(r *Instrumentation) (admission.Warnings, error) { + var warnings []string + switch r.Spec.Sampler.Type { + case "": + warnings = append(warnings, "sampler type not set") case TraceIDRatio, ParentBasedTraceIDRatio: - if in.Spec.Sampler.Argument != "" { - rate, err := strconv.ParseFloat(in.Spec.Sampler.Argument, 64) + if r.Spec.Sampler.Argument != "" { + rate, err := strconv.ParseFloat(r.Spec.Sampler.Argument, 64) if err != nil { - return fmt.Errorf("spec.sampler.argument is not a number: %s", in.Spec.Sampler.Argument) + return warnings, fmt.Errorf("spec.sampler.argument is not a number: %s", r.Spec.Sampler.Argument) } if rate < 0 || rate > 1 { - return fmt.Errorf("spec.sampler.argument should be in rage [0..1]: %s", in.Spec.Sampler.Argument) + return warnings, fmt.Errorf("spec.sampler.argument should be in rage [0..1]: %s", r.Spec.Sampler.Argument) + } + } + case JaegerRemote, ParentBasedJaegerRemote: + // value is a comma separated list of endpoint, pollingIntervalMs, initialSamplingRate + // Example: `endpoint=http://localhost:14250,pollingIntervalMs=5000,initialSamplingRate=0.25` + if r.Spec.Sampler.Argument != "" { + err := validateJaegerRemoteSamplerArgument(r.Spec.Sampler.Argument) + + if err != nil { + return warnings, fmt.Errorf("spec.sampler.argument is not a valid argument for sampler %s: %w", r.Spec.Sampler.Type, err) } } - case AlwaysOn, AlwaysOff, JaegerRemote, ParentBasedAlwaysOn, ParentBasedAlwaysOff, XRaySampler: + case AlwaysOn, AlwaysOff, ParentBasedAlwaysOn, ParentBasedAlwaysOff, XRaySampler: + default: + return warnings, fmt.Errorf("spec.sampler.type is not valid: %s", r.Spec.Sampler.Type) } // validate env vars - if err := in.validateEnv(in.Spec.Env); err != nil { - return err + if err := w.validateEnv(r.Spec.Env); err != nil { + return warnings, err } - if err := in.validateEnv(in.Spec.Java.Env); err != nil { - return err + if err := w.validateEnv(r.Spec.Java.Env); err != nil { + return warnings, err } - if err := in.validateEnv(in.Spec.NodeJS.Env); err != nil { - return err + if err := w.validateEnv(r.Spec.NodeJS.Env); err != nil { + return warnings, err } - if err := in.validateEnv(in.Spec.Python.Env); err != nil { - return err + if err := w.validateEnv(r.Spec.Python.Env); err != nil { + return warnings, err } - if err := in.validateEnv(in.Spec.DotNet.Env); err != nil { - return err + if err := w.validateEnv(r.Spec.DotNet.Env); err != nil { + return warnings, err } - - return nil + if err := w.validateEnv(r.Spec.Go.Env); err != nil { + return warnings, err + } + if err := w.validateEnv(r.Spec.ApacheHttpd.Env); err != nil { + return warnings, err + } + if err := w.validateEnv(r.Spec.Nginx.Env); err != nil { + return warnings, err + } + return warnings, nil } -func (in *Instrumentation) validateEnv(envs []corev1.EnvVar) error { +func (w InstrumentationWebhook) validateEnv(envs []corev1.EnvVar) error { for _, env := range envs { if !strings.HasPrefix(env.Name, envPrefix) && !strings.HasPrefix(env.Name, envSplunkPrefix) { return fmt.Errorf("env name should start with \"OTEL_\" or \"SPLUNK_\": %s", env.Name) @@ -146,3 +282,55 @@ func (in *Instrumentation) validateEnv(envs []corev1.EnvVar) error { } return nil } + +func validateJaegerRemoteSamplerArgument(argument string) error { + parts := strings.Split(argument, ",") + + for _, part := range parts { + kv := strings.Split(part, "=") + if len(kv) != 2 { + return fmt.Errorf("invalid argument: %s, the argument should be in the form of key=value", part) + } + + switch kv[0] { + case "endpoint": + if kv[1] == "" { + return fmt.Errorf("endpoint cannot be empty") + } + case "pollingIntervalMs": + if _, err := strconv.Atoi(kv[1]); err != nil { + return fmt.Errorf("invalid pollingIntervalMs: %s", kv[1]) + } + case "initialSamplingRate": + rate, err := strconv.ParseFloat(kv[1], 64) + if err != nil { + return fmt.Errorf("invalid initialSamplingRate: %s", kv[1]) + } + if rate < 0 || rate > 1 { + return fmt.Errorf("initialSamplingRate should be in rage [0..1]: %s", kv[1]) + } + } + } + return nil +} + +func NewInstrumentationWebhook(logger logr.Logger, scheme *runtime.Scheme, cfg config.Config) *InstrumentationWebhook { + return &InstrumentationWebhook{ + logger: logger, + scheme: scheme, + cfg: cfg, + } +} + +func SetupInstrumentationWebhook(mgr ctrl.Manager, cfg config.Config) error { + ivw := NewInstrumentationWebhook( + mgr.GetLogger().WithValues("handler", "InstrumentationWebhook"), + mgr.GetScheme(), + cfg, + ) + return ctrl.NewWebhookManagedBy(mgr). + For(&Instrumentation{}). + WithValidator(ivw). + WithDefaulter(ivw). + Complete() +} diff --git a/apis/v1alpha1/instrumentation_webhook_test.go b/apis/v1alpha1/instrumentation_webhook_test.go index ef31be3f84..81049cbc0c 100644 --- a/apis/v1alpha1/instrumentation_webhook_test.go +++ b/apis/v1alpha1/instrumentation_webhook_test.go @@ -15,36 +15,59 @@ package v1alpha1 import ( + "context" "testing" "github.com/stretchr/testify/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/open-telemetry/opentelemetry-operator/internal/config" ) func TestInstrumentationDefaultingWebhook(t *testing.T) { - inst := &Instrumentation{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - AnnotationDefaultAutoInstrumentationJava: "java-img:1", - AnnotationDefaultAutoInstrumentationNodeJS: "nodejs-img:1", - AnnotationDefaultAutoInstrumentationPython: "python-img:1", - AnnotationDefaultAutoInstrumentationDotNet: "dotnet-img:1", - }, - }, - } - inst.Default() + inst := &Instrumentation{} + err := InstrumentationWebhook{ + cfg: config.New( + config.WithAutoInstrumentationJavaImage("java-img:1"), + config.WithAutoInstrumentationNodeJSImage("nodejs-img:1"), + config.WithAutoInstrumentationPythonImage("python-img:1"), + config.WithAutoInstrumentationDotNetImage("dotnet-img:1"), + config.WithAutoInstrumentationApacheHttpdImage("apache-httpd-img:1"), + config.WithAutoInstrumentationNginxImage("nginx-img:1"), + ), + }.Default(context.Background(), inst) + assert.NoError(t, err) assert.Equal(t, "java-img:1", inst.Spec.Java.Image) assert.Equal(t, "nodejs-img:1", inst.Spec.NodeJS.Image) assert.Equal(t, "python-img:1", inst.Spec.Python.Image) assert.Equal(t, "dotnet-img:1", inst.Spec.DotNet.Image) + assert.Equal(t, "apache-httpd-img:1", inst.Spec.ApacheHttpd.Image) + assert.Equal(t, "nginx-img:1", inst.Spec.Nginx.Image) } func TestInstrumentationValidatingWebhook(t *testing.T) { tests := []struct { - name string - err string - inst Instrumentation + name string + err string + warnings admission.Warnings + inst Instrumentation }{ + { + name: "all defaults", + inst: Instrumentation{ + Spec: InstrumentationSpec{}, + }, + warnings: []string{"sampler type not set"}, + }, + { + name: "sampler configuration not present", + inst: Instrumentation{ + Spec: InstrumentationSpec{ + Sampler: Sampler{}, + }, + }, + warnings: []string{"sampler type not set"}, + }, { name: "argument is not a number", err: "spec.sampler.argument is not a number", @@ -91,17 +114,87 @@ func TestInstrumentationValidatingWebhook(t *testing.T) { }, }, } + for _, test := range tests { + test := test t.Run(test.name, func(t *testing.T) { + ctx := context.Background() if test.err == "" { - assert.Nil(t, test.inst.ValidateCreate()) - assert.Nil(t, test.inst.ValidateUpdate(nil)) + warnings, err := InstrumentationWebhook{}.ValidateCreate(ctx, &test.inst) + assert.Equal(t, test.warnings, warnings) + assert.Nil(t, err) + warnings, err = InstrumentationWebhook{}.ValidateUpdate(ctx, nil, &test.inst) + assert.Equal(t, test.warnings, warnings) + assert.Nil(t, err) } else { - err := test.inst.ValidateCreate() + warnings, err := InstrumentationWebhook{}.ValidateCreate(ctx, &test.inst) + assert.Equal(t, test.warnings, warnings) assert.Contains(t, err.Error(), test.err) - err = test.inst.ValidateUpdate(nil) + warnings, err = InstrumentationWebhook{}.ValidateUpdate(ctx, nil, &test.inst) + assert.Equal(t, test.warnings, warnings) assert.Contains(t, err.Error(), test.err) } }) } } + +func TestInstrumentationJaegerRemote(t *testing.T) { + tests := []struct { + name string + err string + arg string + }{ + { + name: "pollingIntervalMs is not a number", + err: "invalid pollingIntervalMs: abc", + arg: "pollingIntervalMs=abc", + }, + { + name: "initialSamplingRate is out of range", + err: "initialSamplingRate should be in rage [0..1]", + arg: "initialSamplingRate=1.99", + }, + { + name: "endpoint is missing", + err: "endpoint cannot be empty", + arg: "endpoint=", + }, + { + name: "correct jaeger remote sampler configuration", + arg: "endpoint=http://jaeger-collector:14250/,initialSamplingRate=0.99,pollingIntervalMs=1000", + }, + } + + samplers := []SamplerType{JaegerRemote, ParentBasedJaegerRemote} + + for _, sampler := range samplers { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + inst := Instrumentation{ + Spec: InstrumentationSpec{ + Sampler: Sampler{ + Type: sampler, + Argument: test.arg, + }, + }, + } + ctx := context.Background() + if test.err == "" { + warnings, err := InstrumentationWebhook{}.ValidateCreate(ctx, &inst) + assert.Nil(t, warnings) + assert.Nil(t, err) + warnings, err = InstrumentationWebhook{}.ValidateUpdate(ctx, nil, &inst) + assert.Nil(t, warnings) + assert.Nil(t, err) + } else { + warnings, err := InstrumentationWebhook{}.ValidateCreate(ctx, &inst) + assert.Nil(t, warnings) + assert.Contains(t, err.Error(), test.err) + warnings, err = InstrumentationWebhook{}.ValidateUpdate(ctx, nil, &inst) + assert.Nil(t, warnings) + assert.Contains(t, err.Error(), test.err) + } + }) + } + } +} diff --git a/apis/v1alpha1/opampbridge_capabilities.go b/apis/v1alpha1/opampbridge_capabilities.go new file mode 100644 index 0000000000..607656a448 --- /dev/null +++ b/apis/v1alpha1/opampbridge_capabilities.go @@ -0,0 +1,35 @@ +// 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 v1alpha1 + +type ( + // OpAMPBridgeCapability represents capability supported by OpAMP Bridge. + // +kubebuilder:validation:Enum=AcceptsRemoteConfig;ReportsEffectiveConfig;ReportsOwnTraces;ReportsOwnMetrics;ReportsOwnLogs;AcceptsOpAMPConnectionSettings;AcceptsOtherConnectionSettings;AcceptsRestartCommand;ReportsHealth;ReportsRemoteConfig + OpAMPBridgeCapability string +) + +const ( + OpAMPBridgeCapabilityReportsStatus OpAMPBridgeCapability = "ReportsStatus" + OpAMPBridgeCapabilityAcceptsRemoteConfig OpAMPBridgeCapability = "AcceptsRemoteConfig" + OpAMPBridgeCapabilityReportsEffectiveConfig OpAMPBridgeCapability = "ReportsEffectiveConfig" + OpAMPBridgeCapabilityReportsOwnTraces OpAMPBridgeCapability = "ReportsOwnTraces" + OpAMPBridgeCapabilityReportsOwnMetrics OpAMPBridgeCapability = "ReportsOwnMetrics" + OpAMPBridgeCapabilityReportsOwnLogs OpAMPBridgeCapability = "ReportsOwnLogs" + OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings OpAMPBridgeCapability = "AcceptsOpAMPConnectionSettings" + OpAMPBridgeCapabilityAcceptsOtherConnectionSettings OpAMPBridgeCapability = "AcceptsOtherConnectionSettings" + OpAMPBridgeCapabilityAcceptsRestartCommand OpAMPBridgeCapability = "AcceptsRestartCommand" + OpAMPBridgeCapabilityReportsHealth OpAMPBridgeCapability = "ReportsHealth" + OpAMPBridgeCapabilityReportsRemoteConfig OpAMPBridgeCapability = "ReportsRemoteConfig" +) diff --git a/apis/v1alpha1/opampbridge_types.go b/apis/v1alpha1/opampbridge_types.go new file mode 100644 index 0000000000..0479b10198 --- /dev/null +++ b/apis/v1alpha1/opampbridge_types.go @@ -0,0 +1,145 @@ +// 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 v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// OpAMPBridgeSpec defines the desired state of OpAMPBridge. +type OpAMPBridgeSpec struct { + // OpAMP backend Server endpoint + // +required + Endpoint string `json:"endpoint"` + // Headers is an optional map of headers to use when connecting to the OpAMP Server, + // typically used to set access tokens or other authorization headers. + // +optional + Headers map[string]string `json:"headers,omitempty"` + // Capabilities supported by the OpAMP Bridge + // +required + Capabilities map[OpAMPBridgeCapability]bool `json:"capabilities"` + // ComponentsAllowed is a list of allowed OpenTelemetry components for each pipeline type (receiver, processor, etc.) + // +optional + ComponentsAllowed map[string][]string `json:"componentsAllowed,omitempty"` + // Resources to set on the OpAMPBridge pods. + // +optional + Resources v1.ResourceRequirements `json:"resources,omitempty"` + // NodeSelector to schedule OpAMPBridge pods. + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // Replicas is the number of pod instances for the OpAMPBridge. + // +optional + // +kubebuilder:validation:Maximum=1 + Replicas *int32 `json:"replicas,omitempty"` + // SecurityContext will be set as the container security context. + // +optional + SecurityContext *v1.SecurityContext `json:"securityContext,omitempty"` + // PodSecurityContext will be set as the pod security context. + // +optional + PodSecurityContext *v1.PodSecurityContext `json:"podSecurityContext,omitempty"` + // PodAnnotations is the set of annotations that will be attached to + // OpAMPBridge pods. + // +optional + PodAnnotations map[string]string `json:"podAnnotations,omitempty"` + // ServiceAccount indicates the name of an existing service account to use with this instance. When set, + // the operator will not automatically create a ServiceAccount for the OpAMPBridge. + // +optional + ServiceAccount string `json:"serviceAccount,omitempty"` + // Image indicates the container image to use for the OpAMPBridge. + // +optional + Image string `json:"image,omitempty"` + // UpgradeStrategy represents how the operator will handle upgrades to the CR when a newer version of the operator is deployed + // +optional + UpgradeStrategy UpgradeStrategy `json:"upgradeStrategy"` + // ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent) + // +optional + ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` + // VolumeMounts represents the mount points to use in the underlying OpAMPBridge deployment(s) + // +optional + // +listType=atomic + VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"` + // Ports allows a set of ports to be exposed by the underlying v1.Service. + // +optional + // +listType=atomic + Ports []v1.ServicePort `json:"ports,omitempty"` + // ENV vars to set on the OpAMPBridge Pods. + // +optional + Env []v1.EnvVar `json:"env,omitempty"` + // List of sources to populate environment variables on the OpAMPBridge Pods. + // +optional + EnvFrom []v1.EnvFromSource `json:"envFrom,omitempty"` + // Toleration to schedule OpAMPBridge pods. + // +optional + Tolerations []v1.Toleration `json:"tolerations,omitempty"` + // Volumes represents which volumes to use in the underlying OpAMPBridge deployment(s). + // +optional + // +listType=atomic + Volumes []v1.Volume `json:"volumes,omitempty"` + // HostNetwork indicates if the pod should run in the host networking namespace. + // +optional + HostNetwork bool `json:"hostNetwork,omitempty"` + // If specified, indicates the pod's priority. + // If not specified, the pod priority will be default or zero if there is no + // default. + // +optional + PriorityClassName string `json:"priorityClassName,omitempty"` + // If specified, indicates the pod's scheduling constraints + // +optional + Affinity *v1.Affinity `json:"affinity,omitempty"` + // TopologySpreadConstraints embedded kubernetes pod configuration option, + // controls how pods are spread across your cluster among failure-domains + // such as regions, zones, nodes, and other user-defined topology domains + // https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + // +optional + TopologySpreadConstraints []v1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"` +} + +// OpAMPBridgeStatus defines the observed state of OpAMPBridge. +type OpAMPBridgeStatus struct { + // Version of the managed OpAMP Bridge (operand) + // +optional + Version string `json:"version,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.version",description="OpenTelemetry Version" +// +kubebuilder:printcolumn:name="Endpoint",type="string",JSONPath=".spec.endpoint" +// +operator-sdk:csv:customresourcedefinitions:displayName="OpAMP Bridge" +// +operator-sdk:csv:customresourcedefinitions:resources={{Pod,v1},{Deployment,apps/v1},{ConfigMaps,v1},{Service,v1}} + +// OpAMPBridge is the Schema for the opampbridges API. +type OpAMPBridge struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec OpAMPBridgeSpec `json:"spec,omitempty"` + Status OpAMPBridgeStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// OpAMPBridgeList contains a list of OpAMPBridge. +type OpAMPBridgeList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []OpAMPBridge `json:"items"` +} + +func init() { + SchemeBuilder.Register(&OpAMPBridge{}, &OpAMPBridgeList{}) +} diff --git a/apis/v1alpha1/opampbridge_webhook.go b/apis/v1alpha1/opampbridge_webhook.go new file mode 100644 index 0000000000..77717ca4f0 --- /dev/null +++ b/apis/v1alpha1/opampbridge_webhook.go @@ -0,0 +1,149 @@ +// 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 v1alpha1 + +import ( + "context" + "fmt" + "strings" + + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/config" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/validation" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +var ( + _ admission.CustomValidator = &OpAMPBridgeWebhook{} + _ admission.CustomDefaulter = &OpAMPBridgeWebhook{} +) + +//+kubebuilder:webhook:path=/mutate-opentelemetry-io-v1alpha1-opampbridge,mutating=true,failurePolicy=fail,sideEffects=None,groups=opentelemetry.io,resources=opampbridges,verbs=create;update,versions=v1alpha1,name=mopampbridge.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:path=/validate-opentelemetry-io-v1alpha1-opampbridge,mutating=false,failurePolicy=fail,sideEffects=None,groups=opentelemetry.io,resources=opampbridges,verbs=create;update,versions=v1alpha1,name=vopampbridgecreateupdate.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:path=/validate-opentelemetry-io-v1alpha1-opampbridge,mutating=false,failurePolicy=ignore,sideEffects=None,groups=opentelemetry.io,resources=opampbridges,verbs=delete,versions=v1alpha1,name=vopampbridgedelete.kb.io,admissionReviewVersions=v1 +//+kubebuilder:object:generate=false + +type OpAMPBridgeWebhook struct { + logger logr.Logger + cfg config.Config + scheme *runtime.Scheme +} + +func (o *OpAMPBridgeWebhook) Default(ctx context.Context, obj runtime.Object) error { + opampBridge, ok := obj.(*OpAMPBridge) + if !ok { + return fmt.Errorf("expected an OpAMPBridge, received %T", obj) + } + return o.defaulter(opampBridge) +} + +func (c OpAMPBridgeWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + opampBridge, ok := obj.(*OpAMPBridge) + if !ok { + return nil, fmt.Errorf("expected an OpAMPBridge, received %T", obj) + } + return c.validate(opampBridge) +} + +func (c OpAMPBridgeWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { + opampBridge, ok := newObj.(*OpAMPBridge) + if !ok { + return nil, fmt.Errorf("expected an OpAMPBridge, received %T", newObj) + } + return c.validate(opampBridge) +} + +func (o OpAMPBridgeWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + opampBridge, ok := obj.(*OpAMPBridge) + if !ok || opampBridge == nil { + return nil, fmt.Errorf("expected an OpAMPBridge, received %T", obj) + } + return o.validate(opampBridge) +} + +func (o OpAMPBridgeWebhook) defaulter(r *OpAMPBridge) error { + if len(r.Spec.UpgradeStrategy) == 0 { + r.Spec.UpgradeStrategy = UpgradeStrategyAutomatic + } + + if r.Labels == nil { + r.Labels = map[string]string{} + } + if r.Labels["app.kubernetes.io/managed-by"] == "" { + r.Labels["app.kubernetes.io/managed-by"] = "opentelemetry-operator" + } + + one := int32(1) + if r.Spec.Replicas == nil { + r.Spec.Replicas = &one + } + + // ReportsStatus Capability must be set + if r.Spec.Capabilities == nil { + r.Spec.Capabilities = make(map[OpAMPBridgeCapability]bool) + } + enabled, found := r.Spec.Capabilities[OpAMPBridgeCapabilityReportsStatus] + if !enabled || !found { + r.Spec.Capabilities[OpAMPBridgeCapabilityReportsStatus] = true + } + return nil +} + +func (o OpAMPBridgeWebhook) validate(r *OpAMPBridge) (admission.Warnings, error) { + warnings := admission.Warnings{} + + // validate OpAMP server endpoint + if len(strings.TrimSpace(r.Spec.Endpoint)) == 0 { + return warnings, fmt.Errorf("the OpAMP server endpoint is not specified") + } + + // validate OpAMPBridge capabilities + if len(r.Spec.Capabilities) == 0 { + return warnings, fmt.Errorf("the capabilities supported by OpAMP Bridge are not specified") + } + + // validate port config + for _, p := range r.Spec.Ports { + nameErrs := validation.IsValidPortName(p.Name) + numErrs := validation.IsValidPortNum(int(p.Port)) + if len(nameErrs) > 0 || len(numErrs) > 0 { + return warnings, fmt.Errorf("the OpAMPBridge Spec Ports configuration is incorrect, port name '%s' errors: %s, num '%d' errors: %s", + p.Name, nameErrs, p.Port, numErrs) + } + } + + // check for maximum replica count + if r.Spec.Replicas != nil && *r.Spec.Replicas > 1 { + return warnings, fmt.Errorf("replica count must not be greater than 1") + } + return warnings, nil +} + +func SetupOpAMPBridgeWebhook(mgr ctrl.Manager, cfg config.Config) error { + webhook := &OpAMPBridgeWebhook{ + logger: mgr.GetLogger().WithValues("handler", "OpAMPBridgeWebhook"), + scheme: mgr.GetScheme(), + cfg: cfg, + } + return ctrl.NewWebhookManagedBy(mgr). + For(&OpAMPBridge{}). + WithValidator(webhook). + WithDefaulter(webhook). + Complete() +} diff --git a/apis/v1alpha1/opampbridge_webhook_test.go b/apis/v1alpha1/opampbridge_webhook_test.go new file mode 100644 index 0000000000..b67551b8ee --- /dev/null +++ b/apis/v1alpha1/opampbridge_webhook_test.go @@ -0,0 +1,323 @@ +// 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 v1alpha1 + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/config" + + "github.com/stretchr/testify/assert" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestOpAMPBridgeDefaultingWebhook(t *testing.T) { + one := int32(1) + five := int32(5) + + if err := AddToScheme(testScheme); err != nil { + fmt.Printf("failed to register scheme: %v", err) + os.Exit(1) + } + + tests := []struct { + name string + opampBridge OpAMPBridge + expected OpAMPBridge + }{ + { + name: "all fields default", + opampBridge: OpAMPBridge{}, + expected: OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpAMPBridgeSpec{ + Replicas: &one, + UpgradeStrategy: UpgradeStrategyAutomatic, + Capabilities: map[OpAMPBridgeCapability]bool{OpAMPBridgeCapabilityReportsStatus: true}, + }, + }, + }, + { + name: "provided values in spec", + opampBridge: OpAMPBridge{ + Spec: OpAMPBridgeSpec{ + Replicas: &five, + UpgradeStrategy: "adhoc", + }, + }, + expected: OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpAMPBridgeSpec{ + Replicas: &five, + UpgradeStrategy: "adhoc", + Capabilities: map[OpAMPBridgeCapability]bool{OpAMPBridgeCapabilityReportsStatus: true}, + }, + }, + }, + { + name: "enable ReportsStatus capability if not enabled already", + opampBridge: OpAMPBridge{ + Spec: OpAMPBridgeSpec{ + Capabilities: map[OpAMPBridgeCapability]bool{ + OpAMPBridgeCapabilityReportsStatus: false, + }, + }, + }, + expected: OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: OpAMPBridgeSpec{ + Replicas: &one, + UpgradeStrategy: UpgradeStrategyAutomatic, + Capabilities: map[OpAMPBridgeCapability]bool{ + OpAMPBridgeCapabilityReportsStatus: true, + }, + }, + }, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + webhook := &OpAMPBridgeWebhook{ + logger: logr.Discard(), + scheme: testScheme, + cfg: config.New( + config.WithCollectorImage("collector:v0.0.0"), + config.WithTargetAllocatorImage("ta:v0.0.0"), + config.WithOperatorOpAMPBridgeImage("opampbridge:v0.0.0"), + ), + } + ctx := context.Background() + err := webhook.Default(ctx, &test.opampBridge) + assert.NoError(t, err) + assert.Equal(t, test.expected, test.opampBridge) + }) + } +} + +func TestOpAMPBridgeValidatingWebhook(t *testing.T) { + + two := int32(2) + + tests := []struct { //nolint:govet + name string + opampBridge OpAMPBridge + expectedErr string + expectedWarnings []string + }{ + { + name: "specify all required fields, should not return error", + opampBridge: OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: OpAMPBridgeSpec{ + Endpoint: "ws://opamp-server:4320/v1/opamp", + Capabilities: map[OpAMPBridgeCapability]bool{ + OpAMPBridgeCapabilityReportsStatus: true, + OpAMPBridgeCapabilityAcceptsRemoteConfig: true, + OpAMPBridgeCapabilityReportsEffectiveConfig: true, + OpAMPBridgeCapabilityReportsOwnTraces: true, + OpAMPBridgeCapabilityReportsOwnMetrics: true, + OpAMPBridgeCapabilityReportsOwnLogs: true, + OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsOtherConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsRestartCommand: true, + OpAMPBridgeCapabilityReportsHealth: true, + OpAMPBridgeCapabilityReportsRemoteConfig: true, + }, + }, + }, + }, + { + name: "empty OpAMP Server endpoint", + opampBridge: OpAMPBridge{ + Spec: OpAMPBridgeSpec{ + Endpoint: "", + }, + }, + expectedErr: "the OpAMP server endpoint is not specified", + }, + { + name: "empty capabilities", + opampBridge: OpAMPBridge{ + Spec: OpAMPBridgeSpec{ + Endpoint: "ws://opamp-server:4320/v1/opamp", + }, + }, + expectedErr: "the capabilities supported by OpAMP Bridge are not specified", + }, + { + name: "replica count greater than 1 should return error", + opampBridge: OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: OpAMPBridgeSpec{ + Replicas: &two, + Endpoint: "ws://opamp-server:4320/v1/opamp", + Capabilities: map[OpAMPBridgeCapability]bool{ + OpAMPBridgeCapabilityReportsStatus: true, + OpAMPBridgeCapabilityAcceptsRemoteConfig: true, + OpAMPBridgeCapabilityReportsEffectiveConfig: true, + OpAMPBridgeCapabilityReportsOwnTraces: true, + OpAMPBridgeCapabilityReportsOwnMetrics: true, + OpAMPBridgeCapabilityReportsOwnLogs: true, + OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsOtherConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsRestartCommand: true, + OpAMPBridgeCapabilityReportsHealth: true, + OpAMPBridgeCapabilityReportsRemoteConfig: true, + }, + }, + }, + expectedErr: "replica count must not be greater than 1", + }, + { + name: "invalid port name", + opampBridge: OpAMPBridge{ + Spec: OpAMPBridgeSpec{ + Endpoint: "ws://opamp-server:4320/v1/opamp", + Capabilities: map[OpAMPBridgeCapability]bool{ + OpAMPBridgeCapabilityReportsStatus: true, + OpAMPBridgeCapabilityAcceptsRemoteConfig: true, + OpAMPBridgeCapabilityReportsEffectiveConfig: true, + OpAMPBridgeCapabilityReportsOwnTraces: true, + OpAMPBridgeCapabilityReportsOwnMetrics: true, + OpAMPBridgeCapabilityReportsOwnLogs: true, + OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsOtherConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsRestartCommand: true, + OpAMPBridgeCapabilityReportsHealth: true, + OpAMPBridgeCapabilityReportsRemoteConfig: true, + }, + Ports: []v1.ServicePort{ + { + // this port name contains a non alphanumeric character, which is invalid. + Name: "-test🦄port", + Port: 12345, + Protocol: v1.ProtocolTCP, + }, + }, + }, + }, + expectedErr: "the OpAMPBridge Spec Ports configuration is incorrect", + }, + { + name: "invalid port name, too long", + opampBridge: OpAMPBridge{ + Spec: OpAMPBridgeSpec{ + Endpoint: "ws://opamp-server:4320/v1/opamp", + Capabilities: map[OpAMPBridgeCapability]bool{ + OpAMPBridgeCapabilityReportsStatus: true, + OpAMPBridgeCapabilityAcceptsRemoteConfig: true, + OpAMPBridgeCapabilityReportsEffectiveConfig: true, + OpAMPBridgeCapabilityReportsOwnTraces: true, + OpAMPBridgeCapabilityReportsOwnMetrics: true, + OpAMPBridgeCapabilityReportsOwnLogs: true, + OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsOtherConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsRestartCommand: true, + OpAMPBridgeCapabilityReportsHealth: true, + OpAMPBridgeCapabilityReportsRemoteConfig: true, + }, Ports: []v1.ServicePort{ + { + Name: "aaaabbbbccccdddd", // len: 16, too long + Port: 5555, + }, + }, + }, + }, + expectedErr: "the OpAMPBridge Spec Ports configuration is incorrect", + }, + { + name: "invalid port num", + opampBridge: OpAMPBridge{ + Spec: OpAMPBridgeSpec{ + Endpoint: "ws://opamp-server:4320/v1/opamp", + Capabilities: map[OpAMPBridgeCapability]bool{ + OpAMPBridgeCapabilityReportsStatus: true, + OpAMPBridgeCapabilityAcceptsRemoteConfig: true, + OpAMPBridgeCapabilityReportsEffectiveConfig: true, + OpAMPBridgeCapabilityReportsOwnTraces: true, + OpAMPBridgeCapabilityReportsOwnMetrics: true, + OpAMPBridgeCapabilityReportsOwnLogs: true, + OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsOtherConnectionSettings: true, + OpAMPBridgeCapabilityAcceptsRestartCommand: true, + OpAMPBridgeCapabilityReportsHealth: true, + OpAMPBridgeCapabilityReportsRemoteConfig: true, + }, + Ports: []v1.ServicePort{ + { + Name: "aaaabbbbccccddd", // len: 15 + // no port set means it's 0, which is invalid + }, + }, + }, + }, + expectedErr: "the OpAMPBridge Spec Ports configuration is incorrect", + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + webhook := &OpAMPBridgeWebhook{ + logger: logr.Discard(), + scheme: testScheme, + cfg: config.New( + config.WithCollectorImage("collector:v0.0.0"), + config.WithTargetAllocatorImage("ta:v0.0.0"), + config.WithOperatorOpAMPBridgeImage("opampbridge:v0.0.0"), + ), + } + ctx := context.Background() + warnings, err := webhook.ValidateCreate(ctx, &test.opampBridge) + if test.expectedErr == "" { + assert.NoError(t, err) + return + } + if len(test.expectedWarnings) == 0 { + assert.Empty(t, warnings, test.expectedWarnings) + } else { + assert.ElementsMatch(t, warnings, test.expectedWarnings) + } + assert.ErrorContains(t, err, test.expectedErr) + }) + } +} diff --git a/apis/v1alpha1/opentelemetrycollector_types.go b/apis/v1alpha1/opentelemetrycollector_types.go index 9f34405b30..07ae504dde 100644 --- a/apis/v1alpha1/opentelemetrycollector_types.go +++ b/apis/v1alpha1/opentelemetrycollector_types.go @@ -15,20 +15,50 @@ package v1alpha1 import ( + appsv1 "k8s.io/api/apps/v1" autoscalingv2 "k8s.io/api/autoscaling/v2" v1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +// ManagementStateType defines the type for CR management states. +// +// +kubebuilder:validation:Enum=managed;unmanaged +type ManagementStateType string + +const ( + // ManagementStateManaged when the OpenTelemetryCollector custom resource should be + // reconciled by the operator. + ManagementStateManaged ManagementStateType = "managed" + + // ManagementStateUnmanaged when the OpenTelemetryCollector custom resource should not be + // reconciled by the operator. + ManagementStateUnmanaged ManagementStateType = "unmanaged" ) // Ingress is used to specify how OpenTelemetry Collector is exposed. This // functionality is only available if one of the valid modes is set. // Valid modes are: deployment, daemonset and statefulset. +// NOTE: If this feature is activated, all specified receivers are exposed. +// Currently this has a few limitations. Depending on the ingress controller +// there are problems with TLS and gRPC. +// SEE: https://github.com/open-telemetry/opentelemetry-operator/issues/1306. +// NOTE: As a workaround, port name and appProtocol could be specified directly +// in the CR. +// SEE: OpenTelemetryCollector.spec.ports[index]. type Ingress struct { // Type default value is: "" - // Supported types are: ingress + // Supported types are: ingress, route Type IngressType `json:"type,omitempty"` + // RuleType defines how Ingress exposes collector receivers. + // IngressRuleTypePath ("path") exposes each receiver port on a unique path on single domain defined in Hostname. + // IngressRuleTypeSubdomain ("subdomain") exposes each receiver port on a unique subdomain of Hostname. + // Default is IngressRuleTypePath ("path"). + RuleType IngressRuleType `json:"ruleType,omitempty"` + // Hostname by which the ingress proxy can be reached. // +optional Hostname string `json:"hostname,omitempty"` @@ -41,10 +71,34 @@ type Ingress struct { // TLS configuration. // +optional TLS []networkingv1.IngressTLS `json:"tls,omitempty"` + + // IngressClassName is the name of an IngressClass cluster resource. Ingress + // controller implementations use this field to know whether they should be + // serving this Ingress resource. + // +optional + IngressClassName *string `json:"ingressClassName,omitempty"` + + // Route is an OpenShift specific section that is only considered when + // type "route" is used. + // +optional + Route OpenShiftRoute `json:"route,omitempty"` +} + +// OpenShiftRoute defines openshift route specific settings. +type OpenShiftRoute struct { + // Termination indicates termination type. By default "edge" is used. + Termination TLSRouteTerminationType `json:"termination,omitempty"` } // OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector. type OpenTelemetryCollectorSpec struct { + // ManagementState defines if the CR should be managed by the operator or not. + // Default is managed. + // + // +required + // +kubebuilder:validation:Required + // +kubebuilder:default:=managed + ManagementState ManagementStateType `json:"managementState,omitempty"` // Resources to set on the OpenTelemetry Collector pods. // +optional Resources v1.ResourceRequirements `json:"resources,omitempty"` @@ -58,21 +112,43 @@ type OpenTelemetryCollectorSpec struct { // Replicas is the number of pod instances for the underlying OpenTelemetry Collector. Set this if your are not using autoscaling // +optional Replicas *int32 `json:"replicas,omitempty"` - // MinReplicas sets a lower bound to the autoscaling feature. Set this if your are using autoscaling. It must be at least 1 + // MinReplicas sets a lower bound to the autoscaling feature. Set this if you are using autoscaling. It must be at least 1 // +optional + // Deprecated: use "OpenTelemetryCollector.Spec.Autoscaler.MinReplicas" instead. MinReplicas *int32 `json:"minReplicas,omitempty"` // MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled. // +optional + // Deprecated: use "OpenTelemetryCollector.Spec.Autoscaler.MaxReplicas" instead. MaxReplicas *int32 `json:"maxReplicas,omitempty"` // Autoscaler specifies the pod autoscaling configuration to use // for the OpenTelemetryCollector workload. // // +optional Autoscaler *AutoscalerSpec `json:"autoscaler,omitempty"` - // SecurityContext will be set as the container security context. + // PodDisruptionBudget specifies the pod disruption budget configuration to use + // for the OpenTelemetryCollector workload. + // + // +optional + PodDisruptionBudget *PodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` + // SecurityContext configures the container security context for + // the opentelemetry-collector container. + // + // In deployment, daemonset, or statefulset mode, this controls + // the security context settings for the primary application + // container. + // + // In sidecar mode, this controls the security context for the + // injected sidecar container. + // // +optional SecurityContext *v1.SecurityContext `json:"securityContext,omitempty"` - + // PodSecurityContext configures the pod security context for the + // opentelemetry-collector pod, when running as a deployment, daemonset, + // or statefulset. + // + // In sidecar mode, the opentelemetry-operator will ignore this setting. + // + // +optional PodSecurityContext *v1.PodSecurityContext `json:"podSecurityContext,omitempty"` // PodAnnotations is the set of annotations that will be attached to // Collector and Target Allocator pods. @@ -84,7 +160,8 @@ type OpenTelemetryCollectorSpec struct { // Mode represents how the collector should be deployed (deployment, daemonset, statefulset or sidecar) // +optional Mode Mode `json:"mode,omitempty"` - // ServiceAccount indicates the name of an existing service account to use with this instance. + // ServiceAccount indicates the name of an existing service account to use with this instance. When set, + // the operator will not automatically create a ServiceAccount for the collector. // +optional ServiceAccount string `json:"serviceAccount,omitempty"` // Image indicates the container image to use for the OpenTelemetry Collector. @@ -143,6 +220,67 @@ type OpenTelemetryCollectorSpec struct { // default. // +optional PriorityClassName string `json:"priorityClassName,omitempty"` + // If specified, indicates the pod's scheduling constraints + // +optional + Affinity *v1.Affinity `json:"affinity,omitempty"` + // Actions that the management system should take in response to container lifecycle events. Cannot be updated. + // +optional + Lifecycle *v1.Lifecycle `json:"lifecycle,omitempty"` + // Duration in seconds the pod needs to terminate gracefully upon probe failure. + // +optional + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` + // Liveness config for the OpenTelemetry Collector except the probe handler which is auto generated from the health extension of the collector. + // It is only effective when healthcheckextension is configured in the OpenTelemetry Collector pipeline. + // +optional + LivenessProbe *Probe `json:"livenessProbe,omitempty"` + // InitContainers allows injecting initContainers to the Collector's pod definition. + // These init containers can be used to fetch secrets for injection into the + // configuration from external sources, run added checks, etc. Any errors during the execution of + // an initContainer will lead to a restart of the Pod. More info: + // https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + // +optional + InitContainers []v1.Container `json:"initContainers,omitempty"` + + // AdditionalContainers allows injecting additional containers into the Collector's pod definition. + // These sidecar containers can be used for authentication proxies, log shipping sidecars, agents for shipping + // metrics to their cloud, or in general sidecars that do not support automatic injection. This option only + // applies to Deployment, DaemonSet, and StatefulSet deployment modes of the collector. It does not apply to the sidecar + // deployment mode. More info about sidecars: + // https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/ + // + // Container names managed by the operator: + // * `otc-container` + // + // Overriding containers managed by the operator is outside the scope of what the maintainers will support and by + // doing so, you wil accept the risk of it breaking things. + // + // +optional + AdditionalContainers []v1.Container `json:"additionalContainers,omitempty"` + + // ObservabilitySpec defines how telemetry data gets handled. + // + // +optional + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Observability" + Observability ObservabilitySpec `json:"observability,omitempty"` + + // TopologySpreadConstraints embedded kubernetes pod configuration option, + // controls how pods are spread across your cluster among failure-domains + // such as regions, zones, nodes, and other user-defined topology domains + // https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + // This is only relevant to statefulset, and deployment mode + // +optional + TopologySpreadConstraints []v1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"` + + // ConfigMaps is a list of ConfigMaps in the same namespace as the OpenTelemetryCollector + // object, which shall be mounted into the Collector Pods. + // Each ConfigMap will be added to the Collector's Deployments as a volume named `configmap-`. + ConfigMaps []ConfigMapsSpec `json:"configmaps,omitempty"` + // UpdateStrategy represents the strategy the operator will take replacing existing DaemonSet pods with new pods + // https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/daemon-set-v1/#DaemonSetSpec + // This is only applicable to Daemonset mode. + // +optional + UpdateStrategy appsv1.DaemonSetUpdateStrategy `json:"updateStrategy,omitempty"` } // OpenTelemetryTargetAllocator defines the configurations for the Prometheus target allocator. @@ -152,11 +290,23 @@ type OpenTelemetryTargetAllocator struct { // that can be run in a high availability mode is consistent-hashing. // +optional Replicas *int32 `json:"replicas,omitempty"` + // NodeSelector to schedule OpenTelemetry TargetAllocator pods. + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // Resources to set on the OpenTelemetryTargetAllocator containers. + // +optional + Resources v1.ResourceRequirements `json:"resources,omitempty"` // AllocationStrategy determines which strategy the target allocator should use for allocation. // The current options are least-weighted and consistent-hashing. The default option is least-weighted // +optional - AllocationStrategy string `json:"allocationStrategy,omitempty"` - // ServiceAccount indicates the name of an existing service account to use with this instance. + AllocationStrategy OpenTelemetryTargetAllocatorAllocationStrategy `json:"allocationStrategy,omitempty"` + // FilterStrategy determines how to filter targets before allocating them among the collectors. + // The only current option is relabel-config (drops targets based on prom relabel_config). + // Filtering is disabled by default. + // +optional + FilterStrategy string `json:"filterStrategy,omitempty"` + // ServiceAccount indicates the name of an existing service account to use with this instance. When set, + // the operator will not automatically create a ServiceAccount for the TargetAllocator. // +optional ServiceAccount string `json:"serviceAccount,omitempty"` // Image indicates the container image to use for the OpenTelemetry TargetAllocator. @@ -165,16 +315,53 @@ type OpenTelemetryTargetAllocator struct { // Enabled indicates whether to use a target allocation mechanism for Prometheus targets or not. // +optional Enabled bool `json:"enabled,omitempty"` + // If specified, indicates the pod's scheduling constraints + // +optional + Affinity *v1.Affinity `json:"affinity,omitempty"` // PrometheusCR defines the configuration for the retrieval of PrometheusOperator CRDs ( servicemonitor.monitoring.coreos.com/v1 and podmonitor.monitoring.coreos.com/v1 ) retrieval. // All CR instances which the ServiceAccount has access to will be retrieved. This includes other namespaces. // +optional PrometheusCR OpenTelemetryTargetAllocatorPrometheusCR `json:"prometheusCR,omitempty"` + // SecurityContext configures the container security context for + // the targetallocator. + // +optional + SecurityContext *v1.PodSecurityContext `json:"securityContext,omitempty"` + // TopologySpreadConstraints embedded kubernetes pod configuration option, + // controls how pods are spread across your cluster among failure-domains + // such as regions, zones, nodes, and other user-defined topology domains + // https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + // +optional + TopologySpreadConstraints []v1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"` + // Toleration embedded kubernetes pod configuration option, + // controls how pods can be scheduled with matching taints + // +optional + Tolerations []v1.Toleration `json:"tolerations,omitempty"` + // ENV vars to set on the OpenTelemetry TargetAllocator's Pods. These can then in certain cases be + // consumed in the config file for the TargetAllocator. + // +optional + Env []v1.EnvVar `json:"env,omitempty"` } type OpenTelemetryTargetAllocatorPrometheusCR struct { // Enabled indicates whether to use a PrometheusOperator custom resources as targets or not. // +optional Enabled bool `json:"enabled,omitempty"` + // Interval between consecutive scrapes. Equivalent to the same setting on the Prometheus CRD. + // + // Default: "30s" + // +kubebuilder:default:="30s" + // +kubebuilder:validation:Format:=duration + ScrapeInterval *metav1.Duration `json:"scrapeInterval,omitempty"` + // PodMonitors to be selected for target discovery. + // This is a map of {key,value} pairs. Each {key,value} in the map is going to exactly match a label in a + // PodMonitor's meta labels. The requirements are ANDed. + // +optional + PodMonitorSelector map[string]string `json:"podMonitorSelector,omitempty"` + // ServiceMonitors to be selected for target discovery. + // This is a map of {key,value} pairs. Each {key,value} in the map is going to exactly match a label in a + // ServiceMonitor's meta labels. The requirements are ANDed. + // +optional + ServiceMonitorSelector map[string]string `json:"serviceMonitorSelector,omitempty"` } // ScaleSubresourceStatus defines the observed state of the OpenTelemetryCollector's @@ -189,6 +376,12 @@ type ScaleSubresourceStatus struct { // OpenTelemetryCollector's deployment or statefulSet. // +optional Replicas int32 `json:"replicas,omitempty"` + + // StatusReplicas is the number of pods targeted by this OpenTelemetryCollector's with a Ready Condition / + // Total number of non-terminated pods targeted by this OpenTelemetryCollector's (their labels match the selector). + // Deployment, Daemonset, StatefulSet. + // +optional + StatusReplicas string `json:"statusReplicas,omitempty"` } // OpenTelemetryCollectorStatus defines the observed state of OpenTelemetryCollector. @@ -201,6 +394,10 @@ type OpenTelemetryCollectorStatus struct { // +optional Version string `json:"version,omitempty"` + // Image indicates the container image to use for the OpenTelemetry Collector. + // +optional + Image string `json:"image,omitempty"` + // Messages about actions performed by the operator on this resource. // +optional // +listType=atomic @@ -214,12 +411,16 @@ type OpenTelemetryCollectorStatus struct { } // +kubebuilder:object:root=true +// +kubebuilder:storageversion // +kubebuilder:resource:shortName=otelcol;otelcols // +kubebuilder:subresource:status // +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.scale.replicas,selectorpath=.status.scale.selector // +kubebuilder:printcolumn:name="Mode",type="string",JSONPath=".spec.mode",description="Deployment Mode" // +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".status.version",description="OpenTelemetry Version" +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.scale.statusReplicas" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Image",type="string",JSONPath=".status.image" +// +kubebuilder:printcolumn:name="Management",type="string",JSONPath=".spec.managementState",description="Management State" // +operator-sdk:csv:customresourcedefinitions:displayName="OpenTelemetry Collector" // This annotation provides a hint for OLM which resources are managed by OpenTelemetryCollector kind. // It's not mandatory to list all resources. @@ -245,12 +446,116 @@ type OpenTelemetryCollectorList struct { // AutoscalerSpec defines the OpenTelemetryCollector's pod autoscaling specification. type AutoscalerSpec struct { + // MinReplicas sets a lower bound to the autoscaling feature. Set this if your are using autoscaling. It must be at least 1 + // +optional + MinReplicas *int32 `json:"minReplicas,omitempty"` + // MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled. + // +optional + MaxReplicas *int32 `json:"maxReplicas,omitempty"` // +optional Behavior *autoscalingv2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"` + // Metrics is meant to provide a customizable way to configure HPA metrics. + // currently the only supported custom metrics is type=Pod. + // Use TargetCPUUtilization or TargetMemoryUtilization instead if scaling on these common resource metrics. + // +optional + Metrics []MetricSpec `json:"metrics,omitempty"` // TargetCPUUtilization sets the target average CPU used across all replicas. // If average CPU exceeds this value, the HPA will scale up. Defaults to 90 percent. // +optional TargetCPUUtilization *int32 `json:"targetCPUUtilization,omitempty"` + // +optional + // TargetMemoryUtilization sets the target average memory utilization across all replicas + TargetMemoryUtilization *int32 `json:"targetMemoryUtilization,omitempty"` +} + +// PodDisruptionBudgetSpec defines the OpenTelemetryCollector's pod disruption budget specification. +type PodDisruptionBudgetSpec struct { + // An eviction is allowed if at least "minAvailable" pods selected by + // "selector" will still be available after the eviction, i.e. even in the + // absence of the evicted pod. So for example you can prevent all voluntary + // evictions by specifying "100%". + // +optional + MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty"` + + // An eviction is allowed if at most "maxUnavailable" pods selected by + // "selector" are unavailable after the eviction, i.e. even in absence of + // the evicted pod. For example, one can prevent all voluntary evictions + // by specifying 0. This is a mutually exclusive setting with "minAvailable". + // +optional + MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"` +} + +// MetricsConfigSpec defines a metrics config. +type MetricsConfigSpec struct { + // EnableMetrics specifies if ServiceMonitor or PodMonitor(for sidecar mode) should be created for the OpenTelemetry Collector and Prometheus Exporters. + // The operator.observability.prometheus feature gate must be enabled to use this feature. + // + // +optional + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Create ServiceMonitors for OpenTelemetry Collector" + EnableMetrics bool `json:"enableMetrics,omitempty"` +} + +// ObservabilitySpec defines how telemetry data gets handled. +type ObservabilitySpec struct { + // Metrics defines the metrics configuration for operands. + // + // +optional + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Metrics Config" + Metrics MetricsConfigSpec `json:"metrics,omitempty"` +} + +// Probe defines the OpenTelemetry's pod probe config. Only Liveness probe is supported currently. +type Probe struct { + // Number of seconds after the container has started before liveness probes are initiated. + // Defaults to 0 seconds. Minimum value is 0. + // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + // +optional + InitialDelaySeconds *int32 `json:"initialDelaySeconds,omitempty"` + // Number of seconds after which the probe times out. + // Defaults to 1 second. Minimum value is 1. + // More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + // +optional + TimeoutSeconds *int32 `json:"timeoutSeconds,omitempty"` + // How often (in seconds) to perform the probe. + // Default to 10 seconds. Minimum value is 1. + // +optional + PeriodSeconds *int32 `json:"periodSeconds,omitempty"` + // Minimum consecutive successes for the probe to be considered successful after having failed. + // Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + // +optional + SuccessThreshold *int32 `json:"successThreshold,omitempty"` + // Minimum consecutive failures for the probe to be considered failed after having succeeded. + // Defaults to 3. Minimum value is 1. + // +optional + FailureThreshold *int32 `json:"failureThreshold,omitempty"` + // Optional duration in seconds the pod needs to terminate gracefully upon probe failure. + // The grace period is the duration in seconds after the processes running in the pod are sent + // a termination signal and the time when the processes are forcibly halted with a kill signal. + // Set this value longer than the expected cleanup time for your process. + // If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this + // value overrides the value provided by the pod spec. + // Value must be non-negative integer. The value zero indicates stop immediately via + // the kill signal (no opportunity to shut down). + // This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + // Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. + // +optional + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` +} + +// MetricSpec defines a subset of metrics to be defined for the HPA's metric array +// more metric type can be supported as needed. +// See https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec for reference. +type MetricSpec struct { + Type autoscalingv2.MetricSourceType `json:"type"` + Pods *autoscalingv2.PodsMetricSource `json:"pods,omitempty"` +} + +type ConfigMapsSpec struct { + // Configmap defines name and path where the configMaps should be mounted. + Name string `json:"name"` + MountPath string `json:"mountpath"` } func init() { diff --git a/apis/v1alpha1/opentelemetrycollector_webhook.go b/apis/v1alpha1/opentelemetrycollector_webhook.go deleted file mode 100644 index 6fde5d8070..0000000000 --- a/apis/v1alpha1/opentelemetrycollector_webhook.go +++ /dev/null @@ -1,181 +0,0 @@ -// 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 v1alpha1 - -import ( - "fmt" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/validation" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" - - ta "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator/adapters" -) - -// log is for logging in this package. -var opentelemetrycollectorlog = logf.Log.WithName("opentelemetrycollector-resource") - -func (r *OpenTelemetryCollector) SetupWebhookWithManager(mgr ctrl.Manager) error { - return ctrl.NewWebhookManagedBy(mgr). - For(r). - Complete() -} - -// +kubebuilder:webhook:path=/mutate-opentelemetry-io-v1alpha1-opentelemetrycollector,mutating=true,failurePolicy=fail,groups=opentelemetry.io,resources=opentelemetrycollectors,verbs=create;update,versions=v1alpha1,name=mopentelemetrycollector.kb.io,sideEffects=none,admissionReviewVersions=v1 - -var _ webhook.Defaulter = &OpenTelemetryCollector{} - -// Default implements webhook.Defaulter so a webhook will be registered for the type. -func (r *OpenTelemetryCollector) Default() { - opentelemetrycollectorlog.Info("default", "name", r.Name) - - if len(r.Spec.Mode) == 0 { - r.Spec.Mode = ModeDeployment - } - if len(r.Spec.UpgradeStrategy) == 0 { - r.Spec.UpgradeStrategy = UpgradeStrategyAutomatic - } - - if r.Labels == nil { - r.Labels = map[string]string{} - } - if r.Labels["app.kubernetes.io/managed-by"] == "" { - r.Labels["app.kubernetes.io/managed-by"] = "opentelemetry-operator" - } - - // We can default to one because dependent objects Deployment and HorizontalPodAutoScaler - // default to 1 as well. - one := int32(1) - if r.Spec.Replicas == nil { - r.Spec.Replicas = &one - } - if r.Spec.TargetAllocator.Enabled && r.Spec.TargetAllocator.Replicas == nil { - r.Spec.TargetAllocator.Replicas = &one - } - - // Set default targetCPUUtilization for autoscaler - if r.Spec.MaxReplicas != nil && r.Spec.Autoscaler.TargetCPUUtilization == nil { - defaultCPUTarget := int32(90) - r.Spec.Autoscaler.TargetCPUUtilization = &defaultCPUTarget - } -} - -// +kubebuilder:webhook:verbs=create;update,path=/validate-opentelemetry-io-v1alpha1-opentelemetrycollector,mutating=false,failurePolicy=fail,groups=opentelemetry.io,resources=opentelemetrycollectors,versions=v1alpha1,name=vopentelemetrycollectorcreateupdate.kb.io,sideEffects=none,admissionReviewVersions=v1 -// +kubebuilder:webhook:verbs=delete,path=/validate-opentelemetry-io-v1alpha1-opentelemetrycollector,mutating=false,failurePolicy=ignore,groups=opentelemetry.io,resources=opentelemetrycollectors,versions=v1alpha1,name=vopentelemetrycollectordelete.kb.io,sideEffects=none,admissionReviewVersions=v1 - -var _ webhook.Validator = &OpenTelemetryCollector{} - -// ValidateCreate implements webhook.Validator so a webhook will be registered for the type. -func (r *OpenTelemetryCollector) ValidateCreate() error { - opentelemetrycollectorlog.Info("validate create", "name", r.Name) - return r.validateCRDSpec() -} - -// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (r *OpenTelemetryCollector) ValidateUpdate(old runtime.Object) error { - opentelemetrycollectorlog.Info("validate update", "name", r.Name) - return r.validateCRDSpec() -} - -// ValidateDelete implements webhook.Validator so a webhook will be registered for the type. -func (r *OpenTelemetryCollector) ValidateDelete() error { - opentelemetrycollectorlog.Info("validate delete", "name", r.Name) - return nil -} - -func (r *OpenTelemetryCollector) validateCRDSpec() error { - // validate volumeClaimTemplates - if r.Spec.Mode != ModeStatefulSet && len(r.Spec.VolumeClaimTemplates) > 0 { - return fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'volumeClaimTemplates'", r.Spec.Mode) - } - - // validate tolerations - if r.Spec.Mode == ModeSidecar && len(r.Spec.Tolerations) > 0 { - return fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'tolerations'", r.Spec.Mode) - } - - // validate priorityClassName - if r.Spec.Mode == ModeSidecar && r.Spec.PriorityClassName != "" { - return fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'priorityClassName'", r.Spec.Mode) - } - - // validate target allocation - if r.Spec.TargetAllocator.Enabled && r.Spec.Mode != ModeStatefulSet { - return fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the target allocation deployment", r.Spec.Mode) - } - - // validate Prometheus config for target allocation - if r.Spec.TargetAllocator.Enabled { - _, err := ta.ConfigToPromConfig(r.Spec.Config) - if err != nil { - return fmt.Errorf("the OpenTelemetry Spec Prometheus configuration is incorrect, %s", err) - } - } - - // validator port config - for _, p := range r.Spec.Ports { - nameErrs := validation.IsValidPortName(p.Name) - numErrs := validation.IsValidPortNum(int(p.Port)) - if len(nameErrs) > 0 || len(numErrs) > 0 { - return fmt.Errorf("the OpenTelemetry Spec Ports configuration is incorrect, port name '%s' errors: %s, num '%d' errors: %s", - p.Name, nameErrs, p.Port, numErrs) - } - } - - // validate autoscale with horizontal pod autoscaler - if r.Spec.MaxReplicas != nil { - if *r.Spec.MaxReplicas < int32(1) { - return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, maxReplicas should be defined and one or more") - } - - if r.Spec.Replicas != nil && *r.Spec.Replicas > *r.Spec.MaxReplicas { - return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, replicas must not be greater than maxReplicas") - } - - if r.Spec.MinReplicas != nil && *r.Spec.MinReplicas > *r.Spec.MaxReplicas { - return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, minReplicas must not be greater than maxReplicas") - } - - if r.Spec.MinReplicas != nil && *r.Spec.MinReplicas < int32(1) { - return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, minReplicas should be one or more") - } - - if r.Spec.Autoscaler != nil && r.Spec.Autoscaler.Behavior != nil { - if r.Spec.Autoscaler.Behavior.ScaleDown != nil && r.Spec.Autoscaler.Behavior.ScaleDown.StabilizationWindowSeconds != nil && - *r.Spec.Autoscaler.Behavior.ScaleDown.StabilizationWindowSeconds < int32(1) { - return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, scaleDown should be one or more") - } - - if r.Spec.Autoscaler.Behavior.ScaleUp != nil && r.Spec.Autoscaler.Behavior.ScaleUp.StabilizationWindowSeconds != nil && - *r.Spec.Autoscaler.Behavior.ScaleUp.StabilizationWindowSeconds < int32(1) { - return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, scaleUp should be one or more") - } - } - if r.Spec.Autoscaler != nil && r.Spec.Autoscaler.TargetCPUUtilization != nil && (*r.Spec.Autoscaler.TargetCPUUtilization < int32(1) || *r.Spec.Autoscaler.TargetCPUUtilization > int32(99)) { - return fmt.Errorf("the OpenTelemetry Spec autoscale configuration is incorrect, targetCPUUtilization should be greater than 0 and less than 100") - } - - } - - if r.Spec.Ingress.Type == IngressTypeNginx && r.Spec.Mode == ModeSidecar { - return fmt.Errorf("the OptenTelemetry Spec Ingress configuiration is incorrect. Ingress can only be used in combination with the modes: %s, %s, %s", - ModeDeployment, ModeDaemonSet, ModeStatefulSet, - ) - } - - return nil -} diff --git a/apis/v1alpha1/opentelemetrycollector_webhook_test.go b/apis/v1alpha1/opentelemetrycollector_webhook_test.go deleted file mode 100644 index 3ec2879564..0000000000 --- a/apis/v1alpha1/opentelemetrycollector_webhook_test.go +++ /dev/null @@ -1,357 +0,0 @@ -// 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 v1alpha1 - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - autoscalingv2 "k8s.io/api/autoscaling/v2" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestOTELColDefaultingWebhook(t *testing.T) { - one := int32(1) - five := int32(5) - tests := []struct { - name string - otelcol OpenTelemetryCollector - expected OpenTelemetryCollector - }{ - { - name: "all fields default", - otelcol: OpenTelemetryCollector{}, - expected: OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }, - }, - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeDeployment, - Replicas: &one, - UpgradeStrategy: UpgradeStrategyAutomatic, - }, - }, - }, - { - name: "provided values in spec", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeSidecar, - Replicas: &five, - UpgradeStrategy: "adhoc", - }, - }, - expected: OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{ - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }, - }, - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeSidecar, - Replicas: &five, - UpgradeStrategy: "adhoc", - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - test.otelcol.Default() - assert.Equal(t, test.expected, test.otelcol) - }) - } -} - -func TestOTELColValidatingWebhook(t *testing.T) { - zero := int32(0) - one := int32(1) - three := int32(3) - five := int32(5) - - tests := []struct { //nolint:govet - name string - otelcol OpenTelemetryCollector - expectedErr string - }{ - { - name: "valid empty spec", - otelcol: OpenTelemetryCollector{}, - }, - { - name: "valid full spec", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeStatefulSet, - MinReplicas: &one, - Replicas: &three, - MaxReplicas: &five, - UpgradeStrategy: "adhoc", - TargetAllocator: OpenTelemetryTargetAllocator{ - Enabled: true, - }, - Config: `receivers: - examplereceiver: - endpoint: "0.0.0.0:12345" - examplereceiver/settings: - endpoint: "0.0.0.0:12346" - prometheus: - config: - scrape_config: - job_name: otel-collector - scrape_interval: 10s - jaeger/custom: - protocols: - thrift_http: - endpoint: 0.0.0.0:15268 -`, - Ports: []v1.ServicePort{ - { - Name: "port1", - Port: 5555, - }, - { - Name: "port2", - Port: 5554, - Protocol: v1.ProtocolUDP, - }, - }, - Autoscaler: &AutoscalerSpec{ - Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ - ScaleDown: &autoscalingv2.HPAScalingRules{ - StabilizationWindowSeconds: &three, - }, - ScaleUp: &autoscalingv2.HPAScalingRules{ - StabilizationWindowSeconds: &five, - }, - }, - TargetCPUUtilization: &five, - }, - }, - }, - }, - { - name: "invalid mode with volume claim templates", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeSidecar, - VolumeClaimTemplates: []v1.PersistentVolumeClaim{{}, {}}, - }, - }, - expectedErr: "does not support the attribute 'volumeClaimTemplates'", - }, - { - name: "invalid mode with tolerations", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeSidecar, - Tolerations: []v1.Toleration{{}, {}}, - }, - }, - expectedErr: "does not support the attribute 'tolerations'", - }, - { - name: "invalid mode with target allocator", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeDeployment, - TargetAllocator: OpenTelemetryTargetAllocator{ - Enabled: true, - }, - }, - }, - expectedErr: "does not support the target allocation deployment", - }, - { - name: "invalid target allocator config", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeStatefulSet, - TargetAllocator: OpenTelemetryTargetAllocator{ - Enabled: true, - }, - }, - }, - expectedErr: "the OpenTelemetry Spec Prometheus configuration is incorrect", - }, - { - name: "invalid port name", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Ports: []v1.ServicePort{ - { - // this port name contains a non alphanumeric character, which is invalid. - Name: "-test🦄port", - Port: 12345, - Protocol: v1.ProtocolTCP, - }, - }, - }, - }, - expectedErr: "the OpenTelemetry Spec Ports configuration is incorrect", - }, - { - name: "invalid port name, too long", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Ports: []v1.ServicePort{ - { - Name: "aaaabbbbccccdddd", // len: 16, too long - Port: 5555, - }, - }, - }, - }, - expectedErr: "the OpenTelemetry Spec Ports configuration is incorrect", - }, - { - name: "invalid port num", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Ports: []v1.ServicePort{ - { - Name: "aaaabbbbccccddd", // len: 15 - // no port set means it's 0, which is invalid - }, - }, - }, - }, - expectedErr: "the OpenTelemetry Spec Ports configuration is incorrect", - }, - { - name: "invalid max replicas", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - MaxReplicas: &zero, - }, - }, - expectedErr: "maxReplicas should be defined and one or more", - }, - { - name: "invalid replicas, greater than max", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - MaxReplicas: &three, - Replicas: &five, - }, - }, - expectedErr: "replicas must not be greater than maxReplicas", - }, - { - name: "invalid min replicas, greater than max", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - MaxReplicas: &three, - MinReplicas: &five, - }, - }, - expectedErr: "minReplicas must not be greater than maxReplicas", - }, - { - name: "invalid min replicas, lesser than 1", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - MaxReplicas: &three, - MinReplicas: &zero, - }, - }, - expectedErr: "minReplicas should be one or more", - }, - { - name: "invalid autoscaler scale down", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - MaxReplicas: &three, - Autoscaler: &AutoscalerSpec{ - Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ - ScaleDown: &autoscalingv2.HPAScalingRules{ - StabilizationWindowSeconds: &zero, - }, - }, - }, - }, - }, - expectedErr: "scaleDown should be one or more", - }, - { - name: "invalid autoscaler scale up", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - MaxReplicas: &three, - Autoscaler: &AutoscalerSpec{ - Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{ - ScaleUp: &autoscalingv2.HPAScalingRules{ - StabilizationWindowSeconds: &zero, - }, - }, - }, - }, - }, - expectedErr: "scaleUp should be one or more", - }, - { - name: "invalid autoscaler target cpu utilization", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - MaxReplicas: &three, - Autoscaler: &AutoscalerSpec{ - TargetCPUUtilization: &zero, - }, - }, - }, - expectedErr: "targetCPUUtilization should be greater than 0 and less than 100", - }, - { - name: "invalid deployment mode incompabible with ingress settings", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeSidecar, - Ingress: Ingress{ - Type: IngressTypeNginx, - }, - }, - }, - expectedErr: fmt.Sprintf("Ingress can only be used in combination with the modes: %s, %s, %s", - ModeDeployment, ModeDaemonSet, ModeStatefulSet, - ), - }, - { - name: "invalid mode with priorityClassName", - otelcol: OpenTelemetryCollector{ - Spec: OpenTelemetryCollectorSpec{ - Mode: ModeSidecar, - PriorityClassName: "test-class", - }, - }, - expectedErr: "does not support the attribute 'priorityClassName'", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := test.otelcol.validateCRDSpec() - if test.expectedErr == "" { - assert.NoError(t, err) - return - } - assert.ErrorContains(t, err, test.expectedErr) - }) - } -} diff --git a/apis/v1alpha1/samplers.go b/apis/v1alpha1/samplers.go index beb01b7cee..ff0d23e10f 100644 --- a/apis/v1alpha1/samplers.go +++ b/apis/v1alpha1/samplers.go @@ -35,6 +35,8 @@ const ( ParentBasedTraceIDRatio SamplerType = "parentbased_traceidratio" // JaegerRemote represents JaegerRemoteSampler. JaegerRemote SamplerType = "jaeger_remote" + // ParentBasedJaegerRemote represents ParentBased(root=JaegerRemoteSampler). + ParentBasedJaegerRemote SamplerType = "parentbased_jaeger_remote" // XRay represents AWS X-Ray Centralized Sampling. XRaySampler SamplerType = "xray" ) diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index f03c0e2ce7..749a96a1fb 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -23,22 +23,81 @@ import ( "k8s.io/api/autoscaling/v2" "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/intstr" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApacheHttpd) DeepCopyInto(out *ApacheHttpd) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Attrs != nil { + in, out := &in.Attrs, &out.Attrs + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApacheHttpd. +func (in *ApacheHttpd) DeepCopy() *ApacheHttpd { + if in == nil { + return nil + } + out := new(ApacheHttpd) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AutoscalerSpec) DeepCopyInto(out *AutoscalerSpec) { *out = *in + if in.MinReplicas != nil { + in, out := &in.MinReplicas, &out.MinReplicas + *out = new(int32) + **out = **in + } + if in.MaxReplicas != nil { + in, out := &in.MaxReplicas, &out.MaxReplicas + *out = new(int32) + **out = **in + } if in.Behavior != nil { in, out := &in.Behavior, &out.Behavior *out = new(v2.HorizontalPodAutoscalerBehavior) (*in).DeepCopyInto(*out) } + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = make([]MetricSpec, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.TargetCPUUtilization != nil { in, out := &in.TargetCPUUtilization, &out.TargetCPUUtilization *out = new(int32) **out = **in } + if in.TargetMemoryUtilization != nil { + in, out := &in.TargetMemoryUtilization, &out.TargetMemoryUtilization + *out = new(int32) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalerSpec. @@ -51,9 +110,29 @@ func (in *AutoscalerSpec) DeepCopy() *AutoscalerSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ConfigMapsSpec) DeepCopyInto(out *ConfigMapsSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigMapsSpec. +func (in *ConfigMapsSpec) DeepCopy() *ConfigMapsSpec { + if in == nil { + return nil + } + out := new(ConfigMapsSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DotNet) DeepCopyInto(out *DotNet) { *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } if in.Env != nil { in, out := &in.Env, &out.Env *out = make([]v1.EnvVar, len(*in)) @@ -61,6 +140,7 @@ func (in *DotNet) DeepCopyInto(out *DotNet) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.Resources.DeepCopyInto(&out.Resources) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DotNet. @@ -88,6 +168,34 @@ func (in *Exporter) DeepCopy() *Exporter { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Go) DeepCopyInto(out *Go) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Go. +func (in *Go) DeepCopy() *Go { + if in == nil { + return nil + } + out := new(Go) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Ingress) DeepCopyInto(out *Ingress) { *out = *in @@ -105,6 +213,12 @@ func (in *Ingress) DeepCopyInto(out *Ingress) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.IngressClassName != nil { + in, out := &in.IngressClassName, &out.IngressClassName + *out = new(string) + **out = **in + } + out.Route = in.Route } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Ingress. @@ -198,6 +312,9 @@ func (in *InstrumentationSpec) DeepCopyInto(out *InstrumentationSpec) { in.NodeJS.DeepCopyInto(&out.NodeJS) in.Python.DeepCopyInto(&out.Python) in.DotNet.DeepCopyInto(&out.DotNet) + in.Go.DeepCopyInto(&out.Go) + in.ApacheHttpd.DeepCopyInto(&out.ApacheHttpd) + in.Nginx.DeepCopyInto(&out.Nginx) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationSpec. @@ -228,6 +345,11 @@ func (in *InstrumentationStatus) DeepCopy() *InstrumentationStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Java) DeepCopyInto(out *Java) { *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } if in.Env != nil { in, out := &in.Env, &out.Env *out = make([]v1.EnvVar, len(*in)) @@ -235,6 +357,7 @@ func (in *Java) DeepCopyInto(out *Java) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.Resources.DeepCopyInto(&out.Resources) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Java. @@ -247,9 +370,84 @@ func (in *Java) DeepCopy() *Java { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricSpec) DeepCopyInto(out *MetricSpec) { + *out = *in + if in.Pods != nil { + in, out := &in.Pods, &out.Pods + *out = new(v2.PodsMetricSource) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricSpec. +func (in *MetricSpec) DeepCopy() *MetricSpec { + if in == nil { + return nil + } + out := new(MetricSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MetricsConfigSpec) DeepCopyInto(out *MetricsConfigSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricsConfigSpec. +func (in *MetricsConfigSpec) DeepCopy() *MetricsConfigSpec { + if in == nil { + return nil + } + out := new(MetricsConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Nginx) DeepCopyInto(out *Nginx) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Attrs != nil { + in, out := &in.Attrs, &out.Attrs + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Nginx. +func (in *Nginx) DeepCopy() *Nginx { + if in == nil { + return nil + } + out := new(Nginx) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NodeJS) DeepCopyInto(out *NodeJS) { *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } if in.Env != nil { in, out := &in.Env, &out.Env *out = make([]v1.EnvVar, len(*in)) @@ -257,6 +455,7 @@ func (in *NodeJS) DeepCopyInto(out *NodeJS) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.Resources.DeepCopyInto(&out.Resources) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeJS. @@ -269,6 +468,239 @@ func (in *NodeJS) DeepCopy() *NodeJS { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObservabilitySpec) DeepCopyInto(out *ObservabilitySpec) { + *out = *in + out.Metrics = in.Metrics +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObservabilitySpec. +func (in *ObservabilitySpec) DeepCopy() *ObservabilitySpec { + if in == nil { + return nil + } + out := new(ObservabilitySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpAMPBridge) DeepCopyInto(out *OpAMPBridge) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpAMPBridge. +func (in *OpAMPBridge) DeepCopy() *OpAMPBridge { + if in == nil { + return nil + } + out := new(OpAMPBridge) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OpAMPBridge) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpAMPBridgeList) DeepCopyInto(out *OpAMPBridgeList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OpAMPBridge, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpAMPBridgeList. +func (in *OpAMPBridgeList) DeepCopy() *OpAMPBridgeList { + if in == nil { + return nil + } + out := new(OpAMPBridgeList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OpAMPBridgeList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpAMPBridgeSpec) DeepCopyInto(out *OpAMPBridgeSpec) { + *out = *in + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Capabilities != nil { + in, out := &in.Capabilities, &out.Capabilities + *out = make(map[OpAMPBridgeCapability]bool, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ComponentsAllowed != nil { + in, out := &in.ComponentsAllowed, &out.ComponentsAllowed + *out = make(map[string][]string, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.SecurityContext) + (*in).DeepCopyInto(*out) + } + if in.PodSecurityContext != nil { + in, out := &in.PodSecurityContext, &out.PodSecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.PodAnnotations != nil { + in, out := &in.PodAnnotations, &out.PodAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]v1.ServicePort, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.EnvFrom != nil { + in, out := &in.EnvFrom, &out.EnvFrom + *out = make([]v1.EnvFromSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.TopologySpreadConstraints != nil { + in, out := &in.TopologySpreadConstraints, &out.TopologySpreadConstraints + *out = make([]v1.TopologySpreadConstraint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpAMPBridgeSpec. +func (in *OpAMPBridgeSpec) DeepCopy() *OpAMPBridgeSpec { + if in == nil { + return nil + } + out := new(OpAMPBridgeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpAMPBridgeStatus) DeepCopyInto(out *OpAMPBridgeStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpAMPBridgeStatus. +func (in *OpAMPBridgeStatus) DeepCopy() *OpAMPBridgeStatus { + if in == nil { + return nil + } + out := new(OpAMPBridgeStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenShiftRoute) DeepCopyInto(out *OpenShiftRoute) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenShiftRoute. +func (in *OpenShiftRoute) DeepCopy() *OpenShiftRoute { + if in == nil { + return nil + } + out := new(OpenShiftRoute) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpenTelemetryCollector) DeepCopyInto(out *OpenTelemetryCollector) { *out = *in @@ -366,6 +798,11 @@ func (in *OpenTelemetryCollectorSpec) DeepCopyInto(out *OpenTelemetryCollectorSp *out = new(AutoscalerSpec) (*in).DeepCopyInto(*out) } + if in.PodDisruptionBudget != nil { + in, out := &in.PodDisruptionBudget, &out.PodDisruptionBudget + *out = new(PodDisruptionBudgetSpec) + (*in).DeepCopyInto(*out) + } if in.SecurityContext != nil { in, out := &in.SecurityContext, &out.SecurityContext *out = new(v1.SecurityContext) @@ -434,6 +871,54 @@ func (in *OpenTelemetryCollectorSpec) DeepCopyInto(out *OpenTelemetryCollectorSp } } in.Ingress.DeepCopyInto(&out.Ingress) + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.Lifecycle != nil { + in, out := &in.Lifecycle, &out.Lifecycle + *out = new(v1.Lifecycle) + (*in).DeepCopyInto(*out) + } + if in.TerminationGracePeriodSeconds != nil { + in, out := &in.TerminationGracePeriodSeconds, &out.TerminationGracePeriodSeconds + *out = new(int64) + **out = **in + } + if in.LivenessProbe != nil { + in, out := &in.LivenessProbe, &out.LivenessProbe + *out = new(Probe) + (*in).DeepCopyInto(*out) + } + if in.InitContainers != nil { + in, out := &in.InitContainers, &out.InitContainers + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AdditionalContainers != nil { + in, out := &in.AdditionalContainers, &out.AdditionalContainers + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.Observability = in.Observability + if in.TopologySpreadConstraints != nil { + in, out := &in.TopologySpreadConstraints, &out.TopologySpreadConstraints + *out = make([]v1.TopologySpreadConstraint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ConfigMaps != nil { + in, out := &in.ConfigMaps, &out.ConfigMaps + *out = make([]ConfigMapsSpec, len(*in)) + copy(*out, *in) + } + in.UpdateStrategy.DeepCopyInto(&out.UpdateStrategy) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryCollectorSpec. @@ -475,7 +960,46 @@ func (in *OpenTelemetryTargetAllocator) DeepCopyInto(out *OpenTelemetryTargetAll *out = new(int32) **out = **in } - out.PrometheusCR = in.PrometheusCR + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.Resources.DeepCopyInto(&out.Resources) + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + in.PrometheusCR.DeepCopyInto(&out.PrometheusCR) + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.TopologySpreadConstraints != nil { + in, out := &in.TopologySpreadConstraints, &out.TopologySpreadConstraints + *out = make([]v1.TopologySpreadConstraint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryTargetAllocator. @@ -491,6 +1015,25 @@ func (in *OpenTelemetryTargetAllocator) DeepCopy() *OpenTelemetryTargetAllocator // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpenTelemetryTargetAllocatorPrometheusCR) DeepCopyInto(out *OpenTelemetryTargetAllocatorPrometheusCR) { *out = *in + if in.ScrapeInterval != nil { + in, out := &in.ScrapeInterval, &out.ScrapeInterval + *out = new(metav1.Duration) + **out = **in + } + if in.PodMonitorSelector != nil { + in, out := &in.PodMonitorSelector, &out.PodMonitorSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.ServiceMonitorSelector != nil { + in, out := &in.ServiceMonitorSelector, &out.ServiceMonitorSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryTargetAllocatorPrometheusCR. @@ -503,9 +1046,84 @@ func (in *OpenTelemetryTargetAllocatorPrometheusCR) DeepCopy() *OpenTelemetryTar return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodDisruptionBudgetSpec) DeepCopyInto(out *PodDisruptionBudgetSpec) { + *out = *in + if in.MinAvailable != nil { + in, out := &in.MinAvailable, &out.MinAvailable + *out = new(intstr.IntOrString) + **out = **in + } + if in.MaxUnavailable != nil { + in, out := &in.MaxUnavailable, &out.MaxUnavailable + *out = new(intstr.IntOrString) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodDisruptionBudgetSpec. +func (in *PodDisruptionBudgetSpec) DeepCopy() *PodDisruptionBudgetSpec { + if in == nil { + return nil + } + out := new(PodDisruptionBudgetSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Probe) DeepCopyInto(out *Probe) { + *out = *in + if in.InitialDelaySeconds != nil { + in, out := &in.InitialDelaySeconds, &out.InitialDelaySeconds + *out = new(int32) + **out = **in + } + if in.TimeoutSeconds != nil { + in, out := &in.TimeoutSeconds, &out.TimeoutSeconds + *out = new(int32) + **out = **in + } + if in.PeriodSeconds != nil { + in, out := &in.PeriodSeconds, &out.PeriodSeconds + *out = new(int32) + **out = **in + } + if in.SuccessThreshold != nil { + in, out := &in.SuccessThreshold, &out.SuccessThreshold + *out = new(int32) + **out = **in + } + if in.FailureThreshold != nil { + in, out := &in.FailureThreshold, &out.FailureThreshold + *out = new(int32) + **out = **in + } + if in.TerminationGracePeriodSeconds != nil { + in, out := &in.TerminationGracePeriodSeconds, &out.TerminationGracePeriodSeconds + *out = new(int64) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Probe. +func (in *Probe) DeepCopy() *Probe { + if in == nil { + return nil + } + out := new(Probe) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Python) DeepCopyInto(out *Python) { *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } if in.Env != nil { in, out := &in.Env, &out.Env *out = make([]v1.EnvVar, len(*in)) @@ -513,6 +1131,7 @@ func (in *Python) DeepCopyInto(out *Python) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + in.Resources.DeepCopyInto(&out.Resources) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Python. diff --git a/apis/v1alpha2/groupversion_info.go b/apis/v1alpha2/groupversion_info.go new file mode 100644 index 0000000000..4a5c2ac8c6 --- /dev/null +++ b/apis/v1alpha2/groupversion_info.go @@ -0,0 +1,34 @@ +// 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 v1alpha2 contains API Schema definitions for the v1alpha2 API group +// +kubebuilder:object:generate=true +// +groupName=opentelemetry.io +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "opentelemetry.io", Version: "v1alpha2"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/v1alpha2/instrumentation_types.go b/apis/v1alpha2/instrumentation_types.go new file mode 100644 index 0000000000..e07d350a81 --- /dev/null +++ b/apis/v1alpha2/instrumentation_types.go @@ -0,0 +1,325 @@ +// 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. + +// +kubebuilder:skip + +package v1alpha2 + +import ( + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +// InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumentation. +type InstrumentationSpec struct { + // Exporter defines exporter configuration. + // +optional + Exporter `json:"exporter,omitempty"` + + // Resource defines the configuration for the resource attributes, as defined by the OpenTelemetry specification. + // +optional + Resource Resource `json:"resource,omitempty"` + + // Propagators defines inter-process context propagation configuration. + // Values in this list will be set in the OTEL_PROPAGATORS env var. + // Enum=tracecontext;baggage;b3;b3multi;jaeger;xray;ottrace;none + // +optional + Propagators []v1alpha1.Propagator `json:"propagators,omitempty"` + + // Sampler defines sampling configuration. + // +optional + Sampler `json:"sampler,omitempty"` + + // Env defines common env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Java defines configuration for java auto-instrumentation. + // +optional + Java Java `json:"java,omitempty"` + + // NodeJS defines configuration for nodejs auto-instrumentation. + // +optional + NodeJS NodeJS `json:"nodejs,omitempty"` + + // Python defines configuration for python auto-instrumentation. + // +optional + Python Python `json:"python,omitempty"` + + // DotNet defines configuration for DotNet auto-instrumentation. + // +optional + DotNet DotNet `json:"dotnet,omitempty"` + + // Go defines configuration for Go auto-instrumentation. + // When using Go auto-instrumentation you must provide a value for the OTEL_GO_AUTO_TARGET_EXE env var via the + // Instrumentation env vars or via the instrumentation.opentelemetry.io/otel-go-auto-target-exe pod annotation. + // Failure to set this value causes instrumentation injection to abort, leaving the original pod unchanged. + // +optional + Go Go `json:"go,omitempty"` + + // ApacheHttpd defines configuration for Apache HTTPD auto-instrumentation. + // +optional + ApacheHttpd ApacheHttpd `json:"apacheHttpd,omitempty"` + + // Nginx defines configuration for Nginx auto-instrumentation. + // +optional + Nginx Nginx `json:"nginx,omitempty"` +} + +// Resource defines the configuration for the resource attributes, as defined by the OpenTelemetry specification. +// See also: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.8.0/specification/overview.md#resources +type Resource struct { + // Attributes defines attributes that are added to the resource. + // For example environment: dev + // +optional + Attributes map[string]string `json:"resourceAttributes,omitempty"` + + // AddK8sUIDAttributes defines whether K8s UID attributes should be collected (e.g. k8s.deployment.uid). + // +optional + AddK8sUIDAttributes bool `json:"addK8sUIDAttributes,omitempty"` +} + +// Exporter defines OTLP exporter configuration. +type Exporter struct { + // Endpoint is address of the collector with OTLP endpoint. + // +optional + Endpoint string `json:"endpoint,omitempty"` +} + +// Sampler defines sampling configuration. +type Sampler struct { + // Type defines sampler type. + // The value will be set in the OTEL_TRACES_SAMPLER env var. + // The value can be for instance parentbased_always_on, parentbased_always_off, parentbased_traceidratio... + // +optional + Type v1alpha1.SamplerType `json:"type,omitempty"` + + // Argument defines sampler argument. + // The value depends on the sampler type. + // For instance for parentbased_traceidratio sampler type it is a number in range [0..1] e.g. 0.25. + // The value will be set in the OTEL_TRACES_SAMPLER_ARG env var. + // +optional + Argument string `json:"argument,omitempty"` +} + +// Java defines Java SDK and instrumentation configuration. +type Java struct { + // Image is a container image with javaagent auto-instrumentation JAR. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines java specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resources,omitempty"` +} + +// NodeJS defines NodeJS SDK and instrumentation configuration. +type NodeJS struct { + // Image is a container image with NodeJS SDK and auto-instrumentation. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines nodejs specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` +} + +// Python defines Python SDK and instrumentation configuration. +type Python struct { + // Image is a container image with Python SDK and auto-instrumentation. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines python specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` +} + +// DotNet defines DotNet SDK and instrumentation configuration. +type DotNet struct { + // Image is a container image with DotNet SDK and auto-instrumentation. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines DotNet specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` +} + +type Go struct { + // Image is a container image with Go SDK and auto-instrumentation. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines Go specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` +} + +// ApacheHttpd defines Apache SDK and instrumentation configuration. +type ApacheHttpd struct { + // Image is a container image with Apache SDK and auto-instrumentation. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines Apache HTTPD specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Attrs defines Apache HTTPD agent specific attributes. The precedence is: + // `agent default attributes` > `instrument spec attributes` . + // Attributes are documented at https://github.com/open-telemetry/opentelemetry-cpp-contrib/tree/main/instrumentation/otel-webserver-module + // +optional + Attrs []corev1.EnvVar `json:"attrs,omitempty"` + + // Apache HTTPD server version. One of 2.4 or 2.2. Default is 2.4 + // +optional + Version string `json:"version,omitempty"` + + // Location of Apache HTTPD server configuration. + // Needed only if different from default "/usr/local/apache2/conf" + // +optional + ConfigPath string `json:"configPath,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` +} + +// Nginx defines Nginx SDK and instrumentation configuration. +type Nginx struct { + // Image is a container image with Nginx SDK and auto-instrumentation. + // +optional + Image string `json:"image,omitempty"` + + // VolumeSizeLimit defines size limit for volume used for auto-instrumentation. + // The default size is 200Mi. + VolumeSizeLimit *resource.Quantity `json:"volumeLimitSize,omitempty"` + + // Env defines Nginx specific env vars. There are four layers for env vars' definitions and + // the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. + // If the former var had been defined, then the other vars would be ignored. + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + + // Attrs defines Nginx agent specific attributes. The precedence order is: + // `agent default attributes` > `instrument spec attributes` . + // Attributes are documented at https://github.com/open-telemetry/opentelemetry-cpp-contrib/tree/main/instrumentation/otel-webserver-module + // +optional + Attrs []corev1.EnvVar `json:"attrs,omitempty"` + + // Location of Nginx configuration file. + // Needed only if different from default "/etx/nginx/nginx.conf" + // +optional + ConfigFile string `json:"configFile,omitempty"` + + // Resources describes the compute resource requirements. + // +optional + Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` +} + +// InstrumentationStatus defines status of the instrumentation. +type InstrumentationStatus struct { +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=otelinst;otelinsts +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Endpoint",type="string",JSONPath=".spec.exporter.endpoint" +// +kubebuilder:printcolumn:name="Sampler",type="string",JSONPath=".spec.sampler.type" +// +kubebuilder:printcolumn:name="Sampler Arg",type="string",JSONPath=".spec.sampler.argument" +// +operator-sdk:csv:customresourcedefinitions:displayName="OpenTelemetry Instrumentation" +// +operator-sdk:csv:customresourcedefinitions:resources={{Pod,v1}} + +// Instrumentation is the spec for OpenTelemetry instrumentation. +type Instrumentation struct { + Status InstrumentationStatus `json:"status,omitempty"` + metav1.TypeMeta `json:",inline"` + Spec InstrumentationSpec `json:"spec,omitempty"` + metav1.ObjectMeta `json:"metadata,omitempty"` +} + +// +kubebuilder:object:root=true + +// InstrumentationList contains a list of Instrumentation. +type InstrumentationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Instrumentation `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Instrumentation{}, &InstrumentationList{}) +} diff --git a/apis/v1alpha2/opentelemetrycollector_types.go b/apis/v1alpha2/opentelemetrycollector_types.go new file mode 100644 index 0000000000..710766ffbb --- /dev/null +++ b/apis/v1alpha2/opentelemetrycollector_types.go @@ -0,0 +1,255 @@ +// 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. + +// +kubebuilder:skip + +package v1alpha2 + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +// OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector. +type OpenTelemetryCollectorSpec struct { + // ManagementState defines if the CR should be managed by the operator or not. + // Default is managed. + // + // +required + // +kubebuilder:validation:Required + // +kubebuilder:default:=managed + ManagementState v1alpha1.ManagementStateType `json:"managementState,omitempty"` + // Resources to set on the OpenTelemetry Collector pods. + // +optional + Resources v1.ResourceRequirements `json:"resources,omitempty"` + // NodeSelector to schedule OpenTelemetry Collector pods. + // This is only relevant to daemonset, statefulset, and deployment mode + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + // Args is the set of arguments to pass to the OpenTelemetry Collector binary + // +optional + Args map[string]string `json:"args,omitempty"` + // Replicas is the number of pod instances for the underlying OpenTelemetry Collector. Set this if your are not using autoscaling + // +optional + Replicas *int32 `json:"replicas,omitempty"` + // Autoscaler specifies the pod autoscaling configuration to use + // for the OpenTelemetryCollector workload. + // + // +optional + Autoscaler *v1alpha1.AutoscalerSpec `json:"autoscaler,omitempty"` + // PodDisruptionBudget specifies the pod disruption budget configuration to use + // for the OpenTelemetryCollector workload. + // + // +optional + PodDisruptionBudget *v1alpha1.PodDisruptionBudgetSpec `json:"podDisruptionBudget,omitempty"` + // SecurityContext configures the container security context for + // the opentelemetry-collector container. + // + // In deployment, daemonset, or statefulset mode, this controls + // the security context settings for the primary application + // container. + // + // In sidecar mode, this controls the security context for the + // injected sidecar container. + // + // +optional + SecurityContext *v1.SecurityContext `json:"securityContext,omitempty"` + // PodSecurityContext configures the pod security context for the + // opentelemetry-collector pod, when running as a deployment, daemonset, + // or statefulset. + // + // In sidecar mode, the opentelemetry-operator will ignore this setting. + // + // +optional + PodSecurityContext *v1.PodSecurityContext `json:"podSecurityContext,omitempty"` + // PodAnnotations is the set of annotations that will be attached to + // Collector and Target Allocator pods. + // +optional + PodAnnotations map[string]string `json:"podAnnotations,omitempty"` + // TargetAllocator indicates a value which determines whether to spawn a target allocation resource or not. + // +optional + TargetAllocator v1alpha1.OpenTelemetryTargetAllocator `json:"targetAllocator,omitempty"` + // Mode represents how the collector should be deployed (deployment, daemonset, statefulset or sidecar) + // +optional + Mode v1alpha1.Mode `json:"mode,omitempty"` + // ServiceAccount indicates the name of an existing service account to use with this instance. When set, + // the operator will not automatically create a ServiceAccount for the collector. + // +optional + ServiceAccount string `json:"serviceAccount,omitempty"` + // Image indicates the container image to use for the OpenTelemetry Collector. + // +optional + Image string `json:"image,omitempty"` + // UpgradeStrategy represents how the operator will handle upgrades to the CR when a newer version of the operator is deployed + // +optional + UpgradeStrategy v1alpha1.UpgradeStrategy `json:"upgradeStrategy"` + + // ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent) + // +optional + ImagePullPolicy v1.PullPolicy `json:"imagePullPolicy,omitempty"` + // Config is the raw JSON to be used as the collector's configuration. Refer to the OpenTelemetry Collector documentation for details. + // +required + Config string `json:"config,omitempty"` + // VolumeMounts represents the mount points to use in the underlying collector deployment(s) + // +optional + // +listType=atomic + VolumeMounts []v1.VolumeMount `json:"volumeMounts,omitempty"` + // Ports allows a set of ports to be exposed by the underlying v1.Service. By default, the operator + // will attempt to infer the required ports by parsing the .Spec.Config property but this property can be + // used to open additional ports that can't be inferred by the operator, like for custom receivers. + // +optional + // +listType=atomic + Ports []v1.ServicePort `json:"ports,omitempty"` + // ENV vars to set on the OpenTelemetry Collector's Pods. These can then in certain cases be + // consumed in the config file for the Collector. + // +optional + Env []v1.EnvVar `json:"env,omitempty"` + // List of sources to populate environment variables on the OpenTelemetry Collector's Pods. + // These can then in certain cases be consumed in the config file for the Collector. + // +optional + EnvFrom []v1.EnvFromSource `json:"envFrom,omitempty"` + // VolumeClaimTemplates will provide stable storage using PersistentVolumes. Only available when the mode=statefulset. + // +optional + // +listType=atomic + VolumeClaimTemplates []v1.PersistentVolumeClaim `json:"volumeClaimTemplates,omitempty"` + // Toleration to schedule OpenTelemetry Collector pods. + // This is only relevant to daemonset, statefulset, and deployment mode + // +optional + Tolerations []v1.Toleration `json:"tolerations,omitempty"` + // Volumes represents which volumes to use in the underlying collector deployment(s). + // +optional + // +listType=atomic + Volumes []v1.Volume `json:"volumes,omitempty"` + // Ingress is used to specify how OpenTelemetry Collector is exposed. This + // functionality is only available if one of the valid modes is set. + // Valid modes are: deployment, daemonset and statefulset. + // +optional + Ingress v1alpha1.Ingress `json:"ingress,omitempty"` + // HostNetwork indicates if the pod should run in the host networking namespace. + // +optional + HostNetwork bool `json:"hostNetwork,omitempty"` + // If specified, indicates the pod's priority. + // If not specified, the pod priority will be default or zero if there is no + // default. + // +optional + PriorityClassName string `json:"priorityClassName,omitempty"` + // If specified, indicates the pod's scheduling constraints + // +optional + Affinity *v1.Affinity `json:"affinity,omitempty"` + // Actions that the management system should take in response to container lifecycle events. Cannot be updated. + // +optional + Lifecycle *v1.Lifecycle `json:"lifecycle,omitempty"` + // Duration in seconds the pod needs to terminate gracefully upon probe failure. + // +optional + TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"` + // Liveness config for the OpenTelemetry Collector except the probe handler which is auto generated from the health extension of the collector. + // It is only effective when healthcheckextension is configured in the OpenTelemetry Collector pipeline. + // +optional + LivenessProbe *v1alpha1.Probe `json:"livenessProbe,omitempty"` + // InitContainers allows injecting initContainers to the Collector's pod definition. + // These init containers can be used to fetch secrets for injection into the + // configuration from external sources, run added checks, etc. Any errors during the execution of + // an initContainer will lead to a restart of the Pod. More info: + // https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + // +optional + InitContainers []v1.Container `json:"initContainers,omitempty"` + + // AdditionalContainers allows injecting additional containers into the Collector's pod definition. + // These sidecar containers can be used for authentication proxies, log shipping sidecars, agents for shipping + // metrics to their cloud, or in general sidecars that do not support automatic injection. This option only + // applies to Deployment, DaemonSet, and StatefulSet deployment modes of the collector. It does not apply to the sidecar + // deployment mode. More info about sidecars: + // https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/ + // + // Container names managed by the operator: + // * `otc-container` + // + // Overriding containers managed by the operator is outside the scope of what the maintainers will support and by + // doing so, you wil accept the risk of it breaking things. + // + // +optional + AdditionalContainers []v1.Container `json:"additionalContainers,omitempty"` + + // ObservabilitySpec defines how telemetry data gets handled. + // + // +optional + // +kubebuilder:validation:Optional + // +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Observability" + Observability v1alpha1.ObservabilitySpec `json:"observability,omitempty"` + + // TopologySpreadConstraints embedded kubernetes pod configuration option, + // controls how pods are spread across your cluster among failure-domains + // such as regions, zones, nodes, and other user-defined topology domains + // https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + // This is only relevant to statefulset, and deployment mode + // +optional + TopologySpreadConstraints []v1.TopologySpreadConstraint `json:"topologySpreadConstraints,omitempty"` + + // ConfigMaps is a list of ConfigMaps in the same namespace as the OpenTelemetryCollector + // object, which shall be mounted into the Collector Pods. + // Each ConfigMap will be added to the Collector's Deployments as a volume named `configmap-`. + ConfigMaps []v1alpha1.ConfigMapsSpec `json:"configmaps,omitempty"` +} + +// OpenTelemetryCollectorStatus defines the observed state of OpenTelemetryCollector. +type OpenTelemetryCollectorStatus struct { + // Scale is the OpenTelemetryCollector's scale subresource status. + // +optional + Scale v1alpha1.ScaleSubresourceStatus `json:"scale,omitempty"` + + // Version of the managed OpenTelemetry Collector (operand) + // +optional + Version string `json:"version,omitempty"` + + // Image indicates the container image to use for the OpenTelemetry Collector. + // +optional + Image string `json:"image,omitempty"` + + // Messages about actions performed by the operator on this resource. + // +optional + // +listType=atomic + // Deprecated: use Kubernetes events instead. + Messages []string `json:"messages,omitempty"` + + // Replicas is currently not being set and might be removed in the next version. + // +optional + // Deprecated: use "OpenTelemetryCollector.Status.Scale.Replicas" instead. + Replicas int32 `json:"replicas,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// OpenTelemetryCollector is the Schema for the opentelemetrycollectors API. +type OpenTelemetryCollector struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec OpenTelemetryCollectorSpec `json:"spec,omitempty"` + Status OpenTelemetryCollectorStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// OpenTelemetryCollectorList contains a list of OpenTelemetryCollector. +type OpenTelemetryCollectorList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []OpenTelemetryCollector `json:"items"` +} + +func init() { + SchemeBuilder.Register(&OpenTelemetryCollector{}, &OpenTelemetryCollectorList{}) +} diff --git a/apis/v1alpha2/zz_generated.deepcopy.go b/apis/v1alpha2/zz_generated.deepcopy.go new file mode 100644 index 0000000000..a72a170fb6 --- /dev/null +++ b/apis/v1alpha2/zz_generated.deepcopy.go @@ -0,0 +1,639 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// 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. + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "k8s.io/api/core/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApacheHttpd) DeepCopyInto(out *ApacheHttpd) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Attrs != nil { + in, out := &in.Attrs, &out.Attrs + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApacheHttpd. +func (in *ApacheHttpd) DeepCopy() *ApacheHttpd { + if in == nil { + return nil + } + out := new(ApacheHttpd) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DotNet) DeepCopyInto(out *DotNet) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DotNet. +func (in *DotNet) DeepCopy() *DotNet { + if in == nil { + return nil + } + out := new(DotNet) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Exporter) DeepCopyInto(out *Exporter) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Exporter. +func (in *Exporter) DeepCopy() *Exporter { + if in == nil { + return nil + } + out := new(Exporter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Go) DeepCopyInto(out *Go) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Go. +func (in *Go) DeepCopy() *Go { + if in == nil { + return nil + } + out := new(Go) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Instrumentation) DeepCopyInto(out *Instrumentation) { + *out = *in + out.Status = in.Status + out.TypeMeta = in.TypeMeta + in.Spec.DeepCopyInto(&out.Spec) + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Instrumentation. +func (in *Instrumentation) DeepCopy() *Instrumentation { + if in == nil { + return nil + } + out := new(Instrumentation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Instrumentation) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationList) DeepCopyInto(out *InstrumentationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Instrumentation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationList. +func (in *InstrumentationList) DeepCopy() *InstrumentationList { + if in == nil { + return nil + } + out := new(InstrumentationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InstrumentationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationSpec) DeepCopyInto(out *InstrumentationSpec) { + *out = *in + out.Exporter = in.Exporter + in.Resource.DeepCopyInto(&out.Resource) + if in.Propagators != nil { + in, out := &in.Propagators, &out.Propagators + *out = make([]v1alpha1.Propagator, len(*in)) + copy(*out, *in) + } + out.Sampler = in.Sampler + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Java.DeepCopyInto(&out.Java) + in.NodeJS.DeepCopyInto(&out.NodeJS) + in.Python.DeepCopyInto(&out.Python) + in.DotNet.DeepCopyInto(&out.DotNet) + in.Go.DeepCopyInto(&out.Go) + in.ApacheHttpd.DeepCopyInto(&out.ApacheHttpd) + in.Nginx.DeepCopyInto(&out.Nginx) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationSpec. +func (in *InstrumentationSpec) DeepCopy() *InstrumentationSpec { + if in == nil { + return nil + } + out := new(InstrumentationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InstrumentationStatus) DeepCopyInto(out *InstrumentationStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationStatus. +func (in *InstrumentationStatus) DeepCopy() *InstrumentationStatus { + if in == nil { + return nil + } + out := new(InstrumentationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Java) DeepCopyInto(out *Java) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Java. +func (in *Java) DeepCopy() *Java { + if in == nil { + return nil + } + out := new(Java) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Nginx) DeepCopyInto(out *Nginx) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Attrs != nil { + in, out := &in.Attrs, &out.Attrs + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Nginx. +func (in *Nginx) DeepCopy() *Nginx { + if in == nil { + return nil + } + out := new(Nginx) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeJS) DeepCopyInto(out *NodeJS) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeJS. +func (in *NodeJS) DeepCopy() *NodeJS { + if in == nil { + return nil + } + out := new(NodeJS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenTelemetryCollector) DeepCopyInto(out *OpenTelemetryCollector) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryCollector. +func (in *OpenTelemetryCollector) DeepCopy() *OpenTelemetryCollector { + if in == nil { + return nil + } + out := new(OpenTelemetryCollector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OpenTelemetryCollector) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenTelemetryCollectorList) DeepCopyInto(out *OpenTelemetryCollectorList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]OpenTelemetryCollector, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryCollectorList. +func (in *OpenTelemetryCollectorList) DeepCopy() *OpenTelemetryCollectorList { + if in == nil { + return nil + } + out := new(OpenTelemetryCollectorList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *OpenTelemetryCollectorList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenTelemetryCollectorSpec) DeepCopyInto(out *OpenTelemetryCollectorSpec) { + *out = *in + in.Resources.DeepCopyInto(&out.Resources) + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.Autoscaler != nil { + in, out := &in.Autoscaler, &out.Autoscaler + *out = new(v1alpha1.AutoscalerSpec) + (*in).DeepCopyInto(*out) + } + if in.PodDisruptionBudget != nil { + in, out := &in.PodDisruptionBudget, &out.PodDisruptionBudget + *out = new(v1alpha1.PodDisruptionBudgetSpec) + (*in).DeepCopyInto(*out) + } + if in.SecurityContext != nil { + in, out := &in.SecurityContext, &out.SecurityContext + *out = new(v1.SecurityContext) + (*in).DeepCopyInto(*out) + } + if in.PodSecurityContext != nil { + in, out := &in.PodSecurityContext, &out.PodSecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + if in.PodAnnotations != nil { + in, out := &in.PodAnnotations, &out.PodAnnotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + in.TargetAllocator.DeepCopyInto(&out.TargetAllocator) + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]v1.ServicePort, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.EnvFrom != nil { + in, out := &in.EnvFrom, &out.EnvFrom + *out = make([]v1.EnvFromSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.VolumeClaimTemplates != nil { + in, out := &in.VolumeClaimTemplates, &out.VolumeClaimTemplates + *out = make([]v1.PersistentVolumeClaim, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Volumes != nil { + in, out := &in.Volumes, &out.Volumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Ingress.DeepCopyInto(&out.Ingress) + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.Lifecycle != nil { + in, out := &in.Lifecycle, &out.Lifecycle + *out = new(v1.Lifecycle) + (*in).DeepCopyInto(*out) + } + if in.TerminationGracePeriodSeconds != nil { + in, out := &in.TerminationGracePeriodSeconds, &out.TerminationGracePeriodSeconds + *out = new(int64) + **out = **in + } + if in.LivenessProbe != nil { + in, out := &in.LivenessProbe, &out.LivenessProbe + *out = new(v1alpha1.Probe) + (*in).DeepCopyInto(*out) + } + if in.InitContainers != nil { + in, out := &in.InitContainers, &out.InitContainers + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AdditionalContainers != nil { + in, out := &in.AdditionalContainers, &out.AdditionalContainers + *out = make([]v1.Container, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + out.Observability = in.Observability + if in.TopologySpreadConstraints != nil { + in, out := &in.TopologySpreadConstraints, &out.TopologySpreadConstraints + *out = make([]v1.TopologySpreadConstraint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ConfigMaps != nil { + in, out := &in.ConfigMaps, &out.ConfigMaps + *out = make([]v1alpha1.ConfigMapsSpec, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryCollectorSpec. +func (in *OpenTelemetryCollectorSpec) DeepCopy() *OpenTelemetryCollectorSpec { + if in == nil { + return nil + } + out := new(OpenTelemetryCollectorSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OpenTelemetryCollectorStatus) DeepCopyInto(out *OpenTelemetryCollectorStatus) { + *out = *in + out.Scale = in.Scale + if in.Messages != nil { + in, out := &in.Messages, &out.Messages + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryCollectorStatus. +func (in *OpenTelemetryCollectorStatus) DeepCopy() *OpenTelemetryCollectorStatus { + if in == nil { + return nil + } + out := new(OpenTelemetryCollectorStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Python) DeepCopyInto(out *Python) { + *out = *in + if in.VolumeSizeLimit != nil { + in, out := &in.VolumeSizeLimit, &out.VolumeSizeLimit + x := (*in).DeepCopy() + *out = &x + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Resources.DeepCopyInto(&out.Resources) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Python. +func (in *Python) DeepCopy() *Python { + if in == nil { + return nil + } + out := new(Python) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Resource) DeepCopyInto(out *Resource) { + *out = *in + if in.Attributes != nil { + in, out := &in.Attributes, &out.Attributes + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Resource. +func (in *Resource) DeepCopy() *Resource { + if in == nil { + return nil + } + out := new(Resource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Sampler) DeepCopyInto(out *Sampler) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sampler. +func (in *Sampler) DeepCopy() *Sampler { + if in == nil { + return nil + } + out := new(Sampler) + in.DeepCopyInto(out) + return out +} diff --git a/autoinstrumentation/apache-httpd/Dockerfile b/autoinstrumentation/apache-httpd/Dockerfile new file mode 100644 index 0000000000..af9c12e6c1 --- /dev/null +++ b/autoinstrumentation/apache-httpd/Dockerfile @@ -0,0 +1,26 @@ + +############################ +# STEP 1 download the webserver agent +############################ +FROM alpine:latest as agent + +ARG version + +RUN mkdir /opt/opentelemetry +WORKDIR /opt/opentelemetry + +RUN wget https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/download/webserver%2Fv$version/opentelemetry-webserver-sdk-x64-linux.tgz +RUN mkdir agent +RUN tar -xvf opentelemetry-webserver-sdk-x64-linux.tgz -C agent + +############################ +# STEP 2 download the webserver agent +############################ +FROM alpine:latest + +COPY --from=agent /opt/opentelemetry/agent/opentelemetry-webserver-sdk /opt/opentelemetry + +RUN chmod 775 -R /opt/opentelemetry/ +RUN chmod a+w /opt/opentelemetry/logs + +CMD ["cat", "Just delivering the Opentelemetry Apache/Nginx agent"] \ No newline at end of file diff --git a/autoinstrumentation/apache-httpd/README.md b/autoinstrumentation/apache-httpd/README.md new file mode 100644 index 0000000000..2ff4f1aa60 --- /dev/null +++ b/autoinstrumentation/apache-httpd/README.md @@ -0,0 +1,13 @@ +# How to build Apache HTTPD auto-instrumentation docker image + +To build image for Apache HTTPD auto instrumentation, use the following commands + +``` +export REPO_NAME="" +export IMAGE_NAME_PREFIX="autoinstrumentation-apache-httpd" +export IMAGE_VERSION=`cat version.txt` +export IMAGE_NAME=${REPO_NAME}/${IMAGE_NAME_PREFIX}:${IMAGE_VERSION} +docker build --build-arg version=${IMAGE_VERSION} . -t ${IMAGE_NAME} -t ${REPO_NAME}/${IMAGE_NAME_PREFIX}:latest +docker push ${IMAGE_NAME} +docker push ${REPO_NAME}/${IMAGE_NAME_PREFIX}:latest +``` diff --git a/autoinstrumentation/apache-httpd/version.txt b/autoinstrumentation/apache-httpd/version.txt new file mode 100644 index 0000000000..e4c0d46e55 --- /dev/null +++ b/autoinstrumentation/apache-httpd/version.txt @@ -0,0 +1 @@ +1.0.3 \ No newline at end of file diff --git a/autoinstrumentation/dotnet/Dockerfile b/autoinstrumentation/dotnet/Dockerfile index 2d6d75c2b3..29527b2afb 100644 --- a/autoinstrumentation/dotnet/Dockerfile +++ b/autoinstrumentation/dotnet/Dockerfile @@ -5,11 +5,14 @@ # - Following environment variables are injected to the application container to enable the auto-instrumentation. # CORECLR_ENABLE_PROFILING=1 # CORECLR_PROFILER={918728DD-259F-4A6A-AC2B-B85E1B658318} -# CORECLR_PROFILER_PATH=%InstallationLocation%/OpenTelemetry.AutoInstrumentation.Native.so +# CORECLR_PROFILER_PATH=%InstallationLocation%/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so # for glibc based images +# CORECLR_PROFILER_PATH=%InstallationLocation%/linux-musl-x64/OpenTelemetry.AutoInstrumentation.Native.so # for musl based images # DOTNET_ADDITIONAL_DEPS=%InstallationLocation%/AdditionalDeps # DOTNET_SHARED_STORE=%InstallationLocation%/store -# DOTNET_STARTUP_HOOKS=%InstallationLocation%/netcoreapp3.1/OpenTelemetry.AutoInstrumentation.StartupHook.dll -# OTEL_DOTNET_AUTO_HOME=%InstallationLocation% +# DOTNET_STARTUP_HOOKS=%InstallationLocation%/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll +# OTEL_DOTNET_AUTO_HOME=%InstallationLocation% +# - For auto-instrumentation by container injection, the Linux command cp is +# used and must be availabe in the image. FROM busybox @@ -18,7 +21,9 @@ ARG version WORKDIR /autoinstrumentation ADD https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/v$version/opentelemetry-dotnet-instrumentation-linux-glibc.zip . +ADD https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/v$version/opentelemetry-dotnet-instrumentation-linux-musl.zip . -RUN unzip opentelemetry-dotnet-instrumentation-linux-glibc.zip - -RUN chmod -R go+r . \ No newline at end of file +RUN unzip opentelemetry-dotnet-instrumentation-linux-glibc.zip &&\ + unzip opentelemetry-dotnet-instrumentation-linux-musl.zip "linux-musl-x64/*" -d . &&\ + rm opentelemetry-dotnet-instrumentation-linux-glibc.zip opentelemetry-dotnet-instrumentation-linux-musl.zip &&\ + chmod -R go+r . \ No newline at end of file diff --git a/autoinstrumentation/dotnet/version.txt b/autoinstrumentation/dotnet/version.txt index efa59c1d8f..26aaba0e86 100644 --- a/autoinstrumentation/dotnet/version.txt +++ b/autoinstrumentation/dotnet/version.txt @@ -1 +1 @@ -0.3.1-beta.1 +1.2.0 diff --git a/autoinstrumentation/java/Dockerfile b/autoinstrumentation/java/Dockerfile index c33093b871..41941402f7 100644 --- a/autoinstrumentation/java/Dockerfile +++ b/autoinstrumentation/java/Dockerfile @@ -2,6 +2,8 @@ # - Download your customized `javaagent.jar` to `/javaagent.jar`. This is required as when instrumenting the pod, # one init container will be created to copy the jar to your app's container. # - Grant the necessary access to the jar. `chmod -R go+r /javaagent.jar` +# - For auto-instrumentation by container injection, the Linux command cp is +# used and must be availabe in the image. FROM busybox ARG version diff --git a/autoinstrumentation/java/version.txt b/autoinstrumentation/java/version.txt index 66e2ae6c25..359c41089a 100644 --- a/autoinstrumentation/java/version.txt +++ b/autoinstrumentation/java/version.txt @@ -1 +1 @@ -1.19.1 +1.32.0 diff --git a/autoinstrumentation/nodejs/Dockerfile b/autoinstrumentation/nodejs/Dockerfile index 1e0fd96f9b..48f1f9ae75 100644 --- a/autoinstrumentation/nodejs/Dockerfile +++ b/autoinstrumentation/nodejs/Dockerfile @@ -7,7 +7,9 @@ # - Ensure you have `@opentelemetry/api`, `@opentelemetry/auto-instrumentations-node`, and `@opentelemetry/sdk-node` or your customized # alternatives installed. # - Grant the necessary access to `/autoinstrumentation` directory. `chmod -R go+r /autoinstrumentation` -FROM node:16 AS build +# - For auto-instrumentation by container injection, the Linux command cp is +# used and must be availabe in the image. +FROM node:20 AS build WORKDIR /operator-build COPY . . diff --git a/autoinstrumentation/nodejs/package.json b/autoinstrumentation/nodejs/package.json index 157dcc87ae..733e4bf026 100644 --- a/autoinstrumentation/nodejs/package.json +++ b/autoinstrumentation/nodejs/package.json @@ -14,9 +14,17 @@ "typescript": "^4.4.4" }, "dependencies": { - "@opentelemetry/api": "1.1.0", - "@opentelemetry/auto-instrumentations-node": "0.31.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.31.0", - "@opentelemetry/sdk-node": "0.31.0" + "@opentelemetry/api": "1.6.0", + "@opentelemetry/auto-instrumentations-node": "0.39.4", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.44.0", + "@opentelemetry/exporter-prometheus": "0.44.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.44.0", + "@opentelemetry/resource-detector-alibaba-cloud": "0.28.2", + "@opentelemetry/resource-detector-aws": "1.3.2", + "@opentelemetry/resource-detector-container": "0.3.2", + "@opentelemetry/resource-detector-gcp": "0.29.2", + "@opentelemetry/resources": "1.17.1", + "@opentelemetry/sdk-metrics": "1.17.1", + "@opentelemetry/sdk-node": "0.44.0" } } diff --git a/autoinstrumentation/nodejs/src/autoinstrumentation.ts b/autoinstrumentation/nodejs/src/autoinstrumentation.ts index 3b0d9d2256..928e6d5578 100644 --- a/autoinstrumentation/nodejs/src/autoinstrumentation.ts +++ b/autoinstrumentation/nodejs/src/autoinstrumentation.ts @@ -1,12 +1,59 @@ import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'; +import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'; +import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; +import { alibabaCloudEcsDetector } from '@opentelemetry/resource-detector-alibaba-cloud'; +import { awsEc2Detector, awsEksDetector } from '@opentelemetry/resource-detector-aws'; +import { containerDetector } from '@opentelemetry/resource-detector-container'; +import { gcpDetector } from '@opentelemetry/resource-detector-gcp'; +import { envDetector, hostDetector, osDetector, processDetector } from '@opentelemetry/resources'; +import { diag } from '@opentelemetry/api'; import { NodeSDK } from '@opentelemetry/sdk-node'; +function getMetricReader() { + switch (process.env.OTEL_METRICS_EXPORTER) { + case undefined: + case '': + case 'otlp': + diag.info('using otel metrics exporter'); + return new PeriodicExportingMetricReader({ + exporter: new OTLPMetricExporter(), + }); + case 'prometheus': + diag.info('using prometheus metrics exporter'); + return new PrometheusExporter({}); + case 'none': + diag.info('disabling metrics reader'); + return undefined; + default: + throw Error(`no valid option for OTEL_METRICS_EXPORTER: ${process.env.OTEL_METRICS_EXPORTER}`) + } +} + const sdk = new NodeSDK({ autoDetectResources: true, instrumentations: [getNodeAutoInstrumentations()], traceExporter: new OTLPTraceExporter(), + metricReader: getMetricReader(), + resourceDetectors: + [ + // Standard resource detectors. + containerDetector, + envDetector, + hostDetector, + osDetector, + processDetector, + + // Cloud resource detectors. + alibabaCloudEcsDetector, + // Ordered AWS Resource Detectors as per: + // https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/resourcedetectionprocessor/README.md#ordering + awsEksDetector, + awsEc2Detector, + gcpDetector, + ], }); sdk.start(); diff --git a/autoinstrumentation/python/Dockerfile b/autoinstrumentation/python/Dockerfile index 5355ed2777..9a6dfa7403 100644 --- a/autoinstrumentation/python/Dockerfile +++ b/autoinstrumentation/python/Dockerfile @@ -7,8 +7,9 @@ # - Ensure you have `opentelemetry-distro` and `opentelemetry-instrumentation` or your customized alternatives installed. # Those two packages are essential to Python auto-instrumentation. # - Grant the necessary access to `/autoinstrumentation` directory. `chmod -R go+r /autoinstrumentation` - -FROM python:3.10-alpine AS build +# - For auto-instrumentation by container injection, the Linux command cp is +# used and must be availabe in the image. +FROM python:3.11 AS build WORKDIR /operator-build diff --git a/autoinstrumentation/python/requirements.txt b/autoinstrumentation/python/requirements.txt index 9ae3413981..9ab2d43923 100644 --- a/autoinstrumentation/python/requirements.txt +++ b/autoinstrumentation/python/requirements.txt @@ -1,49 +1,56 @@ -opentelemetry-distro==0.34b0 +opentelemetry-distro==0.41b0 # We don't use the distro[otlp] option which automatically includes exporters since gRPC is not appropriate for # injected auto-instrumentation, where it has a strict dependency on the OS / Python version the artifact is built for. -opentelemetry-exporter-otlp-proto-http==1.13.0 +opentelemetry-exporter-otlp-proto-http==1.20.0 -opentelemetry-propagator-b3==1.13.0 -opentelemetry-propagator-jaeger==1.13.0 +opentelemetry-propagator-b3==1.20.0 +opentelemetry-propagator-jaeger==1.20.0 opentelemetry-propagator-aws-xray==1.0.1 -opentelemetry-instrumentation==0.34b0 -opentelemetry-propagator-ot-trace==0.34b0 +opentelemetry-instrumentation==0.41b0 +opentelemetry-propagator-ot-trace==0.41b0 # Copied in from https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation # except for aws-lambda -opentelemetry-instrumentation-aiohttp-client==0.34b0 -opentelemetry-instrumentation-aiopg==0.34b0 -opentelemetry-instrumentation-asgi==0.34b0 -opentelemetry-instrumentation-asyncpg==0.34b0 -opentelemetry-instrumentation-boto==0.34b0 -opentelemetry-instrumentation-botocore==0.34b0 -opentelemetry-instrumentation-celery==0.34b0 -opentelemetry-instrumentation-dbapi==0.34b0 -opentelemetry-instrumentation-django==0.34b0 -opentelemetry-instrumentation-elasticsearch==0.34b0 -opentelemetry-instrumentation-falcon==0.34b0 -opentelemetry-instrumentation-fastapi==0.34b0 -opentelemetry-instrumentation-flask==0.34b0 -opentelemetry-instrumentation-grpc==0.34b0 -opentelemetry-instrumentation-httpx==0.34b0 -opentelemetry-instrumentation-jinja2==0.34b0 -opentelemetry-instrumentation-kafka-python==0.34b0 -opentelemetry-instrumentation-logging==0.34b0 -opentelemetry-instrumentation-mysql==0.34b0 -opentelemetry-instrumentation-pika==0.34b0 -opentelemetry-instrumentation-psycopg2==0.34b0 -opentelemetry-instrumentation-pymemcache==0.34b0 -opentelemetry-instrumentation-pymongo==0.34b0 -opentelemetry-instrumentation-pymysql==0.34b0 -opentelemetry-instrumentation-pyramid==0.34b0 -opentelemetry-instrumentation-redis==0.34b0 -opentelemetry-instrumentation-requests==0.34b0 -opentelemetry-instrumentation-sklearn==0.34b0 -opentelemetry-instrumentation-sqlalchemy==0.34b0 -opentelemetry-instrumentation-sqlite3==0.34b0 -opentelemetry-instrumentation-starlette==0.34b0 -opentelemetry-instrumentation-tornado==0.34b0 -opentelemetry-instrumentation-urllib==0.34b0 -opentelemetry-instrumentation-urllib3==0.34b0 -opentelemetry-instrumentation-wsgi==0.34b0 +opentelemetry-instrumentation-aio-pika==0.41b0 +opentelemetry-instrumentation-aiohttp-client==0.41b0 +opentelemetry-instrumentation-aiopg==0.41b0 +opentelemetry-instrumentation-asgi==0.41b0 +opentelemetry-instrumentation-asyncpg==0.41b0 +opentelemetry-instrumentation-boto==0.41b0 +opentelemetry-instrumentation-boto3sqs==0.41b0 +opentelemetry-instrumentation-botocore==0.41b0 +opentelemetry-instrumentation-celery==0.41b0 +opentelemetry-instrumentation-confluent-kafka==0.41b0 +opentelemetry-instrumentation-dbapi==0.41b0 +opentelemetry-instrumentation-django==0.41b0 +opentelemetry-instrumentation-elasticsearch==0.41b0 +opentelemetry-instrumentation-falcon==0.41b0 +opentelemetry-instrumentation-fastapi==0.41b0 +opentelemetry-instrumentation-flask==0.41b0 +opentelemetry-instrumentation-grpc==0.41b0 +opentelemetry-instrumentation-httpx==0.41b0 +opentelemetry-instrumentation-jinja2==0.41b0 +opentelemetry-instrumentation-kafka-python==0.41b0 +opentelemetry-instrumentation-logging==0.41b0 +opentelemetry-instrumentation-mysql==0.41b0 +opentelemetry-instrumentation-mysqlclient==0.41b0 +opentelemetry-instrumentation-pika==0.41b0 +opentelemetry-instrumentation-psycopg2==0.41b0 +opentelemetry-instrumentation-pymemcache==0.41b0 +opentelemetry-instrumentation-pymongo==0.41b0 +opentelemetry-instrumentation-pymysql==0.41b0 +opentelemetry-instrumentation-pyramid==0.41b0 +opentelemetry-instrumentation-redis==0.41b0 +opentelemetry-instrumentation-remoulade==0.41b0 +opentelemetry-instrumentation-requests==0.41b0 +opentelemetry-instrumentation-sklearn==0.41b0 +opentelemetry-instrumentation-sqlalchemy==0.41b0 +opentelemetry-instrumentation-sqlite3==0.41b0 +opentelemetry-instrumentation-starlette==0.41b0 +opentelemetry-instrumentation-system-metrics==0.41b0 +opentelemetry-instrumentation-tornado==0.41b0 +opentelemetry-instrumentation-tortoiseorm==0.41b0 +opentelemetry-instrumentation-urllib==0.41b0 +opentelemetry-instrumentation-urllib3==0.41b0 +opentelemetry-instrumentation-wsgi==0.41b0 diff --git a/bundle.Dockerfile b/bundle.Dockerfile index 4a211e0f19..af479f2fa4 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -6,7 +6,7 @@ LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=opentelemetry-operator LABEL operators.operatorframework.io.bundle.channels.v1=alpha -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.23.0 +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.29.0 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v3 diff --git a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml index a6adcc52a8..aae3b1848c 100644 --- a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -16,6 +16,40 @@ metadata: } } }, + { + "apiVersion": "opentelemetry.io/v1alpha1", + "kind": "OpAMPBridge", + "metadata": { + "name": "opampbridge-sample" + }, + "spec": { + "capabilities": { + "AcceptsOpAMPConnectionSettings": true, + "AcceptsOtherConnectionSettings": true, + "AcceptsRemoteConfig": true, + "AcceptsRestartCommand": true, + "ReportsEffectiveConfig": true, + "ReportsHealth": true, + "ReportsOwnLogs": true, + "ReportsOwnMetrics": true, + "ReportsOwnTraces": true, + "ReportsRemoteConfig": true, + "ReportsStatus": true + }, + "componentsAllowed": { + "exporters": [ + "logging" + ], + "processors": [ + "memory_limiter" + ], + "receivers": [ + "otlp" + ] + }, + "endpoint": "ws://opamp-server:4320/v1/opamp" + } + }, { "apiVersion": "opentelemetry.io/v1alpha1", "kind": "OpenTelemetryCollector", @@ -23,21 +57,21 @@ metadata: "name": "otel" }, "spec": { - "config": "receivers:\n otlp:\n protocols: \n grpc:\n http:\n\nexporters:\n logging:\n\nservice:\n pipelines:\n traces:\n receivers: [otlp]\n exporters: [logging]\n" + "config": "receivers:\n otlp:\n protocols: \n grpc:\n http:\n\nexporters:\n debug:\n\nservice:\n pipelines:\n traces:\n receivers: [otlp]\n exporters: [debug]\n" } } ] - capabilities: Basic Install - categories: Logging & Tracing + capabilities: Deep Insights + categories: Logging & Tracing,Monitoring certified: "false" containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator - createdAt: "2020-12-16T13:37:00+00:00" + createdAt: "2023-12-06T14:31:16Z" description: Provides the OpenTelemetry components, including the Collector - operators.operatorframework.io/builder: operator-sdk-v1.23.0 + 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.61.0 + name: opentelemetry-operator.v0.90.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -52,6 +86,24 @@ spec: name: "" version: v1 version: v1alpha1 + - description: OpAMPBridge is the Schema for the opampbridges API. + displayName: OpAMP Bridge + kind: OpAMPBridge + name: opampbridges.opentelemetry.io + resources: + - kind: ConfigMaps + name: "" + version: v1 + - kind: Deployment + name: "" + version: apps/v1 + - kind: Pod + name: "" + version: v1 + - kind: Service + name: "" + version: v1 + version: v1alpha1 - description: OpenTelemetryCollector is the Schema for the opentelemetrycollectors API. displayName: OpenTelemetry Collector @@ -76,6 +128,19 @@ spec: - kind: StatefulSets name: "" version: apps/v1 + specDescriptors: + - description: ObservabilitySpec defines how telemetry data gets handled. + displayName: Observability + path: observability + - description: Metrics defines the metrics configuration for operands. + displayName: Metrics Config + path: observability.metrics + - description: EnableMetrics specifies if ServiceMonitor or PodMonitor(for sidecar + mode) should be created for the OpenTelemetry Collector and Prometheus Exporters. + The operator.observability.prometheus feature gate must be enabled to use + this feature. + displayName: Create ServiceMonitors for OpenTelemetry Collector + path: observability.metrics.enableMetrics version: v1alpha1 description: |- OpenTelemetry is a collection of tools, APIs, and SDKs. You use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior. @@ -98,6 +163,9 @@ spec: - "" resources: - configmaps + - pods + - serviceaccounts + - services verbs: - create - delete @@ -121,9 +189,11 @@ spec: - list - watch - apiGroups: - - "" + - apps resources: - - serviceaccounts + - daemonsets + - deployments + - statefulsets verbs: - create - delete @@ -133,21 +203,17 @@ spec: - update - watch - apiGroups: - - "" + - apps resources: - - services + - replicasets verbs: - - create - - delete - get - list - - patch - - update - watch - apiGroups: - - apps + - autoscaling resources: - - daemonsets + - horizontalpodautoscalers verbs: - create - delete @@ -157,29 +223,19 @@ spec: - update - watch - apiGroups: - - apps + - coordination.k8s.io resources: - - deployments + - leases verbs: - create - - delete - get - list - - patch - update - - watch - apiGroups: - - apps + - monitoring.coreos.com resources: - - replicasets - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets + - podmonitors + - servicemonitors verbs: - create - delete @@ -189,9 +245,9 @@ spec: - update - watch - apiGroups: - - autoscaling + - networking.k8s.io resources: - - horizontalpodautoscalers + - ingresses verbs: - create - delete @@ -201,18 +257,19 @@ spec: - update - watch - apiGroups: - - coordination.k8s.io + - opentelemetry.io resources: - - leases + - instrumentations verbs: - - create - get - list + - patch - update + - watch - apiGroups: - - networking.k8s.io + - opentelemetry.io resources: - - ingresses + - opampbridges verbs: - create - delete @@ -224,20 +281,22 @@ spec: - apiGroups: - opentelemetry.io resources: - - instrumentations + - opampbridges/finalizers + verbs: + - update + - apiGroups: + - opentelemetry.io + resources: + - opampbridges/status verbs: - get - - list - patch - update - - watch - apiGroups: - opentelemetry.io resources: - opentelemetrycollectors verbs: - - create - - delete - get - list - patch @@ -259,6 +318,31 @@ spec: - get - patch - update + - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - route.openshift.io + resources: + - routes + - routes/custom-host + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - authentication.k8s.io resources: @@ -294,7 +378,10 @@ spec: - args: - --metrics-addr=127.0.0.1:8080 - --enable-leader-election - image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.61.0 + - --zap-log-level=info + - --zap-time-encoding=rfc3339nano + - --feature-gates=+operator.autoinstrumentation.go,+operator.autoinstrumentation.nginx + image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.90.0 livenessProbe: httpGet: path: /healthz @@ -313,9 +400,6 @@ spec: initialDelaySeconds: 5 periodSeconds: 10 resources: - limits: - cpu: 200m - memory: 256Mi requests: cpu: 100m memory: 64Mi @@ -328,7 +412,7 @@ spec: - --upstream=http://127.0.0.1:8080/ - --logtostderr=true - --v=0 - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 name: kube-rbac-proxy ports: - containerPort: 8443 @@ -402,9 +486,10 @@ spec: - email: jpkroehling@redhat.com name: Juraci Paixão Kröhling maturity: alpha + minKubeVersion: 1.23.0 provider: name: OpenTelemetry Community - version: 0.61.0 + version: 0.90.0 webhookdefinitions: - admissionReviewVersions: - v1 @@ -426,6 +511,26 @@ spec: targetPort: 9443 type: MutatingAdmissionWebhook webhookPath: /mutate-opentelemetry-io-v1alpha1-instrumentation + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: opentelemetry-operator-controller-manager + failurePolicy: Fail + generateName: mopampbridge.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - opampbridges + sideEffects: None + targetPort: 9443 + type: MutatingAdmissionWebhook + webhookPath: /mutate-opentelemetry-io-v1alpha1-opampbridge - admissionReviewVersions: - v1 containerPort: 443 @@ -505,6 +610,45 @@ spec: targetPort: 9443 type: ValidatingAdmissionWebhook webhookPath: /validate-opentelemetry-io-v1alpha1-instrumentation + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: opentelemetry-operator-controller-manager + failurePolicy: Fail + generateName: vopampbridgecreateupdate.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - opampbridges + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-opentelemetry-io-v1alpha1-opampbridge + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: opentelemetry-operator-controller-manager + failurePolicy: Ignore + generateName: vopampbridgedelete.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - opampbridges + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-opentelemetry-io-v1alpha1-opampbridge - admissionReviewVersions: - v1 containerPort: 443 diff --git a/bundle/manifests/opentelemetry.io_instrumentations.yaml b/bundle/manifests/opentelemetry.io_instrumentations.yaml index d5a8147119..be914a75ca 100644 --- a/bundle/manifests/opentelemetry.io_instrumentations.yaml +++ b/bundle/manifests/opentelemetry.io_instrumentations.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.12.0 creationTimestamp: null labels: app.kubernetes.io/name: opentelemetry-operator @@ -38,14 +38,14 @@ spec: description: Instrumentation is the spec for OpenTelemetry instrumentation. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation + description: APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + internal value, and may reject unrecognized values. type: string kind: - description: 'Kind is a string value representing the REST resource this + description: Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + submits requests to. Cannot be updated. In CamelCase. type: string metadata: type: object @@ -53,15 +53,739 @@ spec: description: InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumentation. properties: + apacheHttpd: + description: ApacheHttpd defines configuration for Apache HTTPD auto-instrumentation. + properties: + attrs: + description: 'Attrs defines Apache HTTPD agent specific attributes. + The precedence is: `agent default attributes` > `instrument + spec attributes` . Attributes are documented at https://github.' + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + configPath: + description: Location of Apache HTTPD server configuration. Needed + only if different from default "/usr/local/apache2/conf" + type: string + env: + description: Env defines Apache HTTPD specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Apache SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + version: + description: Apache HTTPD server version. One of 2.4 or 2.2. Default + is 2.4 + type: string + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object dotnet: description: DotNet defines configuration for DotNet auto-instrumentation. properties: env: - description: 'Env defines DotNet specific env vars. There are - four layers for env vars'' definitions and the precedence order - is: `original container env vars` > `language specific env vars` - > `common env vars` > `instrument spec configs'' vars`. If the - former var had been defined, then the other vars would be ignored.' + description: Env defines DotNet specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with DotNet SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + env: + description: Env defines common env vars. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + exporter: + description: Exporter defines exporter configuration. + properties: + endpoint: + description: Endpoint is address of the collector with OTLP endpoint. + type: string + type: object + go: + description: Go defines configuration for Go auto-instrumentation. + properties: + env: + description: Env defines Go specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Go SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + java: + description: Java defines configuration for java auto-instrumentation. + properties: + env: + description: Env defines java specific env vars. items: description: EnvVar represents an environment variable present in a Container. @@ -71,15 +795,9 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded + description: Variable references $(VAR_NAME) are expanded using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + the container and any service environment variables. type: string valueFrom: description: Source for the environment variable's value. @@ -108,8 +826,7 @@ spec: description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + spec.serviceAccountName, status.hostIP, status.' properties: apiVersion: description: Version of the schema the FieldPath @@ -127,7 +844,7 @@ spec: description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + and requests.' properties: containerName: description: 'Container name: required for volumes, @@ -175,138 +892,181 @@ spec: type: object type: array image: - description: Image is a container image with DotNet SDK and auto-instrumentation. + description: Image is a container image with javaagent auto-instrumentation + JAR. type: string - type: object - env: - description: 'Env defines common env vars. There are four layers for - env vars'' definitions and the precedence order is: `original container - env vars` > `language specific env vars` > `common env vars` > `instrument - spec configs'' vars`. If the former var had been defined, then the - other vars would be ignored.' - items: - description: EnvVar represents an environment variable present in - a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. + resources: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: - key: - description: The key to select. - type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified - API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. type: string required: - - resource + - name type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's namespace + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + nginx: + description: Nginx defines configuration for Nginx auto-instrumentation. + properties: + attrs: + description: 'Attrs defines Nginx agent specific attributes. The + precedence order is: `agent default attributes` > `instrument + spec attributes` . Attributes are documented at https://github.' + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic type: object - x-kubernetes-map-type: atomic + required: + - name type: object - required: - - name - type: object - type: array - exporter: - description: Exporter defines exporter configuration. - properties: - endpoint: - description: Endpoint is address of the collector with OTLP endpoint. + type: array + configFile: + description: Location of Nginx configuration file. Needed only + if different from default "/etx/nginx/nginx.conf" type: string - type: object - java: - description: Java defines configuration for java auto-instrumentation. - properties: env: - description: 'Env defines java specific env vars. There are four - layers for env vars'' definitions and the precedence order is: - `original container env vars` > `language specific env vars` - > `common env vars` > `instrument spec configs'' vars`. If the - former var had been defined, then the other vars would be ignored.' + description: Env defines Nginx specific env vars. items: description: EnvVar represents an environment variable present in a Container. @@ -316,15 +1076,9 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded + description: Variable references $(VAR_NAME) are expanded using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + the container and any service environment variables. type: string valueFrom: description: Source for the environment variable's value. @@ -353,8 +1107,7 @@ spec: description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + spec.serviceAccountName, status.hostIP, status.' properties: apiVersion: description: Version of the schema the FieldPath @@ -372,7 +1125,7 @@ spec: description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + and requests.' properties: containerName: description: 'Container name: required for volumes, @@ -420,19 +1173,67 @@ spec: type: object type: array image: - description: Image is a container image with javaagent auto-instrumentation - JAR. + description: Image is a container image with Nginx SDK and auto-instrumentation. type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object nodejs: description: NodeJS defines configuration for nodejs auto-instrumentation. properties: env: - description: 'Env defines nodejs specific env vars. There are - four layers for env vars'' definitions and the precedence order - is: `original container env vars` > `language specific env vars` - > `common env vars` > `instrument spec configs'' vars`. If the - former var had been defined, then the other vars would be ignored.' + description: Env defines nodejs specific env vars. items: description: EnvVar represents an environment variable present in a Container. @@ -442,15 +1243,9 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded + description: Variable references $(VAR_NAME) are expanded using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + the container and any service environment variables. type: string valueFrom: description: Source for the environment variable's value. @@ -479,8 +1274,7 @@ spec: description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + spec.serviceAccountName, status.hostIP, status.' properties: apiVersion: description: Version of the schema the FieldPath @@ -498,7 +1292,7 @@ spec: description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + and requests.' properties: containerName: description: 'Container name: required for volumes, @@ -548,10 +1342,64 @@ spec: image: description: Image is a container image with NodeJS SDK and auto-instrumentation. type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object propagators: description: Propagators defines inter-process context propagation - configuration. + configuration. Values in this list will be set in the OTEL_PROPAGATORS + env var. Enum=tracecontext;baggage;b3;b3multi;jaeger;xray;ottrace;none items: description: Propagator represents the propagation type. enum: @@ -569,11 +1417,7 @@ spec: description: Python defines configuration for python auto-instrumentation. properties: env: - description: 'Env defines python specific env vars. There are - four layers for env vars'' definitions and the precedence order - is: `original container env vars` > `language specific env vars` - > `common env vars` > `instrument spec configs'' vars`. If the - former var had been defined, then the other vars would be ignored.' + description: Env defines python specific env vars. items: description: EnvVar represents an environment variable present in a Container. @@ -583,15 +1427,9 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded + description: Variable references $(VAR_NAME) are expanded using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + the container and any service environment variables. type: string valueFrom: description: Source for the environment variable's value. @@ -620,8 +1458,7 @@ spec: description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + spec.serviceAccountName, status.hostIP, status.' properties: apiVersion: description: Version of the schema the FieldPath @@ -639,7 +1476,7 @@ spec: description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + and requests.' properties: containerName: description: 'Container name: required for volumes, @@ -689,6 +1526,59 @@ spec: image: description: Image is a container image with Python SDK and auto-instrumentation. type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object resource: description: Resource defines the configuration for the resource attributes, @@ -714,7 +1604,8 @@ spec: sampler type it is a number in range [0..1] e.g. 0.25. type: string type: - description: Type defines sampler type. The value can be for instance + description: Type defines sampler type. The value will be set + in the OTEL_TRACES_SAMPLER env var. The value can be for instance parentbased_always_on, parentbased_always_off, parentbased_traceidratio... enum: - always_on diff --git a/bundle/manifests/opentelemetry.io_opampbridges.yaml b/bundle/manifests/opentelemetry.io_opampbridges.yaml new file mode 100644 index 0000000000..83d809ba69 --- /dev/null +++ b/bundle/manifests/opentelemetry.io_opampbridges.yaml @@ -0,0 +1,2839 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: opentelemetry-operator-system/opentelemetry-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.12.0 + creationTimestamp: null + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opampbridges.opentelemetry.io +spec: + group: opentelemetry.io + names: + kind: OpAMPBridge + listKind: OpAMPBridgeList + plural: opampbridges + singular: opampbridge + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - description: OpenTelemetry Version + jsonPath: .status.version + name: Version + type: string + - jsonPath: .spec.endpoint + name: Endpoint + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: OpAMPBridge is the Schema for the opampbridges API. + properties: + apiVersion: + description: APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. + type: string + kind: + description: Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. + type: string + metadata: + type: object + spec: + description: OpAMPBridgeSpec defines the desired state of OpAMPBridge. + properties: + affinity: + description: If specified, indicates the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + capabilities: + additionalProperties: + type: boolean + description: Capabilities supported by the OpAMP Bridge + type: object + componentsAllowed: + additionalProperties: + items: + type: string + type: array + description: ComponentsAllowed is a list of allowed OpenTelemetry + components for each pipeline type (receiver, processor, etc.) + type: object + endpoint: + description: OpAMP backend Server endpoint + type: string + env: + description: ENV vars to set on the OpAMPBridge Pods. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables on + the OpAMPBridge Pods. + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + headers: + additionalProperties: + type: string + description: Headers is an optional map of headers to use when connecting + to the OpAMP Server, typically used to set access tokens or other + authorization headers. + type: object + hostNetwork: + description: HostNetwork indicates if the pod should run in the host + networking namespace. + type: boolean + image: + description: Image indicates the container image to use for the OpAMPBridge. + type: string + imagePullPolicy: + description: ImagePullPolicy indicates the pull policy to be used + for retrieving the container image (Always, Never, IfNotPresent) + type: string + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to schedule OpAMPBridge pods. + type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations is the set of annotations that will be + attached to OpAMPBridge pods. + type: object + podSecurityContext: + description: PodSecurityContext will be set as the pod security context. + properties: + fsGroup: + description: "A special supplemental group that applies to all + containers in a pod. Some volume types allow the Kubelet to + change the ownership of that volume to be owned by the pod: + \n 1." + format: int64 + type: integer + fsGroupChangePolicy: + description: fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. + type: string + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in SecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in SecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers in this + pod. Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process run + in each container, in addition to the container's primary GID, + the fsGroup (if specified), and group memberships defined in + the container image for th + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used for + the pod. Pods with unsupported sysctls (by the container runtime) + might fail to launch. Note that this field cannot be set when + spec.os. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + ports: + description: Ports allows a set of ports to be exposed by the underlying + v1.Service. + items: + description: ServicePort contains information on service's port. + properties: + appProtocol: + description: The application protocol for this port. This is + used as a hint for implementations to offer richer behavior + for protocols that they understand. This field follows standard + Kubernetes label syntax. + type: string + name: + description: The name of this port within the service. This + must be a DNS_LABEL. All ports within a ServiceSpec must have + unique names. + type: string + nodePort: + description: The port on each node on which this service is + exposed when type is NodePort or LoadBalancer. Usually assigned + by the system. + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the pods + targeted by the service. Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-type: atomic + priorityClassName: + description: If specified, indicates the pod's priority. If not specified, + the pod priority will be default or zero if there is no default. + type: string + replicas: + description: Replicas is the number of pod instances for the OpAMPBridge. + format: int32 + maximum: 1 + type: integer + resources: + description: Resources to set on the OpAMPBridge pods. + properties: + claims: + description: "Claims lists the names of resources, defined in + spec.resourceClaims, that are used by this container. \n This + is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims + of the Pod where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + securityContext: + description: SecurityContext will be set as the container security + context. + properties: + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a process + can gain more privileges than its parent process. This bool + directly controls if the no_new_privs flag will be set on the + container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container + runtime. Note that this field cannot be set when spec.os.name + is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in privileged + containers are essentially equivalent to root on the host. Defaults + to false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use for + the containers. The default is DefaultProcMount which uses the + container runtime defaults for readonly paths and masked paths. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. If + seccomp options are provided at both the pod & container level, + the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will + be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + serviceAccount: + description: ServiceAccount indicates the name of an existing service + account to use with this instance. When set, the operator will not + automatically create a ServiceAccount for the OpAMPBridge. + type: string + tolerations: + description: Toleration to schedule OpAMPBridge pods. + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match all + values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to Equal. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints embedded kubernetes pod configuration + option, controls how pods are spread across your cluster among failure-domains + such as regions, zones, nodes, and other user-defined top + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: MatchLabelKeys is a set of pod label keys to select + the pods over which spreading will be calculated. + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: MaxSkew describes the degree to which pods may + be unevenly distributed. + format: int32 + type: integer + minDomains: + description: MinDomains indicates a minimum number of eligible + domains. + format: int32 + type: integer + nodeAffinityPolicy: + description: NodeAffinityPolicy indicates how we will treat + Pod's nodeAffinity/nodeSelector when calculating pod topology + spread skew. + type: string + nodeTaintsPolicy: + description: NodeTaintsPolicy indicates how we will treat node + taints when calculating pod topology spread skew. + type: string + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with a + pod if it doesn't satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + upgradeStrategy: + description: UpgradeStrategy represents how the operator will handle + upgrades to the CR when a newer version of the operator is deployed + enum: + - automatic + - none + type: string + volumeMounts: + description: VolumeMounts represents the mount points to use in the + underlying OpAMPBridge deployment(s) + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: Path within the container at which the volume should + be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are propagated + from the host to container and the other way around. When + not set, MountPropagationNone is used. This field is beta + in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which the + container's volume should be mounted. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-type: atomic + volumes: + description: Volumes represents which volumes to use in the underlying + OpAMPBridge deployment(s). + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs".' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1".' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent disk + resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set).' + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is the + path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified.' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: 'volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + items: + description: items if unspecified, each key-value pair in + the Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and content + is the value. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated + CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: readOnly specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a Optional: mode bits used to set + permissions on created files by default.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used to set permissions + on this file, must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: medium represents what type of storage medium + should back this directory. The default is "" which means + to use the node's default medium. Must be an empty string + (default) or Memory. + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: sizeLimit is the total amount of local storage + required for this EmptyDir volume. The size limit is also + applicable for memory medium. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: ephemeral represents a volume that is handled by + a cluster storage driver. + properties: + volumeClaimTemplate: + description: Will be used to create a stand-alone PVC to + provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. + properties: + metadata: + description: May contain labels and annotations that + will be copied into the PVC when creating it. No other + fields are allowed and will be rejected during validation. + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the PVC + that gets created from this template. + properties: + accessModes: + description: 'accessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: dataSourceRef specifies the object + from which to populate the volume with data, if + a non-empty volume is desired. + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace + is specified, a gateway.networking.k8s. + type: string + required: + - kind + - name + type: object + resources: + description: resources represents the minimum resources + the volume should have. + properties: + claims: + description: "Claims lists the names of resources, + defined in spec.resourceClaims, that are used + by this container. \n This is an alpha field + and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one + entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name + of one entry in pod.spec.resourceClaims + of the Pod where this field is used. + It makes that resource available inside + a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum + amount of compute resources required. + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: 'storageClassName is the name of the + StorageClass required by the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume + is required by the claim. Value of Filesystem + is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends + on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if no + secret object is specified.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: datasetName is Name of the dataset stored as + metadata -> name on the dataset for Flocker should be + considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.' + properties: + fsType: + description: 'fsType is filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: "ext4", + "xfs", "ntfs".' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1".' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource in + GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated.' + properties: + directory: + description: directory is the target directory name. Must + not contain or start with '..'. If '.' is supplied, the + volume directory will be the git repository. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: hostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + properties: + path: + description: 'path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to + the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs".' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that uses + an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. The + portal is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: targetPortal is iSCSI Target Portal. The Portal + is either an IP or ip_addr:port if the port is other than + default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a DNS_LABEL and unique + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export to + be mounted with read-only permissions. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents a + reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.' + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: defaultMode are the mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu + and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced Secret + will be projected into the volume as a file + whose name is the key and content is the value. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: audience is the intended audience + of the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. + type: string + expirationSeconds: + description: expirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, the + kubelet volume plugin will proactively rotate + the service account token. + format: int64 + type: integer + path: + description: path is the path relative to the + mount point of the file to project the token + into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is no + group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults to + false. + type: boolean + registry: + description: registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs".' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is + nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is the rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage for + a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + items: + description: items If unspecified, each key-value pair in + the Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and content + is the value. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in the + pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: volumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within + a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-type: atomic + required: + - capabilities + - endpoint + type: object + status: + description: OpAMPBridgeStatus defines the observed state of OpAMPBridge. + properties: + version: + description: Version of the managed OpAMP Bridge (operand) + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/opentelemetry.io_opentelemetrycollectors.yaml b/bundle/manifests/opentelemetry.io_opentelemetrycollectors.yaml index 0fdf671e4e..c89d429c48 100644 --- a/bundle/manifests/opentelemetry.io_opentelemetrycollectors.yaml +++ b/bundle/manifests/opentelemetry.io_opentelemetrycollectors.yaml @@ -3,7 +3,7 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: opentelemetry-operator-system/opentelemetry-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.9.2 + controller-gen.kubebuilder.io/version: v0.12.0 creationTimestamp: null labels: app.kubernetes.io/name: opentelemetry-operator @@ -29,9 +29,19 @@ spec: jsonPath: .status.version name: Version type: string + - jsonPath: .status.scale.statusReplicas + name: Ready + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date + - jsonPath: .status.image + name: Image + type: string + - description: Management State + jsonPath: .spec.managementState + name: Management + type: string name: v1alpha1 schema: openAPIV3Schema: @@ -39,869 +49,5276 @@ spec: API. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation + description: APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + internal value, and may reject unrecognized values. type: string kind: - description: 'Kind is a string value representing the REST resource this + description: Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + submits requests to. Cannot be updated. In CamelCase. type: string metadata: type: object spec: description: OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector. properties: - args: - additionalProperties: - type: string - description: Args is the set of arguments to pass to the OpenTelemetry - Collector binary - type: object - autoscaler: - description: Autoscaler specifies the pod autoscaling configuration - to use for the OpenTelemetryCollector workload. - properties: - behavior: - description: HorizontalPodAutoscalerBehavior configures the scaling - behavior of the target in both Up and Down directions (scaleUp - and scaleDown fields respectively). - properties: - scaleDown: - description: scaleDown is scaling policy for scaling Down. - If not set, the default value is to allow to scale down - to minReplicas pods, with a 300 second stabilization window - (i.e., the highest recommendation for the last 300sec is - used). + additionalContainers: + description: AdditionalContainers allows injecting additional containers + into the Collector's pod definition. + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: Arguments to the entrypoint. The container image's + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container's environment. + items: + type: string + type: array + command: + description: Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's + environment. + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. properties: - policies: - description: policies is a list of potential scaling polices - which can be used during scaling. At least one policy - must be specified, otherwise the HPAScalingRules will - be discarded as invalid - items: - description: HPAScalingPolicy is a single policy which - must hold true for a specified past interval. - properties: - periodSeconds: - description: PeriodSeconds specifies the window - of time for which the policy should hold true. - PeriodSeconds must be greater than zero and less - than or equal to 1800 (30 min). - format: int32 - type: integer - type: - description: Type is used to specify the scaling - policy. - type: string - value: - description: Value contains the amount of change - which is permitted by the policy. It must be greater - than zero - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - x-kubernetes-list-type: atomic - selectPolicy: - description: selectPolicy is used to specify which policy - should be used. If not set, the default value Max is - used. + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. type: string - stabilizationWindowSeconds: - description: 'StabilizationWindowSeconds is the number - of seconds for which past recommendations should be - considered while scaling up or scaling down. StabilizationWindowSeconds - must be greater than or equal to zero and less than - or equal to 3600 (one hour). If not set, use the default - values: - For scale up: 0 (i.e. no stabilization is - done). - For scale down: 300 (i.e. the stabilization - window is 300 seconds long).' - format: int32 - type: integer + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name type: object - scaleUp: - description: 'scaleUp is scaling policy for scaling Up. If - not set, the default value is the higher of: * increase - no more than 4 pods per 60 seconds * double the number of - pods per 60 seconds No stabilization is used.' + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be + a C_IDENTIFIER. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps properties: - policies: - description: policies is a list of potential scaling polices - which can be used during scaling. At least one policy - must be specified, otherwise the HPAScalingRules will - be discarded as invalid - items: - description: HPAScalingPolicy is a single policy which - must hold true for a specified past interval. - properties: - periodSeconds: - description: PeriodSeconds specifies the window - of time for which the policy should hold true. - PeriodSeconds must be greater than zero and less - than or equal to 1800 (30 min). - format: int32 - type: integer - type: - description: Type is used to specify the scaling - policy. - type: string - value: - description: Value contains the amount of change - which is permitted by the policy. It must be greater - than zero - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - x-kubernetes-list-type: atomic - selectPolicy: - description: selectPolicy is used to specify which policy - should be used. If not set, the default value Max is - used. + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. type: string - stabilizationWindowSeconds: - description: 'StabilizationWindowSeconds is the number - of seconds for which past recommendations should be - considered while scaling up or scaling down. StabilizationWindowSeconds - must be greater than or equal to zero and less than - or equal to 3600 (one hour). If not set, use the default - values: - For scale up: 0 (i.e. no stabilization is - done). - For scale down: 300 (i.e. the stabilization - window is 300 seconds long).' - format: int32 - type: integer + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic type: object - type: object - targetCPUUtilization: - description: TargetCPUUtilization sets the target average CPU - used across all replicas. If average CPU exceeds this value, - the HPA will scale up. Defaults to 90 percent. - format: int32 - type: integer - type: object - config: - description: Config is the raw JSON to be used as the collector's - configuration. Refer to the OpenTelemetry Collector documentation - for details. - type: string - env: - description: ENV vars to set on the OpenTelemetry Collector's Pods. - These can then in certain cases be consumed in the config file for - the Collector. - items: - description: EnvVar represents an environment variable present in - a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. + type: array + image: + description: 'Container image name. More info: https://kubernetes.' type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.' type: string - valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. + postStart: + description: PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object type: object - x-kubernetes-map-type: atomic - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' + preStop: + description: PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. properties: - apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified - API version. - type: string - required: - - fieldPath + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). type: string - divisor: + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: anyOf: - type: integer - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - - resource + - port type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's namespace + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not + specifying a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default "0.0.0. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: Number of port to expose on the pod's IP + address. This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If + specified, this must be a valid port number, 0 < x < + 65536. If HostNetwork is specified, this must match + ContainerPort. Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod + must have a unique name. Name for the port that can + be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe + fails. Cannot be updated. More info: https://kubernetes.' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean required: - - key + - port type: object - x-kubernetes-map-type: atomic + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment variables on - the OpenTelemetry Collector's Pods. These can then in certain cases - be consumed in the config file for the Collector. - items: - description: EnvFromSource represents the source of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap must be defined - type: boolean + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object type: object - x-kubernetes-map-type: atomic - prefix: - description: An optional identifier to prepend to each key in - the ConfigMap. Must be a C_IDENTIFIER. + restartPolicy: + description: RestartPolicy defines the restart behavior of individual + containers in a pod. This field may only be set for init containers, + and the only allowed value is "Always". type: string - secretRef: - description: The Secret to select from + securityContext: + description: SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by + the container runtime. Note that this field cannot be + set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent to + root on the host. Defaults to false. Note that this field + cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to + use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. type: string - optional: - description: Specify whether the Secret must be defined + readOnlyRootFilesystem: + description: Whether this container has a read-only root + filesystem. Default is false. Note that this field cannot + be set when spec.os.name is windows. type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a + non-root user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a + random SELinux context for each container. May also be + set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile + must be preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - + a profile defined in a file on the node should be + used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set + in PodSecurityContext. + type: string + type: object type: object - x-kubernetes-map-type: atomic - type: object - type: array - hostNetwork: - description: HostNetwork indicates if the pod should run in the host - networking namespace. - type: boolean - image: - description: Image indicates the container image to use for the OpenTelemetry - Collector. - type: string - imagePullPolicy: - description: ImagePullPolicy indicates the pull policy to be used - for retrieving the container image (Always, Never, IfNotPresent) - type: string - ingress: - description: 'Ingress is used to specify how OpenTelemetry Collector - is exposed. This functionality is only available if one of the valid - modes is set. Valid modes are: deployment, daemonset and statefulset.' - properties: - annotations: - additionalProperties: - type: string - description: 'Annotations to add to ingress. e.g. ''cert-manager.io/cluster-issuer: - "letsencrypt"''' - type: object - hostname: - description: Hostname by which the ingress proxy can be reached. - type: string - tls: - description: TLS configuration. - items: - description: IngressTLS describes the transport layer security - associated with an Ingress. - properties: - hosts: - description: Hosts are a list of hosts included in the TLS - certificate. The values in this list must match the name/s - used in the tlsSecret. Defaults to the wildcard host setting - for the loadbalancer controller fulfilling this Ingress, - if left unspecified. - items: - type: string - type: array - x-kubernetes-list-type: atomic - secretName: - description: SecretName is the name of the secret used to - terminate TLS traffic on port 443. Field is left optional - to allow TLS routing based on SNI hostname alone. If the - SNI host in a listener conflicts with the "Host" header - field used by an IngressRule, the SNI host is used for - termination and value of the Host header is used for routing. - type: string - type: object - type: array - type: - description: 'Type default value is: "" Supported types are: ingress' - enum: - - ingress - type: string - type: object - maxReplicas: - description: MaxReplicas sets an upper bound to the autoscaling feature. - If MaxReplicas is set autoscaling is enabled. - format: int32 - type: integer - minReplicas: - description: MinReplicas sets a lower bound to the autoscaling feature. Set - this if your are using autoscaling. It must be at least 1 - format: int32 - type: integer - mode: - description: Mode represents how the collector should be deployed - (deployment, daemonset, statefulset or sidecar) - enum: - - daemonset - - deployment - - sidecar - - statefulset - type: string - nodeSelector: - additionalProperties: - type: string - description: NodeSelector to schedule OpenTelemetry Collector pods. - This is only relevant to daemonset, statefulset, and deployment - mode - type: object - podAnnotations: - additionalProperties: - type: string - description: PodAnnotations is the set of annotations that will be - attached to Collector and Target Allocator pods. - type: object - podSecurityContext: - description: PodSecurityContext holds pod-level security attributes - and common container settings. Some fields are also present in container.securityContext. Field - values of container.securityContext take precedence over field values - of PodSecurityContext. - properties: - fsGroup: - description: "A special supplemental group that applies to all - containers in a pod. Some volume types allow the Kubelet to - change the ownership of that volume to be owned by the pod: - \n 1. The owning GID will be the FSGroup 2. The setgid bit is - set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- \n If unset, - the Kubelet will not modify the ownership and permissions of - any volume. Note that this field cannot be set when spec.os.name - is windows." - format: int64 - type: integer - fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of changing - ownership and permission of the volume before being exposed - inside Pod. This field will only apply to volume types which - support fsGroup based ownership(and permissions). It will have - no effect on ephemeral volume types such as: secret, configmaps - and emptydir. Valid values are "OnRootMismatch" and "Always". - If not specified, "Always" is used. Note that this field cannot - be set when spec.os.name is windows.' - type: string - runAsGroup: - description: The GID to run the entrypoint of the container process. - Uses runtime default if unset. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail to start - the container if it does. If unset or false, no such validation - will be performed. May also be set in SecurityContext. If set - in both SecurityContext and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this field cannot - be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies to - the container. - type: string - role: - description: Role is a SELinux role label that applies to - the container. - type: string - type: - description: Type is a SELinux type label that applies to - the container. - type: string - user: - description: User is a SELinux user label that applies to - the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by the containers in this - pod. Note that this field cannot be set when spec.os.name is - windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must be - preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a profile - defined in a file on the node should be used. RuntimeDefault - - the container runtime default profile should be used. - Unconfined - no profile should be applied." - type: string - required: - - type - type: object - supplementalGroups: - description: A list of groups applied to the first process run - in each container, in addition to the container's primary GID. If - unspecified, no groups will be added to any container. Note - that this field cannot be set when spec.os.name is windows. - items: - format: int64 - type: integer - type: array - sysctls: - description: Sysctls hold a list of namespaced sysctls used for - the pod. Pods with unsupported sysctls (by the container runtime) - might fail to launch. Note that this field cannot be set when - spec.os.name is windows. - items: - description: Sysctl defines a kernel parameter to be set + startupProbe: + description: StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - required: - - name - - value + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer type: object - type: array - windowsOptions: - description: The Windows specific settings applied to all containers. - If unspecified, the options within a container's SecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named by - the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the GMSA - credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is alpha-level - and will only be honored by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature flag - will result in errors when validating the Pod. All of a - Pod's containers must have the same effective HostProcess - value (it is not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in PodSecurityContext. - If set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - type: string - type: object - type: object - ports: - description: Ports allows a set of ports to be exposed by the underlying - v1.Service. By default, the operator will attempt to infer the required - ports by parsing the .Spec.Config property but this property can - be used to open additional ports that can't be inferred by the operator, - like for custom receivers. - items: - description: ServicePort contains information on service's port. - properties: - appProtocol: - description: The application protocol for this port. This field - follows standard Kubernetes label syntax. Un-prefixed names - are reserved for IANA standard service names (as per RFC-6335 - and https://www.iana.org/assignments/service-names). Non-standard - protocols should use prefixed names such as mycompany.com/my-custom-protocol. + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, reads + from stdin in the container will always result in EOF. Default + is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the + stdin channel after it has been opened by a single attach. + When stdin is true the stdin stream will remain open across + multiple attach sessions. + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the + container''s termination message will be written is mounted + into the container''s filesystem.' type: string - name: - description: The name of this port within the service. This - must be a DNS_LABEL. All ports within a ServiceSpec must have - unique names. When considering the endpoints for a Service, - this must match the 'name' field in the EndpointPort. Optional - if only one ServicePort is defined on this service. + terminationMessagePolicy: + description: Indicate how the termination message should be + populated. File will use the contents of terminationMessagePath + to populate the container status message on both success and + failure. type: string - nodePort: - description: 'The port on each node on which this service is - exposed when type is NodePort or LoadBalancer. Usually assigned - by the system. If a value is specified, in-range, and not - in use it will be used, otherwise the operation will fail. If - not specified, a port will be allocated if this Service requires - one. If this field is specified when creating a Service which - does not need it, creation will fail. This field will be wiped - when updating a Service to no longer need it (e.g. changing - type from NodePort to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' - format: int32 - type: integer - port: - description: The port that will be exposed by this service. - format: int32 - type: integer - protocol: - default: TCP - description: The IP protocol for this port. Supports "TCP", - "UDP", and "SCTP". Default is TCP. + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other + way around. When not set, MountPropagationNone is used. + This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. type: string - targetPort: - anyOf: - - type: integer - - type: string - description: 'Number or name of the port to access on the pods - targeted by the service. Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. If this is a string, - it will be looked up as a named port in the target Pod''s - container ports. If this is not specified, the value of the - ''port'' field is used (an identity map). This field is ignored - for services with clusterIP=None, and should be omitted or - set equal to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' - x-kubernetes-int-or-string: true required: - - port + - name type: object type: array - x-kubernetes-list-type: atomic - priorityClassName: - description: If specified, indicates the pod's priority. If not specified, - the pod priority will be default or zero if there is no default. - type: string - replicas: - description: Replicas is the number of pod instances for the underlying - OpenTelemetry Collector. Set this if your are not using autoscaling - format: int32 - type: integer - resources: - description: Resources to set on the OpenTelemetry Collector pods. + affinity: + description: If specified, indicates the pod's scheduling constraints properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources - allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic type: object - type: object - securityContext: - description: SecurityContext will be set as the container security - context. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether a process - can gain more privileges than its parent process. This bool - directly controls if the no_new_privs flag will be set on the - container process. AllowPrivilegeEscalation is true always when - the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN - Note that this field cannot be set when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by the container - runtime. Note that this field cannot be set when spec.os.name - is windows. + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). properties: - add: - description: Added capabilities + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. items: - description: Capability represent POSIX capabilities type - type: string + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object type: array - drop: - description: Removed capabilities + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. items: - description: Capability represent POSIX capabilities type - type: string + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object type: array type: object - privileged: - description: Run container in privileged mode. Processes in privileged - containers are essentially equivalent to root on the host. Defaults - to false. Note that this field cannot be set when spec.os.name - is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to use for - the containers. The default is DefaultProcMount which uses the - container runtime defaults for readonly paths and masked paths. - This requires the ProcMountType feature flag to be enabled. - Note that this field cannot be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root filesystem. - Default is false. Note that this field cannot be set when spec.os.name - is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container process. - Uses runtime default if unset. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence. Note that this - field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail to start - the container if it does. If unset or false, no such validation - will be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when spec.os.name - is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence. Note that this - field cannot be set when spec.os.name is windows. + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). properties: - level: - description: Level is SELinux level label that applies to - the container. - type: string - role: - description: Role is a SELinux role label that applies to - the container. - type: string - type: - description: Type is a SELinux type label that applies to - the container. - type: string - user: - description: User is a SELinux user label that applies to - the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. If - seccomp options are provided at both the pod & container level, - the container options override the pod options. Note that this - field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must be - preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a profile - defined in a file on the node should be used. RuntimeDefault - - the container runtime default profile should be used. - Unconfined - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all containers. - If unspecified, the options from the PodSecurityContext will - be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is linux. + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + args: + additionalProperties: + type: string + description: Args is the set of arguments to pass to the OpenTelemetry + Collector binary + type: object + autoscaler: + description: Autoscaler specifies the pod autoscaling configuration + to use for the OpenTelemetryCollector workload. + properties: + behavior: + description: HorizontalPodAutoscalerBehavior configures the scaling + behavior of the target in both Up and Down directions (scaleUp + and scaleDown fields respectively). properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named by - the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the GMSA - credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is alpha-level - and will only be honored by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature flag - will result in errors when validating the Pod. All of a - Pod's containers must have the same effective HostProcess - value (it is not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in PodSecurityContext. - If set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - type: string + scaleDown: + description: scaleDown is scaling policy for scaling Down. + If not set, the default value is to allow to scale down + to minReplicas pods, with a 300 second stabilization window + (i.e. + properties: + policies: + description: policies is a list of potential scaling polices + which can be used during scaling. At least one policy + must be specified, otherwise the HPAScalingRules will + be discarded as invalid + items: + description: HPAScalingPolicy is a single policy which + must hold true for a specified past interval. + properties: + periodSeconds: + description: periodSeconds specifies the window + of time for which the policy should hold true. + PeriodSeconds must be greater than zero and less + than or equal to 1800 (30 min). + format: int32 + type: integer + type: + description: type is used to specify the scaling + policy. + type: string + value: + description: value contains the amount of change + which is permitted by the policy. It must be greater + than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + x-kubernetes-list-type: atomic + selectPolicy: + description: selectPolicy is used to specify which policy + should be used. If not set, the default value Max is + used. + type: string + stabilizationWindowSeconds: + description: stabilizationWindowSeconds is the number + of seconds for which past recommendations should be + considered while scaling up or scaling down. + format: int32 + type: integer + type: object + scaleUp: + description: scaleUp is scaling policy for scaling Up. + properties: + policies: + description: policies is a list of potential scaling polices + which can be used during scaling. At least one policy + must be specified, otherwise the HPAScalingRules will + be discarded as invalid + items: + description: HPAScalingPolicy is a single policy which + must hold true for a specified past interval. + properties: + periodSeconds: + description: periodSeconds specifies the window + of time for which the policy should hold true. + PeriodSeconds must be greater than zero and less + than or equal to 1800 (30 min). + format: int32 + type: integer + type: + description: type is used to specify the scaling + policy. + type: string + value: + description: value contains the amount of change + which is permitted by the policy. It must be greater + than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + x-kubernetes-list-type: atomic + selectPolicy: + description: selectPolicy is used to specify which policy + should be used. If not set, the default value Max is + used. + type: string + stabilizationWindowSeconds: + description: stabilizationWindowSeconds is the number + of seconds for which past recommendations should be + considered while scaling up or scaling down. + format: int32 + type: integer + type: object + type: object + maxReplicas: + description: MaxReplicas sets an upper bound to the autoscaling + feature. If MaxReplicas is set autoscaling is enabled. + format: int32 + type: integer + metrics: + description: Metrics is meant to provide a customizable way to + configure HPA metrics. currently the only supported custom metrics + is type=Pod. + items: + description: MetricSpec defines a subset of metrics to be defined + for the HPA's metric array more metric type can be supported + as needed. See https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec + for reference. + properties: + pods: + description: PodsMetricSource indicates how to scale on + a metric describing each pod in the current scale target + (for example, transactions-processed-per-second). + properties: + metric: + description: metric identifies the target metric by + name and selector + properties: + name: + description: name is the name of the given metric + type: string + selector: + description: selector is the string-encoded form + of a standard kubernetes label selector for the + given metric When set, it is passed as an additional + parameter to the metrics server for more specific + metrics scopi + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - name + type: object + target: + description: target specifies the target value for the + given metric + properties: + averageUtilization: + description: averageUtilization is the target value + of the average of the resource metric across all + relevant pods, represented as a percentage of + the requested value of the resource for the pods. + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + description: averageValue is the target value of + the average of the metric across all relevant + pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + description: type represents whether the metric + type is Utilization, Value, or AverageValue + type: string + value: + anyOf: + - type: integer + - type: string + description: value is the target value of the metric + (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - metric + - target + type: object + type: + description: MetricSourceType indicates the type of metric. + type: string + required: + - type + type: object + type: array + minReplicas: + description: MinReplicas sets a lower bound to the autoscaling + feature. Set this if your are using autoscaling. It must be + at least 1 + format: int32 + type: integer + targetCPUUtilization: + description: TargetCPUUtilization sets the target average CPU + used across all replicas. If average CPU exceeds this value, + the HPA will scale up. Defaults to 90 percent. + format: int32 + type: integer + targetMemoryUtilization: + description: TargetMemoryUtilization sets the target average memory + utilization across all replicas + format: int32 + type: integer + type: object + config: + description: Config is the raw JSON to be used as the collector's + configuration. Refer to the OpenTelemetry Collector documentation + for details. + type: string + configmaps: + description: ConfigMaps is a list of ConfigMaps in the same namespace + as the OpenTelemetryCollector object, which shall be mounted into + the Collector Pods. + items: + properties: + mountpath: + type: string + name: + description: Configmap defines name and path where the configMaps + should be mounted. + type: string + required: + - mountpath + - name + type: object + type: array + env: + description: ENV vars to set on the OpenTelemetry Collector's Pods. + These can then in certain cases be consumed in the config file for + the Collector. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables on + the OpenTelemetry Collector's Pods. These can then in certain cases + be consumed in the config file for the Collector. + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + hostNetwork: + description: HostNetwork indicates if the pod should run in the host + networking namespace. + type: boolean + image: + description: Image indicates the container image to use for the OpenTelemetry + Collector. + type: string + imagePullPolicy: + description: ImagePullPolicy indicates the pull policy to be used + for retrieving the container image (Always, Never, IfNotPresent) + type: string + ingress: + description: 'Ingress is used to specify how OpenTelemetry Collector + is exposed. This functionality is only available if one of the valid + modes is set. Valid modes are: deployment, daemonset and statefulset.' + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations to add to ingress. e.g. ''cert-manager.io/cluster-issuer: + "letsencrypt"''' + type: object + hostname: + description: Hostname by which the ingress proxy can be reached. + type: string + ingressClassName: + description: IngressClassName is the name of an IngressClass cluster + resource. Ingress controller implementations use this field + to know whether they should be serving this Ingress resource. + type: string + route: + description: Route is an OpenShift specific section that is only + considered when type "route" is used. + properties: + termination: + description: Termination indicates termination type. By default + "edge" is used. + enum: + - insecure + - edge + - passthrough + - reencrypt + type: string + type: object + ruleType: + description: RuleType defines how Ingress exposes collector receivers. + IngressRuleTypePath ("path") exposes each receiver port on a + unique path on single domain defined in Hostname. + enum: + - path + - subdomain + type: string + tls: + description: TLS configuration. + items: + description: IngressTLS describes the transport layer security + associated with an ingress. + properties: + hosts: + description: hosts is a list of hosts included in the TLS + certificate. The values in this list must match the name/s + used in the tlsSecret. + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: secretName is the name of the secret used to + terminate TLS traffic on port 443. Field is left optional + to allow TLS routing based on SNI hostname alone. + type: string + type: object + type: array + type: + description: 'Type default value is: "" Supported types are: ingress, + route' + enum: + - ingress + - route + type: string + type: object + initContainers: + description: InitContainers allows injecting initContainers to the + Collector's pod definition. + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: Arguments to the entrypoint. The container image's + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container's environment. + items: + type: string + type: array + command: + description: Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's + environment. + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be + a C_IDENTIFIER. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + description: 'Container image name. More info: https://kubernetes.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not + specifying a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default "0.0.0. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: Number of port to expose on the pod's IP + address. This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If + specified, this must be a valid port number, 0 < x < + 65536. If HostNetwork is specified, this must match + ContainerPort. Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod + must have a unique name. Name for the port that can + be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe + fails. Cannot be updated. More info: https://kubernetes.' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + restartPolicy: + description: RestartPolicy defines the restart behavior of individual + containers in a pod. This field may only be set for init containers, + and the only allowed value is "Always". + type: string + securityContext: + description: SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. + properties: + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by + the container runtime. Note that this field cannot be + set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent to + root on the host. Defaults to false. Note that this field + cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to + use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root + filesystem. Default is false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a + non-root user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a + random SELinux context for each container. May also be + set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile + must be preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - + a profile defined in a file on the node should be + used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set + in PodSecurityContext. + type: string + type: object + type: object + startupProbe: + description: StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, reads + from stdin in the container will always result in EOF. Default + is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the + stdin channel after it has been opened by a single attach. + When stdin is true the stdin stream will remain open across + multiple attach sessions. + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the + container''s termination message will be written is mounted + into the container''s filesystem.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be + populated. File will use the contents of terminationMessagePath + to populate the container status message on both success and + failure. + type: string + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other + way around. When not set, MountPropagationNone is used. + This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + lifecycle: + description: Actions that the management system should take in response + to container lifecycle events. Cannot be updated. + properties: + postStart: + description: PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name. This will be + canonicalized upon output, so case-variant names + will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler + and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: PreStop is called immediately before a container + is terminated due to an API request or management event such + as liveness/startup probe failure, preemption, resource contention, + etc. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name. This will be + canonicalized upon output, so case-variant names + will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler + and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: Liveness config for the OpenTelemetry Collector except + the probe handler which is auto generated from the health extension + of the collector. + properties: + failureThreshold: + description: Minimum consecutive failures for the probe to be + considered failed after having succeeded. Defaults to 3. Minimum + value is 1. + format: int32 + type: integer + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. Defaults to 0 seconds. + Minimum value is 0. More info: https://kubernetes.' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. Default + to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to be + considered successful after having failed. Defaults to 1. Must + be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate + gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + managementState: + default: managed + description: ManagementState defines if the CR should be managed by + the operator or not. Default is managed. + enum: + - managed + - unmanaged + type: string + maxReplicas: + description: 'MaxReplicas sets an upper bound to the autoscaling feature. + If MaxReplicas is set autoscaling is enabled. Deprecated: use "OpenTelemetryCollector.Spec.Autoscaler.MaxReplicas" + instead.' + format: int32 + type: integer + minReplicas: + description: 'MinReplicas sets a lower bound to the autoscaling feature. Set + this if you are using autoscaling. It must be at least 1 Deprecated: + use "OpenTelemetryCollector.Spec.Autoscaler.MinReplicas" instead.' + format: int32 + type: integer + mode: + description: Mode represents how the collector should be deployed + (deployment, daemonset, statefulset or sidecar) + enum: + - daemonset + - deployment + - sidecar + - statefulset + type: string + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to schedule OpenTelemetry Collector pods. + This is only relevant to daemonset, statefulset, and deployment + mode + type: object + observability: + description: ObservabilitySpec defines how telemetry data gets handled. + properties: + metrics: + description: Metrics defines the metrics configuration for operands. + properties: + enableMetrics: + description: EnableMetrics specifies if ServiceMonitor or + PodMonitor(for sidecar mode) should be created for the OpenTelemetry + Collector and Prometheus Exporters. The operator.observability. + type: boolean + type: object + type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations is the set of annotations that will be + attached to Collector and Target Allocator pods. + type: object + podDisruptionBudget: + description: PodDisruptionBudget specifies the pod disruption budget + configuration to use for the OpenTelemetryCollector workload. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at most "maxUnavailable" + pods selected by "selector" are unavailable after the eviction, + i.e. even in absence of the evicted pod. + x-kubernetes-int-or-string: true + minAvailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at least "minAvailable" + pods selected by "selector" will still be available after the + eviction, i.e. even in the absence of the evicted pod. + x-kubernetes-int-or-string: true + type: object + podSecurityContext: + description: PodSecurityContext configures the pod security context + for the opentelemetry-collector pod, when running as a deployment, + daemonset, or statefulset. + properties: + fsGroup: + description: "A special supplemental group that applies to all + containers in a pod. Some volume types allow the Kubelet to + change the ownership of that volume to be owned by the pod: + \n 1." + format: int64 + type: integer + fsGroupChangePolicy: + description: fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. + type: string + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in SecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in SecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers in this + pod. Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process run + in each container, in addition to the container's primary GID, + the fsGroup (if specified), and group memberships defined in + the container image for th + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used for + the pod. Pods with unsupported sysctls (by the container runtime) + might fail to launch. Note that this field cannot be set when + spec.os. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + ports: + description: Ports allows a set of ports to be exposed by the underlying + v1.Service. By default, the operator will attempt to infer the required + ports by parsing the .Spec. + items: + description: ServicePort contains information on service's port. + properties: + appProtocol: + description: The application protocol for this port. This is + used as a hint for implementations to offer richer behavior + for protocols that they understand. This field follows standard + Kubernetes label syntax. + type: string + name: + description: The name of this port within the service. This + must be a DNS_LABEL. All ports within a ServiceSpec must have + unique names. + type: string + nodePort: + description: The port on each node on which this service is + exposed when type is NodePort or LoadBalancer. Usually assigned + by the system. + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the pods + targeted by the service. Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-type: atomic + priorityClassName: + description: If specified, indicates the pod's priority. If not specified, + the pod priority will be default or zero if there is no default. + type: string + replicas: + description: Replicas is the number of pod instances for the underlying + OpenTelemetry Collector. Set this if your are not using autoscaling + format: int32 + type: integer + resources: + description: Resources to set on the OpenTelemetry Collector pods. + properties: + claims: + description: "Claims lists the names of resources, defined in + spec.resourceClaims, that are used by this container. \n This + is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims + of the Pod where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + securityContext: + description: SecurityContext configures the container security context + for the opentelemetry-collector container. + properties: + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a process + can gain more privileges than its parent process. This bool + directly controls if the no_new_privs flag will be set on the + container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container + runtime. Note that this field cannot be set when spec.os.name + is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in privileged + containers are essentially equivalent to root on the host. Defaults + to false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use for + the containers. The default is DefaultProcMount which uses the + container runtime defaults for readonly paths and masked paths. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. If + seccomp options are provided at both the pod & container level, + the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will + be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + serviceAccount: + description: ServiceAccount indicates the name of an existing service + account to use with this instance. When set, the operator will not + automatically create a ServiceAccount for the collector. + type: string + targetAllocator: + description: TargetAllocator indicates a value which determines whether + to spawn a target allocation resource or not. + properties: + affinity: + description: If specified, indicates the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector in + the specified namespaces, where co-located + is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector in + the specified namespaces, where co-located + is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object type: object - type: object - serviceAccount: - description: ServiceAccount indicates the name of an existing service - account to use with this instance. - type: string - targetAllocator: - description: TargetAllocator indicates a value which determines whether - to spawn a target allocation resource or not. - properties: allocationStrategy: description: AllocationStrategy determines which strategy the target allocator should use for allocation. The current options are least-weighted and consistent-hashing. The default option is least-weighted + enum: + - least-weighted + - consistent-hashing type: string enabled: description: Enabled indicates whether to use a target allocation mechanism for Prometheus targets or not. type: boolean + env: + description: ENV vars to set on the OpenTelemetry TargetAllocator's + Pods. These can then in certain cases be consumed in the config + file for the TargetAllocator. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + filterStrategy: + description: FilterStrategy determines how to filter targets before + allocating them among the collectors. The only current option + is relabel-config (drops targets based on prom relabel_config). + type: string image: description: Image indicates the container image to use for the OpenTelemetry TargetAllocator. type: string + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to schedule OpenTelemetry TargetAllocator + pods. + type: object prometheusCR: description: PrometheusCR defines the configuration for the retrieval of PrometheusOperator CRDs ( servicemonitor.monitoring.coreos.com/v1 - and podmonitor.monitoring.coreos.com/v1 ) retrieval. All CR - instances which the ServiceAccount has access to will be retrieved. - This includes other namespaces. + and podmonitor.monitoring.coreos.com/v1 ) retrieval. properties: enabled: description: Enabled indicates whether to use a PrometheusOperator custom resources as targets or not. type: boolean + podMonitorSelector: + additionalProperties: + type: string + description: PodMonitors to be selected for target discovery. + This is a map of {key,value} pairs. Each {key,value} in + the map is going to exactly match a label in a PodMonitor's + meta labels. + type: object + scrapeInterval: + default: 30s + description: "Interval between consecutive scrapes. Equivalent + to the same setting on the Prometheus CRD. \n Default: \"30s\"" + format: duration + type: string + serviceMonitorSelector: + additionalProperties: + type: string + description: ServiceMonitors to be selected for target discovery. + This is a map of {key,value} pairs. Each {key,value} in + the map is going to exactly match a label in a ServiceMonitor's + meta labels. + type: object type: object replicas: description: Replicas is the number of pod instances for the underlying TargetAllocator. This should only be set to a value other than 1 if a strategy that allows for high availability is chosen. - Currently, the only allocation strategy that can be run in a - high availability mode is consistent-hashing. format: int32 type: integer + resources: + description: Resources to set on the OpenTelemetryTargetAllocator + containers. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + securityContext: + description: SecurityContext configures the container security + context for the targetallocator. + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1." + format: int64 + type: integer + fsGroupChangePolicy: + description: fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers + in this pod. Note that this field cannot be set when spec.os.name + is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID, the fsGroup (if specified), and group memberships defined + in the container image for th + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. Note that this field cannot + be set when spec.os. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. + type: string + type: object + type: object serviceAccount: description: ServiceAccount indicates the name of an existing - service account to use with this instance. + service account to use with this instance. When set, the operator + will not automatically create a ServiceAccount for the TargetAllocator. type: string + tolerations: + description: Toleration embedded kubernetes pod configuration + option, controls how pods can be scheduled with matching taints + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints embedded kubernetes pod + configuration option, controls how pods are spread across your + cluster among failure-domains such as regions, zones, nodes, + and other user-defined top + items: + description: TopologySpreadConstraint specifies how to spread + matching pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine + the number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: MatchLabelKeys is a set of pod label keys to + select the pods over which spreading will be calculated. + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: MaxSkew describes the degree to which pods + may be unevenly distributed. + format: int32 + type: integer + minDomains: + description: MinDomains indicates a minimum number of eligible + domains. + format: int32 + type: integer + nodeAffinityPolicy: + description: NodeAffinityPolicy indicates how we will treat + Pod's nodeAffinity/nodeSelector when calculating pod topology + spread skew. + type: string + nodeTaintsPolicy: + description: NodeTaintsPolicy indicates how we will treat + node taints when calculating pod topology spread skew. + type: string + topologyKey: + description: TopologyKey is the key of node labels. Nodes + that have a label with this key and identical values are + considered to be in the same topology. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with + a pod if it doesn't satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array type: object + terminationGracePeriodSeconds: + description: Duration in seconds the pod needs to terminate gracefully + upon probe failure. + format: int64 + type: integer tolerations: description: Toleration to schedule OpenTelemetry Collector pods. This is only relevant to daemonset, statefulset, and deployment @@ -925,16 +5342,11 @@ spec: operator: description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod - can tolerate all taints of a particular category. type: string tolerationSeconds: description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise - this field is ignored) tolerates the taint. By default, it - is not set, which means tolerate the taint forever (do not - evict). Zero and negative values will be treated as 0 (evict - immediately) by the system. + this field is ignored) tolerates the taint. format: int64 type: integer value: @@ -944,6 +5356,128 @@ spec: type: string type: object type: array + topologySpreadConstraints: + description: TopologySpreadConstraints embedded kubernetes pod configuration + option, controls how pods are spread across your cluster among failure-domains + such as regions, zones, nodes, and other user-defined top + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: MatchLabelKeys is a set of pod label keys to select + the pods over which spreading will be calculated. + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: MaxSkew describes the degree to which pods may + be unevenly distributed. + format: int32 + type: integer + minDomains: + description: MinDomains indicates a minimum number of eligible + domains. + format: int32 + type: integer + nodeAffinityPolicy: + description: NodeAffinityPolicy indicates how we will treat + Pod's nodeAffinity/nodeSelector when calculating pod topology + spread skew. + type: string + nodeTaintsPolicy: + description: NodeTaintsPolicy indicates how we will treat node + taints when calculating pod topology spread skew. + type: string + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with a + pod if it doesn't satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + updateStrategy: + description: UpdateStrategy represents the strategy the operator will + take replacing existing DaemonSet pods with new pods https://kubernetes. + properties: + rollingUpdate: + description: 'Rolling update config params. Present only if type + = "RollingUpdate". --- TODO: Update this to follow our convention + for oneOf, whatever we decide it to be. Same as Deployment `strategy.' + properties: + maxSurge: + anyOf: + - type: integer + - type: string + description: The maximum number of nodes with an existing + available DaemonSet pod that can have an updated DaemonSet + pod during during an update. + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: The maximum number of DaemonSet pods that can + be unavailable during the update. + x-kubernetes-int-or-string: true + type: object + type: + description: Type of daemon set update. Can be "RollingUpdate" + or "OnDelete". Default is RollingUpdate. + type: string + type: object upgradeStrategy: description: UpgradeStrategy represents how the operator will handle upgrades to the CR when a newer version of the operator is deployed @@ -959,16 +5493,15 @@ spec: to a persistent volume properties: apiVersion: - description: 'APIVersion defines the versioned schema of this + description: APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized - values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + values. type: string kind: - description: 'Kind is a string value representing the REST resource + description: Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' @@ -1002,13 +5535,7 @@ spec: type: array dataSource: description: 'dataSource field can be used to specify either: - * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the provisioner - or an external controller can support the specified data - source, it will create a new volume based on the contents - of the specified data source. If the AnyVolumeDataSource - feature gate is enabled, this field will always have the - same contents as the DataSourceRef field.' + * An existing VolumeSnapshot object (snapshot.storage.k8s.' properties: apiGroup: description: APIGroup is the group for the resource @@ -1028,27 +5555,9 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object from which + description: dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume - is desired. This may be any local object from a non-empty - API group (non core object) or a PersistentVolumeClaim - object. When this field is specified, volume binding will - only succeed if the type of the specified object matches - some installed volume populator or dynamic provisioner. - This field will replace the functionality of the DataSource - field and as such if both fields are non-empty, they must - have the same value. For backwards compatibility, both - fields (DataSource and DataSourceRef) will be set to the - same value automatically if one of them is empty and the - other is non-empty. There are two important differences - between DataSource and DataSourceRef: * While DataSource - only allows two specific types of objects, DataSourceRef - allows any non-core object, as well as PersistentVolumeClaim - objects. * While DataSource ignores disallowed values - (dropping them), DataSourceRef preserves all values, and - generates an error if a disallowed value is specified. - (Beta) Using this field requires the AnyVolumeDataSource - feature gate to be enabled.' + is desired. properties: apiGroup: description: APIGroup is the group for the resource @@ -1062,19 +5571,41 @@ spec: name: description: Name is the name of resource being referenced type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace is specified, + a gateway.networking.k8s. + type: string required: - kind - name type: object - x-kubernetes-map-type: atomic resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify resource - requirements that are lower than previous value but must - still be higher than capacity recorded in the status field - of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: resources represents the minimum resources + the volume should have. properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate." + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where + this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1092,11 +5623,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is omitted - for a container, it defaults to Limits if that is - explicitly specified, otherwise to an implementation-defined - value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: Requests describes the minimum amount of + compute resources required. type: object type: object selector: @@ -1125,8 +5653,6 @@ spec: If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. items: type: string type: array @@ -1139,10 +5665,6 @@ spec: additionalProperties: type: string description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic @@ -1170,6 +5692,18 @@ spec: items: type: string type: array + allocatedResourceStatuses: + additionalProperties: + description: When a controller receives persistentvolume + claim update with ClaimResourceStatus for a resource + that it does not recognizes, then it should ignore that + update and let other controllers handle it. + type: string + description: allocatedResourceStatuses stores status of + resource being resized for the given PVC. Key names follow + standard Kubernetes label syntax. + type: object + x-kubernetes-map-type: granular allocatedResources: additionalProperties: anyOf: @@ -1177,18 +5711,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: allocatedResources is the storage resource - within AllocatedResources tracks the capacity allocated - to a PVC. It may be larger than the actual capacity when - a volume expansion operation is requested. For storage - quota, the larger value from allocatedResources and PVC.spec.resources - is used. If allocatedResources is not set, PVC.spec.resources - alone is used for quota calculation. If a volume expansion - capacity request is lowered, allocatedResources is only - lowered if there are no expansion operations in progress - and if the actual volume capacity is equal or lower than - the requested capacity. This is an alpha field and requires - enabling RecoverVolumeExpansionFailure feature. + description: allocatedResources tracks the resources allocated + to a PVC including its capacity. Key names follow standard + Kubernetes label syntax. type: object capacity: additionalProperties: @@ -1205,7 +5730,7 @@ spec: volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'. items: - description: PersistentVolumeClaimCondition contails details + description: PersistentVolumeClaimCondition contains details about state of pvc properties: lastProbeTime: @@ -1225,9 +5750,7 @@ spec: reason: description: reason is a unique, this should be a short, machine understandable string that gives - the reason for condition's last transition. If it - reports "ResizeStarted" that means the underlying - persistent volume is being resized. + the reason for condition's last transition. type: string status: type: string @@ -1243,13 +5766,6 @@ spec: phase: description: phase represents the current phase of PersistentVolumeClaim. type: string - resizeStatus: - description: resizeStatus stores status of resize operation. - ResizeStatus is not set by default but when expansion - is complete resizeStatus is set to empty string by resize - controller or kubelet. This is an alpha field and requires - enabling RecoverVolumeExpansionFailure feature. - type: string type: object type: object type: array @@ -1284,10 +5800,7 @@ spec: type: string subPathExpr: description: Expanded path within the volume from which the - container's volume should be mounted. Behaves similarly to - SubPath but environment variable references $(VAR_NAME) are - expanded using the container's environment. Defaults to "" - (volume's root). SubPathExpr and SubPath are mutually exclusive. + container's volume should be mounted. type: string required: - mountPath @@ -1305,23 +5818,19 @@ spec: awsElasticBlockStore: description: 'awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + to the pod. More info: https://kubernetes.' properties: fsType: description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from - compromising the machine' + "ext4", "xfs", "ntfs".' type: string partition: description: 'partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property empty).' + the partition as "1".' format: int32 type: integer readOnly: @@ -1361,7 +5870,7 @@ spec: description: 'kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data - disk (only in managed availability set). defaults to shared' + disk (only in managed availability set).' type: string readOnly: description: readOnly Defaults to false (read/write). ReadOnly @@ -1440,7 +5949,7 @@ spec: description: 'fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + be "ext4" if unspecified.' type: string readOnly: description: 'readOnly defaults to false (read/write). ReadOnly @@ -1472,24 +5981,14 @@ spec: description: 'defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. Defaults to - 0644. Directories within the path are not affected by - this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set.' + 0 and 511.' format: int32 type: integer items: description: items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content - is the value. If specified, the listed keys will be projected - into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in - the ConfigMap, the volume setup will error unless it is - marked optional. Paths must be relative and may not contain - the '..' path or start with '..'. + is the value. items: description: Maps a string key to a path within a volume. properties: @@ -1500,12 +5999,7 @@ spec: description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. If not - specified, the volume defaultMode will be used. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' + 0 and 511.' format: int32 type: integer path: @@ -1549,10 +6043,7 @@ spec: description: nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume - and NodeUnpublishVolume calls. This field is optional, - and may be empty if no secret is required. If the secret - object contains more than one secret, all secret references - are passed. + and NodeUnpublishVolume calls. properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names @@ -1581,14 +6072,7 @@ spec: defaultMode: description: 'Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set - permissions on created files by default. Must be an octal - value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. Defaults to - 0644. Directories within the path are not affected by - this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set.' + permissions on created files by default.' format: int32 type: integer items: @@ -1617,13 +6101,7 @@ spec: mode: description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 - and 0777 or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON requires - decimal values for mode bits. If not specified, - the volume defaultMode will be used. This might - be in conflict with other options that affect the - file mode, like fsGroup, and the result can be other - mode bits set.' + and 0777 or a decimal value between 0 and 511.' format: int32 type: integer path: @@ -1668,64 +6146,29 @@ spec: shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' properties: medium: - description: 'medium represents what type of storage medium + description: medium represents what type of storage medium should back this directory. The default is "" which means - to use the node''s default medium. Must be an empty string - (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + to use the node's default medium. Must be an empty string + (default) or Memory. type: string sizeLimit: anyOf: - type: integer - type: string - description: 'sizeLimit is the total amount of local storage + description: sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also - applicable for memory medium. The maximum usage on memory - medium EmptyDir would be the minimum value between the - SizeLimit specified here and the sum of memory limits - of all containers in a pod. The default is nil which means - that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + applicable for memory medium. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object ephemeral: - description: "ephemeral represents a volume that is handled - by a cluster storage driver. The volume's lifecycle is tied - to the pod that defines it - it will be created before the - pod starts, and deleted when the pod is removed. \n Use this - if: a) the volume is only needed while the pod runs, b) features - of normal volumes like restoring from snapshot or capacity - tracking are needed, c) the storage driver is specified through - a storage class, and d) the storage driver supports dynamic - volume provisioning through a PersistentVolumeClaim (see EphemeralVolumeSource - for more information on the connection between this volume - type and PersistentVolumeClaim). \n Use PersistentVolumeClaim - or one of the vendor-specific APIs for volumes that persist - for longer than the lifecycle of an individual pod. \n Use - CSI for light-weight local ephemeral volumes if the CSI driver - is meant to be used that way - see the documentation of the - driver for more information. \n A pod can use both types of - ephemeral volumes and persistent volumes at the same time." + description: ephemeral represents a volume that is handled by + a cluster storage driver. properties: volumeClaimTemplate: - description: "Will be used to create a stand-alone PVC to + description: Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource - is embedded will be the owner of the PVC, i.e. the PVC - will be deleted together with the pod. The name of the - PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. - Pod validation will reject the pod if the concatenated - name is not valid for a PVC (for example, too long). \n - An existing PVC with that name that is not owned by the - pod will *not* be used for the pod to avoid using an unrelated - volume by mistake. Starting the pod is then blocked until - the unrelated PVC is removed. If such a pre-created PVC - is meant to be used by the pod, the PVC has to updated - with an owner reference to the pod once the pod exists. - Normally this should not be necessary, but it may be useful - when manually reconstructing a broken cluster. \n This - field is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. \n Required, must - not be nil." + is embedded will be the owner of the PVC, i.e. properties: metadata: description: May contain labels and annotations that @@ -1752,8 +6195,7 @@ spec: spec: description: The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC - that gets created from this template. The same fields - as in a PersistentVolumeClaim are also valid here. + that gets created from this template. properties: accessModes: description: 'accessModes contains the desired access @@ -1763,14 +6205,7 @@ spec: type: array dataSource: description: 'dataSource field can be used to specify - either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the - provisioner or an external controller can support - the specified data source, it will create a new - volume based on the contents of the specified - data source. If the AnyVolumeDataSource feature - gate is enabled, this field will always have the - same contents as the DataSourceRef field.' + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.' properties: apiGroup: description: APIGroup is the group for the resource @@ -1793,30 +6228,9 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object + description: dataSourceRef specifies the object from which to populate the volume with data, if - a non-empty volume is desired. This may be any - local object from a non-empty API group (non core - object) or a PersistentVolumeClaim object. When - this field is specified, volume binding will only - succeed if the type of the specified object matches - some installed volume populator or dynamic provisioner. - This field will replace the functionality of the - DataSource field and as such if both fields are - non-empty, they must have the same value. For - backwards compatibility, both fields (DataSource - and DataSourceRef) will be set to the same value - automatically if one of them is empty and the - other is non-empty. There are two important differences - between DataSource and DataSourceRef: * While - DataSource only allows two specific types of objects, - DataSourceRef allows any non-core object, as well - as PersistentVolumeClaim objects. * While DataSource - ignores disallowed values (dropping them), DataSourceRef - preserves all values, and generates an error if - a disallowed value is specified. (Beta) Using - this field requires the AnyVolumeDataSource feature - gate to be enabled.' + a non-empty volume is desired. properties: apiGroup: description: APIGroup is the group for the resource @@ -1833,19 +6247,43 @@ spec: description: Name is the name of resource being referenced type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace + is specified, a gateway.networking.k8s. + type: string required: - kind - name type: object - x-kubernetes-map-type: atomic resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify - resource requirements that are lower than previous - value but must still be higher than capacity recorded - in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: resources represents the minimum resources + the volume should have. properties: + claims: + description: "Claims lists the names of resources, + defined in spec.resourceClaims, that are used + by this container. \n This is an alpha field + and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one + entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name + of one entry in pod.spec.resourceClaims + of the Pod where this field is used. + It makes that resource available inside + a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1863,12 +6301,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. If Requests - is omitted for a container, it defaults to - Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: Requests describes the minimum + amount of compute resources required. type: object type: object selector: @@ -1900,9 +6334,7 @@ spec: values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. + the values array must be empty. items: type: string type: array @@ -1915,11 +6347,7 @@ spec: additionalProperties: type: string description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + pairs. type: object type: object x-kubernetes-map-type: atomic @@ -1948,11 +6376,10 @@ spec: pod. properties: fsType: - description: 'fsType is the filesystem type to mount. Must + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. TODO: how do we prevent errors in the - filesystem from compromising the machine' + if unspecified. type: string lun: description: 'lun is Optional: FC target lun number' @@ -2004,9 +6431,7 @@ spec: description: 'secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no - secret object is specified. If the secret object contains - more than one secret, all secrets are passed to the plugin - scripts.' + secret object is specified.' properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names @@ -2035,24 +6460,19 @@ spec: gcePersistentDisk: description: 'gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + to the pod. More info: https://kubernetes.' properties: fsType: description: 'fsType is filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from - compromising the machine' + "xfs", "ntfs".' type: string partition: description: 'partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + the partition as "1".' format: int32 type: integer pdName: @@ -2068,17 +6488,12 @@ spec: type: object gitRepo: description: 'gitRepo represents a git repository at a particular - revision. DEPRECATED: GitRepo is deprecated. To provision - a container with a git repo, mount an EmptyDir into an InitContainer - that clones the repo using git, then mount the EmptyDir into - the Pod''s container.' + revision. DEPRECATED: GitRepo is deprecated.' properties: directory: description: directory is the target directory name. Must not contain or start with '..'. If '.' is supplied, the - volume directory will be the git repository. Otherwise, - if specified, the volume will contain the git repository - in the subdirectory with the given name. + volume directory will be the git repository. type: string repository: description: repository is the URL @@ -2112,13 +6527,8 @@ spec: - path type: object hostPath: - description: 'hostPath represents a pre-existing file or directory + description: hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. - This is generally used for system agents or other privileged - things that are allowed to see the host machine. Most containers - will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use host directory - mounts and who can/can not mount host directories as read/write.' properties: path: description: 'path of the directory on the host. If the @@ -2149,16 +6559,11 @@ spec: description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from - compromising the machine' + "ext4", "xfs", "ntfs".' type: string initiatorName: description: initiatorName is the custom iSCSI Initiator - Name. If initiatorName is specified with iscsiInterface - simultaneously, new iSCSI interface : will be created for the connection. + Name. type: string iqn: description: iqn is the target iSCSI Qualified Name. @@ -2230,7 +6635,7 @@ spec: persistentVolumeClaim: description: 'persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + More info: https://kubernetes.' properties: claimName: description: 'claimName is the name of a PersistentVolumeClaim @@ -2288,12 +6693,7 @@ spec: defaultMode: description: defaultMode are the mode bits used to set permissions on created files by default. Must be an octal value between - 0000 and 0777 or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON requires decimal - values for mode bits. Directories within the path are - not affected by this setting. This might be in conflict - with other options that affect the file mode, like fsGroup, - and the result can be other mode bits set. + 0000 and 0777 or a decimal value between 0 and 511. format: int32 type: integer sources: @@ -2311,13 +6711,6 @@ spec: pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. - If specified, the listed keys will be projected - into the specified paths, and unlisted keys - will not be present. If a key is specified which - is not present in the ConfigMap, the volume - setup will error unless it is marked optional. - Paths must be relative and may not contain the - '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -2330,13 +6723,7 @@ spec: used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and - 511. YAML accepts both octal and decimal - values, JSON requires decimal values for - mode bits. If not specified, the volume - defaultMode will be used. This might be - in conflict with other options that affect - the file mode, like fsGroup, and the result - can be other mode bits set.' + 511.' format: int32 type: integer path: @@ -2397,14 +6784,7 @@ spec: description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML - accepts both octal and decimal values, - JSON requires decimal values for mode - bits. If not specified, the volume defaultMode - will be used. This might be in conflict - with other options that affect the file - mode, like fsGroup, and the result can - be other mode bits set.' + a decimal value between 0 and 511.' format: int32 type: integer path: @@ -2456,13 +6836,6 @@ spec: pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. - If specified, the listed keys will be projected - into the specified paths, and unlisted keys - will not be present. If a key is specified which - is not present in the Secret, the volume setup - will error unless it is marked optional. Paths - must be relative and may not contain the '..' - path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -2475,13 +6848,7 @@ spec: used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and - 511. YAML accepts both octal and decimal - values, JSON requires decimal values for - mode bits. If not specified, the volume - defaultMode will be used. This might be - in conflict with other options that affect - the file mode, like fsGroup, and the result - can be other mode bits set.' + 511.' format: int32 type: integer path: @@ -2517,19 +6884,14 @@ spec: of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the - token. The audience defaults to the identifier - of the apiserver. + token. type: string expirationSeconds: description: expirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate - the service account token. The kubelet will - start trying to rotate the token if the token - is older than 80 percent of its time to live - or if the token is older than 24 hours.Defaults - to 1 hour and must be at least 10 minutes. + the service account token. format: int64 type: integer path: @@ -2587,10 +6949,7 @@ spec: description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from - compromising the machine' + "ext4", "xfs", "ntfs".' type: string image: description: 'image is the rados image name. More info: @@ -2700,24 +7059,14 @@ spec: description: 'defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. Defaults to - 0644. Directories within the path are not affected by - this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set.' + 0 and 511.' format: int32 type: integer items: description: items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content - is the value. If specified, the listed keys will be projected - into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in - the Secret, the volume setup will error unless it is marked - optional. Paths must be relative and may not contain the - '..' path or start with '..'. + is the value. items: description: Maps a string key to a path within a volume. properties: @@ -2728,12 +7077,7 @@ spec: description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. If not - specified, the volume defaultMode will be used. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' + 0 and 511.' format: int32 type: integer path: @@ -2789,12 +7133,7 @@ spec: volumeNamespace: description: volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified - then the Pod's namespace will be used. This allows the - Kubernetes name scoping to be mirrored within StorageOS - for tighter integration. Set VolumeName to any name to - override the default behaviour. Set to "default" if you - are not using namespaces within StorageOS. Namespaces - that do not pre-exist within StorageOS will be created. + then the Pod's namespace will be used. type: string type: object vsphereVolume: @@ -2832,6 +7171,10 @@ spec: description: OpenTelemetryCollectorStatus defines the observed state of OpenTelemetryCollector. properties: + image: + description: Image indicates the container image to use for the OpenTelemetry + Collector. + type: string messages: description: 'Messages about actions performed by the operator on this resource. Deprecated: use Kubernetes events instead.' @@ -2858,6 +7201,12 @@ spec: description: The selector used to match the OpenTelemetryCollector's deployment or statefulSet pods. type: string + statusReplicas: + description: StatusReplicas is the number of pods targeted by + this OpenTelemetryCollector's with a Ready Condition / Total + number of non-terminated pods targeted by this OpenTelemetryCollector's + (their labels matc + type: string type: object version: description: Version of the managed OpenTelemetry Collector (operand) diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml index 440076266b..b0f3b46044 100644 --- a/bundle/metadata/annotations.yaml +++ b/bundle/metadata/annotations.yaml @@ -5,7 +5,7 @@ annotations: operators.operatorframework.io.bundle.metadata.v1: metadata/ operators.operatorframework.io.bundle.package.v1: opentelemetry-operator operators.operatorframework.io.bundle.channels.v1: alpha - operators.operatorframework.io.metrics.builder: operator-sdk-v1.23.0 + operators.operatorframework.io.metrics.builder: operator-sdk-v1.29.0 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v3 diff --git a/bundle/tests/scorecard/config.yaml b/bundle/tests/scorecard/config.yaml index 36fd9c2e7f..efe0cbadca 100644 --- a/bundle/tests/scorecard/config.yaml +++ b/bundle/tests/scorecard/config.yaml @@ -8,7 +8,7 @@ stages: - entrypoint: - scorecard-test - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.23.0 + image: quay.io/operator-framework/scorecard-test:v1.27.0 labels: suite: basic test: basic-check-spec-test @@ -18,7 +18,7 @@ stages: - entrypoint: - scorecard-test - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.23.0 + image: quay.io/operator-framework/scorecard-test:v1.27.0 labels: suite: olm test: olm-bundle-validation-test @@ -28,7 +28,7 @@ stages: - entrypoint: - scorecard-test - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.23.0 + image: quay.io/operator-framework/scorecard-test:v1.27.0 labels: suite: olm test: olm-crds-have-validation-test @@ -38,7 +38,7 @@ stages: - entrypoint: - scorecard-test - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.23.0 + image: quay.io/operator-framework/scorecard-test:v1.27.0 labels: suite: olm test: olm-crds-have-resources-test diff --git a/cmd/operator-opamp-bridge/Dockerfile b/cmd/operator-opamp-bridge/Dockerfile new file mode 100644 index 0000000000..ebf707b249 --- /dev/null +++ b/cmd/operator-opamp-bridge/Dockerfile @@ -0,0 +1,21 @@ +# Get CA certificates from the Alpine package repo +FROM alpine:3.19 as certificates + +RUN apk --no-cache add ca-certificates + +# Start a new stage from scratch +FROM scratch + +ARG TARGETARCH + +WORKDIR /root/ + +# Copy the certs +COPY --from=certificates /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +# Copy binary built on the host +COPY bin/opampbridge_${TARGETARCH} ./main + +# "nonroot" +USER 65532:65532 +ENTRYPOINT ["./main"] diff --git a/cmd/operator-opamp-bridge/agent/agent.go b/cmd/operator-opamp-bridge/agent/agent.go new file mode 100644 index 0000000000..e9dc849b16 --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/agent.go @@ -0,0 +1,366 @@ +// 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 agent + +import ( + "bytes" + "context" + "fmt" + "time" + + "github.com/go-logr/logr" + "github.com/oklog/ulid/v2" + "github.com/open-telemetry/opamp-go/client" + "github.com/open-telemetry/opamp-go/client/types" + "github.com/open-telemetry/opamp-go/protobufs" + "go.uber.org/multierr" + "k8s.io/utils/clock" + "sigs.k8s.io/yaml" + + "github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge/config" + "github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge/metrics" + "github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge/operator" +) + +type Agent struct { + logger logr.Logger + + appliedKeys map[collectorKey]bool + clock clock.Clock + startTime uint64 + lastHash []byte + + instanceId ulid.ULID + agentDescription *protobufs.AgentDescription + remoteConfigStatus *protobufs.RemoteConfigStatus + + opampClient client.OpAMPClient + metricReporter *metrics.MetricReporter + config *config.Config + applier operator.ConfigApplier + remoteConfigEnabled bool + + done chan struct{} + ticker *time.Ticker +} + +func NewAgent(logger logr.Logger, applier operator.ConfigApplier, config *config.Config, opampClient client.OpAMPClient) *Agent { + var t *time.Ticker + if config.HeartbeatInterval > 0 { + t = time.NewTicker(config.HeartbeatInterval) + } + agent := &Agent{ + config: config, + applier: applier, + logger: logger, + appliedKeys: map[collectorKey]bool{}, + instanceId: config.GetNewInstanceId(), + agentDescription: config.GetDescription(), + remoteConfigEnabled: config.RemoteConfigEnabled(), + opampClient: opampClient, + clock: clock.RealClock{}, + done: make(chan struct{}, 1), + ticker: t, + } + + agent.logger.V(3).Info("Agent created", + "instanceId", agent.instanceId.String(), + "agentType", config.GetAgentType(), + "agentVersion", config.GetAgentVersion()) + + return agent +} + +// getHealth is called every heartbeat interval to report health. +func (agent *Agent) getHealth() *protobufs.ComponentHealth { + healthMap, err := agent.generateComponentHealthMap() + if err != nil { + return &protobufs.ComponentHealth{ + Healthy: false, + StartTimeUnixNano: agent.startTime, + LastError: err.Error(), + } + } + return &protobufs.ComponentHealth{ + Healthy: true, + StartTimeUnixNano: agent.startTime, + StatusTimeUnixNano: uint64(agent.clock.Now().UnixNano()), + LastError: "", + ComponentHealthMap: healthMap, + } +} + +// generateComponentHealthMap allows the bridge to report the status of the collector pools it owns. +// TODO: implement enhanced health messaging. +func (agent *Agent) generateComponentHealthMap() (map[string]*protobufs.ComponentHealth, error) { + cols, err := agent.applier.ListInstances() + if err != nil { + return nil, err + } + healthMap := map[string]*protobufs.ComponentHealth{} + for _, col := range cols { + key := newCollectorKey(col.GetNamespace(), col.GetName()) + healthMap[key.String()] = &protobufs.ComponentHealth{ + StartTimeUnixNano: uint64(col.ObjectMeta.GetCreationTimestamp().UnixNano()), + StatusTimeUnixNano: uint64(agent.clock.Now().UnixNano()), + Status: col.Status.Scale.StatusReplicas, + } + } + return healthMap, nil +} + +// onConnect is called when an agent is successfully connected to a server. +func (agent *Agent) onConnect() { + agent.logger.V(3).Info("Connected to the server.") +} + +// onConnectFailed is called when an agent was unable to connect to a server. +func (agent *Agent) onConnectFailed(err error) { + agent.logger.Error(err, "failed to connect to the server") +} + +// onError is called when an agent receives an error response from the server. +func (agent *Agent) onError(err *protobufs.ServerErrorResponse) { + agent.logger.Error(fmt.Errorf(err.GetErrorMessage()), "server returned an error response") +} + +// saveRemoteConfigStatus receives a status from the server when the server sets a remote configuration. +func (agent *Agent) saveRemoteConfigStatus(_ context.Context, status *protobufs.RemoteConfigStatus) { + agent.remoteConfigStatus = status +} + +// Start sets up the callbacks for the OpAMP client and begins the client's connection to the server. +func (agent *Agent) Start() error { + agent.startTime = uint64(agent.clock.Now().UnixNano()) + settings := types.StartSettings{ + OpAMPServerURL: agent.config.Endpoint, + Header: agent.config.Headers.ToHTTPHeader(), + InstanceUid: agent.instanceId.String(), + Callbacks: types.CallbacksStruct{ + OnConnectFunc: agent.onConnect, + OnConnectFailedFunc: agent.onConnectFailed, + OnErrorFunc: agent.onError, + SaveRemoteConfigStatusFunc: agent.saveRemoteConfigStatus, + GetEffectiveConfigFunc: agent.getEffectiveConfig, + OnMessageFunc: agent.onMessage, + }, + RemoteConfigStatus: agent.remoteConfigStatus, + PackagesStateProvider: nil, + Capabilities: agent.config.GetCapabilities(), + } + err := agent.opampClient.SetAgentDescription(agent.agentDescription) + if err != nil { + return err + } + err = agent.opampClient.SetHealth(agent.getHealth()) + if err != nil { + return err + } + + agent.logger.V(3).Info("Starting OpAMP client...") + + err = agent.opampClient.Start(context.Background(), settings) + if err != nil { + return err + } + + if agent.config.HeartbeatInterval > 0 { + go agent.runHeartbeat() + } + + agent.logger.V(3).Info("OpAMP Client started.") + + return nil +} + +// runHeartbeat sets health on an interval to keep the connection active. +func (agent *Agent) runHeartbeat() { + if agent.ticker == nil { + agent.logger.Info("cannot run heartbeat without setting an interval for the ticker") + return + } + for { + select { + case <-agent.ticker.C: + agent.logger.V(4).Info("sending heartbeat") + err := agent.opampClient.SetHealth(agent.getHealth()) + if err != nil { + agent.logger.Error(err, "failed to heartbeat") + return + } + case <-agent.done: + agent.ticker.Stop() + agent.logger.Info("stopping heartbeating") + return + } + } +} + +// updateAgentIdentity receives a new instanced Id from the remote server and updates the agent's instanceID field. +// The meter will be reinitialized by the onMessage function. +func (agent *Agent) updateAgentIdentity(instanceId ulid.ULID) { + agent.logger.V(3).Info("Agent identity is being changed", + "old instanceId", agent.instanceId.String(), + "new instanceid", instanceId.String()) + agent.instanceId = instanceId +} + +// getEffectiveConfig is called when a remote server needs to learn of the current effective configuration of each +// collector the agent is managing. +func (agent *Agent) getEffectiveConfig(ctx context.Context) (*protobufs.EffectiveConfig, error) { + instances, err := agent.applier.ListInstances() + if err != nil { + agent.logger.Error(err, "failed to list instances") + return nil, err + } + instanceMap := map[string]*protobufs.AgentConfigFile{} + for _, instance := range instances { + marshaled, err := yaml.Marshal(instance) + if err != nil { + agent.logger.Error(err, "failed to marhsal config") + return nil, err + } + mapKey := newCollectorKey(instance.GetNamespace(), instance.GetName()) + instanceMap[mapKey.String()] = &protobufs.AgentConfigFile{ + Body: marshaled, + ContentType: "yaml", + } + } + return &protobufs.EffectiveConfig{ + ConfigMap: &protobufs.AgentConfigMap{ + ConfigMap: instanceMap, + }, + }, nil +} + +// initMeter initializes a metric reporter instance for the agent to report runtime metrics to the +// configured destination. The settings received will be used to initialize a reporter, shutting down any previously +// running metrics reporting instances. +func (agent *Agent) initMeter(settings *protobufs.TelemetryConnectionSettings) { + reporter, err := metrics.NewMetricReporter(agent.logger, settings, agent.config.GetAgentType(), agent.config.GetAgentVersion(), agent.instanceId) + if err != nil { + agent.logger.Error(err, "failed to create metric reporter") + return + } + + if agent.metricReporter != nil { + agent.metricReporter.Shutdown() + } + agent.metricReporter = reporter +} + +// applyRemoteConfig receives a remote configuration from a remote server of the following form: +// +// map[name/namespace] -> collector CRD spec +// +// For every key in the received remote configuration, the agent attempts to apply it to the connected +// Kubernetes cluster. If an agent fails to apply a collector CRD, it will continue to the next entry. The agent will +// store the received configuration hash regardless of application status as per the OpAMP spec. +// +// INVARIANT: The caller must verify that config isn't nil _and_ the configuration has changed between calls. +func (agent *Agent) applyRemoteConfig(config *protobufs.AgentRemoteConfig) (*protobufs.RemoteConfigStatus, error) { + var multiErr error + // Apply changes from the received config map + for key, file := range config.Config.GetConfigMap() { + if len(key) == 0 || len(file.Body) == 0 { + continue + } + colKey, err := collectorKeyFromKey(key) + if err != nil { + multiErr = multierr.Append(multiErr, err) + continue + } + err = agent.applier.Apply(colKey.name, colKey.namespace, file) + if err != nil { + multiErr = multierr.Append(multiErr, err) + continue + } + agent.appliedKeys[colKey] = true + } + // Check if anything was deleted + for collectorKey := range agent.appliedKeys { + if _, ok := config.Config.GetConfigMap()[collectorKey.String()]; !ok { + err := agent.applier.Delete(collectorKey.name, collectorKey.namespace) + if err != nil { + multiErr = multierr.Append(multiErr, err) + } + } + } + agent.lastHash = config.GetConfigHash() + if multiErr != nil { + return &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: agent.lastHash, + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_FAILED, + ErrorMessage: multiErr.Error(), + }, multiErr + } + return &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: agent.lastHash, + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, nil +} + +// Shutdown will stop the OpAMP client gracefully. +func (agent *Agent) Shutdown() { + agent.logger.V(3).Info("Agent shutting down...") + close(agent.done) + if agent.opampClient != nil { + err := agent.opampClient.Stop(context.Background()) + if err != nil { + agent.logger.Error(err, "failed to stop client") + } + } + if agent.metricReporter != nil { + agent.metricReporter.Shutdown() + } +} + +// onMessage is called when the client receives a new message from the connected OpAMP server. The agent is responsible +// for checking if it should apply a new remote configuration. The agent will also initialize metrics based on the +// settings received from the server. The agent is also able to update its identifier if it needs to. +func (agent *Agent) onMessage(ctx context.Context, msg *types.MessageData) { + // If we received remote configuration, and it's not the same as the previously applied one + if agent.remoteConfigEnabled && msg.RemoteConfig != nil && !bytes.Equal(agent.lastHash, msg.RemoteConfig.GetConfigHash()) { + var err error + status, err := agent.applyRemoteConfig(msg.RemoteConfig) + if err != nil { + agent.logger.Error(err, "failed to apply remote config") + } + err = agent.opampClient.SetRemoteConfigStatus(status) + if err != nil { + agent.logger.Error(err, "failed to set remote config status") + return + } + err = agent.opampClient.UpdateEffectiveConfig(ctx) + if err != nil { + agent.logger.Error(err, "failed to update effective config") + } + } + + // The instance id is updated prior to the meter initialization so that the new meter will report using the updated + // instanceId. + if msg.AgentIdentification != nil { + newInstanceId, err := ulid.Parse(msg.AgentIdentification.NewInstanceUid) + if err != nil { + agent.logger.Error(err, "couldn't parse instance UID") + return + } + agent.updateAgentIdentity(newInstanceId) + } + + if msg.OwnMetricsConnSettings != nil { + agent.initMeter(msg.OwnMetricsConnSettings) + } +} diff --git a/cmd/operator-opamp-bridge/agent/agent_test.go b/cmd/operator-opamp-bridge/agent/agent_test.go new file mode 100644 index 0000000000..b6086c79bf --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/agent_test.go @@ -0,0 +1,768 @@ +// 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 agent + +import ( + "context" + "crypto/rand" + "fmt" + "os" + "sort" + "testing" + "time" + + "github.com/go-logr/logr" + "github.com/oklog/ulid/v2" + "github.com/open-telemetry/opamp-go/client" + "github.com/open-telemetry/opamp-go/client/types" + "github.com/open-telemetry/opamp-go/protobufs" + "github.com/spf13/pflag" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + testingclock "k8s.io/utils/clock/testing" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge/config" + "github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge/operator" +) + +const ( + collectorBasicFile = "testdata/basic.yaml" + collectorUpdatedFile = "testdata/updated.yaml" + collectorInvalidFile = "testdata/invalid.yaml" + + testNamespace = "testnamespace" + testCollectorName = "collector" + otherCollectorName = "other" + emptyConfigHash = "" + testCollectorKey = testNamespace + "/" + testCollectorName + otherCollectorKey = testNamespace + "/" + otherCollectorName + + agentTestFileName = "testdata/agent.yaml" + agentTestFileHttpName = "testdata/agenthttpbasic.yaml" + agentTestFileBasicComponentsAllowedName = "testdata/agentbasiccomponentsallowed.yaml" + agentTestFileBatchNotAllowedName = "testdata/agentbatchnotallowed.yaml" + agentTestFileNoProcessorsAllowedName = "testdata/agentnoprocessorsallowed.yaml" + + // collectorStartTime is set to the result of a zero'd out creation timestamp + // read more here https://github.com/open-telemetry/opentelemetry-go/issues/4268 + // we could attempt to hack the creation timestamp, but this is a constant and far easier. + collectorStartTime = uint64(11651379494838206464) +) + +var ( + l = logr.Discard() + _ client.OpAMPClient = &mockOpampClient{} + + basicYamlConfigHash = getConfigHash(testCollectorKey, collectorBasicFile) + invalidYamlConfigHash = getConfigHash(testCollectorKey, collectorInvalidFile) + updatedYamlConfigHash = getConfigHash(testCollectorKey, collectorUpdatedFile) + otherUpdatedYamlConfigHash = getConfigHash(otherCollectorKey, collectorUpdatedFile) +) + +func getConfigHash(key, file string) string { + fi, err := os.Stat(file) + if err != nil { + return "" + } + // get the size + size := fi.Size() + return fmt.Sprintf("%s%d", key, size) +} + +type mockOpampClient struct { + lastStatus *protobufs.RemoteConfigStatus + lastEffectiveConfig *protobufs.EffectiveConfig + settings types.StartSettings +} + +func (m *mockOpampClient) Start(_ context.Context, settings types.StartSettings) error { + m.settings = settings + return nil +} + +func (m *mockOpampClient) Stop(_ context.Context) error { + return nil +} + +func (m *mockOpampClient) SetAgentDescription(_ *protobufs.AgentDescription) error { + return nil +} + +func (m *mockOpampClient) AgentDescription() *protobufs.AgentDescription { + return nil +} + +func (m *mockOpampClient) SetHealth(_ *protobufs.ComponentHealth) error { + return nil +} + +func (m *mockOpampClient) UpdateEffectiveConfig(ctx context.Context) error { + effectiveConfig, err := m.settings.Callbacks.GetEffectiveConfig(ctx) + if err != nil { + return err + } + m.lastEffectiveConfig = effectiveConfig + return nil +} + +func (m *mockOpampClient) SetRemoteConfigStatus(status *protobufs.RemoteConfigStatus) error { + m.lastStatus = status + return nil +} + +func (m *mockOpampClient) SetPackageStatuses(_ *protobufs.PackageStatuses) error { + return nil +} + +func getFakeApplier(t *testing.T, conf *config.Config) *operator.Client { + schemeBuilder := runtime.NewSchemeBuilder(func(s *runtime.Scheme) error { + s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.OpenTelemetryCollector{}, &v1alpha1.OpenTelemetryCollectorList{}) + metav1.AddToGroupVersion(s, v1alpha1.GroupVersion) + return nil + }) + scheme := runtime.NewScheme() + err := schemeBuilder.AddToScheme(scheme) + require.NoError(t, err, "Should be able to add custom types") + c := fake.NewClientBuilder().WithScheme(scheme) + return operator.NewClient("test-bridge", l, c.Build(), conf.GetComponentsAllowed()) +} + +func TestAgent_getHealth(t *testing.T) { + fakeClock := testingclock.NewFakeClock(time.Now()) + type fields struct { + configFile string + } + type args struct { + ctx context.Context + // List of mappings from namespace/name to a config file, tests are run in order of list + configs []map[string]string + } + tests := []struct { + name string + args args + fields fields + // want is evaluated with the corresponding configs' index. + want []*protobufs.ComponentHealth + }{ + { + name: "no data", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configs: nil, + }, + want: []*protobufs.ComponentHealth{ + { + Healthy: true, + StartTimeUnixNano: uint64(fakeClock.Now().UnixNano()), + LastError: "", + Status: "", + StatusTimeUnixNano: uint64(fakeClock.Now().UnixNano()), + ComponentHealthMap: map[string]*protobufs.ComponentHealth{}, + }, + }, + }, + { + name: "base case", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configs: []map[string]string{ + { + testCollectorKey: collectorBasicFile, + }, + }, + }, + want: []*protobufs.ComponentHealth{ + { + Healthy: true, + StartTimeUnixNano: uint64(fakeClock.Now().UnixNano()), + StatusTimeUnixNano: uint64(fakeClock.Now().UnixNano()), + ComponentHealthMap: map[string]*protobufs.ComponentHealth{ + "testnamespace/collector": { + Healthy: false, // we're working with mocks so the status will never be reconciled. + StartTimeUnixNano: collectorStartTime, + LastError: "", + Status: "", + StatusTimeUnixNano: uint64(fakeClock.Now().UnixNano()), + }, + }, + }, + }, + }, + { + name: "two collectors", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configs: []map[string]string{ + { + testCollectorKey: collectorBasicFile, + otherCollectorKey: collectorUpdatedFile, + }, + }, + }, + want: []*protobufs.ComponentHealth{ + { + Healthy: true, + StartTimeUnixNano: uint64(fakeClock.Now().UnixNano()), + StatusTimeUnixNano: uint64(fakeClock.Now().UnixNano()), + ComponentHealthMap: map[string]*protobufs.ComponentHealth{ + "testnamespace/collector": { + Healthy: false, // we're working with mocks so the status will never be reconciled. + StartTimeUnixNano: collectorStartTime, + LastError: "", + Status: "", + StatusTimeUnixNano: uint64(fakeClock.Now().UnixNano()), + }, + "testnamespace/other": { + Healthy: false, // we're working with mocks so the status will never be reconciled. + StartTimeUnixNano: collectorStartTime, + LastError: "", + Status: "", + StatusTimeUnixNano: uint64(fakeClock.Now().UnixNano()), + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockClient := &mockOpampClient{} + conf := config.NewConfig(logr.Discard()) + loadErr := config.LoadFromFile(conf, tt.fields.configFile) + require.NoError(t, loadErr, "should be able to load config") + applier := getFakeApplier(t, conf) + agent := NewAgent(l, applier, conf, mockClient) + agent.clock = fakeClock + err := agent.Start() + defer agent.Shutdown() + require.NoError(t, err, "should be able to start agent") + if len(tt.args.configs) > 0 { + require.True(t, len(tt.args.configs) == len(tt.want), "must have an equal amount of configs and checks.") + } else { + require.Len(t, tt.want, 1, "must have exactly one want if no config is supplied.") + require.Equal(t, tt.want[0], agent.getHealth()) + } + for i, configMap := range tt.args.configs { + data, err := getMessageDataFromConfigFile(configMap) + require.NoError(t, err, "should be able to load data") + agent.onMessage(tt.args.ctx, data) + effectiveConfig, err := agent.getEffectiveConfig(tt.args.ctx) + require.NoError(t, err, "should be able to get effective config") + // We should only expect this to happen if we supply configuration + assert.Equal(t, effectiveConfig, mockClient.lastEffectiveConfig, "client's config should be updated") + assert.NotNilf(t, effectiveConfig.ConfigMap.GetConfigMap(), "configmap should have data") + assert.Equal(t, tt.want[i], agent.getHealth()) + } + }) + } +} + +func TestAgent_onMessage(t *testing.T) { + type fields struct { + configFile string + } + type args struct { + ctx context.Context + // Mapping from namespace/name to a config in testdata + configFile map[string]string + // Mapping from namespace/name to a config in testdata (for testing updates) + nextConfigFile map[string]string + } + type want struct { + // Mapping from namespace/name to a list of expected contents + contents map[string][]string + // Mapping from namespace/name to a list of updated expected contents + nextContents map[string][]string + // The status after the initial config loading + status *protobufs.RemoteConfigStatus + // The status after the updated config loading + nextStatus *protobufs.RemoteConfigStatus + } + tests := []struct { + name string + fields fields + args args + want want + }{ + { + name: "no data", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configFile: nil, + }, + want: want{ + contents: map[string][]string{}, + status: nil, + }, + }, + { + name: "base case", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorBasicFile, + }, + }, + want: want{ + contents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "receivers: [otlp]", + "status:", + }, + }, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + }, + }, + { + name: "base case http", + fields: fields{ + configFile: agentTestFileHttpName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorBasicFile, + }, + }, + want: want{ + contents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "receivers: [otlp]", + "status:", + }, + }, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + }, + }, + { + name: "failure", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorInvalidFile, + }, + }, + want: want{ + contents: nil, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(invalidYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_FAILED, + ErrorMessage: "error converting YAML to JSON: yaml: line 23: could not find expected ':'", + }, + }, + }, + { + name: "all components are allowed", + fields: fields{ + configFile: agentTestFileBasicComponentsAllowedName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorBasicFile, + }, + }, + want: want{ + contents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "receivers: [otlp]", + "status:", + }, + }, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + }, + }, + { + name: "batch not allowed", + fields: fields{ + configFile: agentTestFileBatchNotAllowedName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorBasicFile, + }, + }, + want: want{ + contents: nil, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_FAILED, + ErrorMessage: "Items in config are not allowed: [processors.batch]", + }, + }, + }, + { + name: "processors not allowed", + fields: fields{ + configFile: agentTestFileNoProcessorsAllowedName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorBasicFile, + }, + }, + want: want{ + contents: nil, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_FAILED, + ErrorMessage: "Items in config are not allowed: [processors]", + }, + }, + }, + { + name: "can update config and replicas", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorBasicFile, + }, + nextConfigFile: map[string]string{ + testCollectorKey: collectorUpdatedFile, + }, + }, + want: want{ + contents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "processors: []", + "replicas: 1", + "status:", + }, + }, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + nextContents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "processors: [memory_limiter, batch]", + "replicas: 3", + "status:", + }, + }, + nextStatus: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(updatedYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + }, + }, + { + name: "cannot update with bad config", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorBasicFile, + }, + nextConfigFile: map[string]string{ + testCollectorKey: collectorInvalidFile, + }, + }, + want: want{ + contents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "processors: []", + "replicas: 1", + "status:", + }, + }, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + nextContents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "processors: []", + "replicas: 1", + "status:", + }, + }, + nextStatus: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(invalidYamlConfigHash), // The new hash should be of the bad config + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_FAILED, + ErrorMessage: "error converting YAML to JSON: yaml: line 23: could not find expected ':'", + }, + }, + }, + { + name: "update with new collector", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorBasicFile, + }, + nextConfigFile: map[string]string{ + testCollectorKey: collectorBasicFile, + otherCollectorKey: collectorUpdatedFile, + }, + }, + want: want{ + contents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "processors: []", + "status:", + }, + }, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + nextContents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "processors: []", + "status:", + }, + otherCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + otherCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "processors: [memory_limiter, batch]", + "status:", + }, + }, + nextStatus: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash + otherUpdatedYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + }, + }, + { + name: "can delete existing collector", + fields: fields{ + configFile: agentTestFileName, + }, + args: args{ + ctx: context.Background(), + configFile: map[string]string{ + testCollectorKey: collectorBasicFile, + }, + nextConfigFile: map[string]string{}, + }, + want: want{ + contents: map[string][]string{ + testCollectorKey: { + "kind: OpenTelemetryCollector", + "name: " + testCollectorName, + "namespace: " + testNamespace, + "send_batch_size: 10000", + "processors: []", + "status:", + }, + }, + status: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(basicYamlConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + nextContents: map[string][]string{}, + nextStatus: &protobufs.RemoteConfigStatus{ + LastRemoteConfigHash: []byte(emptyConfigHash), + Status: protobufs.RemoteConfigStatuses_RemoteConfigStatuses_APPLIED, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mockClient := &mockOpampClient{} + conf := config.NewConfig(logr.Discard()) + loadErr := config.LoadFromFile(conf, tt.fields.configFile) + require.NoError(t, loadErr, "should be able to load config") + applier := getFakeApplier(t, conf) + agent := NewAgent(l, applier, conf, mockClient) + err := agent.Start() + defer agent.Shutdown() + require.NoError(t, err, "should be able to start agent") + data, err := getMessageDataFromConfigFile(tt.args.configFile) + require.NoError(t, err, "should be able to load data") + agent.onMessage(tt.args.ctx, data) + effectiveConfig, err := agent.getEffectiveConfig(tt.args.ctx) + require.NoError(t, err, "should be able to get effective config") + if tt.args.configFile != nil { + // We should only expect this to happen if we supply configuration + assert.Equal(t, effectiveConfig, mockClient.lastEffectiveConfig, "client's config should be updated") + } + assert.NotNilf(t, effectiveConfig.ConfigMap.GetConfigMap(), "configmap should have data") + for colNameNamespace, expectedContents := range tt.want.contents { + assert.Contains(t, effectiveConfig.ConfigMap.GetConfigMap(), colNameNamespace) + for _, content := range expectedContents { + asString := string(effectiveConfig.ConfigMap.GetConfigMap()[colNameNamespace].GetBody()) + assert.Contains(t, asString, content) + } + } + assert.Equal(t, tt.want.status, mockClient.lastStatus) + if tt.args.nextConfigFile == nil { + // Nothing left to do! + return + } + nextData, err := getMessageDataFromConfigFile(tt.args.nextConfigFile) + require.NoError(t, err, "should be able to load updated data") + agent.onMessage(tt.args.ctx, nextData) + nextEffectiveConfig, err := agent.getEffectiveConfig(tt.args.ctx) + require.NoError(t, err, "should be able to get updated effective config") + assert.Equal(t, nextEffectiveConfig, mockClient.lastEffectiveConfig, "client's config should be updated") + assert.NotNilf(t, nextEffectiveConfig.ConfigMap.GetConfigMap(), "configmap should have updated data") + for colNameNamespace, expectedContents := range tt.want.nextContents { + assert.Contains(t, nextEffectiveConfig.ConfigMap.GetConfigMap(), colNameNamespace) + for _, content := range expectedContents { + asString := string(nextEffectiveConfig.ConfigMap.GetConfigMap()[colNameNamespace].GetBody()) + assert.Contains(t, asString, content) + } + } + assert.Equal(t, tt.want.nextStatus, mockClient.lastStatus) + }) + } +} + +func Test_CanUpdateIdentity(t *testing.T) { + mockClient := &mockOpampClient{} + + fs := config.GetFlagSet(pflag.ContinueOnError) + configFlag := []string{"--config-file", agentTestFileName} + err := fs.Parse(configFlag) + assert.NoError(t, err) + conf := config.NewConfig(logr.Discard()) + loadErr := config.LoadFromFile(conf, agentTestFileName) + require.NoError(t, loadErr, "should be able to load config") + applier := getFakeApplier(t, conf) + agent := NewAgent(l, applier, conf, mockClient) + err = agent.Start() + defer agent.Shutdown() + require.NoError(t, err, "should be able to start agent") + previousInstanceId := agent.instanceId.String() + entropy := ulid.Monotonic(rand.Reader, 0) + newId := ulid.MustNew(ulid.MaxTime(), entropy) + agent.onMessage(context.Background(), &types.MessageData{ + AgentIdentification: &protobufs.AgentIdentification{ + NewInstanceUid: newId.String(), + }, + }) + assert.NotEqual(t, previousInstanceId, newId.String()) + assert.Equal(t, agent.instanceId, newId) +} + +func getMessageDataFromConfigFile(filemap map[string]string) (*types.MessageData, error) { + toReturn := &types.MessageData{} + if filemap == nil { + return toReturn, nil + } + configs := map[string]*protobufs.AgentConfigFile{} + hash := "" + fileNames := make([]string, len(filemap)) + i := 0 + for k := range filemap { + fileNames[i] = k + i++ + } + // We sort the filenames so we get consistent results for multiple file loads + sort.Strings(fileNames) + + for _, key := range fileNames { + yamlFile, err := os.ReadFile(filemap[key]) + if err != nil { + return toReturn, err + } + configs[key] = &protobufs.AgentConfigFile{ + Body: yamlFile, + ContentType: "yaml", + } + hash = hash + key + fmt.Sprint(len(yamlFile)) + } + toReturn.RemoteConfig = &protobufs.AgentRemoteConfig{ + Config: &protobufs.AgentConfigMap{ + ConfigMap: configs, + }, + // just use the file name for the hash + ConfigHash: []byte(hash), + } + return toReturn, nil +} diff --git a/cmd/operator-opamp-bridge/agent/collector_key.go b/cmd/operator-opamp-bridge/agent/collector_key.go new file mode 100644 index 0000000000..d2e6c8d4f8 --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/collector_key.go @@ -0,0 +1,43 @@ +// 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 agent + +import ( + "errors" + "fmt" + "strings" +) + +type collectorKey struct { + name string + namespace string +} + +func newCollectorKey(namespace string, name string) collectorKey { + return collectorKey{name: name, namespace: namespace} +} + +func collectorKeyFromKey(key string) (collectorKey, error) { + s := strings.Split(key, "/") + // We expect map keys to be of the form name/namespace + if len(s) != 2 { + return collectorKey{}, errors.New("invalid key") + } + return newCollectorKey(s[0], s[1]), nil +} + +func (k collectorKey) String() string { + return fmt.Sprintf("%s/%s", k.namespace, k.name) +} diff --git a/cmd/operator-opamp-bridge/agent/collector_key_test.go b/cmd/operator-opamp-bridge/agent/collector_key_test.go new file mode 100644 index 0000000000..7a27180f69 --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/collector_key_test.go @@ -0,0 +1,98 @@ +// 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 agent + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_collectorKeyFromKey(t *testing.T) { + type args struct { + key string + } + tests := []struct { + name string + args args + want collectorKey + wantErr assert.ErrorAssertionFunc + }{ + { + name: "base case", + args: args{ + key: "namespace/good", + }, + want: collectorKey{ + name: "good", + namespace: "namespace", + }, + wantErr: assert.NoError, + }, + { + name: "unable to get key", + args: args{ + key: "badnamespace", + }, + want: collectorKey{}, + wantErr: assert.Error, + }, + { + name: "too many slashes", + args: args{ + key: "too/many/slashes", + }, + want: collectorKey{}, + wantErr: assert.Error, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := collectorKeyFromKey(tt.args.key) + if !tt.wantErr(t, err, fmt.Sprintf("collectorKeyFromKey(%v)", tt.args.key)) { + return + } + assert.Equalf(t, tt.want, got, "collectorKeyFromKey(%v)", tt.args.key) + }) + } +} + +func Test_collectorKey_String(t *testing.T) { + type fields struct { + name string + namespace string + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "can make a key", + fields: fields{ + name: "good", + namespace: "namespace", + }, + want: "namespace/good", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k := newCollectorKey(tt.fields.namespace, tt.fields.name) + assert.Equalf(t, tt.want, k.String(), "String()") + }) + } +} diff --git a/cmd/operator-opamp-bridge/agent/testdata/agent.yaml b/cmd/operator-opamp-bridge/agent/testdata/agent.yaml new file mode 100644 index 0000000000..5a70b06a87 --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/testdata/agent.yaml @@ -0,0 +1,14 @@ +endpoint: ws://127.0.0.1:4320/v1/opamp +capabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true diff --git a/cmd/operator-opamp-bridge/agent/testdata/agentbasiccomponentsallowed.yaml b/cmd/operator-opamp-bridge/agent/testdata/agentbasiccomponentsallowed.yaml new file mode 100644 index 0000000000..c994ad122b --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/testdata/agentbasiccomponentsallowed.yaml @@ -0,0 +1,22 @@ +endpoint: ws://127.0.0.1:4320/v1/opamp +capabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true +componentsAllowed: + receivers: + - otlp + processors: + - memory_limiter + - batch + exporters: + - debug diff --git a/cmd/operator-opamp-bridge/agent/testdata/agentbatchnotallowed.yaml b/cmd/operator-opamp-bridge/agent/testdata/agentbatchnotallowed.yaml new file mode 100644 index 0000000000..1009f079a2 --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/testdata/agentbatchnotallowed.yaml @@ -0,0 +1,21 @@ +endpoint: ws://127.0.0.1:4320/v1/opamp +capabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true +componentsAllowed: + receivers: + - otlp + processors: + - memory_limiter + exporters: + - debug diff --git a/cmd/operator-opamp-bridge/agent/testdata/agenthttpbasic.yaml b/cmd/operator-opamp-bridge/agent/testdata/agenthttpbasic.yaml new file mode 100644 index 0000000000..2dc1532d4e --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/testdata/agenthttpbasic.yaml @@ -0,0 +1,14 @@ +endpoint: http://127.0.0.1:4320/v1/opamp +capabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true diff --git a/cmd/operator-opamp-bridge/agent/testdata/agentnoprocessorsallowed.yaml b/cmd/operator-opamp-bridge/agent/testdata/agentnoprocessorsallowed.yaml new file mode 100644 index 0000000000..fe53d0a00e --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/testdata/agentnoprocessorsallowed.yaml @@ -0,0 +1,19 @@ +endpoint: ws://127.0.0.1:4320/v1/opamp +capabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true +componentsAllowed: + receivers: + - otlp + exporters: + - debug diff --git a/cmd/operator-opamp-bridge/agent/testdata/basic.yaml b/cmd/operator-opamp-bridge/agent/testdata/basic.yaml new file mode 100644 index 0000000000..410a328edd --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/testdata/basic.yaml @@ -0,0 +1,30 @@ +kind: OpenTelemetryCollector +metadata: + name: simplest + labels: + "opentelemetry.io/opamp-managed": "true" +spec: + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + memory_limiter: + check_interval: 1s + limit_percentage: 75 + spike_limit_percentage: 15 + batch: + send_batch_size: 10000 + timeout: 10s + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/cmd/operator-opamp-bridge/agent/testdata/invalid.yaml b/cmd/operator-opamp-bridge/agent/testdata/invalid.yaml new file mode 100644 index 0000000000..2a060581f4 --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/testdata/invalid.yaml @@ -0,0 +1,30 @@ +kind: OpenTelemetryCollector +metadata: + name: simplest + labels: + "opentelemetry.io/opamp-managed": "true" +spec: + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + memory_limiter: + check_interval: 1s + limit_percentage: 75 + spike_limit_percentage: 15 + batch: + send_batch_size: 10000 + timeout: 10s + + GARBAGE + exporters: + debug: + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/cmd/operator-opamp-bridge/agent/testdata/updated.yaml b/cmd/operator-opamp-bridge/agent/testdata/updated.yaml new file mode 100644 index 0000000000..53ce4af492 --- /dev/null +++ b/cmd/operator-opamp-bridge/agent/testdata/updated.yaml @@ -0,0 +1,31 @@ +kind: OpenTelemetryCollector +metadata: + name: simplest + labels: + "opentelemetry.io/opamp-managed": "test-bridge" +spec: + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + memory_limiter: + check_interval: 1s + limit_percentage: 75 + spike_limit_percentage: 15 + batch: + send_batch_size: 10000 + timeout: 10s + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch] + exporters: [debug] + replicas: 3 diff --git a/cmd/operator-opamp-bridge/config/config.go b/cmd/operator-opamp-bridge/config/config.go new file mode 100644 index 0000000000..38f06d6ff9 --- /dev/null +++ b/cmd/operator-opamp-bridge/config/config.go @@ -0,0 +1,276 @@ +// 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 config + +import ( + "crypto/rand" + "errors" + "fmt" + "io/fs" + "net/url" + "os" + "runtime" + "time" + + "github.com/go-logr/logr" + "github.com/oklog/ulid/v2" + opampclient "github.com/open-telemetry/opamp-go/client" + "github.com/open-telemetry/opamp-go/protobufs" + "github.com/spf13/pflag" + "gopkg.in/yaml.v2" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge/logger" +) + +const ( + agentType = "io.opentelemetry.operator-opamp-bridge" +) + +var ( + agentVersion = os.Getenv("OPAMP_VERSION") + hostname, _ = os.Hostname() + schemeBuilder = k8sruntime.NewSchemeBuilder(registerKnownTypes) +) + +func registerKnownTypes(s *k8sruntime.Scheme) error { + s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.OpenTelemetryCollector{}, &v1alpha1.OpenTelemetryCollectorList{}) + metav1.AddToGroupVersion(s, v1alpha1.GroupVersion) + return nil +} + +func GetLogger() logr.Logger { + return zap.New(zap.UseFlagOptions(&zapCmdLineOpts)) +} + +type Capability string + +const ( + Unspecified Capability = "Unspecified" + ReportsStatus Capability = "ReportsStatus" + AcceptsRemoteConfig Capability = "AcceptsRemoteConfig" + ReportsEffectiveConfig Capability = "ReportsEffectiveConfig" + AcceptsPackages Capability = "AcceptsPackages" + ReportsPackageStatuses Capability = "ReportsPackageStatuses" + ReportsOwnTraces Capability = "ReportsOwnTraces" + ReportsOwnMetrics Capability = "ReportsOwnMetrics" + ReportsOwnLogs Capability = "ReportsOwnLogs" + AcceptsOpAMPConnectionSettings Capability = "AcceptsOpAMPConnectionSettings" + AcceptsOtherConnectionSettings Capability = "AcceptsOtherConnectionSettings" + AcceptsRestartCommand Capability = "AcceptsRestartCommand" + ReportsHealth Capability = "ReportsHealth" + ReportsRemoteConfig Capability = "ReportsRemoteConfig" +) + +type Config struct { + // KubeConfigFilePath is empty if InClusterConfig() should be used, otherwise it's a path to where a valid + // kubernetes configuration file. + KubeConfigFilePath string `yaml:"kubeConfigFilePath,omitempty"` + ListenAddr string `yaml:"listenAddr,omitempty"` + ClusterConfig *rest.Config `yaml:"-"` + RootLogger logr.Logger `yaml:"-"` + + // ComponentsAllowed is a list of allowed OpenTelemetry components for each pipeline type (receiver, processor, etc.) + ComponentsAllowed map[string][]string `yaml:"componentsAllowed,omitempty"` + Endpoint string `yaml:"endpoint"` + Headers Headers `yaml:"headers,omitempty"` + Capabilities map[Capability]bool `yaml:"capabilities"` + HeartbeatInterval time.Duration `yaml:"heartbeatInterval,omitempty"` + Name string `yaml:"name,omitempty"` +} + +func NewConfig(logger logr.Logger) *Config { + return &Config{ + RootLogger: logger, + } +} + +func (c *Config) CreateClient() opampclient.OpAMPClient { + opampLogger := logger.NewLogger(c.RootLogger.WithName("client")) + agentScheme := c.GetAgentScheme() + if agentScheme == "http" || agentScheme == "https" { + return opampclient.NewHTTP(opampLogger) + } + return opampclient.NewWebSocket(opampLogger) +} + +func (c *Config) GetComponentsAllowed() map[string]map[string]bool { + m := make(map[string]map[string]bool) + for component, componentSet := range c.ComponentsAllowed { + if _, ok := m[component]; !ok { + m[component] = make(map[string]bool) + } + for _, s := range componentSet { + m[component][s] = true + } + } + return m +} + +func (c *Config) GetCapabilities() protobufs.AgentCapabilities { + var capabilities int32 + for capability, enabled := range c.Capabilities { + if !enabled { + continue + } + // This is a helper so that we don't force consumers to prefix every agent capability + formatted := fmt.Sprintf("AgentCapabilities_%s", capability) + if v, ok := protobufs.AgentCapabilities_value[formatted]; ok { + capabilities = v | capabilities + } + } + return protobufs.AgentCapabilities(capabilities) +} + +func (c *Config) GetAgentScheme() string { + uri, err := url.ParseRequestURI(c.Endpoint) + if err != nil { + return "" + } + return uri.Scheme +} + +func (c *Config) GetAgentType() string { + return agentType +} + +func (c *Config) GetAgentVersion() string { + return agentVersion +} + +func (c *Config) GetDescription() *protobufs.AgentDescription { + return &protobufs.AgentDescription{ + IdentifyingAttributes: []*protobufs.KeyValue{ + keyValuePair("service.name", c.GetAgentType()), + keyValuePair("service.version", c.GetAgentVersion()), + }, + NonIdentifyingAttributes: []*protobufs.KeyValue{ + keyValuePair("os.family", runtime.GOOS), + keyValuePair("host.name", hostname), + }, + } +} + +func keyValuePair(key string, value string) *protobufs.KeyValue { + return &protobufs.KeyValue{ + Key: key, + Value: &protobufs.AnyValue{ + Value: &protobufs.AnyValue_StringValue{ + StringValue: value, + }, + }, + } +} + +func (c *Config) GetNewInstanceId() ulid.ULID { + entropy := ulid.Monotonic(rand.Reader, 0) + return ulid.MustNew(ulid.Timestamp(time.Now()), entropy) +} + +func (c *Config) RemoteConfigEnabled() bool { + capabilities := c.GetCapabilities() + return capabilities&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig != 0 +} + +func (c *Config) GetKubernetesClient() (client.Client, error) { + err := schemeBuilder.AddToScheme(scheme.Scheme) + if err != nil { + return nil, err + } + return client.New(c.ClusterConfig, client.Options{ + Scheme: scheme.Scheme, + }) +} + +func Load(logger logr.Logger, flagSet *pflag.FlagSet) (*Config, error) { + cfg := NewConfig(logger) + + // load the config from the config file + configFilePath, err := getConfigFilePath(flagSet) + if err != nil { + return nil, err + } + err = LoadFromFile(cfg, configFilePath) + if err != nil { + return nil, err + } + + err = LoadFromCLI(cfg, flagSet) + if err != nil { + return nil, err + } + + return cfg, nil +} + +func LoadFromCLI(target *Config, flagSet *pflag.FlagSet) error { + var err error + klog.SetLogger(target.RootLogger) + ctrl.SetLogger(target.RootLogger) + + target.KubeConfigFilePath, err = getKubeConfigFilePath(flagSet) + if err != nil { + return err + } + clusterConfig, err := clientcmd.BuildConfigFromFlags("", target.KubeConfigFilePath) + if err != nil { + pathError := &fs.PathError{} + if ok := errors.As(err, &pathError); !ok { + return err + } + clusterConfig, err = rest.InClusterConfig() + if err != nil { + return err + } + } + target.ClusterConfig = clusterConfig + + target.ListenAddr, err = getListenAddr(flagSet) + if err != nil { + return err + } + + target.HeartbeatInterval, err = getHeartbeatInterval(flagSet) + if err != nil { + return err + } + + target.Name, err = getName(flagSet) + if err != nil { + return err + } + + return nil +} + +func LoadFromFile(cfg *Config, configFile string) error { + yamlFile, err := os.ReadFile(configFile) + if err != nil { + return err + } + if err = yaml.UnmarshalStrict(yamlFile, cfg); err != nil { + return fmt.Errorf("error unmarshaling YAML: %w", err) + } + return nil +} diff --git a/cmd/operator-opamp-bridge/config/config_test.go b/cmd/operator-opamp-bridge/config/config_test.go new file mode 100644 index 0000000000..e9caeb2b0c --- /dev/null +++ b/cmd/operator-opamp-bridge/config/config_test.go @@ -0,0 +1,182 @@ +// 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 config + +import ( + "fmt" + "testing" + "time" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" +) + +func TestLoad(t *testing.T) { + type args struct { + file string + } + tests := []struct { + name string + args args + want *Config + wantErr assert.ErrorAssertionFunc + }{ + { + name: "base case", + args: args{ + file: "./testdata/agent.yaml", + }, + want: &Config{ + RootLogger: logr.Discard(), + Endpoint: "ws://127.0.0.1:4320/v1/opamp", + Capabilities: map[Capability]bool{ + AcceptsRemoteConfig: true, + ReportsEffectiveConfig: true, + ReportsOwnTraces: true, + ReportsOwnMetrics: true, + ReportsOwnLogs: true, + AcceptsOpAMPConnectionSettings: true, + AcceptsOtherConnectionSettings: true, + AcceptsRestartCommand: true, + ReportsHealth: true, + ReportsRemoteConfig: true, + AcceptsPackages: false, + ReportsPackageStatuses: false, + }, + }, + wantErr: assert.NoError, + }, + { + name: "http base case", + args: args{ + file: "./testdata/agenthttpbasic.yaml", + }, + want: &Config{ + RootLogger: logr.Discard(), + Endpoint: "http://127.0.0.1:4320/v1/opamp", + HeartbeatInterval: 45 * time.Second, + Name: "http-test-bridge", + Capabilities: map[Capability]bool{ + AcceptsRemoteConfig: true, + ReportsEffectiveConfig: true, + ReportsOwnTraces: true, + ReportsOwnMetrics: true, + ReportsOwnLogs: true, + AcceptsOpAMPConnectionSettings: true, + AcceptsOtherConnectionSettings: true, + AcceptsRestartCommand: true, + ReportsHealth: true, + ReportsRemoteConfig: true, + AcceptsPackages: false, + ReportsPackageStatuses: false, + }, + }, + wantErr: assert.NoError, + }, + { + name: "basic components allowed", + args: args{ + file: "./testdata/agentbasiccomponentsallowed.yaml", + }, + want: &Config{ + RootLogger: logr.Discard(), + Endpoint: "ws://127.0.0.1:4320/v1/opamp", + Capabilities: map[Capability]bool{ + AcceptsRemoteConfig: true, + ReportsEffectiveConfig: true, + ReportsOwnTraces: true, + ReportsOwnMetrics: true, + ReportsOwnLogs: true, + AcceptsOpAMPConnectionSettings: true, + AcceptsOtherConnectionSettings: true, + AcceptsRestartCommand: true, + ReportsHealth: true, + ReportsRemoteConfig: true, + AcceptsPackages: false, + ReportsPackageStatuses: false, + }, + ComponentsAllowed: map[string][]string{ + "receivers": { + "otlp", + }, + "processors": { + "memory_limiter", + "batch", + }, + "exporters": { + "logging", + }, + }, + }, + wantErr: assert.NoError, + }, + { + name: "bad configuration", + args: args{ + file: "./testdata/agentbadconf.yaml", + }, + want: &Config{ + // We do unmarshal partially + Endpoint: "http://127.0.0.1:4320/v1/opamp", + }, + wantErr: func(t assert.TestingT, err error, i ...interface{}) bool { + return assert.ErrorContains(t, err, "error unmarshaling YAML", i...) + }, + }, + { + name: "base case with headers", + args: args{ + file: "./testdata/agentwithheaders.yaml", + }, + want: &Config{ + RootLogger: logr.Discard(), + Endpoint: "ws://127.0.0.1:4320/v1/opamp", + Headers: map[string]string{ + "authentication": "access-12345-token", + "my-header-key": "my-header-value", + }, + Capabilities: map[Capability]bool{ + AcceptsRemoteConfig: true, + ReportsEffectiveConfig: true, + ReportsOwnTraces: true, + ReportsOwnMetrics: true, + ReportsOwnLogs: true, + AcceptsOpAMPConnectionSettings: true, + AcceptsOtherConnectionSettings: true, + AcceptsRestartCommand: true, + ReportsHealth: true, + ReportsRemoteConfig: true, + AcceptsPackages: false, + ReportsPackageStatuses: false, + }, + }, + wantErr: assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := NewConfig(logr.Discard()) + err := LoadFromFile(got, tt.args.file) + if !tt.wantErr(t, err, fmt.Sprintf("Load(%v)", tt.args.file)) { + return + } + // there are some fields we don't care about, so we ignore them. + got.ClusterConfig = tt.want.ClusterConfig + got.RootLogger = tt.want.RootLogger + assert.Equalf(t, tt.want, got, "Load(%v)", tt.args.file) + + }) + } +} diff --git a/cmd/operator-opamp-bridge/config/flags.go b/cmd/operator-opamp-bridge/config/flags.go new file mode 100644 index 0000000000..2fe101e295 --- /dev/null +++ b/cmd/operator-opamp-bridge/config/flags.go @@ -0,0 +1,73 @@ +// 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 config + +import ( + "flag" + "path/filepath" + "time" + + "github.com/spf13/pflag" + "k8s.io/client-go/util/homedir" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +// Flag names. +const ( + opampBridgeName = "opamp-bridge" + defaultConfigFilePath = "/conf/remoteconfiguration.yaml" + configFilePathFlagName = "config-file" + listenAddrFlagName = "listen-addr" + kubeConfigPathFlagName = "kubeconfig-path" + heartbeatIntervalFlagName = "heartbeat-interval" + nameFlagName = "name" + defaultHeartbeatInterval = 30 * time.Second +) + +// We can't bind this flag to our FlagSet, so we need to handle it separately. +var zapCmdLineOpts zap.Options + +func GetFlagSet(errorHandling pflag.ErrorHandling) *pflag.FlagSet { + flagSet := pflag.NewFlagSet(opampBridgeName, errorHandling) + flagSet.String(configFilePathFlagName, defaultConfigFilePath, "The path to the config file.") + flagSet.String(listenAddrFlagName, ":8080", "The address where this service serves.") + flagSet.String(kubeConfigPathFlagName, filepath.Join(homedir.HomeDir(), ".kube", "config"), "absolute path to the KubeconfigPath file.") + flagSet.Duration(heartbeatIntervalFlagName, defaultHeartbeatInterval, "The interval to use for sending a heartbeat. Setting it to 0 disables the heartbeat.") + flagSet.String(nameFlagName, opampBridgeName, "The name of the bridge to use for querying managed collectors.") + zapFlagSet := flag.NewFlagSet("", flag.ErrorHandling(errorHandling)) + zapCmdLineOpts.BindFlags(zapFlagSet) + flagSet.AddGoFlagSet(zapFlagSet) + return flagSet +} + +func getHeartbeatInterval(flagset *pflag.FlagSet) (time.Duration, error) { + return flagset.GetDuration(heartbeatIntervalFlagName) +} + +func getConfigFilePath(flagSet *pflag.FlagSet) (string, error) { + return flagSet.GetString(configFilePathFlagName) +} + +func getName(flagSet *pflag.FlagSet) (string, error) { + return flagSet.GetString(nameFlagName) +} + +func getKubeConfigFilePath(flagSet *pflag.FlagSet) (string, error) { + return flagSet.GetString(kubeConfigPathFlagName) +} + +func getListenAddr(flagSet *pflag.FlagSet) (string, error) { + return flagSet.GetString(listenAddrFlagName) +} diff --git a/cmd/operator-opamp-bridge/config/flags_test.go b/cmd/operator-opamp-bridge/config/flags_test.go new file mode 100644 index 0000000000..b43b99f272 --- /dev/null +++ b/cmd/operator-opamp-bridge/config/flags_test.go @@ -0,0 +1,97 @@ +// 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 config + +import ( + "path/filepath" + "testing" + "time" + + "github.com/spf13/pflag" + "github.com/stretchr/testify/assert" +) + +func TestGetFlagSet(t *testing.T) { + fs := GetFlagSet(pflag.ExitOnError) + + // Check if each flag exists + assert.NotNil(t, fs.Lookup(configFilePathFlagName), "Flag %s not found", configFilePathFlagName) + assert.NotNil(t, fs.Lookup(listenAddrFlagName), "Flag %s not found", listenAddrFlagName) + assert.NotNil(t, fs.Lookup(kubeConfigPathFlagName), "Flag %s not found", kubeConfigPathFlagName) +} + +func TestFlagGetters(t *testing.T) { + tests := []struct { + name string + flagArgs []string + expectedValue interface{} + expectedErr bool + getterFunc func(*pflag.FlagSet) (interface{}, error) + }{ + { + name: "GetConfigFilePath", + flagArgs: []string{"--" + configFilePathFlagName, "/path/to/config"}, + expectedValue: "/path/to/config", + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getConfigFilePath(fs) }, + }, + { + name: "GetKubeConfigFilePath", + flagArgs: []string{"--" + kubeConfigPathFlagName, filepath.Join("~", ".kube", "config")}, + expectedValue: filepath.Join("~", ".kube", "config"), + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getKubeConfigFilePath(fs) }, + }, + { + name: "GetName", + flagArgs: []string{"--" + nameFlagName, "test"}, + expectedValue: "test", + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getName(fs) }, + }, + { + name: "GetListenAddr", + flagArgs: []string{"--" + listenAddrFlagName, ":8081"}, + expectedValue: ":8081", + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getListenAddr(fs) }, + }, + { + name: "GetHeartbeatInterval", + flagArgs: []string{"--" + heartbeatIntervalFlagName, "45s"}, + expectedValue: 45 * time.Second, + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getHeartbeatInterval(fs) }, + }, + { + name: "InvalidFlag", + flagArgs: []string{"--invalid-flag", "value"}, + expectedErr: true, + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getConfigFilePath(fs) }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fs := GetFlagSet(pflag.ContinueOnError) + err := fs.Parse(tt.flagArgs) + + // If an error is expected during parsing, we check it here. + if tt.expectedErr { + assert.Error(t, err) + return + } + + got, err := tt.getterFunc(fs) + assert.NoError(t, err) + assert.Equal(t, tt.expectedValue, got) + }) + } +} diff --git a/cmd/operator-opamp-bridge/config/headers.go b/cmd/operator-opamp-bridge/config/headers.go new file mode 100644 index 0000000000..4ef242e3c5 --- /dev/null +++ b/cmd/operator-opamp-bridge/config/headers.go @@ -0,0 +1,27 @@ +// 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 config + +import "net/http" + +type Headers map[string]string + +func (h Headers) ToHTTPHeader() http.Header { + newMap := make(map[string][]string) + for key, value := range h { + newMap[key] = []string{value} + } + return newMap +} diff --git a/cmd/operator-opamp-bridge/config/testdata/agent.yaml b/cmd/operator-opamp-bridge/config/testdata/agent.yaml new file mode 100644 index 0000000000..5a70b06a87 --- /dev/null +++ b/cmd/operator-opamp-bridge/config/testdata/agent.yaml @@ -0,0 +1,14 @@ +endpoint: ws://127.0.0.1:4320/v1/opamp +capabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true diff --git a/cmd/operator-opamp-bridge/config/testdata/agentbadconf.yaml b/cmd/operator-opamp-bridge/config/testdata/agentbadconf.yaml new file mode 100644 index 0000000000..ba4903daf8 --- /dev/null +++ b/cmd/operator-opamp-bridge/config/testdata/agentbadconf.yaml @@ -0,0 +1,14 @@ +endpoint: http://127.0.0.1:4320/v1/opamp +cawdawapabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true diff --git a/cmd/operator-opamp-bridge/config/testdata/agentbasiccomponentsallowed.yaml b/cmd/operator-opamp-bridge/config/testdata/agentbasiccomponentsallowed.yaml new file mode 100644 index 0000000000..73d5683a3d --- /dev/null +++ b/cmd/operator-opamp-bridge/config/testdata/agentbasiccomponentsallowed.yaml @@ -0,0 +1,22 @@ +endpoint: ws://127.0.0.1:4320/v1/opamp +capabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true +componentsAllowed: + receivers: + - otlp + processors: + - memory_limiter + - batch + exporters: + - logging diff --git a/cmd/operator-opamp-bridge/config/testdata/agenthttpbasic.yaml b/cmd/operator-opamp-bridge/config/testdata/agenthttpbasic.yaml new file mode 100644 index 0000000000..a96abad9dd --- /dev/null +++ b/cmd/operator-opamp-bridge/config/testdata/agenthttpbasic.yaml @@ -0,0 +1,16 @@ +endpoint: http://127.0.0.1:4320/v1/opamp +heartbeatInterval: 45s +name: "http-test-bridge" +capabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true diff --git a/cmd/operator-opamp-bridge/config/testdata/agentwithheaders.yaml b/cmd/operator-opamp-bridge/config/testdata/agentwithheaders.yaml new file mode 100644 index 0000000000..17983e1422 --- /dev/null +++ b/cmd/operator-opamp-bridge/config/testdata/agentwithheaders.yaml @@ -0,0 +1,17 @@ +endpoint: ws://127.0.0.1:4320/v1/opamp +headers: + authentication: "access-12345-token" + my-header-key: "my-header-value" +capabilities: + AcceptsRemoteConfig: true + ReportsEffectiveConfig: true + AcceptsPackages: false + ReportsPackageStatuses: false + ReportsOwnTraces: true + ReportsOwnMetrics: true + ReportsOwnLogs: true + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRestartCommand: true + ReportsHealth: true + ReportsRemoteConfig: true diff --git a/cmd/operator-opamp-bridge/go.mod b/cmd/operator-opamp-bridge/go.mod new file mode 100644 index 0000000000..5eebcb923b --- /dev/null +++ b/cmd/operator-opamp-bridge/go.mod @@ -0,0 +1,95 @@ +module github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge + +go 1.20 + +require ( + github.com/go-logr/logr v1.3.0 + github.com/oklog/ulid/v2 v2.1.0 + github.com/open-telemetry/opamp-go v0.10.0 + github.com/open-telemetry/opentelemetry-operator v0.85.0 + github.com/shirou/gopsutil v3.21.11+incompatible + github.com/spf13/pflag v1.0.5 + github.com/stretchr/testify v1.8.4 + go.opentelemetry.io/otel v1.21.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 + go.opentelemetry.io/otel/metric v1.21.0 + go.opentelemetry.io/otel/sdk v1.21.0 + go.opentelemetry.io/otel/sdk/metric v1.21.0 + go.uber.org/multierr v1.11.0 + gopkg.in/yaml.v2 v2.4.0 + k8s.io/apimachinery v0.28.4 + k8s.io/client-go v0.28.4 + k8s.io/klog/v2 v2.110.1 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b + sigs.k8s.io/controller-runtime v0.16.3 + sigs.k8s.io/yaml v1.4.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.11.0 // indirect + github.com/tklauser/go-sysconf v0.3.11 // indirect + github.com/tklauser/numcpus v0.6.0 // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect + go.opentelemetry.io/collector/featuregate v0.77.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.uber.org/zap v1.25.0 // indirect + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.11.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/time v0.3.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/grpc v1.59.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.28.4 // indirect + k8s.io/apiextensions-apiserver v0.28.3 // indirect + k8s.io/component-base v0.28.3 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect +) diff --git a/cmd/operator-opamp-bridge/go.sum b/cmd/operator-opamp-bridge/go.sum new file mode 100644 index 0000000000..d17e3cc311 --- /dev/null +++ b/cmd/operator-opamp-bridge/go.sum @@ -0,0 +1,275 @@ +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= +github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/open-telemetry/opamp-go v0.10.0 h1:3PdhoKcKY1lPrfdXnsxeLlXluE+Xe1Uc/CpJ4I8uUJ0= +github.com/open-telemetry/opamp-go v0.10.0/go.mod h1:Pfmm5EdWqZCG0dZAJjAinlra3yEpqK5StCblxpbEp6Q= +github.com/open-telemetry/opentelemetry-operator v0.85.0 h1:HwKfaxePbGJCbl3y2rselSnT1QUw1Xd+81DkYTEvws8= +github.com/open-telemetry/opentelemetry-operator v0.85.0/go.mod h1:6od0b2oGG/cqr8+nyOB4wsz1fg7QjnIsZDuynZqzBPE= +github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk= +github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/collector/featuregate v0.77.0 h1:m1/IzaXoQh6SgF6CM80vrBOCf5zSJ2GVISfA27fYzGU= +go.opentelemetry.io/collector/featuregate v0.77.0/go.mod h1:/kVAsGUCyJXIDSgHftCN63QiwAEVHRLX2Kh/S+dqgHY= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= +go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= +golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= +k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= +k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= +k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= +k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= +k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= +k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= +k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= +k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= +k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= +sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/cmd/operator-opamp-bridge/header.txt b/cmd/operator-opamp-bridge/header.txt new file mode 100644 index 0000000000..3881885f31 --- /dev/null +++ b/cmd/operator-opamp-bridge/header.txt @@ -0,0 +1,13 @@ +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. \ No newline at end of file diff --git a/cmd/operator-opamp-bridge/logger/logger.go b/cmd/operator-opamp-bridge/logger/logger.go new file mode 100644 index 0000000000..0beb8cbea6 --- /dev/null +++ b/cmd/operator-opamp-bridge/logger/logger.go @@ -0,0 +1,40 @@ +// 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 logger + +import ( + "fmt" + + "github.com/go-logr/logr" + "github.com/open-telemetry/opamp-go/client/types" +) + +var _ types.Logger = &Logger{} + +type Logger struct { + Logger logr.Logger +} + +func NewLogger(logger logr.Logger) *Logger { + return &Logger{Logger: logger} +} + +func (l *Logger) Debugf(format string, v ...interface{}) { + l.Logger.V(4).Info(fmt.Sprintf(format, v...)) +} + +func (l *Logger) Errorf(format string, v ...interface{}) { + l.Logger.V(0).Error(nil, fmt.Sprintf(format, v...)) +} diff --git a/cmd/operator-opamp-bridge/main.go b/cmd/operator-opamp-bridge/main.go new file mode 100644 index 0000000000..9c312292ed --- /dev/null +++ b/cmd/operator-opamp-bridge/main.go @@ -0,0 +1,63 @@ +// 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 main + +import ( + "os" + "os/signal" + + "github.com/spf13/pflag" + + "github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge/agent" + "github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge/config" + "github.com/open-telemetry/opentelemetry-operator/cmd/operator-opamp-bridge/operator" +) + +func main() { + l := config.GetLogger() + + flagSet := config.GetFlagSet(pflag.ExitOnError) + err := flagSet.Parse(os.Args) + if err != nil { + l.Error(err, "Unable to load flags") + os.Exit(1) + } + cfg, configLoadErr := config.Load(l, flagSet) + if configLoadErr != nil { + l.Error(configLoadErr, "Unable to load configuration") + os.Exit(1) + } + l.Info("Starting the Remote Configuration service") + + kubeClient, kubeErr := cfg.GetKubernetesClient() + if kubeErr != nil { + l.Error(kubeErr, "Couldn't create kubernetes client") + os.Exit(1) + } + operatorClient := operator.NewClient(cfg.Name, l.WithName("operator-client"), kubeClient, cfg.GetComponentsAllowed()) + + opampClient := cfg.CreateClient() + opampAgent := agent.NewAgent(l.WithName("agent"), operatorClient, cfg, opampClient) + + if err := opampAgent.Start(); err != nil { + l.Error(err, "Cannot start OpAMP client") + os.Exit(1) + } + + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt) + <-interrupt + opampAgent.Shutdown() +} diff --git a/cmd/operator-opamp-bridge/metrics/reporter.go b/cmd/operator-opamp-bridge/metrics/reporter.go new file mode 100644 index 0000000000..27dc3f646f --- /dev/null +++ b/cmd/operator-opamp-bridge/metrics/reporter.go @@ -0,0 +1,168 @@ +// 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 metrics + +import ( + "context" + "fmt" + "net/url" + "os" + "time" + + "github.com/go-logr/logr" + "github.com/oklog/ulid/v2" + "github.com/shirou/gopsutil/process" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" + "go.opentelemetry.io/otel/metric" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + otelresource "go.opentelemetry.io/otel/sdk/resource" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + + "github.com/open-telemetry/opamp-go/protobufs" +) + +// MetricReporter is a metric reporter that collects Agent metrics and sends them to an +// OTLP/HTTP destination. +type MetricReporter struct { + logger logr.Logger + + meter metric.Meter + meterShutdowner func() + done chan struct{} + + // The Agent's process. + process *process.Process + + // Some example metrics to report. + processMemoryPhysical metric.Float64ObservableGauge + processCpuTime metric.Float64ObservableCounter +} + +// NewMetricReporter creates an OTLP/HTTP client to the destination address supplied by the server. +// TODO: do more validation on the endpoint, allow for gRPC. +// TODO: set global provider and add more metrics to be reported. +func NewMetricReporter(logger logr.Logger, dest *protobufs.TelemetryConnectionSettings, agentType string, agentVersion string, instanceId ulid.ULID) (*MetricReporter, error) { + + if dest.DestinationEndpoint == "" { + return nil, fmt.Errorf("metric destination must specify DestinationEndpoint") + } + + u, err := url.Parse(dest.DestinationEndpoint) + if err != nil { + return nil, fmt.Errorf("invalid DestinationEndpoint: %w", err) + } + + // Create OTLP/HTTP metric exporter. + opts := []otlpmetrichttp.Option{ + otlpmetrichttp.WithEndpoint(u.Host), + otlpmetrichttp.WithURLPath(u.Path), + } + + headers := map[string]string{} + for _, header := range dest.Headers.GetHeaders() { + headers[header.GetKey()] = header.GetValue() + } + opts = append(opts, otlpmetrichttp.WithHeaders(headers)) + + client, err := otlpmetrichttp.New(context.Background(), opts...) + if err != nil { + return nil, fmt.Errorf("failed to initialize otlp metric http client: %w", err) + } + + // Define the Resource to be exported with all metrics. Use OpenTelemetry semantic + // conventions as the OpAMP spec requires: + // https://github.com/open-telemetry/opamp-spec/blob/main/specification.md#own-telemetry-reporting + resource, resourceErr := otelresource.New(context.Background(), + otelresource.WithAttributes( + semconv.ServiceNameKey.String(agentType), + semconv.ServiceVersionKey.String(agentVersion), + semconv.ServiceInstanceIDKey.String(instanceId.String()), + ), + ) + if resourceErr != nil { + return nil, resourceErr + } + + provider := sdkmetric.NewMeterProvider( + sdkmetric.WithResource(resource), + sdkmetric.WithReader(sdkmetric.NewPeriodicReader(client, sdkmetric.WithInterval(5*time.Second)))) + + reporter := &MetricReporter{ + logger: logger, + } + + reporter.done = make(chan struct{}) + + reporter.meter = provider.Meter("opamp") + + reporter.process, err = process.NewProcess(int32(os.Getpid())) + if err != nil { + return nil, fmt.Errorf("cannot query own process: %w", err) + } + + // Create some metrics that will be reported according to OpenTelemetry semantic + // conventions for process metrics (conventions are TBD for now). + reporter.processCpuTime, err = reporter.meter.Float64ObservableCounter( + "process.cpu.time", + metric.WithFloat64Callback(reporter.processCpuTimeFunc), + ) + if err != nil { + return nil, fmt.Errorf("can't create process time metric: %w", err) + } + + reporter.processMemoryPhysical, err = reporter.meter.Float64ObservableGauge( + "process.memory.physical_usage", + metric.WithFloat64Callback(reporter.processMemoryPhysicalFunc), + ) + if err != nil { + return nil, fmt.Errorf("can't create memory metric: %w", err) + } + + reporter.meterShutdowner = func() { _ = provider.Shutdown(context.Background()) } + + return reporter, nil +} + +func (reporter *MetricReporter) processCpuTimeFunc(_ context.Context, observer metric.Float64Observer) error { + times, err := reporter.process.Times() + if err != nil { + reporter.logger.Error(err, "cannot get process CPU times") + } + observer.Observe(times.User, metric.WithAttributes(attribute.String("state", "user"))) + observer.Observe(times.System, metric.WithAttributes(attribute.String("state", "system"))) + observer.Observe(times.Iowait, metric.WithAttributes(attribute.String("state", "wait"))) + return nil +} + +func (reporter *MetricReporter) processMemoryPhysicalFunc(_ context.Context, observer metric.Float64Observer) error { + memory, err := reporter.process.MemoryInfo() + if err != nil { + reporter.logger.Error(err, "cannot get process memory information") + return nil + } + observer.Observe(float64(memory.RSS)) + return nil +} + +func (reporter *MetricReporter) Shutdown() { + if reporter.done != nil { + close(reporter.done) + } + + if reporter.meterShutdowner != nil { + reporter.meterShutdowner() + } +} diff --git a/cmd/operator-opamp-bridge/operator/client.go b/cmd/operator-opamp-bridge/operator/client.go new file mode 100644 index 0000000000..b892cbe177 --- /dev/null +++ b/cmd/operator-opamp-bridge/operator/client.go @@ -0,0 +1,250 @@ +// 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 operator + +import ( + "context" + "fmt" + "strings" + + "github.com/go-logr/logr" + "github.com/open-telemetry/opamp-go/protobufs" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +const ( + CollectorResource = "OpenTelemetryCollector" + ResourceIdentifierKey = "created-by" + ResourceIdentifierValue = "operator-opamp-bridge" + ReportingLabelKey = "opentelemetry.io/opamp-reporting" + ManagedLabelKey = "opentelemetry.io/opamp-managed" +) + +type ConfigApplier interface { + // Apply receives a name and namespace to apply an OpenTelemetryCollector CRD that is contained in the configmap. + Apply(name string, namespace string, configmap *protobufs.AgentConfigFile) error + + // GetInstance retrieves an OpenTelemetryCollector CRD given a name and namespace. + GetInstance(name string, namespace string) (*v1alpha1.OpenTelemetryCollector, error) + + // ListInstances retrieves all OpenTelemetryCollector CRDs created by the operator-opamp-bridge agent. + ListInstances() ([]v1alpha1.OpenTelemetryCollector, error) + + // Delete attempts to delete an OpenTelemetryCollector object given a name and namespace. + Delete(name string, namespace string) error +} + +type Client struct { + log logr.Logger + componentsAllowed map[string]map[string]bool + k8sClient client.Client + close chan bool + name string +} + +var _ ConfigApplier = &Client{} + +func NewClient(name string, log logr.Logger, c client.Client, componentsAllowed map[string]map[string]bool) *Client { + return &Client{ + log: log, + componentsAllowed: componentsAllowed, + k8sClient: c, + close: make(chan bool, 1), + name: name, + } +} + +func (c Client) labelSetContainsLabel(instance *v1alpha1.OpenTelemetryCollector, label, value string) bool { + if instance == nil || instance.GetLabels() == nil { + return false + } + if labels := instance.GetLabels(); labels != nil && strings.EqualFold(labels[label], value) { + return true + } + return false +} + +func (c Client) create(ctx context.Context, name string, namespace string, collector *v1alpha1.OpenTelemetryCollector) error { + // Set the defaults + collector.Default() + collector.TypeMeta.Kind = CollectorResource + collector.TypeMeta.APIVersion = v1alpha1.GroupVersion.String() + collector.ObjectMeta.Name = name + collector.ObjectMeta.Namespace = namespace + + if collector.ObjectMeta.Labels == nil { + collector.ObjectMeta.Labels = map[string]string{} + } + collector.ObjectMeta.Labels[ResourceIdentifierKey] = ResourceIdentifierValue + warnings, err := collector.ValidateCreate() + if err != nil { + return err + } + if warnings != nil { + c.log.Info("Some warnings present on collector", "warnings", warnings) + } + c.log.Info("Creating collector") + return c.k8sClient.Create(ctx, collector) +} + +func (c Client) update(ctx context.Context, old *v1alpha1.OpenTelemetryCollector, new *v1alpha1.OpenTelemetryCollector) error { + new.ObjectMeta = old.ObjectMeta + new.TypeMeta = old.TypeMeta + warnings, err := new.ValidateUpdate(old) + if err != nil { + return err + } + if warnings != nil { + c.log.Info("Some warnings present on collector", "warnings", warnings) + } + c.log.Info("Updating collector") + return c.k8sClient.Update(ctx, new) +} + +func (c Client) Apply(name string, namespace string, configmap *protobufs.AgentConfigFile) error { + c.log.Info("Received new config", "name", name, "namespace", namespace) + var collector v1alpha1.OpenTelemetryCollector + err := yaml.Unmarshal(configmap.Body, &collector) + if err != nil { + return err + } + if len(collector.Spec.Config) == 0 { + return errors.NewBadRequest("Must supply valid configuration") + } + reasons, validateErr := c.validate(collector.Spec) + if validateErr != nil { + return validateErr + } + if len(reasons) > 0 { + return errors.NewBadRequest(fmt.Sprintf("Items in config are not allowed: %v", reasons)) + } + updatedCollector := collector.DeepCopy() + ctx := context.Background() + instance, err := c.GetInstance(name, namespace) + if err != nil { + return err + } + // If either the received collector or the collector being created has reporting set to true, it should be denied + if c.labelSetContainsLabel(instance, ReportingLabelKey, "true") || + c.labelSetContainsLabel(updatedCollector, ReportingLabelKey, "true") { + return errors.NewBadRequest("cannot modify a collector with `opentelemetry.io/opamp-reporting: true`") + } + // If either the received collector or the collector doesn't have the managed label set to true, it should be denied + if !c.labelSetContainsLabel(instance, ManagedLabelKey, "true") && + !c.labelSetContainsLabel(instance, ManagedLabelKey, c.name) && + !c.labelSetContainsLabel(updatedCollector, ManagedLabelKey, "true") && + !c.labelSetContainsLabel(updatedCollector, ManagedLabelKey, c.name) { + return errors.NewBadRequest("cannot modify a collector that doesn't have `opentelemetry.io/opamp-managed: true | ` set") + } + if instance == nil { + return c.create(ctx, name, namespace, updatedCollector) + } + return c.update(ctx, instance, updatedCollector) +} + +func (c Client) Delete(name string, namespace string) error { + ctx := context.Background() + result := v1alpha1.OpenTelemetryCollector{} + err := c.k8sClient.Get(ctx, client.ObjectKey{ + Namespace: namespace, + Name: name, + }, &result) + if err != nil { + if errors.IsNotFound(err) { + return nil + } + return err + } + return c.k8sClient.Delete(ctx, &result) +} + +func (c Client) ListInstances() ([]v1alpha1.OpenTelemetryCollector, error) { + ctx := context.Background() + result := v1alpha1.OpenTelemetryCollectorList{} + labelSelector := labels.NewSelector() + requirement, err := labels.NewRequirement(ManagedLabelKey, selection.In, []string{c.name, "true"}) + if err != nil { + return nil, err + } + err = c.k8sClient.List(ctx, &result, client.MatchingLabelsSelector{Selector: labelSelector.Add(*requirement)}) + if err != nil { + return nil, err + } + reportingCollectors := v1alpha1.OpenTelemetryCollectorList{} + err = c.k8sClient.List(ctx, &reportingCollectors, client.MatchingLabels{ + ReportingLabelKey: "true", + }) + if err != nil { + return nil, err + } + items := append(result.Items, reportingCollectors.Items...) + for i := range items { + items[i].SetManagedFields(nil) + } + + return items, nil +} + +func (c Client) GetInstance(name string, namespace string) (*v1alpha1.OpenTelemetryCollector, error) { + ctx := context.Background() + result := v1alpha1.OpenTelemetryCollector{} + err := c.k8sClient.Get(ctx, client.ObjectKey{ + Namespace: namespace, + Name: name, + }, &result) + if err != nil { + if errors.IsNotFound(err) { + return nil, nil + } + return nil, err + } + return &result, nil +} + +func (c Client) validate(spec v1alpha1.OpenTelemetryCollectorSpec) ([]string, error) { + // Do not use this feature if it's not specified + if c.componentsAllowed == nil || len(c.componentsAllowed) == 0 { + return nil, nil + } + collectorConfig := make(map[string]map[string]interface{}) + err := yaml.Unmarshal([]byte(spec.Config), &collectorConfig) + if err != nil { + return nil, err + } + var invalidComponents []string + for component, componentMap := range collectorConfig { + if component == "service" { + // We don't care about what's in the service pipelines. + // Only components declared in the configuration can be used in the service pipeline. + continue + } + if _, ok := c.componentsAllowed[component]; !ok { + invalidComponents = append(invalidComponents, component) + continue + } + for componentName := range componentMap { + if _, ok := c.componentsAllowed[component][componentName]; !ok { + invalidComponents = append(invalidComponents, fmt.Sprintf("%s.%s", component, componentName)) + } + } + } + return invalidComponents, nil +} diff --git a/cmd/operator-opamp-bridge/operator/client_test.go b/cmd/operator-opamp-bridge/operator/client_test.go new file mode 100644 index 0000000000..8db8fb1a51 --- /dev/null +++ b/cmd/operator-opamp-bridge/operator/client_test.go @@ -0,0 +1,245 @@ +// 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 operator + +import ( + "context" + "os" + "testing" + + "github.com/go-logr/logr" + "github.com/open-telemetry/opamp-go/protobufs" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/yaml" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +var ( + clientLogger = logr.Discard() +) + +const ( + bridgeName = "bridge-test" +) + +func getFakeClient(t *testing.T) client.WithWatch { + schemeBuilder := runtime.NewSchemeBuilder(func(s *runtime.Scheme) error { + s.AddKnownTypes(v1alpha1.GroupVersion, &v1alpha1.OpenTelemetryCollector{}, &v1alpha1.OpenTelemetryCollectorList{}) + metav1.AddToGroupVersion(s, v1alpha1.GroupVersion) + return nil + }) + scheme := runtime.NewScheme() + err := schemeBuilder.AddToScheme(scheme) + require.NoError(t, err, "Should be able to add custom types") + c := fake.NewClientBuilder().WithScheme(scheme) + return c.Build() +} + +func TestClient_Apply(t *testing.T) { + type args struct { + name string + namespace string + file string + config string + } + tests := []struct { + name string + args args + wantErr bool + errContains string + }{ + { + name: "base case", + args: args{ + name: "test", + namespace: "opentelemetry", + file: "testdata/collector.yaml", + }, + wantErr: false, + }, + { + name: "invalid config", + args: args{ + name: "test", + namespace: "opentelemetry", + file: "testdata/invalid-collector.yaml", + }, + wantErr: true, + errContains: "error converting YAML to JSON", + }, + { + name: "empty config", + args: args{ + name: "test", + namespace: "opentelemetry", + config: "", + }, + wantErr: true, + errContains: "Must supply valid configuration", + }, + { + name: "create reporting-only", + args: args{ + name: "test", + namespace: "opentelemetry", + file: "testdata/reporting-collector.yaml", + }, + wantErr: true, + errContains: "opentelemetry.io/opamp-reporting", + }, + { + name: "create managed false", + args: args{ + name: "test", + namespace: "opentelemetry", + file: "testdata/unmanaged-collector.yaml", + }, + wantErr: true, + errContains: "opentelemetry.io/opamp-managed", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeClient := getFakeClient(t) + c := NewClient(bridgeName, clientLogger, fakeClient, nil) + var colConfig []byte + var err error + if len(tt.args.file) > 0 { + colConfig, err = loadConfig(tt.args.file) + require.NoError(t, err, "Should be no error on loading test configuration") + } else { + colConfig = []byte(tt.args.config) + } + configmap := &protobufs.AgentConfigFile{ + Body: colConfig, + ContentType: "yaml", + } + applyErr := c.Apply(tt.args.name, tt.args.namespace, configmap) + if tt.wantErr { + assert.Error(t, applyErr) + assert.ErrorContains(t, applyErr, tt.errContains) + } + }) + } +} + +func Test_collectorUpdate(t *testing.T) { + name := "test" + namespace := "testing" + fakeClient := getFakeClient(t) + c := NewClient(bridgeName, clientLogger, fakeClient, nil) + + // Load reporting-only collector + reportingColConfig, err := loadConfig("testdata/reporting-collector.yaml") + require.NoError(t, err, "Should be no error on loading test configuration") + var reportingCol v1alpha1.OpenTelemetryCollector + err = yaml.Unmarshal(reportingColConfig, &reportingCol) + require.NoError(t, err, "Should be no error on unmarshal") + reportingCol.Default() + reportingCol.TypeMeta.Kind = CollectorResource + reportingCol.TypeMeta.APIVersion = v1alpha1.GroupVersion.String() + reportingCol.ObjectMeta.Name = "simplest" + reportingCol.ObjectMeta.Namespace = namespace + err = fakeClient.Create(context.Background(), &reportingCol) + require.NoError(t, err, "Should be able to make reporting col") + allInstances, err := c.ListInstances() + require.NoError(t, err, "Should be able to list all collectors") + require.Len(t, allInstances, 1) + + colConfig, err := loadConfig("testdata/collector.yaml") + require.NoError(t, err, "Should be no error on loading test configuration") + configmap := &protobufs.AgentConfigFile{ + Body: colConfig, + ContentType: "yaml", + } + // Apply a valid initial configuration + err = c.Apply(name, namespace, configmap) + require.NoError(t, err, "Should apply base config") + + // Get the newly created collector + instance, err := c.GetInstance(name, namespace) + require.NoError(t, err, "Should be able to get the newly created instance") + assert.Contains(t, instance.Spec.Config, "processors: []") + + // Try updating with an invalid one + configmap.Body = []byte("empty, invalid!") + err = c.Apply(name, namespace, configmap) + assert.Error(t, err, "Should be unable to update") + + // Update successfully with a valid configuration + newColConfig, err := loadConfig("testdata/updated-collector.yaml") + require.NoError(t, err, "Should be no error on loading test configuration") + newConfigMap := &protobufs.AgentConfigFile{ + Body: newColConfig, + ContentType: "yaml", + } + err = c.Apply(name, namespace, newConfigMap) + require.NoError(t, err, "Should be able to update collector") + + // Get the updated collector + updatedInstance, err := c.GetInstance(name, namespace) + require.NoError(t, err, "Should be able to get the updated instance") + assert.Contains(t, updatedInstance.Spec.Config, "processors: [memory_limiter, batch]") + + allInstances, err = c.ListInstances() + require.NoError(t, err, "Should be able to list all collectors") + assert.Len(t, allInstances, 2) + assert.Contains(t, allInstances, reportingCol) + assert.Contains(t, allInstances, *updatedInstance) +} + +func Test_collectorDelete(t *testing.T) { + name := "test" + namespace := "testing" + fakeClient := getFakeClient(t) + c := NewClient(bridgeName, clientLogger, fakeClient, nil) + colConfig, err := loadConfig("testdata/collector.yaml") + require.NoError(t, err, "Should be no error on loading test configuration") + configmap := &protobufs.AgentConfigFile{ + Body: colConfig, + ContentType: "yaml", + } + // Apply a valid initial configuration + err = c.Apply(name, namespace, configmap) + require.NoError(t, err, "Should apply base config") + + // Get the newly created collector + instance, err := c.GetInstance(name, namespace) + require.NoError(t, err, "Should be able to get the newly created instance") + assert.Contains(t, instance.Spec.Config, "processors: []") + + // Delete it + err = c.Delete(name, namespace) + require.NoError(t, err, "Should be able to delete a collector") + + // Check there's nothing left + allInstances, err := c.ListInstances() + require.NoError(t, err, "Should be able to list all collectors") + assert.Len(t, allInstances, 0) +} + +func loadConfig(file string) ([]byte, error) { + yamlFile, err := os.ReadFile(file) + if err != nil { + return []byte{}, err + } + return yamlFile, nil +} diff --git a/cmd/operator-opamp-bridge/operator/testdata/collector.yaml b/cmd/operator-opamp-bridge/operator/testdata/collector.yaml new file mode 100644 index 0000000000..c77c64c488 --- /dev/null +++ b/cmd/operator-opamp-bridge/operator/testdata/collector.yaml @@ -0,0 +1,31 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + labels: + opentelemetry.io/opamp-managed: "true" +spec: + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + memory_limiter: + check_interval: 1s + limit_percentage: 75 + spike_limit_percentage: 15 + batch: + send_batch_size: 10000 + timeout: 10s + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/cmd/operator-opamp-bridge/operator/testdata/invalid-collector.yaml b/cmd/operator-opamp-bridge/operator/testdata/invalid-collector.yaml new file mode 100644 index 0000000000..f64ebcedf4 --- /dev/null +++ b/cmd/operator-opamp-bridge/operator/testdata/invalid-collector.yaml @@ -0,0 +1,28 @@ +kind: OpenTelemetryCollector +metadata: + name: simplest +spec: + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + memory_limiter: + check_interval: 1s + limit_percentage: 75 + spike_limit_percentage: 15 + batch: + send_batch_size: 10000 + timeout: 10s + GARBAGE + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/cmd/operator-opamp-bridge/operator/testdata/reporting-collector.yaml b/cmd/operator-opamp-bridge/operator/testdata/reporting-collector.yaml new file mode 100644 index 0000000000..3b0a925464 --- /dev/null +++ b/cmd/operator-opamp-bridge/operator/testdata/reporting-collector.yaml @@ -0,0 +1,31 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + labels: + "opentelemetry.io/opamp-reporting": "true" +spec: + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + memory_limiter: + check_interval: 1s + limit_percentage: 75 + spike_limit_percentage: 15 + batch: + send_batch_size: 10000 + timeout: 10s + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/cmd/operator-opamp-bridge/operator/testdata/unmanaged-collector.yaml b/cmd/operator-opamp-bridge/operator/testdata/unmanaged-collector.yaml new file mode 100644 index 0000000000..48b793f98d --- /dev/null +++ b/cmd/operator-opamp-bridge/operator/testdata/unmanaged-collector.yaml @@ -0,0 +1,31 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + labels: + "opentelemetry.io/opamp-managed": "false" +spec: + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + memory_limiter: + check_interval: 1s + limit_percentage: 75 + spike_limit_percentage: 15 + batch: + send_batch_size: 10000 + timeout: 10s + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/cmd/operator-opamp-bridge/operator/testdata/updated-collector.yaml b/cmd/operator-opamp-bridge/operator/testdata/updated-collector.yaml new file mode 100644 index 0000000000..81a919b28d --- /dev/null +++ b/cmd/operator-opamp-bridge/operator/testdata/updated-collector.yaml @@ -0,0 +1,31 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + labels: + opentelemetry.io/opamp-managed: "true" +spec: + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + memory_limiter: + check_interval: 1s + limit_percentage: 75 + spike_limit_percentage: 15 + batch: + send_batch_size: 10000 + timeout: 10s + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch] + exporters: [debug] diff --git a/cmd/otel-allocator/Dockerfile b/cmd/otel-allocator/Dockerfile index 3e856c08a0..0d50a4f548 100644 --- a/cmd/otel-allocator/Dockerfile +++ b/cmd/otel-allocator/Dockerfile @@ -1,26 +1,19 @@ -# Build the target allocator binary -FROM golang:1.19 as builder +# Get CA certificates from the Alpine package repo +FROM alpine:3.19 as certificates -WORKDIR /app - -# Copy go mod and sum files -COPY go.mod go.sum ./ - -RUN go mod download - -COPY . . - -# Build the Go app -RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main . +RUN apk --no-cache add ca-certificates -######## Start a new stage from scratch ####### -FROM alpine:latest +# Start a new stage from scratch +FROM scratch -RUN apk --no-cache add ca-certificates +ARG TARGETARCH WORKDIR /root/ -# Copy the pre-built binary file from the previous stage -COPY --from=builder /app/main . +# Copy the certs +COPY --from=certificates /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt + +# Copy binary built on the host +COPY bin/targetallocator_${TARGETARCH} ./main ENTRYPOINT ["./main"] diff --git a/cmd/otel-allocator/README.md b/cmd/otel-allocator/README.md index 88b4cee963..0f4419e8ef 100644 --- a/cmd/otel-allocator/README.md +++ b/cmd/otel-allocator/README.md @@ -1,9 +1,199 @@ # Target Allocator -The TargetAllocator is an optional separately deployed component of an OpenTelemetry Collector setup, which is used to -distribute targets of the PrometheusReceiver on all deployed Collector instances. The release version matches the +Target Allocator is an optional component of the OpenTelemetry Collector [Custom Resource](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) (CR). The release version matches the operator's most recent release as well. +In a nutshell, the TA is a mechanism for decoupling the service discovery and metric collection functions of Prometheus such that they can be scaled independently. The Collector manages Prometheus metrics without needing to install Prometheus. The TA manages the configuration of the Collector's [Prometheus Receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/prometheusreceiver/README.md). + +The TA serves two functions: +* Even distribution of Prometheus targets among a pool of Collectors +* Discovery of Prometheus Custom Resources + +## Even Distribution of Prometheus Targets + +The Target Allocator’s first job is to discover targets to scrape and OTel Collectors to allocate targets to. Then it can distribute the targets it discovers among the Collectors. The Collectors in turn query the Target Allocator for Metrics endpoints to scrape, and then the Collectors’ [Prometheus Receivers](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/prometheusreceiver/README.md) scrape the Metrics targets. + +This means that the OTel Collectors collect the metrics instead of a Prometheus [scraper](https://uzxmx.github.io/prometheus-scrape-internals.html). + + +```mermaid +sequenceDiagram + participant Target Allocator + participant Metrics Targets + participant OTel Collectors + Target Allocator ->>Metrics Targets: 1. Discover Metrics targets + Target Allocator ->>OTel Collectors: 2. Discover available Collectors + Target Allocator ->>Target Allocator: 3. Assign Metrics targets + OTel Collectors ->>Target Allocator: 4. Query TA for Metrics endpoints scrape + OTel Collectors ->>Metrics Targets: 5. Scrape Metrics target +``` + +## Discovery of Prometheus Custom Resources + +The Target Allocator also provides for the discovery of [Prometheus Operator CRs](https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/user-guides/getting-started.md), namely the [ServiceMonitor and PodMonitor](https://github.com/open-telemetry/opentelemetry-operator/tree/main/cmd/otel-allocator#target-allocator). The ServiceMonitors and the PodMonitors purpose is to inform the Target Allocator (or PrometheusOperator) to add a new job to their scrape configuration. The Target Allocator then provides the jobs to the OTel Collector [Prometheus Receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/prometheusreceiver/README.md). + +```mermaid +flowchart RL + pm(PodMonitor) + sm(ServiceMonitor) + ta(Target Allocator) + oc1(OTel Collector) + oc2(OTel Collector) + oc3(OTel Collector) + ta --> pm + ta --> sm + oc1 --> ta + oc2 --> ta + oc3 --> ta + sm ~~~|"1. Discover Prometheus Operator CRs"| sm + ta ~~~|"2. Add job to TA scrape configuration"| ta + oc3 ~~~|"3. Add job to OTel Collector scrape configuration"| oc3 +``` + +Even though Prometheus is not required to be installed in your Kubernetes cluster to use the Target Allocator for Prometheus CR discovery, the TA does require that the ServiceMonitor and PodMonitor be installed. These CRs are bundled with Prometheus Operator; however, they can be installed standalone as well. + +The easiest way to do this is by going to the [Prometheus Operator’s Releases page](https://github.com/prometheus-operator/prometheus-operator/releases), grabbing a copy of the latest `bundle.yaml` file (for example, [this one](https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.66.0/bundle.yaml)), and stripping out all of the YAML except the ServiceMonitor and PodMonitor YAML definitions. + +# Usage +The `spec.targetAllocator:` controls the TargetAllocator general properties. Full API spec can be found here: [api.md#opentelemetrycollectorspectargetallocator](../../docs/api.md#opentelemetrycollectorspectargetallocator) + +A basic example that deploys. +```yaml +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: collector-with-ta +spec: + mode: statefulset + targetAllocator: + enabled: true + config: | + receivers: + prometheus: + config: + scrape_configs: + - job_name: 'otel-collector' + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888' ] + + exporters: + logging: + + service: + pipelines: + metrics: + receivers: [prometheus] + processors: [] + exporters: [logging] +``` + +In essence, Prometheus Receiver configs are overridden with a `http_sd_config` directive that points to the +Allocator, these are then loadbalanced/sharded to the Collectors. The [Prometheus Receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/prometheusreceiver/README.md) configs that are overridden +are what will be distributed with the same name. + +## PrometheusCR specifics + +TargetAllocator discovery of PrometheusCRs can be turned on by setting +`.spec.targetAllocator.prometheusCR.enabled` to `true`, which it presents as scrape configs +and jobs on the `/scrape_configs` and `/jobs` endpoints respectively. + +The CRs can be filtered by labels as documented here: [api.md#opentelemetrycollectorspectargetallocatorprometheuscr](../../docs/api.md#opentelemetrycollectorspectargetallocatorprometheuscr) + +The Prometheus Receiver in the deployed Collector also has to know where the Allocator service exists. This is done by a +OpenTelemetry Collector Operator-specific config. + +```yaml + config: | + receivers: + prometheus: + config: + scrape_configs: + - job_name: 'otel-collector' + target_allocator: + endpoint: http://my-targetallocator-service + interval: 30s + collector_id: "${POD_NAME}" +``` + +Upstream documentation here: [PrometheusReceiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/prometheusreceiver#opentelemetry-operator) + +The TargetAllocator service is named based on the OpenTelemetryCollector CR name. `collector_id` should be unique per +collector instance, such as the pod name. The `POD_NAME` environment variable is convenient since this is supplied +to collector instance pods by default. + + +### RBAC +The ServiceAccount that the TargetAllocator runs as, has to have access to the CRs. A role like this will provide that +access. +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: opentelemetry-targetallocator-cr-role +rules: +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + - podmonitors + verbs: + - '*' +``` +In addition, the TargetAllocator needs the same permissions as a Prometheus instance would to find the matching targets +from the CR instances. +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: opentelemetry-targetallocator-role +rules: +- apiGroups: [""] + resources: + - nodes + - nodes/metrics + - services + - endpoints + - pods + verbs: ["get", "list", "watch"] +- apiGroups: [""] + resources: + - configmaps + verbs: ["get"] +- apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: ["get", "list", "watch"] +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: ["get", "list", "watch"] +- nonResourceURLs: ["/metrics"] + verbs: ["get"] +``` +These roles can be combined. + +A ServiceAccount bound with the above permissions in the namespaces that are to be monitored can then be referenced in +the `targetAllocator:` part of the OpenTelemetryCollector CR. +```yaml + targetAllocator: + enabled: true + serviceAccount: opentelemetry-targetallocator-sa + prometheusCR: + enabled: true +``` +**Note**: The Collector part of this same CR *also* has a serviceAccount key which only affects the collector and *not* +the TargetAllocator. + +### Service / Pod monitor endpoint credentials + +If your service or pod monitor endpoints require credentials or other supported form of authentication (bearer token, basic auth, OAuth2 etc.), you need to ensure that the collector has access to this information. Due to some limitations in how the endpoints configuration is handled, target allocator currently does **not** support credentials provided via secrets. It is only possible to provide credentials in a file (for more details see issue https://github.com/open-telemetry/opentelemetry-operator/issues/1669). + +In order to ensure your endpoints can be scraped, your collector instance needs to have the particular secret mounted as a file at the correct path. + + # Design If the Allocator is activated, all Prometheus configurations will be transferred in a separate ConfigMap which get in diff --git a/cmd/otel-allocator/allocation/allocatortest.go b/cmd/otel-allocator/allocation/allocatortest.go new file mode 100644 index 0000000000..88312a80a5 --- /dev/null +++ b/cmd/otel-allocator/allocation/allocatortest.go @@ -0,0 +1,71 @@ +// 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 ( + "fmt" + "strconv" + + "github.com/prometheus/common/model" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" +) + +func colIndex(index, numCols int) int { + if numCols == 0 { + return -1 + } + return index % numCols +} + +func MakeNNewTargets(n int, numCollectors int, startingIndex int) map[string]*target.Item { + toReturn := map[string]*target.Item{} + for i := startingIndex; i < n+startingIndex; i++ { + collector := fmt.Sprintf("collector-%d", colIndex(i, numCollectors)) + label := model.LabelSet{ + "collector": model.LabelValue(collector), + "i": model.LabelValue(strconv.Itoa(i)), + "total": model.LabelValue(strconv.Itoa(n + startingIndex)), + } + newTarget := target.NewItem(fmt.Sprintf("test-job-%d", i), fmt.Sprintf("test-url-%d", i), label, collector) + toReturn[newTarget.Hash()] = newTarget + } + return toReturn +} + +func MakeNCollectors(n int, startingIndex int) map[string]*Collector { + toReturn := map[string]*Collector{} + for i := startingIndex; i < n+startingIndex; i++ { + collector := fmt.Sprintf("collector-%d", i) + toReturn[collector] = &Collector{ + Name: collector, + NumTargets: 0, + } + } + return toReturn +} + +func MakeNNewTargetsWithEmptyCollectors(n int, startingIndex int) map[string]*target.Item { + toReturn := map[string]*target.Item{} + for i := startingIndex; i < n+startingIndex; i++ { + label := model.LabelSet{ + "i": model.LabelValue(strconv.Itoa(i)), + "total": model.LabelValue(strconv.Itoa(n + startingIndex)), + } + newTarget := target.NewItem(fmt.Sprintf("test-job-%d", i), fmt.Sprintf("test-url-%d", i), label, "") + toReturn[newTarget.Hash()] = newTarget + } + return toReturn +} diff --git a/cmd/otel-allocator/allocation/consistent_hashing.go b/cmd/otel-allocator/allocation/consistent_hashing.go index 41f31a4e92..d98aca72f6 100644 --- a/cmd/otel-allocator/allocation/consistent_hashing.go +++ b/cmd/otel-allocator/allocation/consistent_hashing.go @@ -15,8 +15,7 @@ package allocation import ( - "fmt" - "net/url" + "strings" "sync" "github.com/buraksezer/consistent" @@ -25,6 +24,7 @@ import ( "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{} @@ -44,15 +44,22 @@ type consistentHashingAllocator struct { 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 - targetItems map[string]*TargetItem + // 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) Allocator { +func newConsistentHashingAllocator(log logr.Logger, opts ...AllocationOption) Allocator { config := consistent.Config{ PartitionCount: 1061, ReplicationFactor: 5, @@ -60,33 +67,55 @@ func newConsistentHashingAllocator(log logr.Logger) Allocator { Hasher: hasher{}, } consistentHasher := consistent.New(nil, config) - return &consistentHashingAllocator{ - consistentHasher: consistentHasher, - collectors: make(map[string]*Collector), - targetItems: make(map[string]*TargetItem), - log: log, + 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, } + for _, opt := range opts { + opt(chAllocator) + } + + return chAllocator +} + +// SetFilter sets the filtering hook to use. +func (c *consistentHashingAllocator) SetFilter(filter Filter) { + c.filter = filter +} + +// 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 } // 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. -func (c *consistentHashingAllocator) addTargetToTargetItems(target *TargetItem) { +// 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[target.CollectorName]; ok { + 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)) } - colOwner := c.consistentHasher.LocateKey([]byte(target.Hash())) - targetItem := &TargetItem{ - JobName: target.JobName, - Link: LinkJSON{Link: fmt.Sprintf("/jobs/%s/targets", url.QueryEscape(target.JobName))}, - TargetURL: target.TargetURL, - Label: target.Label, - CollectorName: colOwner.String(), - } - c.targetItems[targetItem.Hash()] = targetItem + 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)) } @@ -94,26 +123,27 @@ func (c *consistentHashingAllocator) addTargetToTargetItems(target *TargetItem) // 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[*TargetItem]) { +func (c *consistentHashingAllocator) handleTargets(diff diff.Changes[*target.Item]) { // Check for removals - for k, target := range c.targetItems { - // if the current target is in the removals list + for k, item := range c.targetItems { + // if the current item is in the removals list if _, ok := diff.Removals()[k]; ok { - col := c.collectors[target.CollectorName] + col := c.collectors[item.CollectorName] col.NumTargets-- delete(c.targetItems, k) - TargetsPerCollector.WithLabelValues(target.CollectorName, consistentHashingStrategyName).Set(float64(col.NumTargets)) + delete(c.targetItemsPerJobPerCollector[item.CollectorName][item.JobName], item.Hash()) + TargetsPerCollector.WithLabelValues(item.CollectorName, consistentHashingStrategyName).Set(float64(col.NumTargets)) } } // Check for additions - for k, target := range diff.Additions() { + for k, item := range diff.Additions() { // Do nothing if the item is already there if _, ok := c.targetItems[k]; ok { continue } else { - // Add target to target pool and assign a collector - c.addTargetToTargetItems(target) + // Add item to item pool and assign a collector + c.addTargetToTargetItems(item) } } } @@ -125,6 +155,7 @@ func (c *consistentHashingAllocator) handleCollectors(diff diff.Changes[*Collect // 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) } @@ -143,15 +174,53 @@ func (c *consistentHashingAllocator) handleCollectors(diff diff.Changes[*Collect // 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]*TargetItem) { +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() if len(c.collectors) == 0 { - c.log.Info("No collector instances present, cannot set targets") + 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 @@ -165,13 +234,12 @@ func (c *consistentHashingAllocator) SetTargets(targets map[string]*TargetItem) // 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) { - log := c.log.WithValues("component", "opentelemetry-targetallocator") timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetCollectors", consistentHashingStrategyName)) defer timer.ObserveDuration() CollectorsAllocatable.WithLabelValues(consistentHashingStrategyName).Set(float64(len(collectors))) if len(collectors) == 0 { - log.Info("No collector instances present") + c.log.Info("No collector instances present") return } @@ -185,11 +253,29 @@ func (c *consistentHashingAllocator) SetCollectors(collectors map[string]*Collec } } +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]*TargetItem { +func (c *consistentHashingAllocator) TargetItems() map[string]*target.Item { c.m.RLock() defer c.m.RUnlock() - targetItemsCopy := make(map[string]*TargetItem) + targetItemsCopy := make(map[string]*target.Item) for k, v := range c.targetItems { targetItemsCopy[k] = v } diff --git a/cmd/otel-allocator/allocation/consistent_hashing_test.go b/cmd/otel-allocator/allocation/consistent_hashing_test.go index 31ac9c2007..bbd4295202 100644 --- a/cmd/otel-allocator/allocation/consistent_hashing_test.go +++ b/cmd/otel-allocator/allocation/consistent_hashing_test.go @@ -21,46 +21,45 @@ import ( ) func TestCanSetSingleTarget(t *testing.T) { - cols := makeNCollectors(3, 0) + cols := MakeNCollectors(3, 0) c := newConsistentHashingAllocator(logger) c.SetCollectors(cols) - c.SetTargets(makeNNewTargets(1, 3, 0)) + c.SetTargets(MakeNNewTargets(1, 3, 0)) actualTargetItems := c.TargetItems() assert.Len(t, actualTargetItems, 1) for _, item := range actualTargetItems { - assert.Equal(t, "collector-2", item.CollectorName) + assert.Equal(t, "collector-0", item.CollectorName) } } func TestRelativelyEvenDistribution(t *testing.T) { numCols := 15 numItems := 10000 - cols := makeNCollectors(numCols, 0) + cols := MakeNCollectors(numCols, 0) var expectedPerCollector = float64(numItems / numCols) expectedDelta := (expectedPerCollector * 1.5) - expectedPerCollector c := newConsistentHashingAllocator(logger) c.SetCollectors(cols) - c.SetTargets(makeNNewTargets(numItems, 0, 0)) + c.SetTargets(MakeNNewTargets(numItems, 0, 0)) actualTargetItems := c.TargetItems() assert.Len(t, actualTargetItems, numItems) actualCollectors := c.Collectors() assert.Len(t, actualCollectors, numCols) for _, col := range actualCollectors { - t.Logf("col: %s \ttargets: %d", col.Name, col.NumTargets) assert.InDelta(t, col.NumTargets, expectedPerCollector, expectedDelta) } } func TestFullReallocation(t *testing.T) { - cols := makeNCollectors(10, 0) + cols := MakeNCollectors(10, 0) c := newConsistentHashingAllocator(logger) c.SetCollectors(cols) - c.SetTargets(makeNNewTargets(10000, 10, 0)) + c.SetTargets(MakeNNewTargets(10000, 10, 0)) actualTargetItems := c.TargetItems() assert.Len(t, actualTargetItems, 10000) actualCollectors := c.Collectors() assert.Len(t, actualCollectors, 10) - newCols := makeNCollectors(10, 10) + newCols := MakeNCollectors(10, 10) c.SetCollectors(newCols) updatedTargetItems := c.TargetItems() assert.Len(t, updatedTargetItems, 10000) @@ -77,15 +76,15 @@ func TestNumRemapped(t *testing.T) { numInitialCols := 15 numFinalCols := 16 expectedDelta := float64((numFinalCols - numInitialCols) * (numItems / numFinalCols)) - cols := makeNCollectors(numInitialCols, 0) + cols := MakeNCollectors(numInitialCols, 0) c := newConsistentHashingAllocator(logger) c.SetCollectors(cols) - c.SetTargets(makeNNewTargets(numItems, numInitialCols, 0)) + c.SetTargets(MakeNNewTargets(numItems, numInitialCols, 0)) actualTargetItems := c.TargetItems() assert.Len(t, actualTargetItems, numItems) actualCollectors := c.Collectors() assert.Len(t, actualCollectors, numInitialCols) - newCols := makeNCollectors(numFinalCols, 0) + newCols := MakeNCollectors(numFinalCols, 0) c.SetCollectors(newCols) updatedTargetItems := c.TargetItems() assert.Len(t, updatedTargetItems, numItems) @@ -104,3 +103,42 @@ func TestNumRemapped(t *testing.T) { } assert.InDelta(t, numItems/numFinalCols, countRemapped, expectedDelta) } + +func TestTargetsWithNoCollectorsConsistentHashing(t *testing.T) { + + c := newConsistentHashingAllocator(logger) + + // Adding 10 new targets + numItems := 10 + c.SetTargets(MakeNNewTargetsWithEmptyCollectors(numItems, 0)) + actualTargetItems := c.TargetItems() + assert.Len(t, actualTargetItems, numItems) + + // Adding 5 new targets, and removing the old 10 targets + numItemsUpdate := 5 + c.SetTargets(MakeNNewTargetsWithEmptyCollectors(numItemsUpdate, 10)) + actualTargetItemsUpdated := c.TargetItems() + assert.Len(t, actualTargetItemsUpdated, numItemsUpdate) + + // Adding 5 new targets, and one existing target + numItemsUpdate = 6 + c.SetTargets(MakeNNewTargetsWithEmptyCollectors(numItemsUpdate, 14)) + actualTargetItemsUpdated = c.TargetItems() + assert.Len(t, actualTargetItemsUpdated, numItemsUpdate) + + // Adding collectors to test allocation + numCols := 2 + cols := MakeNCollectors(2, 0) + c.SetCollectors(cols) + var expectedPerCollector = float64(numItemsUpdate / numCols) + expectedDelta := (expectedPerCollector * 1.5) - expectedPerCollector + // Checking to see that there is no change to number of targets + actualTargetItems = c.TargetItems() + assert.Len(t, actualTargetItems, numItemsUpdate) + // Checking to see collectors are added correctly + actualCollectors := c.Collectors() + assert.Len(t, actualCollectors, numCols) + for _, col := range actualCollectors { + assert.InDelta(t, col.NumTargets, expectedPerCollector, expectedDelta) + } +} diff --git a/cmd/otel-allocator/allocation/http.go b/cmd/otel-allocator/allocation/http.go deleted file mode 100644 index ee60f31609..0000000000 --- a/cmd/otel-allocator/allocation/http.go +++ /dev/null @@ -1,80 +0,0 @@ -// 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 ( - "fmt" - "net/url" - - "github.com/prometheus/common/model" -) - -type LinkJSON struct { - Link string `json:"_link"` -} - -type collectorJSON struct { - Link string `json:"_link"` - Jobs []targetGroupJSON `json:"targets"` -} - -type targetGroupJSON struct { - Targets []string `json:"targets"` - Labels model.LabelSet `json:"labels"` -} - -func GetAllTargetsByJob(job string, cMap map[string][]TargetItem, allocator Allocator) map[string]collectorJSON { - displayData := make(map[string]collectorJSON) - for _, j := range allocator.TargetItems() { - if j.JobName == job { - var targetList []TargetItem - targetList = append(targetList, cMap[j.CollectorName+j.JobName]...) - - var targetGroupList []targetGroupJSON - - for _, t := range targetList { - targetGroupList = append(targetGroupList, targetGroupJSON{ - Targets: []string{t.TargetURL}, - Labels: t.Label, - }) - } - - displayData[j.CollectorName] = collectorJSON{Link: fmt.Sprintf("/jobs/%s/targets?collector_id=%s", url.QueryEscape(j.JobName), j.CollectorName), Jobs: targetGroupList} - - } - } - return displayData -} - -func GetAllTargetsByCollectorAndJob(collector string, job string, cMap map[string][]TargetItem, allocator Allocator) []targetGroupJSON { - var tgs []targetGroupJSON - group := make(map[string]TargetItem) - labelSet := make(map[string]model.LabelSet) - if _, ok := allocator.Collectors()[collector]; ok { - for _, targetItemArr := range cMap { - for _, targetItem := range targetItemArr { - if targetItem.CollectorName == collector && targetItem.JobName == job { - group[targetItem.Label.String()] = targetItem - labelSet[targetItem.Hash()] = targetItem.Label - } - } - } - } - for _, v := range group { - tgs = append(tgs, targetGroupJSON{Targets: []string{v.TargetURL}, Labels: labelSet[v.Hash()]}) - } - - return tgs -} diff --git a/cmd/otel-allocator/allocation/http_test.go b/cmd/otel-allocator/allocation/http_test.go deleted file mode 100644 index e4e7fb7b61..0000000000 --- a/cmd/otel-allocator/allocation/http_test.go +++ /dev/null @@ -1,222 +0,0 @@ -// 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 ( - "reflect" - "testing" - - "github.com/prometheus/common/model" - "github.com/stretchr/testify/assert" -) - -func TestGetAllTargetsByCollectorAndJob(t *testing.T) { - baseAllocator, _ := New("least-weighted", logger) - baseAllocator.SetCollectors(map[string]*Collector{"test-collector": {Name: "test-collector"}}) - statefulAllocator, _ := New("least-weighted", logger) - statefulAllocator.SetCollectors(map[string]*Collector{"test-collector-0": {Name: "test-collector-0"}}) - type args struct { - collector string - job string - cMap map[string][]TargetItem - allocator Allocator - } - var tests = []struct { - name string - args args - want []targetGroupJSON - }{ - { - name: "Empty target map", - args: args{ - collector: "test-collector", - job: "test-job", - cMap: map[string][]TargetItem{}, - allocator: baseAllocator, - }, - want: nil, - }, - { - name: "Single entry target map", - args: args{ - collector: "test-collector", - job: "test-job", - cMap: map[string][]TargetItem{ - "test-collectortest-job": { - TargetItem{ - JobName: "test-job", - Label: model.LabelSet{ - "test-label": "test-value", - }, - TargetURL: "test-url", - CollectorName: "test-collector", - }, - }, - }, - allocator: baseAllocator, - }, - want: []targetGroupJSON{ - { - Targets: []string{"test-url"}, - Labels: map[model.LabelName]model.LabelValue{ - "test-label": "test-value", - }, - }, - }, - }, - { - name: "Multiple entry target map", - args: args{ - collector: "test-collector", - job: "test-job", - cMap: map[string][]TargetItem{ - "test-collectortest-job": { - TargetItem{ - JobName: "test-job", - Label: model.LabelSet{ - "test-label": "test-value", - }, - TargetURL: "test-url", - CollectorName: "test-collector", - }, - }, - "test-collectortest-job2": { - TargetItem{ - JobName: "test-job2", - Label: model.LabelSet{ - "test-label": "test-value", - }, - TargetURL: "test-url", - CollectorName: "test-collector", - }, - }, - }, - allocator: baseAllocator, - }, - want: []targetGroupJSON{ - { - Targets: []string{"test-url"}, - Labels: map[model.LabelName]model.LabelValue{ - "test-label": "test-value", - }, - }, - }, - }, - { - name: "Multiple entry target map of same job with label merge", - args: args{ - collector: "test-collector", - job: "test-job", - cMap: map[string][]TargetItem{ - "test-collectortest-job": { - TargetItem{ - JobName: "test-job", - Label: model.LabelSet{ - "test-label": "test-value", - "foo": "bar", - }, - TargetURL: "test-url1", - CollectorName: "test-collector", - }, - }, - "test-collectortest-job2": { - TargetItem{ - JobName: "test-job", - Label: model.LabelSet{ - "test-label": "test-value", - }, - TargetURL: "test-url2", - CollectorName: "test-collector", - }, - }, - }, - allocator: baseAllocator, - }, - want: []targetGroupJSON{ - { - Targets: []string{"test-url1"}, - Labels: map[model.LabelName]model.LabelValue{ - "test-label": "test-value", - "foo": "bar", - }, - }, - { - Targets: []string{"test-url2"}, - Labels: map[model.LabelName]model.LabelValue{ - "test-label": "test-value", - }, - }, - }, - }, - { - name: "Multiple entry target map with same target address", - args: args{ - collector: "test-collector", - job: "test-job", - cMap: map[string][]TargetItem{ - "test-collectortest-job": { - TargetItem{ - JobName: "test-job", - Label: model.LabelSet{ - "test-label": "test-value", - "foo": "bar", - }, - TargetURL: "test-url", - CollectorName: "test-collector", - }, - TargetItem{ - JobName: "test-job", - Label: model.LabelSet{ - "test-label": "test-value", - }, - TargetURL: "test-url", - CollectorName: "test-collector", - }, - }, - }, - allocator: baseAllocator, - }, - want: []targetGroupJSON{ - { - Targets: []string{"test-url"}, - Labels: map[model.LabelName]model.LabelValue{ - "test-label": "test-value", - "foo": "bar", - }, - }, - { - Targets: []string{"test-url"}, - Labels: map[model.LabelName]model.LabelValue{ - "test-label": "test-value", - }, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - targetGroups := GetAllTargetsByCollectorAndJob(tt.args.collector, tt.args.job, tt.args.cMap, tt.args.allocator) - for _, wantGroupJson := range tt.want { - exist := false - for _, groupJSON := range targetGroups { - if groupJSON.Labels.String() == wantGroupJson.Labels.String() { - exist = reflect.DeepEqual(groupJSON, wantGroupJson) - } - } - assert.Equal(t, true, exist) - } - }) - } -} diff --git a/cmd/otel-allocator/allocation/least_weighted.go b/cmd/otel-allocator/allocation/least_weighted.go index bed1dbd51f..6ae9c5eb2b 100644 --- a/cmd/otel-allocator/allocation/least_weighted.go +++ b/cmd/otel-allocator/allocation/least_weighted.go @@ -15,11 +15,10 @@ package allocation import ( - "fmt" - "net/url" "sync" "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/diff" + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" "github.com/go-logr/logr" "github.com/prometheus/client_golang/prometheus" @@ -30,9 +29,9 @@ var _ Allocator = &leastWeightedAllocator{} const leastWeightedStrategyName = "least-weighted" /* - Load balancer will serve on an HTTP server exposing /jobs//targets + Target Allocator will serve on an HTTP server exposing /jobs//targets The targets are allocated using the least connection method - Load balancer will need information about the collectors in order to set the URLs + 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 */ @@ -46,16 +45,44 @@ type leastWeightedAllocator struct { // 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]*TargetItem + targetItems map[string]*target.Item + + // collectorKey -> job -> target item hash -> true + targetItemsPerJobPerCollector map[string]map[string]map[string]bool log logr.Logger + + filter Filter +} + +// SetFilter sets the filtering hook to use. +func (allocator *leastWeightedAllocator) SetFilter(filter Filter) { + allocator.filter = filter +} + +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 } // TargetItems returns a shallow copy of the targetItems map. -func (allocator *leastWeightedAllocator) TargetItems() map[string]*TargetItem { +func (allocator *leastWeightedAllocator) TargetItems() map[string]*target.Item { allocator.m.RLock() defer allocator.m.RUnlock() - targetItemsCopy := make(map[string]*TargetItem) + targetItemsCopy := make(map[string]*target.Item) for k, v := range allocator.targetItems { targetItemsCopy[k] = v } @@ -90,20 +117,30 @@ func (allocator *leastWeightedAllocator) findNextCollector() *Collector { 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. -func (allocator *leastWeightedAllocator) addTargetToTargetItems(target *TargetItem) { +// 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() - targetItem := &TargetItem{ - JobName: target.JobName, - Link: LinkJSON{Link: fmt.Sprintf("/jobs/%s/targets", url.QueryEscape(target.JobName))}, - TargetURL: target.TargetURL, - Label: target.Label, - CollectorName: chosenCollector.Name, - } - allocator.targetItems[targetItem.Hash()] = targetItem + tg.CollectorName = chosenCollector.Name + allocator.targetItems[tg.Hash()] = tg + allocator.addCollectorTargetItemMapping(tg) chosenCollector.NumTargets++ TargetsPerCollector.WithLabelValues(chosenCollector.Name, leastWeightedStrategyName).Set(float64(chosenCollector.NumTargets)) } @@ -111,26 +148,27 @@ func (allocator *leastWeightedAllocator) addTargetToTargetItems(target *TargetIt // 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[*TargetItem]) { +func (allocator *leastWeightedAllocator) handleTargets(diff diff.Changes[*target.Item]) { // Check for removals - for k, target := range allocator.targetItems { - // if the current target is in the removals list + for k, item := range allocator.targetItems { + // if the current item is in the removals list if _, ok := diff.Removals()[k]; ok { - c := allocator.collectors[target.CollectorName] + c := allocator.collectors[item.CollectorName] c.NumTargets-- delete(allocator.targetItems, k) - TargetsPerCollector.WithLabelValues(target.CollectorName, leastWeightedStrategyName).Set(float64(c.NumTargets)) + delete(allocator.targetItemsPerJobPerCollector[item.CollectorName][item.JobName], item.Hash()) + TargetsPerCollector.WithLabelValues(item.CollectorName, leastWeightedStrategyName).Set(float64(c.NumTargets)) } } // Check for additions - for k, target := range diff.Additions() { + for k, item := range diff.Additions() { // Do nothing if the item is already there if _, ok := allocator.targetItems[k]; ok { continue } else { - // Add target to target pool and assign a collector - allocator.addTargetToTargetItems(target) + // Add item to item pool and assign a collector + allocator.addTargetToTargetItems(item) } } } @@ -142,12 +180,24 @@ func (allocator *leastWeightedAllocator) handleCollectors(diff diff.Changes[*Col // 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) } + if allocateTargets { + for _, item := range allocator.targetItems { + allocator.addTargetToTargetItems(item) + } + } // Re-Allocate targets of the removed collectors for _, item := range allocator.targetItems { @@ -160,15 +210,53 @@ func (allocator *leastWeightedAllocator) handleCollectors(diff diff.Changes[*Col // 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]*TargetItem) { +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, cannot set targets") + 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 @@ -182,13 +270,12 @@ func (allocator *leastWeightedAllocator) SetTargets(targets map[string]*TargetIt // 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) { - log := allocator.log.WithValues("component", "opentelemetry-targetallocator") timer := prometheus.NewTimer(TimeToAssign.WithLabelValues("SetCollectors", leastWeightedStrategyName)) defer timer.ObserveDuration() CollectorsAllocatable.WithLabelValues(leastWeightedStrategyName).Set(float64(len(collectors))) if len(collectors) == 0 { - log.Info("No collector instances present") + allocator.log.Info("No collector instances present") return } @@ -202,10 +289,17 @@ func (allocator *leastWeightedAllocator) SetCollectors(collectors map[string]*Co } } -func newLeastWeightedAllocator(log logr.Logger) Allocator { - return &leastWeightedAllocator{ - log: log, - collectors: make(map[string]*Collector), - targetItems: make(map[string]*TargetItem), +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 } diff --git a/cmd/otel-allocator/allocation/least_weighted_test.go b/cmd/otel-allocator/allocation/least_weighted_test.go index 3b6209ddd5..417c0e5ed3 100644 --- a/cmd/otel-allocator/allocation/least_weighted_test.go +++ b/cmd/otel-allocator/allocation/least_weighted_test.go @@ -15,57 +15,23 @@ package allocation import ( - "fmt" "math" "math/rand" - "strconv" "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 colIndex(index, numCols int) int { - if numCols == 0 { - return -1 - } - return index % numCols -} - -func makeNNewTargets(n int, numCollectors int, startingIndex int) map[string]*TargetItem { - toReturn := map[string]*TargetItem{} - for i := startingIndex; i < n+startingIndex; i++ { - collector := fmt.Sprintf("collector-%d", colIndex(i, numCollectors)) - label := model.LabelSet{ - "collector": model.LabelValue(collector), - "i": model.LabelValue(strconv.Itoa(i)), - "total": model.LabelValue(strconv.Itoa(n + startingIndex)), - } - newTarget := NewTargetItem(fmt.Sprintf("test-job-%d", i), "test-url", label, collector) - toReturn[newTarget.Hash()] = newTarget - } - return toReturn -} - -func makeNCollectors(n int, startingIndex int) map[string]*Collector { - toReturn := map[string]*Collector{} - for i := startingIndex; i < n+startingIndex; i++ { - collector := fmt.Sprintf("collector-%d", i) - toReturn[collector] = &Collector{ - Name: collector, - NumTargets: 0, - } - } - return toReturn -} - func TestSetCollectors(t *testing.T) { s, _ := New("least-weighted", logger) - cols := makeNCollectors(3, 0) + cols := MakeNCollectors(3, 0) s.SetCollectors(cols) expectedColLen := len(cols) @@ -81,10 +47,10 @@ func TestAddingAndRemovingTargets(t *testing.T) { // prepare allocator with initial targets and collectors s, _ := New("least-weighted", logger) - cols := makeNCollectors(3, 0) + cols := MakeNCollectors(3, 0) s.SetCollectors(cols) - initTargets := makeNNewTargets(6, 3, 0) + initTargets := MakeNNewTargets(6, 3, 0) // test that targets and collectors are added properly s.SetTargets(initTargets) @@ -94,7 +60,7 @@ func TestAddingAndRemovingTargets(t *testing.T) { assert.Len(t, s.TargetItems(), expectedTargetLen) // prepare second round of targets - tar := makeNNewTargets(4, 3, 0) + tar := MakeNNewTargets(4, 3, 0) // test that fewer targets are found - removed s.SetTargets(tar) @@ -116,7 +82,7 @@ func TestAllocationCollision(t *testing.T) { // prepare allocator with initial targets and collectors s, _ := New("least-weighted", logger) - cols := makeNCollectors(3, 0) + cols := MakeNCollectors(3, 0) s.SetCollectors(cols) firstLabels := model.LabelSet{ "test": "test1", @@ -124,10 +90,10 @@ func TestAllocationCollision(t *testing.T) { secondLabels := model.LabelSet{ "test": "test2", } - firstTarget := NewTargetItem("sample-name", "0.0.0.0:8000", firstLabels, "") - secondTarget := NewTargetItem("sample-name", "0.0.0.0:8000", secondLabels, "") + 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]*TargetItem{ + targetList := map[string]*target.Item{ firstTarget.Hash(): firstTarget, secondTarget.Hash(): secondTarget, } @@ -150,7 +116,7 @@ func TestAllocationCollision(t *testing.T) { func TestNoCollectorReassignment(t *testing.T) { s, _ := New("least-weighted", logger) - cols := makeNCollectors(3, 0) + cols := MakeNCollectors(3, 0) s.SetCollectors(cols) expectedColLen := len(cols) @@ -159,7 +125,7 @@ func TestNoCollectorReassignment(t *testing.T) { for _, i := range cols { assert.NotNil(t, s.Collectors()[i.Name]) } - initTargets := makeNNewTargets(6, 3, 0) + initTargets := MakeNNewTargets(6, 3, 0) // test that targets and collectors are added properly s.SetTargets(initTargets) @@ -170,7 +136,7 @@ func TestNoCollectorReassignment(t *testing.T) { assert.Len(t, targetItems, expectedTargetLen) // assign new set of collectors with the same names - newCols := makeNCollectors(3, 0) + newCols := MakeNCollectors(3, 0) s.SetCollectors(newCols) newTargetItems := s.TargetItems() @@ -179,9 +145,10 @@ func TestNoCollectorReassignment(t *testing.T) { } func TestSmartCollectorReassignment(t *testing.T) { + t.Skip("This test is flaky and fails frequently, see issue 1291") s, _ := New("least-weighted", logger) - cols := makeNCollectors(4, 0) + cols := MakeNCollectors(4, 0) s.SetCollectors(cols) expectedColLen := len(cols) @@ -190,7 +157,7 @@ func TestSmartCollectorReassignment(t *testing.T) { for _, i := range cols { assert.NotNil(t, s.Collectors()[i.Name]) } - initTargets := makeNNewTargets(6, 0, 0) + initTargets := MakeNNewTargets(6, 0, 0) // test that targets and collectors are added properly s.SetTargets(initTargets) @@ -232,10 +199,10 @@ func TestCollectorBalanceWhenAddingAndRemovingAtRandom(t *testing.T) { // prepare allocator with 3 collectors and 'random' amount of targets s, _ := New("least-weighted", logger) - cols := makeNCollectors(3, 0) + cols := MakeNCollectors(3, 0) s.SetCollectors(cols) - targets := makeNNewTargets(27, 3, 0) + targets := MakeNNewTargets(27, 3, 0) s.SetTargets(targets) // Divisor needed to get 15% @@ -274,7 +241,7 @@ func TestCollectorBalanceWhenAddingAndRemovingAtRandom(t *testing.T) { assert.InDelta(t, i.NumTargets, count, math.Round(percent)) } // adding targets at 'random' - for _, item := range makeNNewTargets(13, 3, 100) { + for _, item := range MakeNNewTargets(13, 3, 100) { targets[item.Hash()] = item } s.SetTargets(targets) @@ -289,3 +256,51 @@ func TestCollectorBalanceWhenAddingAndRemovingAtRandom(t *testing.T) { assert.InDelta(t, i.NumTargets, count, math.Round(percent)) } } + +func TestTargetsWithNoCollectorsLeastWeighted(t *testing.T) { + s, _ := New("least-weighted", logger) + + // Adding 10 new targets + numItems := 10 + initTargets := MakeNNewTargetsWithEmptyCollectors(numItems, 0) + s.SetTargets(initTargets) + actualTargetItems := s.TargetItems() + assert.Len(t, actualTargetItems, numItems) + + // Adding 5 new targets, and removing the old 10 targets + numItemsUpdate := 5 + newTargets := MakeNNewTargetsWithEmptyCollectors(numItemsUpdate, 10) + s.SetTargets(newTargets) + actualTargetItems = s.TargetItems() + assert.Len(t, actualTargetItems, numItemsUpdate) + + // Adding 5 new targets, and one existing target + numItemsUpdate = 6 + newTargets = MakeNNewTargetsWithEmptyCollectors(numItemsUpdate, 14) + s.SetTargets(newTargets) + actualTargetItems = s.TargetItems() + assert.Len(t, actualTargetItems, numItemsUpdate) + + // Adding collectors to test allocation + numCols := 2 + cols := MakeNCollectors(2, 0) + s.SetCollectors(cols) + + // Checking to see that there is no change to number of targets + actualTargetItems = s.TargetItems() + assert.Len(t, actualTargetItems, numItemsUpdate) + // Checking to see collectors are added correctly + actualCollectors := s.Collectors() + assert.Len(t, actualCollectors, numCols) + + // Divisor needed to get 15% + divisor := 6.7 + targetItemLen := len(actualTargetItems) + count := targetItemLen / len(actualCollectors) + percent := float64(targetItemLen) / divisor + + // Check to see targets are allocated with the expected delta + for _, i := range actualCollectors { + assert.InDelta(t, i.NumTargets, count, math.Round(percent)) + } +} diff --git a/cmd/otel-allocator/allocation/strategy.go b/cmd/otel-allocator/allocation/strategy.go index 2808c32b5d..b994557732 100644 --- a/cmd/otel-allocator/allocation/strategy.go +++ b/cmd/otel-allocator/allocation/strategy.go @@ -17,16 +17,16 @@ package allocation import ( "errors" "fmt" - "net/url" "github.com/buraksezer/consistent" "github.com/go-logr/logr" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/prometheus/common/model" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" ) -type AllocatorProvider func(log logr.Logger) Allocator +type AllocatorProvider func(log logr.Logger, opts ...AllocationOption) Allocator var ( registry = map[string]AllocatorProvider{} @@ -45,11 +45,31 @@ var ( Name: "opentelemetry_allocator_time_to_allocate", Help: "The time it takes to allocate", }, []string{"method", "strategy"}) + targetsRemaining = promauto.NewCounter(prometheus.CounterOpts{ + Name: "opentelemetry_allocator_targets_remaining", + Help: "Number of targets kept after filtering.", + }) ) -func New(name string, log logr.Logger) (Allocator, error) { +type AllocationOption func(Allocator) + +type Filter interface { + Apply(map[string]*target.Item) map[string]*target.Item +} + +func WithFilter(filter Filter) AllocationOption { + return func(allocator Allocator) { + allocator.SetFilter(filter) + } +} + +func RecordTargetsKept(targets map[string]*target.Item) { + targetsRemaining.Add(float64(len(targets))) +} + +func New(name string, log logr.Logger, opts ...AllocationOption) (Allocator, error) { if p, ok := registry[name]; ok { - return p(log), nil + return p(log.WithValues("allocator", name), opts...), nil } return nil, fmt.Errorf("unregistered strategy: %s", name) } @@ -62,33 +82,21 @@ func Register(name string, provider AllocatorProvider) error { return nil } -type Allocator interface { - SetCollectors(collectors map[string]*Collector) - SetTargets(targets map[string]*TargetItem) - TargetItems() map[string]*TargetItem - Collectors() map[string]*Collector -} - -type TargetItem struct { - JobName string - Link LinkJSON - TargetURL string - Label model.LabelSet - CollectorName string -} - -func NewTargetItem(jobName string, targetURL string, label model.LabelSet, collectorName string) *TargetItem { - return &TargetItem{ - JobName: jobName, - Link: LinkJSON{fmt.Sprintf("/jobs/%s/targets", url.QueryEscape(jobName))}, - TargetURL: targetURL, - Label: label, - CollectorName: collectorName, +func GetRegisteredAllocatorNames() []string { + var names []string + for s := range registry { + names = append(names, s) } + return names } -func (t TargetItem) Hash() string { - return t.JobName + t.TargetURL + t.Label.Fingerprint().String() +type Allocator interface { + SetCollectors(collectors map[string]*Collector) + SetTargets(targets map[string]*target.Item) + TargetItems() map[string]*target.Item + Collectors() map[string]*Collector + GetTargetsForCollectorAndJob(collector string, job string) []*target.Item + SetFilter(filter Filter) } var _ consistent.Member = Collector{} @@ -101,6 +109,10 @@ type Collector struct { NumTargets int } +func (c Collector) Hash() string { + return c.Name +} + func (c Collector) String() string { return c.Name } diff --git a/cmd/otel-allocator/allocation/strategy_test.go b/cmd/otel-allocator/allocation/strategy_test.go new file mode 100644 index 0000000000..c12529d8d8 --- /dev/null +++ b/cmd/otel-allocator/allocation/strategy_test.go @@ -0,0 +1,136 @@ +// 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 ( + "fmt" + "reflect" + "testing" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/diff" +) + +func BenchmarkGetAllTargetsByCollectorAndJob(b *testing.B) { + var table = []struct { + numCollectors int + numJobs int + }{ + {numCollectors: 100, numJobs: 100}, + {numCollectors: 100, numJobs: 1000}, + {numCollectors: 100, numJobs: 10000}, + {numCollectors: 100, numJobs: 100000}, + {numCollectors: 1000, numJobs: 100}, + {numCollectors: 1000, numJobs: 1000}, + {numCollectors: 1000, numJobs: 10000}, + {numCollectors: 1000, numJobs: 100000}, + } + for _, s := range GetRegisteredAllocatorNames() { + for _, v := range table { + a, err := New(s, logger) + if err != nil { + b.Log(err) + b.Fail() + } + cols := MakeNCollectors(v.numCollectors, 0) + jobs := MakeNNewTargets(v.numJobs, v.numCollectors, 0) + a.SetCollectors(cols) + a.SetTargets(jobs) + b.Run(fmt.Sprintf("%s_num_cols_%d_num_jobs_%d", s, v.numCollectors, v.numJobs), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + a.GetTargetsForCollectorAndJob(fmt.Sprintf("collector-%d", v.numCollectors/2), fmt.Sprintf("test-job-%d", v.numJobs/2)) + } + }) + } + } +} + +func Benchmark_Setting(b *testing.B) { + var table = []struct { + numCollectors int + numTargets int + }{ + {numCollectors: 100, numTargets: 100}, + {numCollectors: 100, numTargets: 1000}, + {numCollectors: 100, numTargets: 10000}, + {numCollectors: 100, numTargets: 100000}, + {numCollectors: 1000, numTargets: 100}, + {numCollectors: 1000, numTargets: 1000}, + {numCollectors: 1000, numTargets: 10000}, + {numCollectors: 1000, numTargets: 100000}, + } + + for _, s := range GetRegisteredAllocatorNames() { + for _, v := range table { + a, _ := New(s, logger) + cols := MakeNCollectors(v.numCollectors, 0) + targets := MakeNNewTargets(v.numTargets, v.numCollectors, 0) + b.Run(fmt.Sprintf("%s_num_cols_%d_num_jobs_%d", s, v.numCollectors, v.numTargets), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + a.SetCollectors(cols) + a.SetTargets(targets) + } + }) + } + } +} + +func TestCollectorDiff(t *testing.T) { + collector0 := NewCollector("collector-0") + collector1 := NewCollector("collector-1") + collector2 := NewCollector("collector-2") + collector3 := NewCollector("collector-3") + collector4 := NewCollector("collector-4") + type args struct { + current map[string]*Collector + new map[string]*Collector + } + tests := []struct { + name string + args args + want diff.Changes[*Collector] + }{ + { + name: "diff two collector maps", + args: args{ + current: map[string]*Collector{ + "collector-0": collector0, + "collector-1": collector1, + "collector-2": collector2, + "collector-3": collector3, + }, + new: map[string]*Collector{ + "collector-0": collector0, + "collector-1": collector1, + "collector-2": collector2, + "collector-4": collector4, + }, + }, + want: diff.NewChanges(map[string]*Collector{ + "collector-4": collector4, + }, map[string]*Collector{ + "collector-3": collector3, + }), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := diff.Maps(tt.args.current, tt.args.new); !reflect.DeepEqual(got, tt.want) { + t.Errorf("DiffMaps() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/otel-allocator/collector/collector.go b/cmd/otel-allocator/collector/collector.go index f6e7f0ff89..eaeee8e16c 100644 --- a/cmd/otel-allocator/collector/collector.go +++ b/cmd/otel-allocator/collector/collector.go @@ -17,7 +17,6 @@ package collector import ( "context" "os" - "strconv" "time" "github.com/go-logr/logr" @@ -58,22 +57,21 @@ func NewClient(logger logr.Logger, kubeConfig *rest.Config) (*Client, error) { } return &Client{ - log: logger, + log: logger.WithValues("component", "opentelemetry-targetallocator"), k8sClient: clientset, close: make(chan struct{}), }, nil } -func (k *Client) Watch(ctx context.Context, labelMap map[string]string, fn func(collectors map[string]*allocation.Collector)) { +func (k *Client) Watch(ctx context.Context, labelMap map[string]string, fn func(collectors map[string]*allocation.Collector)) error { collectorMap := map[string]*allocation.Collector{} - log := k.log.WithValues("component", "opentelemetry-targetallocator") opts := metav1.ListOptions{ LabelSelector: labels.SelectorFromSet(labelMap).String(), } pods, err := k.k8sClient.CoreV1().Pods(ns).List(ctx, opts) if err != nil { - log.Error(err, "Pod failure") + k.log.Error(err, "Pod failure") os.Exit(1) } for i := range pods.Items { @@ -85,41 +83,49 @@ func (k *Client) Watch(ctx context.Context, labelMap map[string]string, fn func( fn(collectorMap) - go func() { - for { - watcher, err := k.k8sClient.CoreV1().Pods(ns).Watch(ctx, opts) - if err != nil { - log.Error(err, "unable to create collector pod watcher") - return - } - log.Info("Successfully started a collector pod watcher") - if msg := runWatch(ctx, k, watcher.ResultChan(), collectorMap, fn); msg != "" { - log.Info("Collector pod watch event stopped " + msg) - return - } + for { + if !k.restartWatch(ctx, opts, collectorMap, fn) { + return nil } - }() + } +} + +func (k *Client) restartWatch(ctx context.Context, opts metav1.ListOptions, collectorMap map[string]*allocation.Collector, fn func(collectors map[string]*allocation.Collector)) bool { + // add timeout to the context before calling Watch + ctx, cancel := context.WithTimeout(ctx, watcherTimeout) + defer cancel() + watcher, err := k.k8sClient.CoreV1().Pods(ns).Watch(ctx, opts) + if err != nil { + k.log.Error(err, "unable to create collector pod watcher") + return false + } + k.log.Info("Successfully started a collector pod watcher") + if msg := runWatch(ctx, k, watcher.ResultChan(), collectorMap, fn); msg != "" { + k.log.Info("Collector pod watch event stopped " + msg) + return false + } + + return true } func runWatch(ctx context.Context, k *Client, c <-chan watch.Event, collectorMap map[string]*allocation.Collector, fn func(collectors map[string]*allocation.Collector)) string { - log := k.log.WithValues("component", "opentelemetry-targetallocator") for { collectorsDiscovered.Set(float64(len(collectorMap))) select { case <-k.close: return "kubernetes client closed" case <-ctx.Done(): - return "context done" + return "" case event, ok := <-c: if !ok { - log.Info(strconv.FormatBool(ok)) - return "no event" + k.log.Info("No event found. Restarting watch routine") + return "" } pod, ok := event.Object.(*v1.Pod) if !ok { - log.Info(strconv.FormatBool(ok)) - return "no event" + k.log.Info("No pod found in event Object. Restarting watch routine") + return "" } switch event.Type { //nolint:exhaustive @@ -129,9 +135,6 @@ func runWatch(ctx context.Context, k *Client, c <-chan watch.Event, collectorMap delete(collectorMap, pod.Name) } fn(collectorMap) - case <-time.After(watcherTimeout): - log.Info("Restarting watch routine") - return "" } } } diff --git a/cmd/otel-allocator/collector/collector_test.go b/cmd/otel-allocator/collector/collector_test.go index d4bd1680dc..5367828e23 100644 --- a/cmd/otel-allocator/collector/collector_test.go +++ b/cmd/otel-allocator/collector/collector_test.go @@ -18,7 +18,12 @@ import ( "context" "fmt" "os" + "sync" "testing" + "time" + + "k8s.io/apimachinery/pkg/watch" + logf "sigs.k8s.io/controller-runtime/pkg/log" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" @@ -29,13 +34,13 @@ import ( "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation" ) -var client Client -var collectors = map[string]*allocation.Collector{} +var logger = logf.Log.WithName("collector-unit-tests") -func TestMain(m *testing.M) { - client = Client{ +func getTestClient() (Client, watch.Interface) { + kubeClient := Client{ k8sClient: fake.NewSimpleClientset(), close: make(chan struct{}), + log: logger, } labelMap := map[string]string{ @@ -46,72 +51,171 @@ func TestMain(m *testing.M) { opts := metav1.ListOptions{ LabelSelector: labels.SelectorFromSet(labelMap).String(), } - - watcher, err := client.k8sClient.CoreV1().Pods("test-ns").Watch(context.Background(), opts) + watcher, err := kubeClient.k8sClient.CoreV1().Pods("test-ns").Watch(context.Background(), opts) if err != nil { fmt.Printf("failed to setup a Collector Pod watcher: %v", err) os.Exit(1) } + return kubeClient, watcher +} - go runWatch(context.Background(), &client, watcher.ResultChan(), map[string]*allocation.Collector{}, func(colMap map[string]*allocation.Collector) { getCollectors(colMap) }) - - code := m.Run() - - close(client.close) +func pod(name string) *v1.Pod { + labelSet := make(map[string]string) + labelSet["app.kubernetes.io/instance"] = "default.test" + labelSet["app.kubernetes.io/managed-by"] = "opentelemetry-operator" - os.Exit(code) + return &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "test-ns", + Labels: labelSet, + }, + } } -func TestWatchPodAddition(t *testing.T) { - expected := map[string]*allocation.Collector{ - "test-pod1": { - Name: "test-pod1", - }, - "test-pod2": { - Name: "test-pod2", +func Test_runWatch(t *testing.T) { + type args struct { + kubeFn func(t *testing.T, client Client, group *sync.WaitGroup) + collectorMap map[string]*allocation.Collector + } + tests := []struct { + name string + args args + want map[string]*allocation.Collector + }{ + { + name: "pod add", + args: args{ + kubeFn: func(t *testing.T, client Client, group *sync.WaitGroup) { + for _, k := range []string{"test-pod1", "test-pod2", "test-pod3"} { + p := pod(k) + group.Add(1) + _, err := client.k8sClient.CoreV1().Pods("test-ns").Create(context.Background(), p, metav1.CreateOptions{}) + assert.NoError(t, err) + } + }, + collectorMap: map[string]*allocation.Collector{}, + }, + want: map[string]*allocation.Collector{ + "test-pod1": { + Name: "test-pod1", + }, + "test-pod2": { + Name: "test-pod2", + }, + "test-pod3": { + Name: "test-pod3", + }, + }, }, - "test-pod3": { - Name: "test-pod3", + { + name: "pod delete", + args: args{ + kubeFn: func(t *testing.T, client Client, group *sync.WaitGroup) { + for _, k := range []string{"test-pod2", "test-pod3"} { + group.Add(1) + err := client.k8sClient.CoreV1().Pods("test-ns").Delete(context.Background(), k, metav1.DeleteOptions{}) + assert.NoError(t, err) + } + }, + collectorMap: map[string]*allocation.Collector{ + "test-pod1": { + Name: "test-pod1", + }, + "test-pod2": { + Name: "test-pod2", + }, + "test-pod3": { + Name: "test-pod3", + }, + }, + }, + want: map[string]*allocation.Collector{ + "test-pod1": { + Name: "test-pod1", + }, + }, }, } - - for _, k := range []string{"test-pod1", "test-pod2", "test-pod3"} { - expected := pod(k) - _, err := client.k8sClient.CoreV1().Pods("test-ns").Create(context.Background(), expected, metav1.CreateOptions{}) - assert.NoError(t, err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + kubeClient, watcher := getTestClient() + defer func() { + close(kubeClient.close) + watcher.Stop() + }() + var wg sync.WaitGroup + actual := make(map[string]*allocation.Collector) + for _, k := range tt.args.collectorMap { + p := pod(k.Name) + _, err := kubeClient.k8sClient.CoreV1().Pods("test-ns").Create(context.Background(), p, metav1.CreateOptions{}) + wg.Add(1) + assert.NoError(t, err) + } + go runWatch(context.Background(), &kubeClient, watcher.ResultChan(), map[string]*allocation.Collector{}, func(colMap map[string]*allocation.Collector) { + actual = colMap + wg.Done() + }) + + tt.args.kubeFn(t, kubeClient, &wg) + wg.Wait() + + assert.Len(t, actual, len(tt.want)) + assert.Equal(t, actual, tt.want) + }) } - - assert.Len(t, collectors, 3) - assert.Equal(t, collectors, expected) } -func TestWatchPodDeletion(t *testing.T) { - expected := []string{"test-pod1"} - - for _, k := range []string{"test-pod2", "test-pod3"} { - err := client.k8sClient.CoreV1().Pods("test-ns").Delete(context.Background(), k, metav1.DeleteOptions{}) - assert.NoError(t, err) +// this tests runWatch in the case of watcher channel closing and watcher timing out. +func Test_closeChannel(t *testing.T) { + tests := []struct { + description string + isCloseChannel bool + timeoutSeconds time.Duration + }{ + { + // event is triggered by channel closing. + description: "close_channel", + isCloseChannel: true, + // channel should be closed before this timeout occurs + timeoutSeconds: 10 * time.Second, + }, + { + // event triggered by timeout. + description: "watcher_timeout", + isCloseChannel: false, + timeoutSeconds: 0 * time.Second, + }, } - assert.Len(t, collectors, 1) - - assert.Equal(t, collectors, expected) -} - -func getCollectors(c map[string]*allocation.Collector) { - collectors = c -} - -func pod(name string) *v1.Pod { - labels := make(map[string]string) - labels["app.kubernetes.io/instance"] = "default.test" - labels["app.kubernetes.io/managed-by"] = "opentelemetry-operator" - - return &v1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: "test-ns", - Labels: labels, - }, + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + kubeClient, watcher := getTestClient() + + defer func() { + close(kubeClient.close) + watcher.Stop() + }() + var wg sync.WaitGroup + wg.Add(1) + terminated := false + + go func(watcher watch.Interface) { + defer wg.Done() + ctx, cancel := context.WithTimeout(context.Background(), tc.timeoutSeconds) + defer cancel() + if msg := runWatch(ctx, &kubeClient, watcher.ResultChan(), map[string]*allocation.Collector{}, func(colMap map[string]*allocation.Collector) {}); msg != "" { + terminated = true + return + } + }(watcher) + + if tc.isCloseChannel { + // stop pod watcher to trigger event. + watcher.Stop() + } + wg.Wait() + assert.False(t, terminated) + }) } } diff --git a/cmd/otel-allocator/config/config.go b/cmd/otel-allocator/config/config.go index 0fa8879f0e..d91b057e19 100644 --- a/cmd/otel-allocator/config/config.go +++ b/cmd/otel-allocator/config/config.go @@ -15,21 +15,20 @@ package config import ( - "flag" + "errors" "fmt" "io/fs" "os" - "path/filepath" "time" "github.com/go-logr/logr" + "github.com/prometheus/common/model" promconfig "github.com/prometheus/prometheus/config" _ "github.com/prometheus/prometheus/discovery/install" "github.com/spf13/pflag" "gopkg.in/yaml.v2" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/homedir" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -37,11 +36,25 @@ import ( const DefaultResyncTime = 5 * time.Minute const DefaultConfigFilePath string = "/conf/targetallocator.yaml" +const DefaultCRScrapeInterval model.Duration = model.Duration(time.Second * 30) type Config struct { - LabelSelector map[string]string `yaml:"label_selector,omitempty"` - Config *promconfig.Config `yaml:"config"` - AllocationStrategy *string `yaml:"allocation_strategy,omitempty"` + ListenAddr string `yaml:"listen_addr,omitempty"` + KubeConfigFilePath string `yaml:"kube_config_file_path,omitempty"` + ClusterConfig *rest.Config `yaml:"-"` + RootLogger logr.Logger `yaml:"-"` + LabelSelector map[string]string `yaml:"label_selector,omitempty"` + PromConfig *promconfig.Config `yaml:"config"` + AllocationStrategy *string `yaml:"allocation_strategy,omitempty"` + FilterStrategy *string `yaml:"filter_strategy,omitempty"` + PrometheusCR PrometheusCRConfig `yaml:"prometheus_cr,omitempty"` + PodMonitorSelector map[string]string `yaml:"pod_monitor_selector,omitempty"` + ServiceMonitorSelector map[string]string `yaml:"service_monitor_selector,omitempty"` +} + +type PrometheusCRConfig struct { + Enabled bool `yaml:"enabled,omitempty"` + ScrapeInterval model.Duration `yaml:"scrape_interval,omitempty"` } func (c Config) GetAllocationStrategy() string { @@ -51,26 +64,53 @@ func (c Config) GetAllocationStrategy() string { return "least-weighted" } -type PrometheusCRWatcherConfig struct { - Enabled *bool +func (c Config) GetTargetsFilterStrategy() string { + if c.FilterStrategy != nil { + return *c.FilterStrategy + } + return "" } -type CLIConfig struct { - ListenAddr *string - ConfigFilePath *string - ClusterConfig *rest.Config - // KubeConfigFilePath empty if in cluster configuration is in use - KubeConfigFilePath string - RootLogger logr.Logger - PromCRWatcherConf PrometheusCRWatcherConfig +func LoadFromFile(file string, target *Config) error { + return unmarshal(target, file) } -func Load(file string) (Config, error) { - var cfg Config - if err := unmarshal(&cfg, file); err != nil { - return Config{}, err +func LoadFromCLI(target *Config, flagSet *pflag.FlagSet) error { + var err error + // set the rest of the config attributes based on command-line flag values + target.RootLogger = zap.New(zap.UseFlagOptions(&zapCmdLineOpts)) + klog.SetLogger(target.RootLogger) + ctrl.SetLogger(target.RootLogger) + + target.KubeConfigFilePath, err = getKubeConfigFilePath(flagSet) + if err != nil { + return err } - return cfg, nil + clusterConfig, err := clientcmd.BuildConfigFromFlags("", target.KubeConfigFilePath) + if err != nil { + pathError := &fs.PathError{} + if ok := errors.As(err, &pathError); !ok { + return err + } + clusterConfig, err = rest.InClusterConfig() + if err != nil { + return err + } + target.KubeConfigFilePath = "" + } + target.ClusterConfig = clusterConfig + + target.ListenAddr, err = getListenAddr(flagSet) + if err != nil { + return err + } + + target.PrometheusCR.Enabled, err = getPrometheusCREnabled(flagSet) + if err != nil { + return err + } + + return nil } func unmarshal(cfg *Config, configFile string) error { @@ -85,35 +125,48 @@ func unmarshal(cfg *Config, configFile string) error { return nil } -func ParseCLI() (CLIConfig, error) { - opts := zap.Options{} - opts.BindFlags(flag.CommandLine) - cLIConf := CLIConfig{ - ListenAddr: pflag.String("listen-addr", ":8080", "The address where this service serves."), - ConfigFilePath: pflag.String("config-file", DefaultConfigFilePath, "The path to the config file."), - PromCRWatcherConf: PrometheusCRWatcherConfig{ - Enabled: pflag.Bool("enable-prometheus-cr-watcher", false, "Enable Prometheus CRs as target sources"), +func CreateDefaultConfig() Config { + return Config{ + PrometheusCR: PrometheusCRConfig{ + ScrapeInterval: DefaultCRScrapeInterval, }, } - kubeconfigPath := pflag.String("kubeconfig-path", filepath.Join(homedir.HomeDir(), ".kube", "config"), "absolute path to the KubeconfigPath file") - pflag.Parse() +} - cLIConf.RootLogger = zap.New(zap.UseFlagOptions(&opts)) - klog.SetLogger(cLIConf.RootLogger) - ctrl.SetLogger(cLIConf.RootLogger) +func Load() (*Config, error) { + var err error - clusterConfig, err := clientcmd.BuildConfigFromFlags("", *kubeconfigPath) - cLIConf.KubeConfigFilePath = *kubeconfigPath + flagSet := getFlagSet(pflag.ExitOnError) + err = flagSet.Parse(os.Args) if err != nil { - if _, ok := err.(*fs.PathError); !ok { - return CLIConfig{}, err - } - clusterConfig, err = rest.InClusterConfig() - if err != nil { - return CLIConfig{}, err - } - cLIConf.KubeConfigFilePath = "" // reset as we use in cluster configuration + return nil, err + } + + config := CreateDefaultConfig() + + // load the config from the config file + configFilePath, err := getConfigFilePath(flagSet) + if err != nil { + return nil, err } - cLIConf.ClusterConfig = clusterConfig - return cLIConf, nil + err = LoadFromFile(configFilePath, &config) + if err != nil { + return nil, err + } + + err = LoadFromCLI(&config, flagSet) + if err != nil { + return nil, err + } + + return &config, nil +} + +// ValidateConfig validates the cli and file configs together. +func ValidateConfig(config *Config) error { + scrapeConfigsPresent := (config.PromConfig != nil && len(config.PromConfig.ScrapeConfigs) > 0) + if !(config.PrometheusCR.Enabled || scrapeConfigsPresent) { + return fmt.Errorf("at least one scrape config must be defined, or Prometheus CR watching must be enabled") + } + return nil } diff --git a/cmd/otel-allocator/config/config_test.go b/cmd/otel-allocator/config/config_test.go index 3c45ffefe4..89f6307d85 100644 --- a/cmd/otel-allocator/config/config_test.go +++ b/cmd/otel-allocator/config/config_test.go @@ -15,7 +15,12 @@ package config import ( + "fmt" "testing" + "time" + + commonconfig "github.com/prometheus/common/config" + promconfig "github.com/prometheus/prometheus/config" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/discovery" @@ -23,39 +28,193 @@ import ( "github.com/stretchr/testify/assert" ) -const testFile = "./testdata/config_test.yaml" - -func TestConfigLoad(t *testing.T) { - expectedFileSDConfig := &file.SDConfig{ - Files: []string{"./file_sd_test.json"}, - RefreshInterval: model.Duration(300000000000), +func TestLoad(t *testing.T) { + type args struct { + file string } - expectedStaticSDConfig := discovery.StaticConfig{ + tests := []struct { + name string + args args + want Config + wantErr assert.ErrorAssertionFunc + }{ { - Targets: []model.LabelSet{ - {model.AddressLabel: "prom.domain:9001"}, - {model.AddressLabel: "prom.domain:9002"}, - {model.AddressLabel: "prom.domain:9003"}, + name: "file sd load", + args: args{ + file: "./testdata/config_test.yaml", }, - Labels: model.LabelSet{ - "my": "label", + want: Config{ + LabelSelector: map[string]string{ + "app.kubernetes.io/instance": "default.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + PrometheusCR: PrometheusCRConfig{ + ScrapeInterval: model.Duration(time.Second * 60), + }, + PromConfig: &promconfig.Config{ + GlobalConfig: promconfig.GlobalConfig{ + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeTimeout: model.Duration(10 * time.Second), + EvaluationInterval: model.Duration(60 * time.Second), + }, + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "prometheus", + HonorTimestamps: true, + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeTimeout: model.Duration(10 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + EnableHTTP2: true, + }, + ServiceDiscoveryConfigs: []discovery.Config{ + &file.SDConfig{ + Files: []string{"./file_sd_test.json"}, + RefreshInterval: model.Duration(5 * time.Minute), + }, + discovery.StaticConfig{ + { + Targets: []model.LabelSet{ + {model.AddressLabel: "prom.domain:9001"}, + {model.AddressLabel: "prom.domain:9002"}, + {model.AddressLabel: "prom.domain:9003"}, + }, + Labels: model.LabelSet{ + "my": "label", + }, + Source: "0", + }, + }, + }, + }, + }, + }, }, - Source: "0", + wantErr: assert.NoError, + }, + { + name: "no config", + args: args{ + file: "./testdata/no_config.yaml", + }, + want: CreateDefaultConfig(), + wantErr: assert.NoError, + }, + { + name: "service monitor pod monitor selector", + args: args{ + file: "./testdata/pod_service_selector_test.yaml", + }, + want: Config{ + LabelSelector: map[string]string{ + "app.kubernetes.io/instance": "default.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + PrometheusCR: PrometheusCRConfig{ + ScrapeInterval: DefaultCRScrapeInterval, + }, + PromConfig: &promconfig.Config{ + GlobalConfig: promconfig.GlobalConfig{ + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeTimeout: model.Duration(10 * time.Second), + EvaluationInterval: model.Duration(60 * time.Second), + }, + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "prometheus", + HonorTimestamps: true, + ScrapeInterval: model.Duration(60 * time.Second), + ScrapeTimeout: model.Duration(10 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + EnableHTTP2: true, + }, + ServiceDiscoveryConfigs: []discovery.Config{ + discovery.StaticConfig{ + { + Targets: []model.LabelSet{ + {model.AddressLabel: "prom.domain:9001"}, + {model.AddressLabel: "prom.domain:9002"}, + {model.AddressLabel: "prom.domain:9003"}, + }, + Labels: model.LabelSet{ + "my": "label", + }, + Source: "0", + }, + }, + }, + }, + }, + }, + PodMonitorSelector: map[string]string{ + "release": "test", + }, + ServiceMonitorSelector: map[string]string{ + "release": "test", + }, + }, + wantErr: assert.NoError, }, } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := CreateDefaultConfig() + err := LoadFromFile(tt.args.file, &got) + if !tt.wantErr(t, err, fmt.Sprintf("Load(%v)", tt.args.file)) { + return + } + assert.Equalf(t, tt.want, got, "Load(%v)", tt.args.file) + }) + } +} - cfg := Config{} - err := unmarshal(&cfg, testFile) - assert.NoError(t, err) - - scrapeConfig := *cfg.Config.ScrapeConfigs[0] - actualFileSDConfig := scrapeConfig.ServiceDiscoveryConfigs[0] - actulaStaticSDConfig := scrapeConfig.ServiceDiscoveryConfigs[1] - t.Log(actulaStaticSDConfig) +func TestValidateConfig(t *testing.T) { + testCases := []struct { + name string + fileConfig Config + expectedErr error + }{ + { + name: "promCR enabled, no Prometheus config", + fileConfig: Config{PromConfig: nil, PrometheusCR: PrometheusCRConfig{Enabled: true}}, + expectedErr: nil, + }, + { + name: "promCR disabled, no Prometheus config", + fileConfig: Config{PromConfig: nil}, + expectedErr: fmt.Errorf("at least one scrape config must be defined, or Prometheus CR watching must be enabled"), + }, + { + name: "promCR disabled, Prometheus config present, no scrapeConfigs", + fileConfig: Config{PromConfig: &promconfig.Config{}}, + expectedErr: fmt.Errorf("at least one scrape config must be defined, or Prometheus CR watching must be enabled"), + }, + { + name: "promCR disabled, Prometheus config present, scrapeConfigs present", + fileConfig: Config{ + PromConfig: &promconfig.Config{ScrapeConfigs: []*promconfig.ScrapeConfig{{}}}, + }, + expectedErr: nil, + }, + { + name: "promCR enabled, Prometheus config present, scrapeConfigs present", + fileConfig: Config{ + PromConfig: &promconfig.Config{ScrapeConfigs: []*promconfig.ScrapeConfig{{}}}, + PrometheusCR: PrometheusCRConfig{Enabled: true}, + }, + expectedErr: nil, + }, + } - assert.Equal(t, cfg.LabelSelector["app.kubernetes.io/instance"], "default.test") - assert.Equal(t, cfg.LabelSelector["app.kubernetes.io/managed-by"], "opentelemetry-operator") - assert.Equal(t, scrapeConfig.JobName, "prometheus") - assert.Equal(t, expectedFileSDConfig, actualFileSDConfig) - assert.Equal(t, expectedStaticSDConfig, actulaStaticSDConfig) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := ValidateConfig(&tc.fileConfig) + assert.Equal(t, tc.expectedErr, err) + }) + } } diff --git a/cmd/otel-allocator/config/flags.go b/cmd/otel-allocator/config/flags.go new file mode 100644 index 0000000000..bb7dbbb344 --- /dev/null +++ b/cmd/otel-allocator/config/flags.go @@ -0,0 +1,64 @@ +// 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 config + +import ( + "flag" + "path/filepath" + + "github.com/spf13/pflag" + "k8s.io/client-go/util/homedir" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +// Flag names. +const ( + targetAllocatorName = "target-allocator" + configFilePathFlagName = "config-file" + listenAddrFlagName = "listen-addr" + prometheusCREnabledFlagName = "enable-prometheus-cr-watcher" + kubeConfigPathFlagName = "kubeconfig-path" +) + +// We can't bind this flag to our FlagSet, so we need to handle it separately. +var zapCmdLineOpts zap.Options + +func getFlagSet(errorHandling pflag.ErrorHandling) *pflag.FlagSet { + flagSet := pflag.NewFlagSet(targetAllocatorName, errorHandling) + flagSet.String(configFilePathFlagName, DefaultConfigFilePath, "The path to the config file.") + flagSet.String(listenAddrFlagName, ":8080", "The address where this service serves.") + flagSet.Bool(prometheusCREnabledFlagName, false, "Enable Prometheus CRs as target sources") + flagSet.String(kubeConfigPathFlagName, filepath.Join(homedir.HomeDir(), ".kube", "config"), "absolute path to the KubeconfigPath file") + zapFlagSet := flag.NewFlagSet("", flag.ErrorHandling(errorHandling)) + zapCmdLineOpts.BindFlags(zapFlagSet) + flagSet.AddGoFlagSet(zapFlagSet) + return flagSet +} + +func getConfigFilePath(flagSet *pflag.FlagSet) (string, error) { + return flagSet.GetString(configFilePathFlagName) +} + +func getKubeConfigFilePath(flagSet *pflag.FlagSet) (string, error) { + return flagSet.GetString(kubeConfigPathFlagName) +} + +func getListenAddr(flagSet *pflag.FlagSet) (string, error) { + return flagSet.GetString(listenAddrFlagName) +} + +func getPrometheusCREnabled(flagSet *pflag.FlagSet) (bool, error) { + return flagSet.GetBool(prometheusCREnabledFlagName) +} diff --git a/cmd/otel-allocator/config/flags_test.go b/cmd/otel-allocator/config/flags_test.go new file mode 100644 index 0000000000..b1bf11b6ce --- /dev/null +++ b/cmd/otel-allocator/config/flags_test.go @@ -0,0 +1,91 @@ +// 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 config + +import ( + "path/filepath" + "testing" + + "github.com/spf13/pflag" + "github.com/stretchr/testify/assert" +) + +func TestGetFlagSet(t *testing.T) { + fs := getFlagSet(pflag.ExitOnError) + + // Check if each flag exists + assert.NotNil(t, fs.Lookup(configFilePathFlagName), "Flag %s not found", configFilePathFlagName) + assert.NotNil(t, fs.Lookup(listenAddrFlagName), "Flag %s not found", listenAddrFlagName) + assert.NotNil(t, fs.Lookup(prometheusCREnabledFlagName), "Flag %s not found", prometheusCREnabledFlagName) + assert.NotNil(t, fs.Lookup(kubeConfigPathFlagName), "Flag %s not found", kubeConfigPathFlagName) +} + +func TestFlagGetters(t *testing.T) { + tests := []struct { + name string + flagArgs []string + expectedValue interface{} + expectedErr bool + getterFunc func(*pflag.FlagSet) (interface{}, error) + }{ + { + name: "GetConfigFilePath", + flagArgs: []string{"--" + configFilePathFlagName, "/path/to/config"}, + expectedValue: "/path/to/config", + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getConfigFilePath(fs) }, + }, + { + name: "GetKubeConfigFilePath", + flagArgs: []string{"--" + kubeConfigPathFlagName, filepath.Join("~", ".kube", "config")}, + expectedValue: filepath.Join("~", ".kube", "config"), + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getKubeConfigFilePath(fs) }, + }, + { + name: "GetListenAddr", + flagArgs: []string{"--" + listenAddrFlagName, ":8081"}, + expectedValue: ":8081", + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getListenAddr(fs) }, + }, + { + name: "GetPrometheusCREnabled", + flagArgs: []string{"--" + prometheusCREnabledFlagName, "true"}, + expectedValue: true, + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getPrometheusCREnabled(fs) }, + }, + { + name: "InvalidFlag", + flagArgs: []string{"--invalid-flag", "value"}, + expectedErr: true, + getterFunc: func(fs *pflag.FlagSet) (interface{}, error) { return getConfigFilePath(fs) }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fs := getFlagSet(pflag.ContinueOnError) + err := fs.Parse(tt.flagArgs) + + // If an error is expected during parsing, we check it here. + if tt.expectedErr { + assert.Error(t, err) + return + } + + got, err := tt.getterFunc(fs) + assert.NoError(t, err) + assert.Equal(t, tt.expectedValue, got) + }) + } +} diff --git a/cmd/otel-allocator/config/testdata/config_test.yaml b/cmd/otel-allocator/config/testdata/config_test.yaml index 67670f26f7..efdc27bc39 100644 --- a/cmd/otel-allocator/config/testdata/config_test.yaml +++ b/cmd/otel-allocator/config/testdata/config_test.yaml @@ -1,6 +1,8 @@ label_selector: app.kubernetes.io/instance: default.test app.kubernetes.io/managed-by: opentelemetry-operator +prometheus_cr: + scrape_interval: 60s config: scrape_configs: - job_name: prometheus @@ -12,4 +14,4 @@ config: static_configs: - targets: ["prom.domain:9001", "prom.domain:9002", "prom.domain:9003"] labels: - my: label \ No newline at end of file + my: label diff --git a/cmd/otel-allocator/config/testdata/no_config.yaml b/cmd/otel-allocator/config/testdata/no_config.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cmd/otel-allocator/config/testdata/pod_service_selector_test.yaml b/cmd/otel-allocator/config/testdata/pod_service_selector_test.yaml new file mode 100644 index 0000000000..c0ff54ad36 --- /dev/null +++ b/cmd/otel-allocator/config/testdata/pod_service_selector_test.yaml @@ -0,0 +1,14 @@ +label_selector: + app.kubernetes.io/instance: default.test + app.kubernetes.io/managed-by: opentelemetry-operator +pod_monitor_selector: + release: test +service_monitor_selector: + release: test +config: + scrape_configs: + - job_name: prometheus + static_configs: + - targets: ["prom.domain:9001", "prom.domain:9002", "prom.domain:9003"] + labels: + my: label \ No newline at end of file diff --git a/cmd/otel-allocator/diff/diff.go b/cmd/otel-allocator/diff/diff.go index 4bb201cfc0..93bed4daed 100644 --- a/cmd/otel-allocator/diff/diff.go +++ b/cmd/otel-allocator/diff/diff.go @@ -16,11 +16,19 @@ package diff // Changes is the result of the difference between two maps – items that are added and items that are removed // This map is used to reconcile state differences. -type Changes[T any] struct { +type Changes[T Hasher] struct { additions map[string]T removals map[string]T } +type Hasher interface { + Hash() string +} + +func NewChanges[T Hasher](additions map[string]T, removals map[string]T) Changes[T] { + return Changes[T]{additions: additions, removals: removals} +} + func (c Changes[T]) Additions() map[string]T { return c.additions } @@ -31,19 +39,20 @@ func (c Changes[T]) Removals() map[string]T { // Maps generates Changes for two maps with the same type signature by checking for any removals and then checking for // additions. -func Maps[T any](current, new map[string]T) Changes[T] { +// TODO: This doesn't need to create maps, it can return slices only. This function doesn't need to insert the values. +func Maps[T Hasher](current, new map[string]T) Changes[T] { additions := map[string]T{} removals := map[string]T{} - // Used as a set to check for removed items - newMembership := map[string]bool{} - for key, value := range new { - if _, found := current[key]; !found { - additions[key] = value + for key, newValue := range new { + if currentValue, found := current[key]; !found { + additions[key] = newValue + } else if currentValue.Hash() != newValue.Hash() { + additions[key] = newValue + removals[key] = currentValue } - newMembership[key] = true } for key, value := range current { - if _, found := newMembership[key]; !found { + if _, found := new[key]; !found { removals[key] = value } } diff --git a/cmd/otel-allocator/diff/diff_test.go b/cmd/otel-allocator/diff/diff_test.go index 9dec103b79..7b8cda5b34 100644 --- a/cmd/otel-allocator/diff/diff_test.go +++ b/cmd/otel-allocator/diff/diff_test.go @@ -19,51 +19,82 @@ import ( "testing" ) +type HasherString string + +func (s HasherString) Hash() string { + return string(s) +} + func TestDiffMaps(t *testing.T) { type args struct { - current map[string]string - new map[string]string + current map[string]Hasher + new map[string]Hasher } tests := []struct { name string args args - want Changes[string] + want Changes[Hasher] }{ { name: "basic replacement", args: args{ - current: map[string]string{ - "current": "one", + current: map[string]Hasher{ + "current": HasherString("one"), }, - new: map[string]string{ - "new": "another", + new: map[string]Hasher{ + "new": HasherString("another"), }, }, - want: Changes[string]{ - additions: map[string]string{ - "new": "another", + want: Changes[Hasher]{ + additions: map[string]Hasher{ + "new": HasherString("another"), }, - removals: map[string]string{ - "current": "one", + removals: map[string]Hasher{ + "current": HasherString("one"), }, }, }, { name: "single addition", args: args{ - current: map[string]string{ - "current": "one", + current: map[string]Hasher{ + "current": HasherString("one"), + }, + new: map[string]Hasher{ + "current": HasherString("one"), + "new": HasherString("another"), + }, + }, + want: Changes[Hasher]{ + additions: map[string]Hasher{ + "new": HasherString("another"), + }, + removals: map[string]Hasher{}, + }, + }, + { + name: "value change", + args: args{ + current: map[string]Hasher{ + "k1": HasherString("v1"), + "k2": HasherString("v2"), + "change": HasherString("before"), }, - new: map[string]string{ - "current": "one", - "new": "another", + new: map[string]Hasher{ + "k1": HasherString("v1"), + "k3": HasherString("v3"), + "change": HasherString("after"), }, }, - want: Changes[string]{ - additions: map[string]string{ - "new": "another", + want: Changes[Hasher]{ + additions: map[string]Hasher{ + "k3": HasherString("v3"), + "change": HasherString("after"), + }, + removals: map[string]Hasher{ + "k2": HasherString("v2"), + "change": HasherString("before"), }, - removals: map[string]string{}, }, }, } diff --git a/cmd/otel-allocator/discovery/discovery.go b/cmd/otel-allocator/discovery/discovery.go deleted file mode 100644 index 9cdadde887..0000000000 --- a/cmd/otel-allocator/discovery/discovery.go +++ /dev/null @@ -1,122 +0,0 @@ -// 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 discovery - -import ( - "context" - - "github.com/go-kit/log" - "github.com/go-logr/logr" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/prometheus/common/model" - "github.com/prometheus/prometheus/config" - "github.com/prometheus/prometheus/discovery" - - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation" - allocatorWatcher "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/watcher" -) - -var ( - targetsDiscovered = promauto.NewGaugeVec(prometheus.GaugeOpts{ - Name: "opentelemetry_allocator_targets", - Help: "Number of targets discovered.", - }, []string{"job_name"}) -) - -type Manager struct { - log logr.Logger - manager *discovery.Manager - logger log.Logger - close chan struct{} - configsMap map[allocatorWatcher.EventSource]*config.Config -} - -func NewManager(log logr.Logger, ctx context.Context, logger log.Logger, options ...func(*discovery.Manager)) *Manager { - manager := discovery.NewManager(ctx, logger, options...) - - go func() { - if err := manager.Run(); err != nil { - log.Error(err, "Discovery manager failed") - } - }() - return &Manager{ - log: log, - manager: manager, - logger: logger, - close: make(chan struct{}), - configsMap: make(map[allocatorWatcher.EventSource]*config.Config), - } -} - -func (m *Manager) GetScrapeConfigs() map[string]*config.ScrapeConfig { - jobToScrapeConfig := map[string]*config.ScrapeConfig{} - for _, c := range m.configsMap { - for _, scrapeConfig := range c.ScrapeConfigs { - jobToScrapeConfig[scrapeConfig.JobName] = scrapeConfig - } - } - return jobToScrapeConfig -} - -func (m *Manager) ApplyConfig(source allocatorWatcher.EventSource, cfg *config.Config) error { - m.configsMap[source] = cfg - - discoveryCfg := make(map[string]discovery.Configs) - - for _, value := range m.configsMap { - for _, scrapeConfig := range value.ScrapeConfigs { - discoveryCfg[scrapeConfig.JobName] = scrapeConfig.ServiceDiscoveryConfigs - } - } - return m.manager.ApplyConfig(discoveryCfg) -} - -func (m *Manager) Watch(fn func(targets map[string]*allocation.TargetItem)) { - log := m.log.WithValues("component", "opentelemetry-targetallocator") - - go func() { - for { - select { - case <-m.close: - log.Info("Service Discovery watch event stopped: discovery manager closed") - return - case tsets := <-m.manager.SyncCh(): - targets := map[string]*allocation.TargetItem{} - - for jobName, tgs := range tsets { - var count float64 = 0 - for _, tg := range tgs { - for _, t := range tg.Targets { - count++ - item := &allocation.TargetItem{ - JobName: jobName, - TargetURL: string(t[model.AddressLabel]), - Label: t.Merge(tg.Labels), - } - targets[item.Hash()] = item - } - } - targetsDiscovered.WithLabelValues(jobName).Set(count) - } - fn(targets) - } - } - }() -} - -func (m *Manager) Close() { - close(m.close) -} diff --git a/cmd/otel-allocator/discovery/discovery_test.go b/cmd/otel-allocator/discovery/discovery_test.go deleted file mode 100644 index afea787176..0000000000 --- a/cmd/otel-allocator/discovery/discovery_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// 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 discovery - -import ( - "context" - "fmt" - "os" - "sort" - "testing" - - gokitlog "github.com/go-kit/log" - "github.com/prometheus/common/model" - promconfig "github.com/prometheus/prometheus/config" - "github.com/prometheus/prometheus/discovery" - "github.com/stretchr/testify/assert" - ctrl "sigs.k8s.io/controller-runtime" - - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation" - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/config" - allocatorWatcher "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/watcher" -) - -var cfg config.Config -var manager *Manager -var results chan []string - -func TestMain(m *testing.M) { - var err error - cfg, err = config.Load("./testdata/test.yaml") - if err != nil { - fmt.Printf("failed to load config file: %v", err) - os.Exit(1) - } - manager = NewManager(ctrl.Log.WithName("test"), context.Background(), gokitlog.NewNopLogger()) - - results = make(chan []string) - manager.Watch(func(targets map[string]*allocation.TargetItem) { - var result []string - for _, t := range targets { - result = append(result, t.TargetURL) - } - results <- result - }) - - code := m.Run() - - close(manager.close) - - os.Exit(code) -} - -func TestTargetDiscovery(t *testing.T) { - err := manager.ApplyConfig(allocatorWatcher.EventSourcePrometheusCR, &promconfig.Config{}) - assert.NoError(t, err) - - gotTargets := <-results - wantTargets := []string{"prom.domain:9001", "prom.domain:9002", "prom.domain:9003", "promfile.domain:1001", "promfile.domain:3000"} - - sort.Strings(gotTargets) - sort.Strings(wantTargets) - assert.Equal(t, gotTargets, wantTargets) -} - -func TestTargetUpdate(t *testing.T) { - cfg.Config.ScrapeConfigs[0].ServiceDiscoveryConfigs[1] = discovery.StaticConfig{ - { - Targets: []model.LabelSet{ - {model.AddressLabel: "prom.domain:9004"}, - {model.AddressLabel: "prom.domain:9005"}, - }, - Labels: model.LabelSet{ - "my": "label", - }, - Source: "0", - }, - } - - err := manager.ApplyConfig(allocatorWatcher.EventSourcePrometheusCR, &promconfig.Config{}) - assert.NoError(t, err) - - gotTargets := <-results - wantTargets := []string{"prom.domain:9004", "prom.domain:9005", "promfile.domain:1001", "promfile.domain:3000"} - - sort.Strings(gotTargets) - sort.Strings(wantTargets) - assert.Equal(t, gotTargets, wantTargets) -} diff --git a/cmd/otel-allocator/go.mod b/cmd/otel-allocator/go.mod index 83d2ee1b68..61994501a9 100644 --- a/cmd/otel-allocator/go.mod +++ b/cmd/otel-allocator/go.mod @@ -1,167 +1,202 @@ module github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator -go 1.19 +go 1.21 + +toolchain go1.21.3 require ( - github.com/buraksezer/consistent v0.9.0 - github.com/cespare/xxhash/v2 v2.1.2 - github.com/fsnotify/fsnotify v1.5.1 + github.com/buraksezer/consistent v0.10.0 + github.com/cespare/xxhash/v2 v2.2.0 + github.com/fsnotify/fsnotify v1.7.0 github.com/ghodss/yaml v1.0.0 - github.com/go-kit/log v0.2.0 - github.com/go-logr/logr v1.2.0 - github.com/gorilla/mux v1.8.0 - github.com/prometheus-operator/prometheus-operator v0.53.1 - github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.53.1 - github.com/prometheus-operator/prometheus-operator/pkg/client v0.53.1 - github.com/prometheus/client_golang v1.11.0 - github.com/prometheus/common v0.32.1 - github.com/prometheus/prometheus v1.8.2-0.20211214150951-52c693a63be1 + github.com/gin-gonic/gin v1.9.1 + github.com/go-kit/log v0.2.1 + github.com/go-logr/logr v1.3.0 + github.com/json-iterator/go v1.1.12 + github.com/oklog/run v1.1.0 + github.com/prometheus-operator/prometheus-operator v0.69.1 + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.69.1 + github.com/prometheus-operator/prometheus-operator/pkg/client v0.69.1 + github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/common v0.45.0 + github.com/prometheus/prometheus v0.48.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.8.4 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.23.0 - k8s.io/apimachinery v0.23.0 - k8s.io/client-go v0.23.0 - k8s.io/klog/v2 v2.30.0 - sigs.k8s.io/controller-runtime v0.11.0 + gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.28.4 + k8s.io/apimachinery v0.28.4 + k8s.io/client-go v0.28.4 + k8s.io/klog/v2 v2.110.1 + sigs.k8s.io/controller-runtime v0.16.3 ) require ( - cloud.google.com/go v0.97.0 // indirect - github.com/Azure/azure-sdk-for-go v59.4.0+incompatible // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.22 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.17 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/Microsoft/go-winio v0.4.17 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a // indirect - github.com/armon/go-metrics v0.3.9 // indirect - github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect - github.com/aws/aws-sdk-go v1.44.41 // indirect + cloud.google.com/go/compute v1.23.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/aws/aws-sdk-go v1.45.25 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe // indirect - github.com/containerd/containerd v1.5.7 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dennwc/varint v1.0.0 // indirect - github.com/digitalocean/godo v1.72.0 // indirect - github.com/docker/distribution v2.7.1+incompatible // indirect - github.com/docker/docker v20.10.11+incompatible // indirect + github.com/digitalocean/godo v1.104.1 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.4.0 // indirect - github.com/edsrzf/mmap-go v1.0.0 // indirect - github.com/efficientgo/tools/core v0.0.0-20210829154005-c7bad8450208 // indirect - github.com/envoyproxy/go-control-plane v0.10.1 // indirect - github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect - github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/fatih/color v1.12.0 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/go-logr/zapr v1.2.0 // indirect - github.com/go-openapi/analysis v0.20.0 // indirect - github.com/go-openapi/errors v0.20.0 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/loads v0.20.2 // indirect - github.com/go-openapi/runtime v0.19.29 // indirect - github.com/go-openapi/spec v0.20.3 // indirect - github.com/go-openapi/strfmt v0.21.1 // indirect - github.com/go-openapi/swag v0.19.15 // indirect - github.com/go-openapi/validate v0.20.2 // indirect - github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect - github.com/go-stack/stack v1.8.0 // indirect - github.com/go-zookeeper/zk v1.0.2 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/edsrzf/mmap-go v1.1.0 // indirect + github.com/efficientgo/core v1.0.0-rc.2 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/envoyproxy/go-control-plane v0.11.1 // indirect + github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.7.0 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-openapi/analysis v0.21.4 // indirect + github.com/go-openapi/errors v0.20.4 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/loads v0.21.2 // indirect + github.com/go-openapi/runtime v0.26.0 // indirect + github.com/go-openapi/spec v0.20.9 // indirect + github.com/go-openapi/strfmt v0.21.7 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/validate v0.22.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-resty/resty/v2 v2.7.0 // indirect + github.com/go-zookeeper/zk v1.0.3 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.0.0 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.5.6 // indirect - github.com/google/go-querystring v1.0.0 // indirect - github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.2.0 // indirect - github.com/googleapis/gax-go/v2 v2.1.1 // indirect - github.com/googleapis/gnostic v0.5.5 // indirect - github.com/gophercloud/gophercloud v0.23.0 // indirect - github.com/hashicorp/consul/api v1.11.0 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/gophercloud/gophercloud v1.7.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect + github.com/hashicorp/consul/api v1.25.1 // indirect + github.com/hashicorp/cronexpr v1.1.2 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v0.16.2 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-version v1.3.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/serf v0.9.5 // indirect - github.com/hetznercloud/hcloud-go v1.33.1 // indirect - github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/hashicorp/golang-lru v0.6.0 // indirect + github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/hetznercloud/hcloud-go/v2 v2.4.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/ionos-cloud/sdk-go/v6 v6.1.9 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b // indirect - github.com/linode/linodego v1.2.1 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/miekg/dns v1.1.43 // indirect + github.com/klauspost/compress v1.17.1 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/linode/linodego v1.23.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a // indirect + github.com/miekg/dns v1.1.56 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/hashstructure v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.4.2 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.1 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/ovh/go-ovh v1.4.3 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus-community/prom-label-proxy v0.4.1-0.20211215142838-1eac0933d512 // indirect - github.com/prometheus/alertmanager v0.23.1-0.20210914172521-e35efbddb66a // indirect - github.com/prometheus/client_model v0.2.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus-community/prom-label-proxy v0.7.0 // indirect + github.com/prometheus/alertmanager v0.26.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common/sigv4 v0.1.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect - github.com/spf13/cobra v1.2.1 // indirect - github.com/thanos-io/thanos v0.24.0-rc.1 // indirect - github.com/uber/jaeger-client-go v2.29.1+incompatible // indirect - github.com/uber/jaeger-lib v2.4.1+incompatible // indirect - go.mongodb.org/mongo-driver v1.7.5 // indirect - go.opencensus.io v0.23.0 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/goleak v1.1.12 // indirect - go.uber.org/multierr v1.7.0 // indirect - go.uber.org/zap v1.19.1 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/api v0.60.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 // indirect - google.golang.org/grpc v1.40.0 // indirect - google.golang.org/protobuf v1.27.1 // indirect + github.com/prometheus/procfs v0.11.1 // indirect + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + github.com/vultr/govultr/v2 v2.17.2 // indirect + go.mongodb.org/mongo-driver v1.12.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/goleak v1.2.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.25.0 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/oauth2 v0.13.0 // indirect + golang.org/x/sync v0.4.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.14.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/api v0.147.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect + google.golang.org/grpc v1.58.3 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - k8s.io/apiextensions-apiserver v0.23.0 // indirect - k8s.io/component-base v0.23.0 // indirect - k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect - k8s.io/utils v0.0.0-20211208161948-7d6a63dca704 // indirect - sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.0 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + k8s.io/apiextensions-apiserver v0.28.3 // indirect + k8s.io/component-base v0.28.3 // indirect + k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) // A exclude directive is needed for k8s.io/client-go because Cortex (which diff --git a/cmd/otel-allocator/go.sum b/cmd/otel-allocator/go.sum index 5230d2b703..bd66fd31ee 100644 --- a/cmd/otel-allocator/go.sum +++ b/cmd/otel-allocator/go.sum @@ -1,16 +1,11 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.49.0/go.mod h1:hGvAdzcWNbyuxS3nWhD7H2cIJxjRRTRLQVB0bdputVY= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= @@ -18,790 +13,238 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigtable v1.1.0/go.mod h1:B6ByKcIdYmhoyDzmOnQxyOhN6r05qnewYIxxG6L0/b4= -cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/bigtable v1.3.0/go.mod h1:z5EyKrPE8OQmeg4h5MNdKvuSnI9CCT49Ki3f23aBzio= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.3.0/go.mod h1:9IAwXhoyBJ7z9LcAwkj0/7NnPzYaPeZxxVp3zm+5IqA= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g= -collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= -contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= -github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= -github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v36.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v41.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v43.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v44.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v44.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v45.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v48.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v51.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v52.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v54.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v58.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v59.4.0+incompatible h1:gDA8odnngdNd3KYHL2NoK1j9vpWBgEnFSjKKLpkC8Aw= -github.com/Azure/azure-sdk-for-go v59.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= -github.com/Azure/azure-storage-blob-go v0.13.0/go.mod h1:pA9kNqtjUeQF2zOSu4s//nUdBD+e64lEuc4sVnuOfNs= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 h1:9kDVnTz3vbfweTqAUmk/a/pH5pWFCHtvRpHYC0G/dcA= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2/go.mod h1:FbdwsQ2EzwvXxOPcMFYO8ogEc9uMMIj3YkmCdXdAFmk= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1 h1:bWh0Z2rOEDfB/ywv/l0iHN1JgyazE6kW/aIA89+CEK0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1/go.mod h1:Bzf34hhAE9NSxailk8xVeLEZbUjOXcC+GnU1mMKdhLw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0/go.mod h1:s1tW/At+xHqjNFvWU4G0c0Qv33KOhvbGNj0RCTQDV8s= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v11.2.8+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.9.3-0.20191028180845-3492b2aff503/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.2/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.4/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.9/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest v0.11.10/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest v0.11.11/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.22 h1:bXiQwDjrRmBQOE67bwlvUKAC1EU1yZTPQ38c+bstZws= -github.com/Azure/go-autorest/autorest v0.11.22/go.mod h1:BAWYUWGPEtKPzjVkp0Q6an0MJcJDsoh5Z1BFAEFs4Xs= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.1-0.20191028180845-3492b2aff503/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= -github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.17 h1:esOPl2dhcz9P3jqBSJ8tPGEj2EqzPPT6zfyuloiogKY= -github.com/Azure/go-autorest/autorest/adal v0.9.17/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.3/go.mod h1:4bJZhUhcq8LB20TruwHbAQsmUs2Xh+QR7utuJpLXX3A= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.8/go.mod h1:kxyKZTSfKh8OVFWPAgOgQ/frrJgeYQJPyR5fLFmXko4= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= -github.com/Azure/go-autorest/autorest/to v0.3.1-0.20191028180845-3492b2aff503/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= -github.com/Azure/go-autorest/autorest/validation v0.2.1-0.20191028180845-3492b2aff503/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/HdrHistogram/hdrhistogram-go v0.9.0/go.mod h1:nxrse8/Tzg2tg3DZcZjm6qEclQKK70g0KxO61gFFZD4= -github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM= -github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/squirrel v0.0.0-20161115235646-20f192218cf5/go.mod h1:xnKTFzjGUiZtiOagBsfnvomW+nJg2usB1ZpordQWqNM= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/OneOfOne/xxhash v1.2.6/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= -github.com/SAP/go-hdb v0.14.1/go.mod h1:7fdQLVC2lER3urZLjZCm0AuMQfApof92n3aylBPEkMo= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= -github.com/alicebob/miniredis/v2 v2.14.3/go.mod h1:gquAfGbzn92jvtrSC69+6zZnwSODVXVpYDRaGhWaL6I= -github.com/aliyun/aliyun-oss-go-sdk v2.0.4+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= -github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/apache/arrow/go/arrow v0.0.0-20200601151325-b2287a20f230/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= -github.com/apache/arrow/go/arrow v0.0.0-20200923215132-ac86123a3f01/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= -github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= -github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.3.6/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18= -github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= -github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.22.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.29.16/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= -github.com/aws/aws-sdk-go v1.30.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.33.5/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.33.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= -github.com/aws/aws-sdk-go v1.35.5/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= -github.com/aws/aws-sdk-go v1.35.31/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.37.8/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.38.3/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.40.11/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.8/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.16/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.44.41 h1:FNW3Tb8vKvXLZ7lzGlg/dCAXhK4RC5fyFewD11oJhUM= -github.com/aws/aws-sdk-go v1.44.41/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/baidubce/bce-sdk-go v0.9.81/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= -github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= -github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/aws/aws-sdk-go v1.45.25 h1:c4fLlh5sLdK2DCRTY1z0hyuJZU4ygxX8m1FswL6/nF4= +github.com/aws/aws-sdk-go v1.45.25/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/immutable v0.2.1/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= -github.com/benbjohnson/tmpl v1.0.0/go.mod h1:igT620JFIi44B6awvU9IsDhR77IXWtFigTLil/RPdps= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/bonitoo-io/go-sql-bigquery v0.3.4-1.4.0/go.mod h1:J4Y6YJm0qTWB9aFziB7cPeSyc6dOZFyJdteSeybVpXQ= -github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/buraksezer/consistent v0.9.0 h1:Zfs6bX62wbP3QlbPGKUhqDw7SmNkOzY5bHZIYXYpR5g= -github.com/buraksezer/consistent v0.9.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw= -github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= -github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= -github.com/cenkalti/backoff v0.0.0-20181003080854-62661b46c409/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff v1.0.0/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= -github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/buraksezer/consistent v0.10.0 h1:hqBgz1PvNLC5rkWcEBVAL9dFMBWz6I0VgUCW25rrZlU= +github.com/buraksezer/consistent v0.10.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v0.0.0-20181017004759-096ff4a8a059/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/chromedp/cdproto v0.0.0-20200116234248-4da64dd111ac/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g= -github.com/chromedp/cdproto v0.0.0-20200424080200-0de008e41fa0/go.mod h1:PfAWWKJqjlGFYJEidUM6aVIWPr0EpobeyVWEEmplX7g= -github.com/chromedp/chromedp v0.5.3/go.mod h1:YLdPtndaHQ4rCpSpBG+IPpy9JvX0VD+7aaLxYgYj28w= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe h1:QJDJubh0OEcpeGjC7/8uF9tt4e39U/Ya1uyK+itnNPQ= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= -github.com/cockroachdb/datadriven v0.0.0-20190531201743-edce55837238/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/containerd v1.2.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cortexproject/cortex v0.6.1-0.20200228110116-92ab6cbe0995/go.mod h1:3Xa3DjJxtpXqxcMGdk850lcIRb81M0fyY1MQ6udY134= -github.com/cortexproject/cortex v1.2.1-0.20200805064754-d8edc95e2c91/go.mod h1:PVPxNLrxKH+yc8asaJOxuz7TiRmMizFfnSMOnRzM6oM= -github.com/cortexproject/cortex v1.3.1-0.20200923145333-8587ea61fe17/go.mod h1:dJ9gpW7dzQ7z09cKtNN9PfebumgyO4dtNdFQ6eQEed0= -github.com/cortexproject/cortex v1.4.1-0.20201030080541-83ad6df2abea/go.mod h1:kXo5F3jlF7Ky3+I31jt/bXTzOlQjl2X/vGDpy0RY1gU= -github.com/cortexproject/cortex v1.5.1-0.20201111110551-ba512881b076/go.mod h1:zFBGVsvRBfVp6ARXZ7pmiLaGlbjda5ZnA4Y6qSJyrQg= -github.com/cortexproject/cortex v1.6.1-0.20210108144208-6c2dab103f20/go.mod h1:fOsaeeFSyWrjd9nFJO8KVUpsikcxnYsjEzQyjURBoQk= -github.com/cortexproject/cortex v1.6.1-0.20210215155036-dfededd9f331/go.mod h1:8bRHNDawVx8te5lIqJ+/AcNTyfosYNC34Qah7+jX/8c= -github.com/cortexproject/cortex v1.7.1-0.20210224085859-66d6fb5b0d42/go.mod h1:u2dxcHInYbe45wxhLoWVdlFJyDhXewsMcxtnbq/QbH4= -github.com/cortexproject/cortex v1.7.1-0.20210316085356-3fedc1108a49/go.mod h1:/DBOW8TzYBTE/U+O7Whs7i7E2eeeZl1iRVDtIqxn5kg= -github.com/cortexproject/cortex v1.8.1-0.20210422151339-cf1c444e0905/go.mod h1:xxm4/CLvTmDxwE7yXwtClR4dIvkG4S09o5DygPOgc1U= -github.com/cortexproject/cortex v1.10.1-0.20211124141505-4e9fc3a2b5ab/go.mod h1:njSBkQ1wUNx9X4knV/j65Pi4ItlJXX4QwXRKoMflJd8= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cucumber/godog v0.8.1/go.mod h1:vSh3r/lM+psC1BPXvdkSEuNjmXfpVqrMGYAElF6hxnA= -github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= -github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= -github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= -github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00/go.mod h1:olo7eAdKwJdXxb55TKGLiJ6xt1H0/tiiRCWKVLmtjY4= -github.com/cznic/lldb v1.1.0/go.mod h1:FIZVUmYUVhPwRiPzL8nD/mpFcJ/G7SSXjjXYG4uRI3A= -github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE= -github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= -github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= -github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= -github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= -github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= -github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= -github.com/digitalocean/godo v1.37.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.38.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.42.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.42.1/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.46.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.52.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.57.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.58.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.60.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.71.0/go.mod h1:GBmu8MkjZmNARE7IXRPmkbbnocNN8+uBm0xbEVw2LCs= -github.com/digitalocean/godo v1.72.0 h1:Y5+H6zO8UOmiwETd40Uee1Io8kGPCb5fBN76Kf7UyAI= -github.com/digitalocean/godo v1.72.0/go.mod h1:GBmu8MkjZmNARE7IXRPmkbbnocNN8+uBm0xbEVw2LCs= -github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v0.7.3-0.20190817195342-4760db040282/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v17.12.0-ce-rc1.0.20200706150819-a40b877fbb9e+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo= -github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/digitalocean/godo v1.104.1 h1:SZNxjAsskM/su0YW9P8Wx3gU0W1Z13b6tZlYNpl5BnA= +github.com/digitalocean/godo v1.104.1/go.mod h1:VAI/L5YDzMuPRU01lEEUSQ/sp5Z//1HnnFv/RBTEdbg= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/efficientgo/e2e v0.11.2-0.20211027134903-67d538984a47/go.mod h1:vDnF4AAEZmO0mvyFIATeDJPFaSRM7ywaOnKd61zaSoE= -github.com/efficientgo/tools/core v0.0.0-20210129205121-421d0828c9a6/go.mod h1:OmVcnJopJL8d3X3sSXTiypGoUSgFq1aDGmlrdi9dn/M= -github.com/efficientgo/tools/core v0.0.0-20210201224146-3d78f4d30648/go.mod h1:cFZoHUhKg31xkPnPjhPKFtevnx0Xcg67ptBRxbpaxtk= -github.com/efficientgo/tools/core v0.0.0-20210829154005-c7bad8450208 h1:jIALuFymwBqVsF32JhgzVsbCB6QsWvXqhetn8QgyrZ4= -github.com/efficientgo/tools/core v0.0.0-20210829154005-c7bad8450208/go.mod h1:OmVcnJopJL8d3X3sSXTiypGoUSgFq1aDGmlrdi9dn/M= -github.com/efficientgo/tools/extkingpin v0.0.0-20210609125236-d73259166f20/go.mod h1:ZV0utlglOczUWv3ih2AbqPSoLoFzdplUYxwV62eZi6Q= -github.com/elastic/go-sysinfo v1.0.1/go.mod h1:O/D5m1VpYLwGjCYzEt63g3Z1uO3jXfwyzzjiW90t8cY= -github.com/elastic/go-sysinfo v1.1.1/go.mod h1:i1ZYdU10oLNfRzq4vq62BEwD2fH8KaWh6eh0ikPT9F0= -github.com/elastic/go-windows v1.0.0/go.mod h1:TsU0Nrp7/y3+VwE82FoZF8gC/XFg/Elz6CcloAxnPgU= -github.com/elastic/go-windows v1.0.1/go.mod h1:FoVvqWSun28vaDQPbj2Elfc0JahhPB7WQEGa3c814Ss= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/ema/qdisc v0.0.0-20190904071900-b82c76788043/go.mod h1:ix4kG2zvdUd8kEKSW0ZTr1XLks0epFpI4j745DXxlNE= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= +github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/efficientgo/core v1.0.0-rc.2 h1:7j62qHLnrZqO3V3UA0AqOGd5d5aXV3AX6m/NZBHp78I= +github.com/efficientgo/core v1.0.0-rc.2/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrjaL6x/GLcQ4vJps= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.10.1 h1:cgDRLG7bs59Zd+apAWuzLQL95obVYAymNJek76W3mgw= -github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= +github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= +github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2 h1:JiO+kJTpmYGjEodY7O1Zk8oZcNz1+f30UtwtXoFUPzE= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= -github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b/go.mod h1:NAJj0yf/KaRKURN6nyi7A9IZydMivZEm9oQLWNjfKDc= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc= +github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/structtag v1.1.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/felixge/fgprof v0.9.1/go.mod h1:7/HK6JFtFaARhIljgP2IV8rJLIoHDoOYoUphsnGvqxE= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/foxcpp/go-mockdns v0.0.0-20201212160233-ede2f9158d15/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goblin v0.0.0-20210519012713-85d372ac71e2/go.mod h1:VzmDKDJVZI3aJmnRI9VjAn9nJ8qPPsN1fqzr9dqInIo= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-chi/chi v4.1.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= -github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.0.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= -github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.17.2/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= -github.com/go-openapi/analysis v0.19.14/go.mod h1:zN0kY6i38wo2LQOwltVyMk61bqlqOm86n1/Iszo8F8Y= -github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk= -github.com/go-openapi/analysis v0.20.0 h1:UN09o0kNhleunxW7LR+KnltD0YrJ8FF03pSqvAN3Vro= -github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.17.2/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/errors v0.19.4/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= +github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.0 h1:Sxpo9PjEHDzhs3FbnGNonvDgWcMW2U7wGTcDDSFSceM= -github.com/go-openapi/errors v0.20.0/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.17.2/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M= +github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.17.2/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.17.2/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= -github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= -github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= -github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= -github.com/go-openapi/loads v0.20.2 h1:z5p5Xf5wujMxS1y8aP+vxwW5qYT2zdJBbXKmQUG3lcc= -github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.18.0/go.mod h1:uI6pHuxWYTy94zZxgcwJkUWa9wbIlhteGfloI10GD4U= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.3/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= -github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= -github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= -github.com/go-openapi/runtime v0.19.26/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M= -github.com/go-openapi/runtime v0.19.29 h1:5IIvCaIDbxetN674vX9eOxvoZ9mYGQ16fV1Q0VSG+NA= -github.com/go-openapi/runtime v0.19.29/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.17.2/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA= -github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= -github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= -github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= -github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= -github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.17.2/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= -github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= -github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= -github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= -github.com/go-openapi/strfmt v0.20.1/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= +github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= +github.com/go-openapi/runtime v0.26.0 h1:HYOFtG00FM1UvqrcxbEJg/SwvDRvYLQKGhw2zaQjTcc= +github.com/go-openapi/runtime v0.26.0/go.mod h1:QgRGeZwrUcSHdeh4Ka9Glvo0ug1LC5WyE+EV88plZrQ= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= +github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1 h1:G6s2t5V5kGCHLVbSdZ/6lI8Wm4OzoPFkc3/cjAsKQrM= github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.17.2/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.4/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= +github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= -github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= -github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= -github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= -github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/validate v0.17.2/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= -github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= -github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= -github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= -github.com/go-openapi/validate v0.19.14/go.mod h1:PdGrHe0rp6MG3A1SrAY/rIHATqzJEEhohGE1atLkBEQ= -github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= -github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= -github.com/go-openapi/validate v0.20.2 h1:AhqDegYV3J3iQkMPJSXkvzymHKMTw0BST3RK3hTT4ts= -github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= -github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= -github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= -github.com/go-redis/redis/v8 v8.0.0-beta.10.0.20200905143926-df7fe4e2ce72/go.mod h1:CJP1ZIHwhosNYwIdaHPZK9vHsM3+roNBaZ7U9Of1DXc= -github.com/go-redis/redis/v8 v8.2.3/go.mod h1:ysgGY09J/QeDYbu3HikWEIPCwaeOkuNoTgKayTEaEOw= -github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= +github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= @@ -826,55 +269,19 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= -github.com/gocql/gocql v0.0.0-20200121121104-95d072f1b5bb/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= -github.com/gocql/gocql v0.0.0-20200526081602-cd04bd7f22a7/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= -github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/gogo/status v1.0.3/go.mod h1:SavQ51ycCLnc7dGyJxp8YAmudx8xqiVrRf+6IXRsugc= -github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= -github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.0.0 h1:RAqyYixv1p7uEnocuy8P1nru5wprCh/MH2BIlW5z5/o= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-migrate/migrate/v4 v4.7.0/go.mod h1:Qvut3N4xKWjoH3sokBccML6WyHSnggXm/DvMMnTsQIc= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -882,9 +289,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -900,30 +304,18 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3-0.20201103224600-674baa8c7fc3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= -github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.9.0/go.mod h1:U7ayypeSkw23szu4GaQTPJGx66c20mx8JklMSxrmI1w= -github.com/google/cel-spec v0.6.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= -github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -935,163 +327,72 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200417002340-c6e0a841f49a/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200615235658-03e1cf38a040/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201007051231-1066cbb265c7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201117184057-ae444373da19/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210208152844-1612e9be7af6/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210323184331-8eee2492667d/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210504235042-3a04a4d88a10/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20211008130755-947d60d73cc0/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/pprof v0.0.0-20211122183932-1daafda22083/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM= -github.com/gophercloud/gophercloud v0.10.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss= -github.com/gophercloud/gophercloud v0.11.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss= -github.com/gophercloud/gophercloud v0.12.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss= -github.com/gophercloud/gophercloud v0.13.0/go.mod h1:VX0Ibx85B60B5XOrZr6kaNwrmPUzcmMpwxvQ1WQIIWM= -github.com/gophercloud/gophercloud v0.14.0/go.mod h1:VX0Ibx85B60B5XOrZr6kaNwrmPUzcmMpwxvQ1WQIIWM= -github.com/gophercloud/gophercloud v0.15.0/go.mod h1:VX0Ibx85B60B5XOrZr6kaNwrmPUzcmMpwxvQ1WQIIWM= -github.com/gophercloud/gophercloud v0.16.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= -github.com/gophercloud/gophercloud v0.17.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= -github.com/gophercloud/gophercloud v0.23.0 h1:I3P10oKlGu3DHP9PrEWMr1ya+/+3Rc9uRHNkRZ9wO7g= -github.com/gophercloud/gophercloud v0.23.0/go.mod h1:MRw6uyLj8uCGbIvBlqL7QW67t0QtNZnzydUzewo1Ioc= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grafana/dskit v0.0.0-20211021180445-3bd016e9d7f1/go.mod h1:uPG2nyK4CtgNDmWv7qyzYcdI+S90kHHRWvHnBtEMBXM= -github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-middleware/providers/kit/v2 v2.0.0-20201002093600-73cf2ae9d891/go.mod h1:516cTXxZzi4NBUBbKcwmO4Eqbb6GHAEd3o4N+GYyCBY= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-20200501113911-9a95f0fdbfea/go.mod h1:GugMBs30ZSAkckqXEAIEGyYdDH6EgqowG8ppA3Zt+AY= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.2.0.20201207153454-9f6bf00c00a7/go.mod h1:GhphxcdlaRyAuBSvo6rV71BvQcvB/vuX8ugCyybuS2k= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.4.1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= -github.com/grpc-ecosystem/grpc-gateway v1.14.4/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= -github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= -github.com/grpc-ecosystem/grpc-gateway v1.15.0/go.mod h1:vO11I9oWA+KsxmfFQPhLnnIb1VDE24M+pdxZFiuZcA8= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU= -github.com/hashicorp/consul/api v1.5.0/go.mod h1:LqwrLNW876eYSuUOo4ZLHBcdKc038txr/IMfbLPATa4= -github.com/hashicorp/consul/api v1.6.0/go.mod h1:1NSuaUUkFaJzMasbfq/11wKYWSR67Xn6r2DXKhuDNFg= -github.com/hashicorp/consul/api v1.7.0/go.mod h1:1NSuaUUkFaJzMasbfq/11wKYWSR67Xn6r2DXKhuDNFg= -github.com/hashicorp/consul/api v1.8.1/go.mod h1:sDjTOq0yUyv5G4h+BqSea7Fn6BU+XbolEz1952UB+mk= -github.com/hashicorp/consul/api v1.9.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/api v1.11.0 h1:Hw/G8TtRvOElqxVIhBzXciiSTbapq8hZ2XKZsXk5ZCE= -github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= -github.com/hashicorp/consul/sdk v0.5.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= -github.com/hashicorp/consul/sdk v0.6.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= -github.com/hashicorp/consul/sdk v0.7.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= -github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/gophercloud/gophercloud v1.7.0 h1:fyJGKh0LBvIZKLvBWvQdIgkaV5yTM3Jh9EYUh+UNCAs= +github.com/gophercloud/gophercloud v1.7.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= +github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= +github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= +github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= +github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= @@ -1099,119 +400,45 @@ github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0S github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= -github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= +github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.2.3/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.2.4 h1:OOhYzSvFnkFQXm1ysE8RjXTHsqSRDyP4emusC9K7DYg= -github.com/hashicorp/memberlist v0.2.4/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= -github.com/hashicorp/serf v0.8.5/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= -github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU= -github.com/hashicorp/serf v0.9.3/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hetznercloud/hcloud-go v1.21.1/go.mod h1:xng8lbDUg+xM1dgc0yGHX5EeqbwIq7UYlMWMTx3SQVg= -github.com/hetznercloud/hcloud-go v1.22.0/go.mod h1:xng8lbDUg+xM1dgc0yGHX5EeqbwIq7UYlMWMTx3SQVg= -github.com/hetznercloud/hcloud-go v1.23.1/go.mod h1:xng8lbDUg+xM1dgc0yGHX5EeqbwIq7UYlMWMTx3SQVg= -github.com/hetznercloud/hcloud-go v1.24.0/go.mod h1:3YmyK8yaZZ48syie6xpm3dt26rtB6s65AisBHylXYFA= -github.com/hetznercloud/hcloud-go v1.25.0/go.mod h1:2C5uMtBiMoFr3m7lBFPf7wXTdh33CevmZpQIIDPGYJI= -github.com/hetznercloud/hcloud-go v1.33.1 h1:W1HdO2bRLTKU4WsyqAasDSpt54fYO4WNckWYfH5AuCQ= -github.com/hetznercloud/hcloud-go v1.33.1/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME= -github.com/hodgesds/perf-utils v0.0.8/go.mod h1:F6TfvsbtrF88i++hou29dTXlI2sfsJv+gRZDtmTJkAs= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c h1:Nc3Mt2BAnq0/VoLEntF/nipX+K1S7pG+RgwiitSv6v0= +github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c/go.mod h1:O23qLAZuCx4htdY9zBaO4cJPXgleSFEdq6D/sezGgYE= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hetznercloud/hcloud-go/v2 v2.4.0 h1:MqlAE+w125PLvJRCpAJmEwrIxoVdUdOyuFUhE/Ukbok= +github.com/hetznercloud/hcloud-go/v2 v2.4.0/go.mod h1:l7fA5xsncFBzQTyw29/dw5Yr88yEGKKdc6BHf24ONS0= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= -github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY= -github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= -github.com/influxdata/flux v0.131.0/go.mod h1:CKvnYe6FHpTj/E0YGI7TcOZdGiYHoToOPSnoa12RtKI= -github.com/influxdata/httprouter v1.3.1-0.20191122104820-ee83e2772f69/go.mod h1:pwymjR6SrP3gD3pRj9RJwdl1j5s3doEEV8gS4X9qSzA= -github.com/influxdata/influxdb v1.7.7/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY= -github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ= -github.com/influxdata/influxdb v1.8.1/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ= -github.com/influxdata/influxdb v1.8.2/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ= -github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= -github.com/influxdata/influxdb v1.8.4/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= -github.com/influxdata/influxdb v1.8.5/go.mod h1:oFH+pbEyDln/1TKwa98oJzVrkZwdjrJOwIDGYZj7Ma0= -github.com/influxdata/influxdb v1.9.5/go.mod h1:4uPVvcry9KWQVWLxyT9641qpkRXUBN+xa0MJFFNNLKo= -github.com/influxdata/influxdb-client-go/v2 v2.3.1-0.20210518120617-5d1fff431040/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo= -github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/influxql v1.1.1-0.20210223160523-b6ab99450c93/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= -github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/influxdata/pkg-config v0.2.8/go.mod h1:EMS7Ll0S4qkzDk53XS3Z72/egBsPInt+BeRxb0WeSwk= -github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= -github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= -github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= -github.com/jessevdk/go-flags v0.0.0-20180331124232-1c38ed7ad0cc/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/ionos-cloud/sdk-go/v6 v6.1.9 h1:Iq3VIXzeEbc8EbButuACgfLMiY5TPVWUPNrF+Vsddo4= +github.com/ionos-cloud/sdk-go/v6 v6.1.9/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= +github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= +github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20190830100107-3784a6c7c552/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -1219,849 +446,300 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= -github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0= -github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b h1:iNjcivnc6lhbvJA3LD622NPrUponluJrBWPIwGG/3Bg= -github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= +github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g= +github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.0.0/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= -github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= -github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/lann/builder v0.0.0-20150808151131-f22ce00fd939/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= -github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= -github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -github.com/leanovate/gopter v0.2.4/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8= -github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.0/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/linode/linodego v1.2.1 h1:v0vS/n9dGMA9evG+fhLJcy7hsf6TUVmacfYiYzARhts= -github.com/linode/linodego v1.2.1/go.mod h1:x/7+BoaKd4unViBmS2umdjYyVAmpFtBtEXZ0wou7FYQ= -github.com/lovoo/gcloud-opentracing v0.3.0/go.mod h1:ZFqk2y38kMDDikZPAK7ynTTGuyt17nSPdS3K5e+ZTBY= -github.com/lufia/iostat v1.1.0/go.mod h1:rEPNA0xXgjHQjuI5Cy05sLlS2oRcSlWHRLrvh/AQ+Pg= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/linode/linodego v1.23.0 h1:s0ReCZtuN9Z1IoUN9w1RLeYO1dMZUGPwOQ/IBFsBHtU= +github.com/linode/linodego v1.23.0/go.mod h1:0U7wj/UQOqBNbKv1FYTXiBUXueR8DY4HvIotwE0ENgg= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= -github.com/mattn/go-ieproxy v0.0.0-20191113090002-7c0f6868bffe/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= -github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= -github.com/mattn/go-xmlrpc v0.0.3/go.mod h1:mqc2dz7tP5x5BKlCahN/n+hs7OSZKJkS9JsHNBRlrxA= -github.com/matttproud/golang_protobuf_extensions v1.0.0/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= -github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= -github.com/mdlayher/netlink v0.0.0-20190828143259-340058475d09/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= -github.com/mdlayher/wifi v0.0.0-20190303161829-b1436901ddee/go.mod h1:Evt/EIne46u9PtQbeTx2NTcqURpr5K4SvKtGmBuDPN8= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= +github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= +github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a h1:0usWxe5SGXKQovz3p+BiQ81Jy845xSMu2CWKuXsXuUM= +github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a/go.mod h1:3OETvrxfELvGsU2RoGGWercfeZ4bCL3+SOwzIWtJH/Q= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.38/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mileusna/useragent v0.0.0-20190129205925-3e331f0949a5/go.mod h1:JWhYAp2EXqUtsxTKdeGlY8Wp44M7VxThC9FEoNGi2IE= -github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw= -github.com/minio/minio-go/v6 v6.0.44/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg= -github.com/minio/minio-go/v6 v6.0.56/go.mod h1:KQMM+/44DSlSGSQWSfRrAZ12FVMmpWNuX37i2AX0jfI= -github.com/minio/minio-go/v7 v7.0.2/go.mod h1:dJ80Mv2HeGkYLH1sqS/ksz07ON6csH3S6JUMSQ2zAns= -github.com/minio/minio-go/v7 v7.0.10/go.mod h1:td4gW1ldOsj1PbSNS+WYK43j+P1XVhX/8W8awaYlBFo= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0= -github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= -github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mozillazg/go-cos v0.13.0/go.mod h1:Zp6DvvXn0RUOXGJ2chmWt2bLEqRAnJnS3DnAZsJsoaE= -github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= -github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= -github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/ncw/swift v1.0.50/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/ncw/swift v1.0.52/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/oklog/ulid v0.0.0-20170117200651-66bb6560562f/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02/go.mod h1:JNdpVEzCpXBgIiv4ds+TzhN1hrtxq6ClLrTlT9OQRSc= -github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= -github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.1-0.20200124165624-2876d2018785/go.mod h1:C+iumr2ni468+1jvcHXLCdqP9uQnoQbdX93F3aWahWU= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0= +github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= -github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus-community/prom-label-proxy v0.4.1-0.20211215142838-1eac0933d512 h1:2TVScDEIfhUCwVNrUenXBGke17gURHMi4nIjZ5iOLBc= -github.com/prometheus-community/prom-label-proxy v0.4.1-0.20211215142838-1eac0933d512/go.mod h1:qYNF+Y9Fm8LQEsYPHPx67Sw7gXNGNFSWtuQpL/4tvbk= -github.com/prometheus-operator/prometheus-operator v0.53.1 h1:UN1/7cX0bcL321R2MdLMU0olHaTh0kWZzub26sBarpY= -github.com/prometheus-operator/prometheus-operator v0.53.1/go.mod h1:fVDQolAz9K/ThjZ6Ze5Z/HmxJxYNcIA2Yfsq9yokV7g= -github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.53.1 h1:VYWk40/hnOlk7T64najC0RIvYv4RJ9SwLAJyAu5qWyI= -github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.53.1/go.mod h1:/xf16Bu3krDP6G5WhrJL9avDnLW/AN0g7hAIK63mbes= -github.com/prometheus-operator/prometheus-operator/pkg/client v0.53.1 h1:tKzmD3DQoK3OIcIxWhEmjdrjAr8ufVRpOO5JvpoDgic= -github.com/prometheus-operator/prometheus-operator/pkg/client v0.53.1/go.mod h1:qP20QQUPGhQMp8yD68N17s3VvMeuLYrh+An0M4Q/zs0= -github.com/prometheus/alertmanager v0.18.0/go.mod h1:WcxHBl40VSPuOaqWae6l6HpnEOVRIycEJ7i9iYkadEE= -github.com/prometheus/alertmanager v0.19.0/go.mod h1:Eyp94Yi/T+kdeb2qvq66E3RGuph5T/jm/RBVh4yz1xo= -github.com/prometheus/alertmanager v0.20.0/go.mod h1:9g2i48FAyZW6BtbsnvHtMHQXl2aVtrORKwKVCQ+nbrg= -github.com/prometheus/alertmanager v0.21.0/go.mod h1:h7tJ81NA0VLWvWEayi1QltevFkLF3KxmC/malTcT8Go= -github.com/prometheus/alertmanager v0.21.1-0.20200911160112-1fdff6b3f939/go.mod h1:imXRHOP6QTsE0fFsIsAV/cXimS32m7gVZOiUj11m6Ig= -github.com/prometheus/alertmanager v0.21.1-0.20201106142418-c39b78780054/go.mod h1:imXRHOP6QTsE0fFsIsAV/cXimS32m7gVZOiUj11m6Ig= -github.com/prometheus/alertmanager v0.21.1-0.20210310093010-0f9cab6991e6/go.mod h1:MTqVn+vIupE0dzdgo+sMcNCp37SCAi8vPrvKTTnTz9g= -github.com/prometheus/alertmanager v0.21.1-0.20210422101724-8176f78a70e1/go.mod h1:gsEqwD5BHHW9RNKvCuPOrrTMiP5I+faJUyLXvnivHik= -github.com/prometheus/alertmanager v0.23.0/go.mod h1:0MLTrjQI8EuVmvykEhcfr/7X0xmaDAZrqMgxIq3OXHk= -github.com/prometheus/alertmanager v0.23.1-0.20210914172521-e35efbddb66a h1:qroc/F4ygaQ0uc2S+Pyk/exMwnSpokGyN1QjfZ1DiWU= -github.com/prometheus/alertmanager v0.23.1-0.20210914172521-e35efbddb66a/go.mod h1:U7pGu+z7A9ZKhK8lq1MvIOp5GdVlZjwOYk+S0h3LSbA= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus-community/prom-label-proxy v0.7.0 h1:1iNHXF7V8z2iOCinEyxKDUHu2jppPAAd6PmBCi3naok= +github.com/prometheus-community/prom-label-proxy v0.7.0/go.mod h1:wR9C/Mwp5aBbiqM6gQ+FZdFRwL8pCzzhsje8lTAx/aA= +github.com/prometheus-operator/prometheus-operator v0.69.1 h1:pqNMssMBBaM6mYg7FKK7kQi9sIyWYCA33z5FSmmbybw= +github.com/prometheus-operator/prometheus-operator v0.69.1/go.mod h1:1GSjL8dKOO9be+b7aSowZo7cuTdsdTqLT3ZETktVqvU= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.69.1 h1:hOnp+1FLBm+ifsyiRbunmfSs99jKAq+Tr5elCmo5l5U= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.69.1/go.mod h1:JtflYMUMay9HGil4aRg+dSj6X6mngtuBJf/ULOCxbxI= +github.com/prometheus-operator/prometheus-operator/pkg/client v0.69.1 h1:hCqId2xt6kOPuoeTxVxV/GiKmZyCJjYeuRRL90qcwH0= +github.com/prometheus-operator/prometheus-operator/pkg/client v0.69.1/go.mod h1:XfInnOa8WYXOlzOb0Iw+9eg0s/bkeOnXyten1pKFjuc= +github.com/prometheus/alertmanager v0.26.0 h1:uOMJWfIwJguc3NaM3appWNbbrh6G/OjvaHMk22aBBYc= +github.com/prometheus/alertmanager v0.26.0/go.mod h1:rVcnARltVjavgVaNnmevxK7kOn7IZavyf0KNgHkbEpU= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.2.0/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= -github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= -github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_model v0.0.0-20170216185247-6f3806018612/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.8.0/go.mod h1:PC/OgXc+UN7B4ALwvn1yzVZmVwvhXp5JsbBv6wSv6i0= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.11.1/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.12.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.20.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.21.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.23.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.28.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= -github.com/prometheus/exporter-toolkit v0.5.0/go.mod h1:OCkM4805mmisBhLmVFw858QYi3v0wKdY6/UxrT0pZVg= -github.com/prometheus/exporter-toolkit v0.5.1/go.mod h1:OCkM4805mmisBhLmVFw858QYi3v0wKdY6/UxrT0pZVg= -github.com/prometheus/exporter-toolkit v0.6.1/go.mod h1:ZUBIj498ePooX9t/2xtDjeQYwvRpiPP2lh5u4iblj2g= -github.com/prometheus/exporter-toolkit v0.7.0/go.mod h1:ZUBIj498ePooX9t/2xtDjeQYwvRpiPP2lh5u4iblj2g= -github.com/prometheus/node_exporter v1.0.0-rc.0.0.20200428091818-01054558c289/go.mod h1:FGbBv5OPKjch+jNUJmEQpMZytIdyW0NdBtWFcfSKusc= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20180612222113-7d6f385de8be/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.6/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/prometheus v0.0.0-20180315085919-58e2a31db8de/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s= -github.com/prometheus/prometheus v0.0.0-20190818123050-43acd0e2e93f/go.mod h1:rMTlmxGCvukf2KMu3fClMDKLLoJ5hl61MhcJ7xKakf0= -github.com/prometheus/prometheus v0.0.0-20200609090129-a6600f564e3c/go.mod h1:S5n0C6tSgdnwWshBUceRx5G1OsjLv/EeZ9t3wIfEtsY= -github.com/prometheus/prometheus v1.8.2-0.20200107122003-4708915ac6ef/go.mod h1:7U90zPoLkWjEIQcy/rweQla82OCTUzxVHE51G3OhJbI= -github.com/prometheus/prometheus v1.8.2-0.20200213233353-b90be6f32a33/go.mod h1:fkIPPkuZnkXyopYHmXPxf9rgiPkVgZCN8w9o8+UgBlY= -github.com/prometheus/prometheus v1.8.2-0.20200707115909-30505a202a4c/go.mod h1:/kMSPIRsxr/apyHxlzYMdFnaPXUXXqILU5uzIoNhOvc= -github.com/prometheus/prometheus v1.8.2-0.20200722151933-4a8531a64b32/go.mod h1:+/y4DzJ62qmhy0o/H4PtXegRXw+80E8RVRHhLbv+bkM= -github.com/prometheus/prometheus v1.8.2-0.20200805082714-e0cf219f0de2/go.mod h1:i1KZsZmyDTJRvnR7zE8z/u2v+tkpPjoiPpnWp6nwhr0= -github.com/prometheus/prometheus v1.8.2-0.20200819132913-cb830b0a9c78/go.mod h1:zfAqy/MwhMFajB9E2n12/9gG2fvofIE9uKDtlZCDxqs= -github.com/prometheus/prometheus v1.8.2-0.20200923143134-7e2db3d092f3/go.mod h1:9VNWoDFHOMovlubld5uKKxfCDcPBj2GMOCjcUFXkYaM= -github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24/go.mod h1:MDRkz271loM/PrYN+wUNEaTMDGSP760MQzB0yEjdgSQ= -github.com/prometheus/prometheus v1.8.2-0.20201029103703-63be30dceed9/go.mod h1:MDRkz271loM/PrYN+wUNEaTMDGSP760MQzB0yEjdgSQ= -github.com/prometheus/prometheus v1.8.2-0.20201119142752-3ad25a6dc3d9/go.mod h1:1MDE/bXgu4gqd5w/otko6WQpXZX9vu8QX4KbitCmaPg= -github.com/prometheus/prometheus v1.8.2-0.20201119181812-c8f810083d3f/go.mod h1:1MDE/bXgu4gqd5w/otko6WQpXZX9vu8QX4KbitCmaPg= -github.com/prometheus/prometheus v1.8.2-0.20210215121130-6f488061dfb4/go.mod h1:NAYujktP0dmSSpeV155mtnwX2pndLpVVK/Ps68R01TA= -github.com/prometheus/prometheus v1.8.2-0.20210315220929-1cba1741828b/go.mod h1:MS/bpdil77lPbfQeKk6OqVQ9OLnpN3Rszd0hka0EOWE= -github.com/prometheus/prometheus v1.8.2-0.20210324152458-c7a62b95cea0/go.mod h1:sf7j/iAbhZahjeC0s3wwMmp5dksrJ/Za1UKdR+j6Hmw= -github.com/prometheus/prometheus v1.8.2-0.20210519120135-d95b0972505f/go.mod h1:yUzDYX0hIYu5YVHmpj/JXLOclB6QcLNDgmagD3FUnSU= -github.com/prometheus/prometheus v1.8.2-0.20211119115433-692a54649ed7/go.mod h1:outfylaI89+D5IO87TRPRmxfucIobTO3Rb0l2TKqpj0= -github.com/prometheus/prometheus v1.8.2-0.20211214150951-52c693a63be1 h1:2FprD6qMVmu/qOclTsMpMReGn1EcQ7SUk++SIycVlYk= -github.com/prometheus/prometheus v1.8.2-0.20211214150951-52c693a63be1/go.mod h1:bBByfKZ/sC+obk4CX3GpJ2Ul+fKZGtXneLYFG8C//nc= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rafaeljusto/redigomock v0.0.0-20190202135759-257e089e14a1/go.mod h1:JaY6n2sDr+z2WTsXkOmNRUfDy6FN0L6Nk7x06ndm4tY= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/prometheus v0.48.0 h1:yrBloImGQ7je4h8M10ujGh4R6oxYQJQKlMuETwNskGk= +github.com/prometheus/prometheus v0.48.0/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/samuel/go-zookeeper v0.0.0-20200724154423-2164a8ac840e/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/samuel/go-zookeeper v0.0.0-20201211165307-7117e9ea2414/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= -github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44 h1:3egqo0Vut6daANFm7tOXdNAa8v5/uLU+sgCJrc88Meo= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21 h1:yWfiTPwYxB0l5fGMhl/G+liULugVIHD9AU77iNLrURQ= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e/go.mod h1:tm/wZFQ8e24NYaBGIlnO2WGCAi67re4HHuOm0sftE/M= -github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/sercand/kuberesolver v2.1.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJPl/ZshwPsX/n4Y7u0CW9E7aQIQ= -github.com/sercand/kuberesolver v2.4.0+incompatible/go.mod h1:lWF3GL0xptCB/vCiJPl/ZshwPsX/n4Y7u0CW9E7aQIQ= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20180825020608-02ddb050ef6b/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/shurcooL/vfsgen v0.0.0-20200627165143-92b8a710ab6c/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/siebenmann/go-kstat v0.0.0-20160321171754-d34789b79745/go.mod h1:G81aIFAMS9ECrwBYR9YxhlPjWgrItd+Kje78O6+uqm8= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/shoenig/test v0.6.6 h1:Oe8TPH9wAbv++YPNDKJWUnI8Q4PPWCx3UbOfH+FxiMU= +github.com/shoenig/test v0.6.6/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snowflakedb/gosnowflake v1.3.13/go.mod h1:6nfka9aTXkUNha1p1cjeeyjDvcyh7jfjp0l8kGpDBok= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5-0.20210205191134-5ec6847320e5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/soundcloud/go-runit v0.0.0-20150630195641-06ad41a06c4a/go.mod h1:LeFCbQYJ3KJlPs/FvPz2dy1tkpxyeNESVyCNNzRXFR0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= -github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.194/go.mod h1:yrBKWhChnDqNz1xuXdSbWXG56XawEq0G5j1lg4VwBD4= -github.com/tencentyun/cos-go-sdk-v5 v0.7.31/go.mod h1:4E4+bQ2gBVJcgEC9Cufwylio4mXOct2iu05WjgEBx1o= -github.com/thanos-io/thanos v0.8.1-0.20200109203923-552ffa4c1a0d/go.mod h1:usT/TxtJQ7DzinTt+G9kinDQmRS5sxwu0unVKZ9vdcw= -github.com/thanos-io/thanos v0.13.1-0.20200731083140-69b87607decf/go.mod h1:G8caR6G7pSDreRDvFm9wFuyjEBztmr8Ag3kBYpa/fEc= -github.com/thanos-io/thanos v0.13.1-0.20200807203500-9b578afb4763/go.mod h1:KyW0a93tsh7v4hXAwo2CVAIRYuZT1Kkf4e04gisQjAg= -github.com/thanos-io/thanos v0.13.1-0.20201019130456-f41940581d9a/go.mod h1:A3qUEEbsVkplJnxyDLwuIuvTDaJPByTH+hMdTl9ujAA= -github.com/thanos-io/thanos v0.13.1-0.20201030101306-47f9a225cc52/go.mod h1:OqqX4x21cg5N5MMHd/yGQAc/V3wg8a7Do4Jk8HfaFZQ= -github.com/thanos-io/thanos v0.13.1-0.20210108102609-f85e4003ba51/go.mod h1:kPvI4H0AynFiHDN95ZB28/k70ZPGCx+pBrRh6RZPimw= -github.com/thanos-io/thanos v0.13.1-0.20210204123931-82545cdd16fe/go.mod h1:ZLDGYRNkgM+FCwYNOD+6tOV+DE2fpjzfV6iqXyOgFIw= -github.com/thanos-io/thanos v0.13.1-0.20210224074000-659446cab117/go.mod h1:kdqFpzdkveIKpNNECVJd75RPvgsAifQgJymwCdfev1w= -github.com/thanos-io/thanos v0.13.1-0.20210226164558-03dace0a1aa1/go.mod h1:gMCy4oCteKTT7VuXVvXLTPGzzjovX1VPE5p+HgL1hyU= -github.com/thanos-io/thanos v0.13.1-0.20210401085038-d7dff0c84d17/go.mod h1:zU8KqE+6A+HksK4wiep8e/3UvCZLm+Wrw9AqZGaAm9k= -github.com/thanos-io/thanos v0.22.0/go.mod h1:SZDWz3phcUcBr4MYFoPFRvl+Z9Nbi45HlwQlwSZSt+Q= -github.com/thanos-io/thanos v0.24.0-rc.1 h1:DyR8rDMGbDVsxgX2EQZ5ye+AE4HyY4QevuBKhnJhw9o= -github.com/thanos-io/thanos v0.24.0-rc.1/go.mod h1:sfnKJG7cDA41ixNL4gsTJEa3w9Qt8lwAjw+dqRMSDG0= -github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/uber-go/tally v3.3.15+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= -github.com/uber/athenadriver v1.1.4/go.mod h1:tQjho4NzXw55LGfSZEcETuYydpY1vtmixUabHkC1K/E= -github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.20.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.23.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.23.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.24.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.28.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.29.1+incompatible h1:R9ec3zO3sGpzs0abd43Y+fBZRJ9uiH6lXyR/+u6brW4= -github.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v1.5.1-0.20181102163054-1fc5c315e03c/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= -github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/weaveworks/common v0.0.0-20200206153930-760e36ae819a/go.mod h1:6enWAqfQBFrE8X/XdJwZr8IKgh1chStuFR0mjU/UOUw= -github.com/weaveworks/common v0.0.0-20200625145055-4b1847531bc9/go.mod h1:c98fKi5B9u8OsKGiWHLRKus6ToQ1Tubeow44ECO1uxY= -github.com/weaveworks/common v0.0.0-20200914083218-61ffdd448099/go.mod h1:hz10LOsAdzC3K/iXaKoFxOKTDRgxJl+BTGX1GY+TzO4= -github.com/weaveworks/common v0.0.0-20201119133501-0619918236ec/go.mod h1:ykzWac1LtVfOxdCK+jD754at1Ws9dKCwFeUzkFBffPs= -github.com/weaveworks/common v0.0.0-20210112142934-23c8d7fa6120/go.mod h1:ykzWac1LtVfOxdCK+jD754at1Ws9dKCwFeUzkFBffPs= -github.com/weaveworks/common v0.0.0-20210419092856-009d1eebd624/go.mod h1:ykzWac1LtVfOxdCK+jD754at1Ws9dKCwFeUzkFBffPs= -github.com/weaveworks/common v0.0.0-20210913144402-035033b78a78/go.mod h1:YU9FvnS7kUnRt6HY10G+2qHkwzP3n3Vb1XsXDsJTSp8= -github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA= -github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= +github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/xlab/treeprint v1.0.0/go.mod h1:IoImgRak9i3zJyuxOKUP1v4UZd1tMoKkq/Cimt1uhCg= -github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.elastic.co/apm v1.5.0/go.mod h1:OdB9sPtM6Vt7oz3VXt7+KR96i9li74qrxBGHTQygFvk= -go.elastic.co/apm v1.11.0/go.mod h1:qoOSi09pnzJDh5fKnfY7bPmQgl8yl2tULdOu03xhui0= -go.elastic.co/apm/module/apmhttp v1.5.0/go.mod h1:1FbmNuyD3ddauwzgVwFB0fqY6KbZt3JkV187tGCYYhY= -go.elastic.co/apm/module/apmhttp v1.11.0/go.mod h1:5JFMIxdeS4vJy+D1PPPjINuX6hZ3AHalZXoOgyqZAkk= -go.elastic.co/apm/module/apmot v1.5.0/go.mod h1:d2KYwhJParTpyw2WnTNy8geNlHKKFX+4oK3YLlsesWE= -go.elastic.co/apm/module/apmot v1.11.0/go.mod h1:Qnbt3w1DvUd/5QugAF1AJ3mR4AG86EcJFBnAGW77EmU= -go.elastic.co/fastjson v1.0.0/go.mod h1:PmeUOMMtLHQr9ZS9J9owrAVg0FkaZDRZJEFTTGHtchs= -go.elastic.co/fastjson v1.1.0/go.mod h1:boNGISWMjQsUPy/t6yqt2/1Wx4YNPSe+mZjlyw9vKKI= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.0.0-20190709142735-eb7dd97135a5/go.mod h1:N0RPWo9FXJYZQI4BTkDtQylrstIigYHeR18ONnyTufk= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200520232829-54ba9589114f/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= -go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0-alpha.0/go.mod h1:kdV+xzCJ3luEBSIeQyB/OEKkWKd8Zkux4sbDeANrosU= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0-alpha.0/go.mod h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8= -go.etcd.io/etcd/client/v3 v3.5.0-alpha.0.0.20210225194612-fa82d11a958a/go.mod h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0/go.mod h1:tV31atvwzcybuqejDoY3oaNRTtlD2l/Ot78Pc9w7DMY= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0/go.mod h1:FAwse6Zlm5v4tEWZaTjmNhe17Int4Oxbu7+2r0DiD3w= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0-alpha.0.0.20210225194612-fa82d11a958a/go.mod h1:tsKetYpt980ZTpzl/gb+UOJj9RkIyCb1u4wjzMg90BQ= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.0.4/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= -go.mongodb.org/mongo-driver v1.3.2/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= -go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= -go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= -go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= -go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= -go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5 h1:ny3p0reEpgsR2cfA5cjgwFZg3Cv/ofFh/8jbhGtz9VI= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= +go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.2.0/go.mod h1:YfO3fm683kQpzETxlTGZhGIVmXAhaw3gxeBADbpZtnU= -go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= -go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180505025534-4ec37c66abab/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180608092829-8ac0e0d97ce4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210915214749-c084706c2272/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191029154019-8994fa331a53/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= -golang.org/x/exp v0.0.0-20200821190819-94841d0725da/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -2074,9 +752,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -2085,47 +760,25 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -2133,68 +786,34 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210324051636-2c4c8ecb7826/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210210192628-66670185b0cd/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2204,305 +823,155 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200930132711-30421366ff76/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190902133755-9109b7679e13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201008064518-c1f3e3309c71/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210314195730-07df6a141424/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180805044716-cb6730876b98/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190118193359-16909d206f00/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190813034749-528a2984e271/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190918214516-5a1a30219888/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191111182352-50fa39b762bc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304024140-c4206d458c3f/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200422205258-72e4a01eba43/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200513201620-d5fe73897c97/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200603131246-cc40288be839/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200710042808-f1c4188a97a1/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200721032237-77f530d86f9a/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200725200936-102e7d357031/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200822203824-307de81be3f4/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201020161133-226fd2f889ca/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201119054027-25dc3e1ccc3c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= -golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -2516,64 +985,32 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.26.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.39.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.42.0/go.mod h1:+Oj4s6ch2SEGtPjGqfUfZonBH0GjQH89gTeKKAEGZKI= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= -google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.60.0 h1:eq/zs5WPH4J9undYM9IP1O7dSr7Yh8Y0GtSCpzGzIUk= -google.golang.org/api v0.60.0/go.mod h1:d7rl65NZAkEQ90JFzqBjcRq1TVeG5ZoGV3sSpEnnVb4= +google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= +google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= -google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180608181217-32ee49c4dd80/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -2582,97 +1019,35 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200420144010-e5e8543f8aeb/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200603110839-e855014d5736/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200710124503-20a17af7bd0e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200724131911-43cab4749ae7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201102152239-715cce707fb0/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 h1:DN5b3HU13J4sMd/QjDx34U6afpaexKTDdop+26pdjdk= -google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI= +google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/examples v0.0.0-20211119005141-f45e61797429/go.mod h1:gID3PKrg7pWKntu9Ss6zTLJ0ttC0X9IHgREOCZwbCVU= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2685,63 +1060,36 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= -gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200603094226-e3079894b1e8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2749,150 +1097,31 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= -k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= -k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN7Vh59Bw0vh9jhoX+V58= -k8s.io/api v0.0.0-20191115095533-47f6de673b26/go.mod h1:iA/8arsvelvo4IDqIhX4IbjTEKBGgvsf2OraTuRtLFU= -k8s.io/api v0.17.5/go.mod h1:0zV5/ungglgy2Rlm3QK8fbxkXVs+BSJWpJP/+8gUVLY= -k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA= -k8s.io/api v0.18.5/go.mod h1:tN+e/2nbdGKOAH55NMV8oGrMG+3uRlA9GaRfvnCCSNk= -k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI= -k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY= -k8s.io/api v0.19.2/go.mod h1:IQpK0zFQ1xc5iNIQPqzgoOwuFugaYHK4iCknlAQP9nI= -k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.5/go.mod h1:FQjAceXnVaWDeov2YUWhOb6Yt+5UjErkp6UO3nczO1Y= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= -k8s.io/api v0.22.3/go.mod h1:azgiXFiXqiWyLCfI62/eYBOu19rj2LKmIhFPP4+33fs= -k8s.io/api v0.22.4/go.mod h1:Rgs+9gIGYC5laXQSZZ9JqT5NevNgoGiOdVWi1BAB3qk= -k8s.io/api v0.23.0 h1:WrL1gb73VSC8obi8cuYETJGXEoFNEh3LU0Pt+Sokgro= -k8s.io/api v0.23.0/go.mod h1:8wmDdLBHBNxtOIytwLstXt5E9PddnZb0GaMcqsvDBpg= -k8s.io/apiextensions-apiserver v0.23.0 h1:uii8BYmHYiT2ZTAJxmvc3X8UhNYMxl2A0z0Xq3Pm+WY= -k8s.io/apiextensions-apiserver v0.23.0/go.mod h1:xIFAEEDlAZgpVBl/1VSjGDmLoXAWRG40+GsWhKhAxY4= -k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= -k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8= -k8s.io/apimachinery v0.0.0-20191115015347-3c7067801da2/go.mod h1:dXFS2zaQR8fyzuvRdJDHw2Aerij/yVGJSre0bZQSVJA= -k8s.io/apimachinery v0.17.5/go.mod h1:ioIo1G/a+uONV7Tv+ZmCbMG1/a3kVw5YcDdncd8ugQ0= -k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= -k8s.io/apimachinery v0.18.5/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= -k8s.io/apimachinery v0.18.6/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= -k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig= -k8s.io/apimachinery v0.19.2/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.5/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= -k8s.io/apimachinery v0.22.3/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.4/go.mod h1:yU6oA6Gnax9RrxGzVvPFFJ+mpnW6PBSqp0sx0I0HHW0= -k8s.io/apimachinery v0.23.0 h1:mIfWRMjBuMdolAWJ3Fd+aPTMv3X9z+waiARMpvvb0HQ= -k8s.io/apimachinery v0.23.0/go.mod h1:fFCTTBKvKcwTPFzjlcxp91uPFZr+JA0FubU4fLzzFYc= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.23.0/go.mod h1:Cec35u/9zAepDPPFyT+UMrgqOCjgJ5qtfVJDxjZYmt4= -k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= -k8s.io/client-go v0.17.5/go.mod h1:S8uZpBpjJJdEH/fEyxcqg7Rn0P5jH+ilkgBHjriSmNo= -k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw= -k8s.io/client-go v0.18.5/go.mod h1:EsiD+7Fx+bRckKWZXnAXRKKetm1WuzPagH4iOSC8x58= -k8s.io/client-go v0.18.6/go.mod h1:/fwtGLjYMS1MaM5oi+eXhKwG+1UHidUEXRh6cNsdO0Q= -k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU= -k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA= -k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.5/go.mod h1:Ee5OOMMYvlH8FCZhDsacjMlCBwetbGZETwo1OA+e6Zw= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= -k8s.io/client-go v0.22.3/go.mod h1:ElDjYf8gvZsKDYexmsmnMQ0DYO8W9RwBjfQ1PI53yow= -k8s.io/client-go v0.22.4/go.mod h1:Yzw4e5e7h1LNHA4uqnMVrpEpUs1hJOiuBsJKIlRCHDA= -k8s.io/client-go v0.23.0 h1:vcsOqyPq7XV3QmQRCBH/t9BICJM9Q1M18qahjv+rebY= -k8s.io/client-go v0.23.0/go.mod h1:hrDnpnK1mSr65lHHcUuIZIXDgEbzc7/683c6hyG4jTA= -k8s.io/code-generator v0.23.0/go.mod h1:vQvOhDXhuzqiVfM/YHp+dmg10WDZCchJVObc9MvowsE= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.23.0 h1:UAnyzjvVZ2ZR1lF35YwtNY6VMN94WtOnArcXBu34es8= -k8s.io/component-base v0.23.0/go.mod h1:DHH5uiFvLC1edCpvcTDV++NKULdYYU6pR9Tt3HIKMKI= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.4.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.3.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.5.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.20.0/go.mod h1:Gm8eSIfQN6457haJuPaMxZw4wyP5k+ykPFlrhQDvhvw= -k8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= -k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= -k8s.io/kube-openapi v0.0.0-20190722073852-5e22f3d471e6/go.mod h1:RZvgC8MSN6DjiMV6oIfEE9pDL9CYXokkfaCKZeHm3nc= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -k8s.io/kube-openapi v0.0.0-20200316234421-82d701f24f9d/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU= -k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 h1:E3J9oCLlaobFUqsjG9DfKbP2BmgwBL2p7pn0A3dG9W4= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= -k8s.io/utils v0.0.0-20190809000727-6c36bc71fc4a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20191114200735-6ca3b61696b6/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200414100711-2df71ebbae66/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20211208161948-7d6a63dca704 h1:ZKMMxTvduyf5WUtREOqg5LiXaN1KO/+0oOQPRFrClpo= -k8s.io/utils v0.0.0-20211208161948-7d6a63dca704/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= +k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= +k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= +k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= +k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= +k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= +k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= +k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= +k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= +k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f h1:eeEUOoGYWhOz7EyXqhlR2zHKNw2mNJ9vzJmub6YN6kk= +k8s.io/kube-openapi v0.0.0-20230905202853-d090da108d2f/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.25/go.mod h1:Mlj9PNLmG9bZ6BHFwFKDo5afkpWyUISkb9Me0GnK66I= -sigs.k8s.io/controller-runtime v0.11.0 h1:DqO+c8mywcZLFJWILq4iktoECTyn30Bkj0CwgqMpZWQ= -sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/structured-merge-diff/v2 v2.0.1/go.mod h1:Wb7vfKAodbKgf6tn1Kl0VvGj7mRH6DGaRcixXEJXTsE= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/structured-merge-diff/v4 v4.2.0 h1:kDvPBbnPk+qYmkHmSo8vKGp438IASWofnbbUKDE/bv0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.0/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= +sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= +sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/cmd/otel-allocator/main.go b/cmd/otel-allocator/main.go index 85e1ca4c45..4f805519b1 100644 --- a/cmd/otel-allocator/main.go +++ b/cmd/otel-allocator/main.go @@ -16,38 +16,30 @@ package main import ( "context" - "encoding/json" - "net/http" - "net/url" + "fmt" "os" "os/signal" "syscall" - "time" - "github.com/ghodss/yaml" gokitlog "github.com/go-kit/log" - "github.com/go-logr/logr" - "github.com/gorilla/mux" + "github.com/oklog/run" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/prometheus/client_golang/prometheus/promhttp" - yaml2 "gopkg.in/yaml.v2" + "github.com/prometheus/prometheus/discovery" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ctrl "sigs.k8s.io/controller-runtime" "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation" "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/collector" "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/config" - lbdiscovery "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/discovery" + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/prehook" + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/server" + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" allocatorWatcher "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/watcher" ) var ( setupLog = ctrl.Log.WithName("setup") - httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ - Name: "opentelemetry_allocator_http_duration_seconds", - Help: "Duration of received HTTP requests.", - }, []string{"path"}) eventsMetric = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "opentelemetry_allocator_events", Help: "Number of events in the channel.", @@ -55,236 +47,172 @@ var ( ) func main() { - cliConf, err := config.ParseCLI() + var ( + // allocatorPrehook will be nil if filterStrategy is not set or + // unrecognized. No filtering will be used in this case. + allocatorPrehook prehook.Hook + allocator allocation.Allocator + discoveryManager *discovery.Manager + collectorWatcher *collector.Client + promWatcher allocatorWatcher.Watcher + targetDiscoverer *target.Discoverer + + discoveryCancel context.CancelFunc + runGroup run.Group + eventChan = make(chan allocatorWatcher.Event) + eventCloser = make(chan bool, 1) + interrupts = make(chan os.Signal, 1) + errChan = make(chan error) + ) + cfg, err := config.Load() if err != nil { - setupLog.Error(err, "Failed to parse parameters") + fmt.Printf("Failed to load config: %v", err) os.Exit(1) } - cfg, err := config.Load(*cliConf.ConfigFilePath) - if err != nil { - setupLog.Error(err, "Unable to load configuration") - } + ctrl.SetLogger(cfg.RootLogger) - cliConf.RootLogger.Info("Starting the Target Allocator") + if validationErr := config.ValidateConfig(cfg); validationErr != nil { + setupLog.Error(validationErr, "Invalid configuration") + os.Exit(1) + } + cfg.RootLogger.Info("Starting the Target Allocator") ctx := context.Background() - log := ctrl.Log.WithName("allocator") - allocator, err := allocation.New(cfg.GetAllocationStrategy(), log) + allocatorPrehook = prehook.New(cfg.GetTargetsFilterStrategy(), log) + allocator, err = allocation.New(cfg.GetAllocationStrategy(), log, allocation.WithFilter(allocatorPrehook)) if err != nil { setupLog.Error(err, "Unable to initialize allocation strategy") os.Exit(1) } - watcher, err := allocatorWatcher.NewWatcher(setupLog, cliConf, allocator) - if err != nil { - setupLog.Error(err, "Can't start the watchers") - os.Exit(1) - } - defer func() { - err := watcher.Close() - if err != nil { - log.Error(err, "failed to close watcher") - } - }() + srv := server.NewServer(log, allocator, cfg.ListenAddr) - // creates a new discovery manager - discoveryManager := lbdiscovery.NewManager(log, ctx, gokitlog.NewNopLogger()) - defer discoveryManager.Close() - discoveryManager.Watch(allocator.SetTargets) + discoveryCtx, discoveryCancel := context.WithCancel(ctx) + discoveryManager = discovery.NewManager(discoveryCtx, gokitlog.NewNopLogger()) + discovery.RegisterMetrics() // discovery manager metrics need to be enabled explicitly - k8sclient, err := configureFileDiscovery(log, allocator, discoveryManager, context.Background(), cliConf) - if err != nil { - setupLog.Error(err, "Can't start the k8s client") + targetDiscoverer = target.NewDiscoverer(log, discoveryManager, allocatorPrehook, srv) + collectorWatcher, collectorWatcherErr := collector.NewClient(log, cfg.ClusterConfig) + if collectorWatcherErr != nil { + setupLog.Error(collectorWatcherErr, "Unable to initialize collector watcher") os.Exit(1) } + signal.Notify(interrupts, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + defer close(interrupts) - srv := newServer(log, allocator, discoveryManager, k8sclient, cliConf.ListenAddr) - - interrupts := make(chan os.Signal, 1) - signal.Notify(interrupts, os.Interrupt, syscall.SIGTERM) - - go func() { - if err := srv.Start(); err != http.ErrServerClosed { - setupLog.Error(err, "Can't start the server") + if cfg.PrometheusCR.Enabled { + promWatcher, err = allocatorWatcher.NewPrometheusCRWatcher(setupLog.WithName("prometheus-cr-watcher"), *cfg) + if err != nil { + setupLog.Error(err, "Can't start the prometheus watcher") + os.Exit(1) } - }() - - for { - select { - case <-interrupts: - if err := srv.Shutdown(ctx); err != nil { - setupLog.Error(err, "Error on server shutdown") - os.Exit(1) - } - os.Exit(0) - case event := <-watcher.Events: - eventsMetric.WithLabelValues(event.Source.String()).Inc() - switch event.Source { - case allocatorWatcher.EventSourceConfigMap: - setupLog.Info("ConfigMap updated!") - // Restart the server to pickup the new config. - if err := srv.Shutdown(ctx); err != nil { - setupLog.Error(err, "Cannot shutdown the server") + runGroup.Add( + func() error { + promWatcherErr := promWatcher.Watch(eventChan, errChan) + setupLog.Info("Prometheus watcher exited") + return promWatcherErr + }, + func(_ error) { + setupLog.Info("Closing prometheus watcher") + promWatcherErr := promWatcher.Close() + if promWatcherErr != nil { + setupLog.Error(promWatcherErr, "prometheus watcher failed to close") } - srv = newServer(log, allocator, discoveryManager, k8sclient, cliConf.ListenAddr) - go func() { - if err := srv.Start(); err != http.ErrServerClosed { - setupLog.Error(err, "Can't restart the server") + }) + } + runGroup.Add( + func() error { + discoveryManagerErr := discoveryManager.Run() + setupLog.Info("Discovery manager exited") + return discoveryManagerErr + }, + func(_ error) { + setupLog.Info("Closing discovery manager") + discoveryCancel() + }) + runGroup.Add( + func() error { + // Initial loading of the config file's scrape config + err = targetDiscoverer.ApplyConfig(allocatorWatcher.EventSourceConfigMap, cfg.PromConfig) + if err != nil { + setupLog.Error(err, "Unable to apply initial configuration") + return err + } + err := targetDiscoverer.Watch(allocator.SetTargets) + setupLog.Info("Target discoverer exited") + return err + }, + func(_ error) { + setupLog.Info("Closing target discoverer") + targetDiscoverer.Close() + }) + runGroup.Add( + func() error { + err := collectorWatcher.Watch(ctx, cfg.LabelSelector, allocator.SetCollectors) + setupLog.Info("Collector watcher exited") + return err + }, + func(_ error) { + setupLog.Info("Closing collector watcher") + collectorWatcher.Close() + }) + runGroup.Add( + func() error { + err := srv.Start() + setupLog.Info("Server failed to start") + return err + }, + func(_ error) { + setupLog.Info("Closing server") + if shutdownErr := srv.Shutdown(ctx); shutdownErr != nil { + setupLog.Error(shutdownErr, "Error on server shutdown") + } + }) + runGroup.Add( + func() error { + for { + select { + case event := <-eventChan: + eventsMetric.WithLabelValues(event.Source.String()).Inc() + loadConfig, err := event.Watcher.LoadConfig(ctx) + if err != nil { + setupLog.Error(err, "Unable to load configuration") + continue } - }() - - case allocatorWatcher.EventSourcePrometheusCR: - setupLog.Info("PrometheusCRs changed") - promConfig, err := interface{}(*event.Watcher).(*allocatorWatcher.PrometheusCRWatcher).CreatePromConfig(cliConf.KubeConfigFilePath) - if err != nil { - setupLog.Error(err, "failed to compile Prometheus config") + err = targetDiscoverer.ApplyConfig(event.Source, loadConfig) + if err != nil { + setupLog.Error(err, "Unable to apply configuration") + continue + } + case err := <-errChan: + setupLog.Error(err, "Watcher error") + case <-eventCloser: + return nil } - err = discoveryManager.ApplyConfig(allocatorWatcher.EventSourcePrometheusCR, promConfig) - if err != nil { - setupLog.Error(err, "failed to apply Prometheus config") + } + }, + func(_ error) { + setupLog.Info("Closing watcher loop") + close(eventCloser) + }) + runGroup.Add( + func() error { + for { + select { + case <-interrupts: + setupLog.Info("Received interrupt") + return nil + case <-eventCloser: + return nil } } - case err := <-watcher.Errors: - setupLog.Error(err, "Watcher error") - } - } -} - -type server struct { - logger logr.Logger - allocator allocation.Allocator - discoveryManager *lbdiscovery.Manager - k8sClient *collector.Client - server *http.Server -} - -func newServer(log logr.Logger, allocator allocation.Allocator, discoveryManager *lbdiscovery.Manager, k8sclient *collector.Client, listenAddr *string) *server { - s := &server{ - logger: log, - allocator: allocator, - discoveryManager: discoveryManager, - k8sClient: k8sclient, - } - router := mux.NewRouter().UseEncodedPath() - router.Use(s.PrometheusMiddleware) - router.HandleFunc("/scrape_configs", s.ScrapeConfigsHandler).Methods("GET") - router.HandleFunc("/jobs", s.JobHandler).Methods("GET") - router.HandleFunc("/jobs/{job_id}/targets", s.TargetsHandler).Methods("GET") - router.Path("/metrics").Handler(promhttp.Handler()) - s.server = &http.Server{Addr: *listenAddr, Handler: router, ReadHeaderTimeout: 90 * time.Second} - return s -} - -func configureFileDiscovery(log logr.Logger, allocator allocation.Allocator, discoveryManager *lbdiscovery.Manager, ctx context.Context, cliConfig config.CLIConfig) (*collector.Client, error) { - cfg, err := config.Load(*cliConfig.ConfigFilePath) - if err != nil { - return nil, err - } - - k8sClient, err := collector.NewClient(log, cliConfig.ClusterConfig) - if err != nil { - return nil, err - } - - // returns the list of targets - if err := discoveryManager.ApplyConfig(allocatorWatcher.EventSourceConfigMap, cfg.Config); err != nil { - return nil, err - } - - k8sClient.Watch(ctx, cfg.LabelSelector, allocator.SetCollectors) - return k8sClient, nil -} - -func (s *server) Start() error { - setupLog.Info("Starting server...") - return s.server.ListenAndServe() -} - -func (s *server) Shutdown(ctx context.Context) error { - s.logger.Info("Shutting down server...") - s.k8sClient.Close() - return s.server.Shutdown(ctx) -} - -// ScrapeConfigsHandler returns the available scrape configuration discovered by the target allocator. -// The target allocator first marshals these configurations such that the underlying prometheus marshaling is used. -// After that, the YAML is converted in to a JSON format for consumers to use. -func (s *server) ScrapeConfigsHandler(w http.ResponseWriter, r *http.Request) { - configs := s.discoveryManager.GetScrapeConfigs() - configBytes, err := yaml2.Marshal(configs) - if err != nil { - s.errorHandler(w, err) - } - jsonConfig, err := yaml.YAMLToJSON(configBytes) - if err != nil { - s.errorHandler(w, err) - } - // We don't use the jsonHandler method because we don't want our bytes to be re-encoded - w.Header().Set("Content-Type", "application/json") - _, err = w.Write(jsonConfig) - if err != nil { - s.errorHandler(w, err) - } -} - -func (s *server) JobHandler(w http.ResponseWriter, r *http.Request) { - displayData := make(map[string]allocation.LinkJSON) - for _, v := range s.allocator.TargetItems() { - displayData[v.JobName] = allocation.LinkJSON{Link: v.Link.Link} - } - s.jsonHandler(w, displayData) -} - -// PrometheusMiddleware implements mux.MiddlewareFunc. -func (s *server) PrometheusMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - route := mux.CurrentRoute(r) - path, _ := route.GetPathTemplate() - timer := prometheus.NewTimer(httpDuration.WithLabelValues(path)) - next.ServeHTTP(w, r) - timer.ObserveDuration() - }) -} - -func (s *server) TargetsHandler(w http.ResponseWriter, r *http.Request) { - q := r.URL.Query()["collector_id"] - - var compareMap = make(map[string][]allocation.TargetItem) // CollectorName+jobName -> TargetItem - for _, v := range s.allocator.TargetItems() { - compareMap[v.CollectorName+v.JobName] = append(compareMap[v.CollectorName+v.JobName], *v) - } - params := mux.Vars(r) - jobId, err := url.QueryUnescape(params["job_id"]) - if err != nil { - s.errorHandler(w, err) - return - } - - if len(q) == 0 { - displayData := allocation.GetAllTargetsByJob(jobId, compareMap, s.allocator) - s.jsonHandler(w, displayData) - - } else { - tgs := allocation.GetAllTargetsByCollectorAndJob(q[0], jobId, compareMap, s.allocator) - // Displays empty list if nothing matches - if len(tgs) == 0 { - s.jsonHandler(w, []interface{}{}) - return - } - s.jsonHandler(w, tgs) - } -} - -func (s *server) errorHandler(w http.ResponseWriter, err error) { - w.WriteHeader(500) - s.jsonHandler(w, err) -} - -func (s *server) jsonHandler(w http.ResponseWriter, data interface{}) { - w.Header().Set("Content-Type", "application/json") - err := json.NewEncoder(w).Encode(data) - if err != nil { - s.logger.Error(err, "failed to encode data for http response") - } + }, + func(_ error) { + setupLog.Info("Closing interrupt loop") + }) + if runErr := runGroup.Run(); runErr != nil { + setupLog.Error(runErr, "run group exited") + } + setupLog.Info("Target allocator exited.") } diff --git a/cmd/otel-allocator/prehook/prehook.go b/cmd/otel-allocator/prehook/prehook.go new file mode 100644 index 0000000000..f356f7c2d8 --- /dev/null +++ b/cmd/otel-allocator/prehook/prehook.go @@ -0,0 +1,64 @@ +// 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 prehook + +import ( + "errors" + + "github.com/go-logr/logr" + "github.com/prometheus/prometheus/model/relabel" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" +) + +const ( + relabelConfigTargetFilterName = "relabel-config" +) + +type Hook interface { + Apply(map[string]*target.Item) map[string]*target.Item + SetConfig(map[string][]*relabel.Config) + GetConfig() map[string][]*relabel.Config +} + +type HookProvider func(log logr.Logger) Hook + +var ( + registry = map[string]HookProvider{} +) + +func New(name string, log logr.Logger) Hook { + if p, ok := registry[name]; ok { + return p(log.WithName("Prehook").WithName(name)) + } + + log.Info("Unrecognized filter strategy; filtering disabled") + return nil +} + +func Register(name string, provider HookProvider) error { + if _, ok := registry[name]; ok { + return errors.New("already registered") + } + registry[name] = provider + return nil +} + +func init() { + err := Register(relabelConfigTargetFilterName, NewRelabelConfigTargetFilter) + if err != nil { + panic(err) + } +} diff --git a/cmd/otel-allocator/prehook/relabel.go b/cmd/otel-allocator/prehook/relabel.go new file mode 100644 index 0000000000..3595cb888e --- /dev/null +++ b/cmd/otel-allocator/prehook/relabel.go @@ -0,0 +1,110 @@ +// 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 prehook + +import ( + "github.com/go-logr/logr" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/model/relabel" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" +) + +type RelabelConfigTargetFilter struct { + log logr.Logger + relabelCfg map[string][]*relabel.Config +} + +func NewRelabelConfigTargetFilter(log logr.Logger) Hook { + return &RelabelConfigTargetFilter{ + log: log, + relabelCfg: make(map[string][]*relabel.Config), + } +} + +// helper function converts from model.LabelSet to []labels.Label. +func convertLabelToPromLabelSet(lbls model.LabelSet) []labels.Label { + newLabels := make([]labels.Label, len(lbls)) + index := 0 + for k, v := range lbls { + newLabels[index].Name = string(k) + newLabels[index].Value = string(v) + index++ + } + return newLabels +} + +func (tf *RelabelConfigTargetFilter) Apply(targets map[string]*target.Item) map[string]*target.Item { + numTargets := len(targets) + + // need to wait until relabelCfg is set + if len(tf.relabelCfg) == 0 { + return targets + } + + // Note: jobNameKey != tItem.JobName (jobNameKey is hashed) + for jobNameKey, tItem := range targets { + keepTarget := true + lset := convertLabelToPromLabelSet(tItem.Labels) + for _, cfg := range tf.relabelCfg[tItem.JobName] { + if newLset, keep := relabel.Process(lset, cfg); !keep { + keepTarget = false + break // inner loop + } else { + lset = newLset + } + } + + if !keepTarget { + delete(targets, jobNameKey) + } + } + + tf.log.V(2).Info("Filtering complete", "seen", numTargets, "kept", len(targets)) + return targets +} + +func (tf *RelabelConfigTargetFilter) SetConfig(cfgs map[string][]*relabel.Config) { + relabelCfgCopy := make(map[string][]*relabel.Config) + for key, val := range cfgs { + relabelCfgCopy[key] = tf.replaceRelabelConfig(val) + } + + tf.relabelCfg = relabelCfgCopy +} + +// See this thread [https://github.com/open-telemetry/opentelemetry-operator/pull/1124/files#r983145795] +// for why SHARD == 0 is a necessary substitution. Otherwise the keep action that uses this env variable, +// would not match the regex and all targets end up dropped. Also note, $(SHARD) will always be 0 and it +// does not make sense to read from the environment because it is never set in the allocator. +func (tf *RelabelConfigTargetFilter) replaceRelabelConfig(cfg []*relabel.Config) []*relabel.Config { + for i := range cfg { + str := cfg[i].Regex.String() + if str == "$(SHARD)" { + cfg[i].Regex = relabel.MustNewRegexp("0") + } + } + + return cfg +} + +func (tf *RelabelConfigTargetFilter) GetConfig() map[string][]*relabel.Config { + relabelCfgCopy := make(map[string][]*relabel.Config) + for k, v := range tf.relabelCfg { + relabelCfgCopy[k] = v + } + return relabelCfgCopy +} diff --git a/cmd/otel-allocator/prehook/relabel_test.go b/cmd/otel-allocator/prehook/relabel_test.go new file mode 100644 index 0000000000..d30f645eba --- /dev/null +++ b/cmd/otel-allocator/prehook/relabel_test.go @@ -0,0 +1,270 @@ +// 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 prehook + +import ( + "crypto/rand" + "fmt" + "math/big" + "strconv" + "testing" + + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/model/relabel" + "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") + defaultNumTargets = 100 + defaultNumCollectors = 3 + defaultStartIndex = 0 + + relabelConfigs = []relabelConfigObj{ + { + cfg: []*relabel.Config{ + { + SourceLabels: model.LabelNames{"i"}, + Action: "replace", + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + Replacement: "$1", + TargetLabel: "foo", + }, + }, + isDrop: false, + }, + { + cfg: []*relabel.Config{ + { + SourceLabels: model.LabelNames{"i"}, + Regex: relabel.MustNewRegexp("(.*)"), + Separator: ";", + Action: "keep", + Replacement: "$1", + }, + }, + isDrop: false, + }, + { + cfg: []*relabel.Config{ + { + SourceLabels: model.LabelNames{"i"}, + Regex: relabel.MustNewRegexp("bad.*match"), + Action: "drop", + Separator: ";", + Replacement: "$1", + }, + }, + isDrop: false, + }, + { + cfg: []*relabel.Config{ + { + SourceLabels: model.LabelNames{"label_not_present"}, + Regex: relabel.MustNewRegexp("(.*)"), + Separator: ";", + Action: "keep", + Replacement: "$1", + }, + }, + isDrop: false, + }, + { + cfg: []*relabel.Config{ + { + SourceLabels: model.LabelNames{"i"}, + Regex: relabel.MustNewRegexp("(.*)"), + Separator: ";", + Action: "drop", + Replacement: "$1", + }, + }, + isDrop: true, + }, + { + cfg: []*relabel.Config{ + { + SourceLabels: model.LabelNames{"collector"}, + Regex: relabel.MustNewRegexp("(collector.*)"), + Separator: ";", + Action: "drop", + Replacement: "$1", + }, + }, + isDrop: true, + }, + { + cfg: []*relabel.Config{ + { + SourceLabels: model.LabelNames{"i"}, + Regex: relabel.MustNewRegexp("bad.*match"), + Separator: ";", + Action: "keep", + Replacement: "$1", + }, + }, + isDrop: true, + }, + { + cfg: []*relabel.Config{ + { + SourceLabels: model.LabelNames{"collector"}, + Regex: relabel.MustNewRegexp("collectors-n"), + Separator: ";", + Action: "keep", + Replacement: "$1", + }, + }, + isDrop: true, + }, + } + + HashmodConfig = relabelConfigObj{ + cfg: []*relabel.Config{ + { + SourceLabels: model.LabelNames{"i"}, + Regex: relabel.MustNewRegexp("(.*)"), + Separator: ";", + Modulus: 1, + TargetLabel: "tmp-0", + Action: "hashmod", + Replacement: "$1", + }, + + { + SourceLabels: model.LabelNames{"tmp-$(SHARD)"}, + Regex: relabel.MustNewRegexp("$(SHARD)"), + Separator: ";", + Action: "keep", + Replacement: "$1", + }, + }, + isDrop: false, + } + + DefaultDropRelabelConfig = relabel.Config{ + SourceLabels: model.LabelNames{"i"}, + Regex: relabel.MustNewRegexp("(.*)"), + Action: "drop", + } +) + +type relabelConfigObj struct { + cfg []*relabel.Config + isDrop bool +} + +func colIndex(index, numCols int) int { + if numCols == 0 { + return -1 + } + return index % numCols +} + +func makeNNewTargets(rCfgs []relabelConfigObj, n int, numCollectors int, startingIndex int) (map[string]*target.Item, int, map[string]*target.Item, map[string][]*relabel.Config) { + toReturn := map[string]*target.Item{} + expectedMap := make(map[string]*target.Item) + numItemsRemaining := n + relabelConfig := make(map[string][]*relabel.Config) + for i := startingIndex; i < n+startingIndex; i++ { + collector := fmt.Sprintf("collector-%d", colIndex(i, numCollectors)) + label := model.LabelSet{ + "collector": model.LabelValue(collector), + "i": model.LabelValue(strconv.Itoa(i)), + "total": model.LabelValue(strconv.Itoa(n + startingIndex)), + } + jobName := fmt.Sprintf("test-job-%d", i) + newTarget := target.NewItem(jobName, "test-url", label, collector) + // add a single replace, drop, or keep action as relabel_config for targets + var index int + ind, _ := rand.Int(rand.Reader, big.NewInt(int64(len(relabelConfigs)))) + + index = int(ind.Int64()) + + relabelConfig[jobName] = rCfgs[index].cfg + + targetKey := newTarget.Hash() + if relabelConfigs[index].isDrop { + numItemsRemaining-- + } else { + expectedMap[targetKey] = newTarget + } + toReturn[targetKey] = newTarget + } + return toReturn, numItemsRemaining, expectedMap, relabelConfig +} + +func TestApply(t *testing.T) { + allocatorPrehook := New("relabel-config", logger) + assert.NotNil(t, allocatorPrehook) + + targets, numRemaining, expectedTargetMap, relabelCfg := makeNNewTargets(relabelConfigs, defaultNumTargets, defaultNumCollectors, defaultStartIndex) + allocatorPrehook.SetConfig(relabelCfg) + remainingItems := allocatorPrehook.Apply(targets) + assert.Len(t, remainingItems, numRemaining) + assert.Equal(t, remainingItems, expectedTargetMap) + + // clear out relabelCfg to test with empty values + for key := range relabelCfg { + relabelCfg[key] = nil + } + + // cfg = createMockConfig(relabelCfg) + allocatorPrehook.SetConfig(relabelCfg) + remainingItems = allocatorPrehook.Apply(targets) + // relabelCfg is empty so targets should be unfiltered + assert.Len(t, remainingItems, len(targets)) + assert.Equal(t, remainingItems, targets) +} + +func TestApplyHashmodAction(t *testing.T) { + allocatorPrehook := New("relabel-config", logger) + assert.NotNil(t, allocatorPrehook) + + hashRelabelConfigs := append(relabelConfigs, HashmodConfig) + targets, numRemaining, expectedTargetMap, relabelCfg := makeNNewTargets(hashRelabelConfigs, defaultNumTargets, defaultNumCollectors, defaultStartIndex) + allocatorPrehook.SetConfig(relabelCfg) + remainingItems := allocatorPrehook.Apply(targets) + assert.Len(t, remainingItems, numRemaining) + assert.Equal(t, remainingItems, expectedTargetMap) +} + +func TestApplyEmptyRelabelCfg(t *testing.T) { + + allocatorPrehook := New("relabel-config", logger) + assert.NotNil(t, allocatorPrehook) + + targets, _, _, _ := makeNNewTargets(relabelConfigs, defaultNumTargets, defaultNumCollectors, defaultStartIndex) + + relabelCfg := map[string][]*relabel.Config{} + allocatorPrehook.SetConfig(relabelCfg) + remainingItems := allocatorPrehook.Apply(targets) + // relabelCfg is empty so targets should be unfiltered + assert.Len(t, remainingItems, len(targets)) + assert.Equal(t, remainingItems, targets) +} + +func TestSetConfig(t *testing.T) { + allocatorPrehook := New("relabel-config", logger) + assert.NotNil(t, allocatorPrehook) + + _, _, _, relabelCfg := makeNNewTargets(relabelConfigs, defaultNumTargets, defaultNumCollectors, defaultStartIndex) + allocatorPrehook.SetConfig(relabelCfg) + assert.Equal(t, relabelCfg, allocatorPrehook.GetConfig()) +} diff --git a/cmd/otel-allocator/server/bench_test.go b/cmd/otel-allocator/server/bench_test.go new file mode 100644 index 0000000000..8fcea90b0e --- /dev/null +++ b/cmd/otel-allocator/server/bench_test.go @@ -0,0 +1,270 @@ +// 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 server + +import ( + "fmt" + "math/rand" + "net/http/httptest" + "testing" + "time" + + "github.com/gin-gonic/gin" + "github.com/prometheus/common/model" + promconfig "github.com/prometheus/prometheus/config" + "github.com/stretchr/testify/assert" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation" + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" +) + +func BenchmarkServerTargetsHandler(b *testing.B) { + random := rand.New(rand.NewSource(time.Now().UnixNano())) // nolint: gosec + var table = []struct { + numCollectors int + numJobs int + }{ + {numCollectors: 100, numJobs: 100}, + {numCollectors: 100, numJobs: 1000}, + {numCollectors: 100, numJobs: 10000}, + {numCollectors: 100, numJobs: 100000}, + {numCollectors: 1000, numJobs: 100}, + {numCollectors: 1000, numJobs: 1000}, + {numCollectors: 1000, numJobs: 10000}, + {numCollectors: 1000, numJobs: 100000}, + } + + for _, allocatorName := range allocation.GetRegisteredAllocatorNames() { + for _, v := range table { + a, _ := allocation.New(allocatorName, logger) + cols := allocation.MakeNCollectors(v.numCollectors, 0) + targets := allocation.MakeNNewTargets(v.numJobs, v.numCollectors, 0) + listenAddr := ":8080" + a.SetCollectors(cols) + a.SetTargets(targets) + s := NewServer(logger, a, listenAddr) + b.Run(fmt.Sprintf("%s_num_cols_%d_num_jobs_%d", allocatorName, v.numCollectors, v.numJobs), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + randomJob := random.Intn(v.numJobs) //nolint: gosec + randomCol := random.Intn(v.numCollectors) //nolint: gosec + request := httptest.NewRequest("GET", fmt.Sprintf("/jobs/test-job-%d/targets?collector_id=collector-%d", randomJob, randomCol), nil) + w := httptest.NewRecorder() + s.server.Handler.ServeHTTP(w, request) + } + }) + } + } +} + +func BenchmarkScrapeConfigsHandler(b *testing.B) { + random := rand.New(rand.NewSource(time.Now().UnixNano())) // nolint: gosec + s := &Server{ + logger: logger, + } + + tests := []int{0, 5, 10, 50, 100, 500} + for _, n := range tests { + data := makeNScrapeConfigs(*random, n) + assert.NoError(b, s.UpdateScrapeConfigResponse(data)) + + b.Run(fmt.Sprintf("%d_targets", n), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + c, _ := gin.CreateTestContext(httptest.NewRecorder()) + gin.SetMode(gin.ReleaseMode) + c.Request = httptest.NewRequest("GET", "/scrape_configs", nil) + + s.ScrapeConfigsHandler(c) + } + }) + } +} + +func BenchmarkCollectorMapJSONHandler(b *testing.B) { + random := rand.New(rand.NewSource(time.Now().UnixNano())) // nolint: gosec + s := &Server{ + logger: logger, + jsonMarshaller: jsonConfig, + } + + tests := []struct { + numCollectors int + numTargets int + }{ + { + numCollectors: 0, + numTargets: 0, + }, + { + numCollectors: 5, + numTargets: 5, + }, + { + numCollectors: 5, + numTargets: 50, + }, + { + numCollectors: 5, + numTargets: 500, + }, + { + numCollectors: 50, + numTargets: 5, + }, + { + numCollectors: 50, + numTargets: 50, + }, + { + numCollectors: 50, + numTargets: 500, + }, + { + numCollectors: 50, + numTargets: 5000, + }, + } + for _, tc := range tests { + data := makeNCollectorJSON(*random, tc.numCollectors, tc.numTargets) + b.Run(fmt.Sprintf("%d_collectors_%d_targets", tc.numCollectors, tc.numTargets), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + resp := httptest.NewRecorder() + s.jsonHandler(resp, data) + } + }) + } +} + +func BenchmarkTargetItemsJSONHandler(b *testing.B) { + random := rand.New(rand.NewSource(time.Now().UnixNano())) // nolint: gosec + s := &Server{ + logger: logger, + jsonMarshaller: jsonConfig, + } + + tests := []struct { + numTargets int + numLabels int + }{ + { + numTargets: 0, + numLabels: 0, + }, + { + numTargets: 5, + numLabels: 5, + }, + { + numTargets: 5, + numLabels: 50, + }, + { + numTargets: 50, + numLabels: 5, + }, + { + numTargets: 50, + numLabels: 50, + }, + { + numTargets: 500, + numLabels: 50, + }, + { + numTargets: 500, + numLabels: 500, + }, + { + numTargets: 5000, + numLabels: 50, + }, + { + numTargets: 5000, + numLabels: 500, + }, + } + for _, tc := range tests { + data := makeNTargetItems(*random, tc.numTargets, tc.numLabels) + b.Run(fmt.Sprintf("%d_targets_%d_labels", tc.numTargets, tc.numLabels), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + resp := httptest.NewRecorder() + s.jsonHandler(resp, data) + } + }) + } +} + +var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_/") + +func randSeq(random rand.Rand, n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letters[random.Intn(len(letters))] //nolint:gosec + } + return string(b) +} + +func makeNScrapeConfigs(random rand.Rand, n int) map[string]*promconfig.ScrapeConfig { + items := make(map[string]*promconfig.ScrapeConfig, n) + for i := 0; i < n; i++ { + items[randSeq(random, 20)] = &promconfig.ScrapeConfig{ + JobName: randSeq(random, 20), + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(time.Minute), + MetricsPath: randSeq(random, 50), + SampleLimit: 5, + TargetLimit: 200, + LabelLimit: 20, + LabelNameLengthLimit: 50, + LabelValueLengthLimit: 100, + } + } + return items +} + +func makeNCollectorJSON(random rand.Rand, numCollectors, numItems int) map[string]collectorJSON { + items := make(map[string]collectorJSON, numCollectors) + for i := 0; i < numCollectors; i++ { + items[randSeq(random, 20)] = collectorJSON{ + Link: randSeq(random, 120), + Jobs: makeNTargetItems(random, numItems, 50), + } + } + return items +} + +func makeNTargetItems(random rand.Rand, numItems, numLabels int) []*target.Item { + items := make([]*target.Item, 0, numItems) + for i := 0; i < numItems; i++ { + items = append(items, target.NewItem( + randSeq(random, 80), + randSeq(random, 150), + makeNNewLabels(random, numLabels), + randSeq(random, 30), + )) + } + return items +} + +func makeNNewLabels(random rand.Rand, n int) model.LabelSet { + labels := make(map[model.LabelName]model.LabelValue, n) + for i := 0; i < n; i++ { + labels[model.LabelName(randSeq(random, 20))] = model.LabelValue(randSeq(random, 20)) + } + return labels +} diff --git a/cmd/otel-allocator/server/mocks_test.go b/cmd/otel-allocator/server/mocks_test.go new file mode 100644 index 0000000000..e44b178fa8 --- /dev/null +++ b/cmd/otel-allocator/server/mocks_test.go @@ -0,0 +1,38 @@ +// 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 server + +import ( + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation" + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" +) + +var _ allocation.Allocator = &mockAllocator{} + +// mockAllocator implements the Allocator interface, but all funcs other than +// TargetItems() are a no-op. +type mockAllocator struct { + targetItems map[string]*target.Item +} + +func (m *mockAllocator) SetCollectors(_ map[string]*allocation.Collector) {} +func (m *mockAllocator) SetTargets(_ map[string]*target.Item) {} +func (m *mockAllocator) Collectors() map[string]*allocation.Collector { return nil } +func (m *mockAllocator) GetTargetsForCollectorAndJob(_ string, _ string) []*target.Item { return nil } +func (m *mockAllocator) SetFilter(_ allocation.Filter) {} + +func (m *mockAllocator) TargetItems() map[string]*target.Item { + return m.targetItems +} diff --git a/cmd/otel-allocator/server/server.go b/cmd/otel-allocator/server/server.go new file mode 100644 index 0000000000..9dda8347a7 --- /dev/null +++ b/cmd/otel-allocator/server/server.go @@ -0,0 +1,240 @@ +// 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 server + +import ( + "context" + "fmt" + "net/http" + "net/http/pprof" + "net/url" + "strings" + "sync" + "time" + + yaml2 "github.com/ghodss/yaml" + "github.com/gin-gonic/gin" + "github.com/go-logr/logr" + jsoniter "github.com/json-iterator/go" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" + promconfig "github.com/prometheus/prometheus/config" + "gopkg.in/yaml.v2" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation" + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" +) + +var ( + httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{ + Name: "opentelemetry_allocator_http_duration_seconds", + Help: "Duration of received HTTP requests.", + }, []string{"path"}) +) + +var ( + jsonConfig = jsoniter.Config{ + EscapeHTML: false, + MarshalFloatWith6Digits: true, + ObjectFieldMustBeSimpleString: true, + }.Froze() +) + +type collectorJSON struct { + Link string `json:"_link"` + Jobs []*target.Item `json:"targets"` +} + +type Server struct { + logger logr.Logger + allocator allocation.Allocator + server *http.Server + jsonMarshaller jsoniter.API + + // Use RWMutex to protect scrapeConfigResponse, since it + // will be predominantly read and only written when config + // is applied. + mtx sync.RWMutex + scrapeConfigResponse []byte +} + +func NewServer(log logr.Logger, allocator allocation.Allocator, listenAddr string) *Server { + s := &Server{ + logger: log, + allocator: allocator, + jsonMarshaller: jsonConfig, + } + + gin.SetMode(gin.ReleaseMode) + router := gin.New() + router.Use(gin.Recovery()) + router.UseRawPath = true + router.UnescapePathValues = false + router.Use(s.PrometheusMiddleware) + router.GET("/scrape_configs", s.ScrapeConfigsHandler) + router.GET("/jobs", s.JobHandler) + router.GET("/jobs/:job_id/targets", s.TargetsHandler) + router.GET("/metrics", gin.WrapH(promhttp.Handler())) + router.GET("/livez", s.LivenessProbeHandler) + router.GET("/readyz", s.ReadinessProbeHandler) + registerPprof(router.Group("/debug/pprof/")) + + s.server = &http.Server{Addr: listenAddr, Handler: router, ReadHeaderTimeout: 90 * time.Second} + return s +} + +func (s *Server) Start() error { + s.logger.Info("Starting server...") + return s.server.ListenAndServe() +} + +func (s *Server) Shutdown(ctx context.Context) error { + s.logger.Info("Shutting down server...") + return s.server.Shutdown(ctx) +} + +// UpdateScrapeConfigResponse updates the scrape config response. The target allocator first marshals these +// configurations such that the underlying prometheus marshaling is used. After that, the YAML is converted +// in to a JSON format for consumers to use. +func (s *Server) UpdateScrapeConfigResponse(configs map[string]*promconfig.ScrapeConfig) error { + var configBytes []byte + configBytes, err := yaml.Marshal(configs) + if err != nil { + return err + } + var jsonConfig []byte + jsonConfig, err = yaml2.YAMLToJSON(configBytes) + if err != nil { + return err + } + s.mtx.Lock() + s.scrapeConfigResponse = jsonConfig + s.mtx.Unlock() + return nil +} + +// ScrapeConfigsHandler returns the available scrape configuration discovered by the target allocator. +func (s *Server) ScrapeConfigsHandler(c *gin.Context) { + s.mtx.RLock() + result := s.scrapeConfigResponse + s.mtx.RUnlock() + + // We don't use the jsonHandler method because we don't want our bytes to be re-encoded + c.Writer.Header().Set("Content-Type", "application/json") + _, err := c.Writer.Write(result) + if err != nil { + s.errorHandler(c.Writer, err) + } +} + +func (s *Server) ReadinessProbeHandler(c *gin.Context) { + s.mtx.RLock() + result := s.scrapeConfigResponse + s.mtx.RUnlock() + + if result != nil { + c.Status(http.StatusOK) + } else { + c.Status(http.StatusServiceUnavailable) + } +} + +func (s *Server) JobHandler(c *gin.Context) { + displayData := make(map[string]target.LinkJSON) + for _, v := range s.allocator.TargetItems() { + displayData[v.JobName] = target.LinkJSON{Link: v.Link.Link} + } + s.jsonHandler(c.Writer, displayData) +} + +func (s *Server) LivenessProbeHandler(c *gin.Context) { + c.Status(http.StatusOK) +} + +func (s *Server) PrometheusMiddleware(c *gin.Context) { + path := c.FullPath() + timer := prometheus.NewTimer(httpDuration.WithLabelValues(path)) + c.Next() + timer.ObserveDuration() +} + +func (s *Server) TargetsHandler(c *gin.Context) { + q := c.Request.URL.Query()["collector_id"] + + jobIdParam := c.Params.ByName("job_id") + jobId, err := url.QueryUnescape(jobIdParam) + if err != nil { + s.errorHandler(c.Writer, err) + return + } + + if len(q) == 0 { + displayData := GetAllTargetsByJob(s.allocator, jobId) + s.jsonHandler(c.Writer, displayData) + + } else { + tgs := s.allocator.GetTargetsForCollectorAndJob(q[0], jobId) + // Displays empty list if nothing matches + if len(tgs) == 0 { + s.jsonHandler(c.Writer, []interface{}{}) + return + } + s.jsonHandler(c.Writer, tgs) + } +} + +func (s *Server) errorHandler(w http.ResponseWriter, err error) { + w.WriteHeader(http.StatusInternalServerError) + s.jsonHandler(w, err) +} + +func (s *Server) jsonHandler(w http.ResponseWriter, data interface{}) { + w.Header().Set("Content-Type", "application/json") + err := s.jsonMarshaller.NewEncoder(w).Encode(data) + if err != nil { + s.logger.Error(err, "failed to encode data for http response") + } +} + +// GetAllTargetsByJob is a relatively expensive call that is usually only used for debugging purposes. +func GetAllTargetsByJob(allocator allocation.Allocator, job string) map[string]collectorJSON { + displayData := make(map[string]collectorJSON) + for _, col := range allocator.Collectors() { + items := allocator.GetTargetsForCollectorAndJob(col.Name, job) + displayData[col.Name] = collectorJSON{Link: fmt.Sprintf("/jobs/%s/targets?collector_id=%s", url.QueryEscape(job), col.Name), Jobs: items} + } + return displayData +} + +// registerPprof registers the pprof handlers and either serves the requested +// specific profile or falls back to index handler. +func registerPprof(g *gin.RouterGroup) { + g.GET("/*profile", func(c *gin.Context) { + path := c.Param("profile") + switch strings.TrimPrefix(path, "/") { + case "cmdline": + gin.WrapF(pprof.Cmdline)(c) + case "profile": + gin.WrapF(pprof.Profile)(c) + case "symbol": + gin.WrapF(pprof.Symbol)(c) + case "trace": + gin.WrapF(pprof.Trace)(c) + default: + gin.WrapF(pprof.Index)(c) + } + }) +} diff --git a/cmd/otel-allocator/server/server_test.go b/cmd/otel-allocator/server/server_test.go new file mode 100644 index 0000000000..edda74e074 --- /dev/null +++ b/cmd/otel-allocator/server/server_test.go @@ -0,0 +1,617 @@ +// 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 server + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + promconfig "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/model/relabel" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation" + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/target" +) + +var ( + logger = logf.Log.WithName("server-unit-tests") + baseLabelSet = model.LabelSet{ + "test_label": "test-value", + } + testJobLabelSetTwo = model.LabelSet{ + "test_label": "test-value2", + } + baseTargetItem = target.NewItem("test-job", "test-url", baseLabelSet, "test-collector") + secondTargetItem = target.NewItem("test-job", "test-url", baseLabelSet, "test-collector") + testJobTargetItemTwo = target.NewItem("test-job", "test-url2", testJobLabelSetTwo, "test-collector2") +) + +func TestServer_LivenessProbeHandler(t *testing.T) { + leastWeighted, _ := allocation.New("least-weighted", logger) + listenAddr := ":8080" + s := NewServer(logger, leastWeighted, listenAddr) + request := httptest.NewRequest("GET", "/livez", nil) + w := httptest.NewRecorder() + + s.server.Handler.ServeHTTP(w, request) + result := w.Result() + + assert.Equal(t, http.StatusOK, result.StatusCode) +} + +func TestServer_TargetsHandler(t *testing.T) { + leastWeighted, _ := allocation.New("least-weighted", logger) + type args struct { + collector string + job string + cMap map[string]*target.Item + allocator allocation.Allocator + } + type want struct { + items []*target.Item + errString string + } + tests := []struct { + name string + args args + want want + }{ + { + name: "Empty target map", + args: args{ + collector: "test-collector", + job: "test-job", + cMap: map[string]*target.Item{}, + allocator: leastWeighted, + }, + want: want{ + items: []*target.Item{}, + }, + }, + { + name: "Single entry target map", + args: args{ + collector: "test-collector", + job: "test-job", + cMap: map[string]*target.Item{ + baseTargetItem.Hash(): baseTargetItem, + }, + allocator: leastWeighted, + }, + want: want{ + items: []*target.Item{ + { + TargetURL: []string{"test-url"}, + Labels: map[model.LabelName]model.LabelValue{ + "test_label": "test-value", + }, + }, + }, + }, + }, + { + name: "Multiple entry target map", + args: args{ + collector: "test-collector", + job: "test-job", + cMap: map[string]*target.Item{ + baseTargetItem.Hash(): baseTargetItem, + secondTargetItem.Hash(): secondTargetItem, + }, + allocator: leastWeighted, + }, + want: want{ + items: []*target.Item{ + { + TargetURL: []string{"test-url"}, + Labels: map[model.LabelName]model.LabelValue{ + "test_label": "test-value", + }, + }, + }, + }, + }, + { + name: "Multiple entry target map of same job with label merge", + args: args{ + collector: "test-collector", + job: "test-job", + cMap: map[string]*target.Item{ + baseTargetItem.Hash(): baseTargetItem, + testJobTargetItemTwo.Hash(): testJobTargetItemTwo, + }, + allocator: leastWeighted, + }, + want: want{ + items: []*target.Item{ + { + TargetURL: []string{"test-url"}, + Labels: map[model.LabelName]model.LabelValue{ + "test_label": "test-value", + }, + }, + { + TargetURL: []string{"test-url2"}, + Labels: map[model.LabelName]model.LabelValue{ + "test_label": "test-value2", + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + listenAddr := ":8080" + s := NewServer(logger, tt.args.allocator, listenAddr) + tt.args.allocator.SetCollectors(map[string]*allocation.Collector{"test-collector": {Name: "test-collector"}}) + tt.args.allocator.SetTargets(tt.args.cMap) + request := httptest.NewRequest("GET", fmt.Sprintf("/jobs/%s/targets?collector_id=%s", tt.args.job, tt.args.collector), nil) + w := httptest.NewRecorder() + + s.server.Handler.ServeHTTP(w, request) + result := w.Result() + + assert.Equal(t, http.StatusOK, result.StatusCode) + body := result.Body + bodyBytes, err := io.ReadAll(body) + assert.NoError(t, err) + if len(tt.want.errString) != 0 { + assert.EqualError(t, err, tt.want.errString) + return + } + var itemResponse []*target.Item + err = json.Unmarshal(bodyBytes, &itemResponse) + assert.NoError(t, err) + assert.ElementsMatch(t, tt.want.items, itemResponse) + }) + } +} + +func TestServer_ScrapeConfigsHandler(t *testing.T) { + tests := []struct { + description string + scrapeConfigs map[string]*promconfig.ScrapeConfig + expectedCode int + expectedBody []byte + }{ + { + description: "nil scrape config", + scrapeConfigs: nil, + expectedCode: http.StatusOK, + expectedBody: []byte("{}"), + }, + { + description: "empty scrape config", + scrapeConfigs: map[string]*promconfig.ScrapeConfig{}, + expectedCode: http.StatusOK, + expectedBody: []byte("{}"), + }, + { + description: "single entry", + scrapeConfigs: map[string]*promconfig.ScrapeConfig{ + "serviceMonitor/testapp/testapp/0": { + JobName: "serviceMonitor/testapp/testapp/0", + HonorTimestamps: true, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: config.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + }, + expectedCode: http.StatusOK, + }, + { + description: "multiple entries", + scrapeConfigs: map[string]*promconfig.ScrapeConfig{ + "serviceMonitor/testapp/testapp/0": { + JobName: "serviceMonitor/testapp/testapp/0", + HonorTimestamps: true, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: config.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{ + model.LabelName("__meta_kubernetes_service_label_app_kubernetes_io_name"), + model.LabelName("__meta_kubernetes_service_labelpresent_app_kubernetes_io_name"), + }, + Separator: ";", + Regex: relabel.MustNewRegexp("(testapp);true"), + Replacement: "$$1", + Action: relabel.Keep, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_endpoint_port_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("http"), + Replacement: "$$1", + Action: relabel.Keep, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_namespace")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "namespace", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_service_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "service", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_pod_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "pod", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_pod_container_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "container", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + "serviceMonitor/testapp/testapp1/0": { + JobName: "serviceMonitor/testapp/testapp1/0", + HonorTimestamps: true, + ScrapeInterval: model.Duration(5 * time.Minute), + ScrapeTimeout: model.Duration(10 * time.Second), + MetricsPath: "/v2/metrics", + Scheme: "http", + HTTPClientConfig: config.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{ + model.LabelName("__meta_kubernetes_service_label_app_kubernetes_io_name"), + model.LabelName("__meta_kubernetes_service_labelpresent_app_kubernetes_io_name"), + }, + Separator: ";", + Regex: relabel.MustNewRegexp("(testapp);true"), + Replacement: "$$1", + Action: relabel.Keep, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_endpoint_port_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("http"), + Replacement: "$$1", + Action: relabel.Keep, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_namespace")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "namespace", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_service_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "service", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_pod_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "pod", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_pod_container_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "container", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + "serviceMonitor/testapp/testapp2/0": { + JobName: "serviceMonitor/testapp/testapp2/0", + HonorTimestamps: true, + ScrapeInterval: model.Duration(30 * time.Minute), + ScrapeTimeout: model.Duration(2 * time.Minute), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: config.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{ + model.LabelName("__meta_kubernetes_service_label_app_kubernetes_io_name"), + model.LabelName("__meta_kubernetes_service_labelpresent_app_kubernetes_io_name"), + }, + Separator: ";", + Regex: relabel.MustNewRegexp("(testapp);true"), + Replacement: "$$1", + Action: relabel.Keep, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_endpoint_port_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("http"), + Replacement: "$$1", + Action: relabel.Keep, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_namespace")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "namespace", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_service_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "service", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_pod_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "pod", + Replacement: "$$1", + Action: relabel.Replace, + }, + { + SourceLabels: model.LabelNames{model.LabelName("__meta_kubernetes_pod_container_name")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "container", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + }, + expectedCode: http.StatusOK, + }, + } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + listenAddr := ":8080" + s := NewServer(logger, nil, listenAddr) + assert.NoError(t, s.UpdateScrapeConfigResponse(tc.scrapeConfigs)) + + request := httptest.NewRequest("GET", "/scrape_configs", nil) + w := httptest.NewRecorder() + + s.server.Handler.ServeHTTP(w, request) + result := w.Result() + + assert.Equal(t, tc.expectedCode, result.StatusCode) + bodyBytes, err := io.ReadAll(result.Body) + require.NoError(t, err) + if tc.expectedBody != nil { + assert.Equal(t, tc.expectedBody, bodyBytes) + return + } + scrapeConfigs := map[string]*promconfig.ScrapeConfig{} + err = yaml.Unmarshal(bodyBytes, scrapeConfigs) + require.NoError(t, err) + assert.Equal(t, tc.scrapeConfigs, scrapeConfigs) + }) + } +} + +func TestServer_JobHandler(t *testing.T) { + tests := []struct { + description string + targetItems map[string]*target.Item + expectedCode int + expectedJobs map[string]target.LinkJSON + }{ + { + description: "nil jobs", + targetItems: nil, + expectedCode: http.StatusOK, + expectedJobs: make(map[string]target.LinkJSON), + }, + { + description: "empty jobs", + targetItems: map[string]*target.Item{}, + expectedCode: http.StatusOK, + expectedJobs: make(map[string]target.LinkJSON), + }, + { + description: "one job", + targetItems: map[string]*target.Item{ + "targetitem": target.NewItem("job1", "", model.LabelSet{}, ""), + }, + expectedCode: http.StatusOK, + expectedJobs: map[string]target.LinkJSON{ + "job1": newLink("job1"), + }, + }, + { + description: "multiple jobs", + targetItems: map[string]*target.Item{ + "a": target.NewItem("job1", "", model.LabelSet{}, ""), + "b": target.NewItem("job2", "", model.LabelSet{}, ""), + "c": target.NewItem("job3", "", model.LabelSet{}, ""), + "d": target.NewItem("job3", "", model.LabelSet{}, ""), + "e": target.NewItem("job3", "", model.LabelSet{}, "")}, + expectedCode: http.StatusOK, + expectedJobs: map[string]target.LinkJSON{ + "job1": newLink("job1"), + "job2": newLink("job2"), + "job3": newLink("job3"), + }, + }, + } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + listenAddr := ":8080" + a := &mockAllocator{targetItems: tc.targetItems} + s := NewServer(logger, a, listenAddr) + request := httptest.NewRequest("GET", "/jobs", nil) + w := httptest.NewRecorder() + + s.server.Handler.ServeHTTP(w, request) + result := w.Result() + + assert.Equal(t, tc.expectedCode, result.StatusCode) + bodyBytes, err := io.ReadAll(result.Body) + require.NoError(t, err) + jobs := map[string]target.LinkJSON{} + err = json.Unmarshal(bodyBytes, &jobs) + require.NoError(t, err) + assert.Equal(t, tc.expectedJobs, jobs) + }) + } +} +func TestServer_Readiness(t *testing.T) { + tests := []struct { + description string + scrapeConfigs map[string]*promconfig.ScrapeConfig + expectedCode int + expectedBody []byte + }{ + { + description: "nil scrape config", + scrapeConfigs: nil, + expectedCode: http.StatusServiceUnavailable, + }, + { + description: "empty scrape config", + scrapeConfigs: map[string]*promconfig.ScrapeConfig{}, + expectedCode: http.StatusOK, + }, + { + description: "single entry", + scrapeConfigs: map[string]*promconfig.ScrapeConfig{ + "serviceMonitor/testapp/testapp/0": { + JobName: "serviceMonitor/testapp/testapp/0", + HonorTimestamps: true, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: config.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + }, + expectedCode: http.StatusOK, + }, + } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + listenAddr := ":8080" + s := NewServer(logger, nil, listenAddr) + if tc.scrapeConfigs != nil { + assert.NoError(t, s.UpdateScrapeConfigResponse(tc.scrapeConfigs)) + } + + request := httptest.NewRequest("GET", "/readyz", nil) + w := httptest.NewRecorder() + + s.server.Handler.ServeHTTP(w, request) + result := w.Result() + + assert.Equal(t, tc.expectedCode, result.StatusCode) + }) + } +} + +func newLink(jobName string) target.LinkJSON { + return target.LinkJSON{Link: fmt.Sprintf("/jobs/%s/targets", url.QueryEscape(jobName))} +} diff --git a/cmd/otel-allocator/target/discovery.go b/cmd/otel-allocator/target/discovery.go new file mode 100644 index 0000000000..9b16f9b699 --- /dev/null +++ b/cmd/otel-allocator/target/discovery.go @@ -0,0 +1,156 @@ +// 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 target + +import ( + "hash" + "hash/fnv" + + "github.com/go-logr/logr" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/discovery" + "github.com/prometheus/prometheus/model/relabel" + "gopkg.in/yaml.v3" + + allocatorWatcher "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/watcher" +) + +var ( + targetsDiscovered = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "opentelemetry_allocator_targets", + Help: "Number of targets discovered.", + }, []string{"job_name"}) +) + +type Discoverer struct { + log logr.Logger + manager *discovery.Manager + close chan struct{} + configsMap map[allocatorWatcher.EventSource]*config.Config + hook discoveryHook + scrapeConfigsHash hash.Hash + scrapeConfigsUpdater scrapeConfigsUpdater +} + +type discoveryHook interface { + SetConfig(map[string][]*relabel.Config) +} + +type scrapeConfigsUpdater interface { + UpdateScrapeConfigResponse(map[string]*config.ScrapeConfig) error +} + +func NewDiscoverer(log logr.Logger, manager *discovery.Manager, hook discoveryHook, scrapeConfigsUpdater scrapeConfigsUpdater) *Discoverer { + return &Discoverer{ + log: log, + manager: manager, + close: make(chan struct{}), + configsMap: make(map[allocatorWatcher.EventSource]*config.Config), + hook: hook, + scrapeConfigsUpdater: scrapeConfigsUpdater, + } +} + +func (m *Discoverer) ApplyConfig(source allocatorWatcher.EventSource, cfg *config.Config) error { + if cfg == nil { + m.log.Info("Service Discovery got empty Prometheus config", "source", source.String()) + return nil + } + m.configsMap[source] = cfg + jobToScrapeConfig := make(map[string]*config.ScrapeConfig) + + discoveryCfg := make(map[string]discovery.Configs) + relabelCfg := make(map[string][]*relabel.Config) + + for _, value := range m.configsMap { + for _, scrapeConfig := range value.ScrapeConfigs { + jobToScrapeConfig[scrapeConfig.JobName] = scrapeConfig + discoveryCfg[scrapeConfig.JobName] = scrapeConfig.ServiceDiscoveryConfigs + relabelCfg[scrapeConfig.JobName] = scrapeConfig.RelabelConfigs + } + } + + hash, err := getScrapeConfigHash(jobToScrapeConfig) + if err != nil { + return err + } + // If the hash has changed, updated stored hash and send the new config. + // Otherwise skip updating scrape configs. + if m.scrapeConfigsUpdater != nil && m.scrapeConfigsHash != hash { + err := m.scrapeConfigsUpdater.UpdateScrapeConfigResponse(jobToScrapeConfig) + if err != nil { + return err + } + + m.scrapeConfigsHash = hash + } + + if m.hook != nil { + m.hook.SetConfig(relabelCfg) + } + return m.manager.ApplyConfig(discoveryCfg) +} + +func (m *Discoverer) Watch(fn func(targets map[string]*Item)) error { + for { + select { + case <-m.close: + m.log.Info("Service Discovery watch event stopped: discovery manager closed") + return nil + case tsets := <-m.manager.SyncCh(): + targets := map[string]*Item{} + + for jobName, tgs := range tsets { + var count float64 = 0 + for _, tg := range tgs { + for _, t := range tg.Targets { + count++ + item := NewItem(jobName, string(t[model.AddressLabel]), t.Merge(tg.Labels), "") + targets[item.Hash()] = item + } + } + targetsDiscovered.WithLabelValues(jobName).Set(count) + } + fn(targets) + } + } +} + +func (m *Discoverer) Close() { + close(m.close) +} + +// Calculate a hash for a scrape config map. +// This is done by marshaling to YAML because it's the most straightforward and doesn't run into problems with unexported fields. +func getScrapeConfigHash(jobToScrapeConfig map[string]*config.ScrapeConfig) (hash.Hash64, error) { + var err error + hash := fnv.New64() + yamlEncoder := yaml.NewEncoder(hash) + for jobName, scrapeConfig := range jobToScrapeConfig { + _, err = hash.Write([]byte(jobName)) + if err != nil { + return nil, err + } + err = yamlEncoder.Encode(scrapeConfig) + if err != nil { + return nil, err + } + } + yamlEncoder.Close() + return hash, err +} diff --git a/cmd/otel-allocator/target/discovery_test.go b/cmd/otel-allocator/target/discovery_test.go new file mode 100644 index 0000000000..506fb01b5b --- /dev/null +++ b/cmd/otel-allocator/target/discovery_test.go @@ -0,0 +1,413 @@ +// 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 target + +import ( + "context" + "errors" + "hash" + "sort" + "testing" + "time" + + gokitlog "github.com/go-kit/log" + commonconfig "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + promconfig "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/discovery" + "github.com/prometheus/prometheus/model/relabel" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/config" + allocatorWatcher "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/watcher" +) + +func TestDiscovery(t *testing.T) { + type args struct { + file string + } + tests := []struct { + name string + args args + want []string + }{ + { + name: "base case", + args: args{ + file: "./testdata/test.yaml", + }, + want: []string{"prom.domain:9001", "prom.domain:9002", "prom.domain:9003", "prom.domain:8001", "promfile.domain:1001", "promfile.domain:3000"}, + }, + { + name: "update", + args: args{ + file: "./testdata/test_update.yaml", + }, + want: []string{"prom.domain:9004", "prom.domain:9005", "promfile.domain:1001", "promfile.domain:3000"}, + }, + } + scu := &mockScrapeConfigUpdater{} + ctx, cancelFunc := context.WithCancel(context.Background()) + d := discovery.NewManager(ctx, gokitlog.NewNopLogger()) + manager := NewDiscoverer(ctrl.Log.WithName("test"), d, nil, scu) + + defer func() { manager.Close() }() + defer cancelFunc() + + results := make(chan []string) + go func() { + err := d.Run() + assert.Error(t, err) + }() + go func() { + err := manager.Watch(func(targets map[string]*Item) { + var result []string + for _, t := range targets { + result = append(result, t.TargetURL[0]) + } + results <- result + }) + assert.NoError(t, err) + }() + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := config.CreateDefaultConfig() + err := config.LoadFromFile(tt.args.file, &cfg) + assert.NoError(t, err) + assert.True(t, len(cfg.PromConfig.ScrapeConfigs) > 0) + err = manager.ApplyConfig(allocatorWatcher.EventSourcePrometheusCR, cfg.PromConfig) + assert.NoError(t, err) + + gotTargets := <-results + sort.Strings(gotTargets) + sort.Strings(tt.want) + assert.Equal(t, tt.want, gotTargets) + + // check the updated scrape configs + expectedScrapeConfigs := map[string]*promconfig.ScrapeConfig{} + for _, scrapeConfig := range cfg.PromConfig.ScrapeConfigs { + expectedScrapeConfigs[scrapeConfig.JobName] = scrapeConfig + } + assert.Equal(t, expectedScrapeConfigs, scu.mockCfg) + }) + } +} + +func TestDiscovery_ScrapeConfigHashing(t *testing.T) { + // these tests are meant to be run sequentially in this order, to test + // that hashing doesn't cause us to send the wrong information. + tests := []struct { + description string + cfg *promconfig.Config + expectErr bool + }{ + { + description: "base config", + cfg: &promconfig.Config{ + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "serviceMonitor/testapp/testapp/0", + HonorTimestamps: true, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + }, + }, + }, + { + description: "different bool", + cfg: &promconfig.Config{ + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "serviceMonitor/testapp/testapp/0", + HonorTimestamps: false, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + }, + }, + }, + { + description: "different job name", + cfg: &promconfig.Config{ + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "serviceMonitor/testapp/testapp/1", + HonorTimestamps: false, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + }, + }, + }, + { + description: "different key", + cfg: &promconfig.Config{ + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "serviceMonitor/testapp/testapp/1", + HonorTimestamps: false, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + }, + }, + }, + { + description: "unset scrape interval", + cfg: &promconfig.Config{ + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "serviceMonitor/testapp/testapp/1", + HonorTimestamps: false, + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + }, + }, + }, + { + description: "different regex", + cfg: &promconfig.Config{ + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "serviceMonitor/testapp/testapp/1", + HonorTimestamps: false, + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.+)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + }, + }, + }, + }, + { + description: "mock error on update - no hash update", + cfg: &promconfig.Config{ + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "error", + }, + }, + }, + expectErr: true, + }, + } + var ( + lastValidHash hash.Hash + expectedConfig map[string]*promconfig.ScrapeConfig + lastValidConfig map[string]*promconfig.ScrapeConfig + ) + + scu := &mockScrapeConfigUpdater{} + ctx := context.Background() + d := discovery.NewManager(ctx, gokitlog.NewNopLogger()) + manager := NewDiscoverer(ctrl.Log.WithName("test"), d, nil, scu) + + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + err := manager.ApplyConfig(allocatorWatcher.EventSourcePrometheusCR, tc.cfg) + if !tc.expectErr { + expectedConfig = make(map[string]*promconfig.ScrapeConfig) + for _, value := range manager.configsMap { + for _, scrapeConfig := range value.ScrapeConfigs { + expectedConfig[scrapeConfig.JobName] = scrapeConfig + } + } + assert.NoError(t, err) + assert.NotZero(t, manager.scrapeConfigsHash) + // Assert that scrape configs in manager are correctly + // reflected in the scrape job updater. + assert.Equal(t, expectedConfig, scu.mockCfg) + + lastValidHash = manager.scrapeConfigsHash + lastValidConfig = expectedConfig + } else { + // In case of error, assert that we retain the last + // known valid config. + assert.Error(t, err) + assert.Equal(t, lastValidHash, manager.scrapeConfigsHash) + assert.Equal(t, lastValidConfig, scu.mockCfg) + } + + }) + } +} + +func TestDiscovery_NoConfig(t *testing.T) { + scu := &mockScrapeConfigUpdater{mockCfg: map[string]*promconfig.ScrapeConfig{}} + ctx, cancelFunc := context.WithCancel(context.Background()) + d := discovery.NewManager(ctx, gokitlog.NewNopLogger()) + manager := NewDiscoverer(ctrl.Log.WithName("test"), d, nil, scu) + defer close(manager.close) + defer cancelFunc() + + go func() { + err := d.Run() + assert.Error(t, err) + }() + // check the updated scrape configs + expectedScrapeConfigs := map[string]*promconfig.ScrapeConfig{} + assert.Equal(t, expectedScrapeConfigs, scu.mockCfg) +} + +func BenchmarkApplyScrapeConfig(b *testing.B) { + numConfigs := 1000 + scrapeConfig := promconfig.ScrapeConfig{ + JobName: "serviceMonitor/testapp/testapp/0", + HonorTimestamps: true, + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(30 * time.Second), + MetricsPath: "/metrics", + Scheme: "http", + HTTPClientConfig: commonconfig.HTTPClientConfig{ + FollowRedirects: true, + }, + RelabelConfigs: []*relabel.Config{ + { + SourceLabels: model.LabelNames{model.LabelName("job")}, + Separator: ";", + Regex: relabel.MustNewRegexp("(.*)"), + TargetLabel: "__tmp_prometheus_job_name", + Replacement: "$$1", + Action: relabel.Replace, + }, + }, + } + cfg := &promconfig.Config{ + ScrapeConfigs: make([]*promconfig.ScrapeConfig, numConfigs), + } + + for i := 0; i < numConfigs; i++ { + cfg.ScrapeConfigs[i] = &scrapeConfig + } + + scu := &mockScrapeConfigUpdater{} + ctx := context.Background() + d := discovery.NewManager(ctx, gokitlog.NewNopLogger()) + manager := NewDiscoverer(ctrl.Log.WithName("test"), d, nil, scu) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := manager.ApplyConfig(allocatorWatcher.EventSourcePrometheusCR, cfg) + require.NoError(b, err) + } +} + +var _ scrapeConfigsUpdater = &mockScrapeConfigUpdater{} + +// mockScrapeConfigUpdater is a mock implementation of the scrapeConfigsUpdater. +// If a job with name "error" is provided to the UpdateScrapeConfigResponse, +// it will return an error for testing purposes. +type mockScrapeConfigUpdater struct { + mockCfg map[string]*promconfig.ScrapeConfig +} + +func (m *mockScrapeConfigUpdater) UpdateScrapeConfigResponse(cfg map[string]*promconfig.ScrapeConfig) error { + if _, ok := cfg["error"]; ok { + return errors.New("error") + } + + m.mockCfg = cfg + return nil +} diff --git a/cmd/otel-allocator/target/target.go b/cmd/otel-allocator/target/target.go new file mode 100644 index 0000000000..ee5d58cf95 --- /dev/null +++ b/cmd/otel-allocator/target/target.go @@ -0,0 +1,55 @@ +// 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 target + +import ( + "fmt" + "net/url" + + "github.com/prometheus/common/model" +) + +// LinkJSON This package contains common structs and methods that relate to scrape targets. +type LinkJSON struct { + Link string `json:"_link"` +} + +type Item struct { + JobName string `json:"-"` + Link LinkJSON `json:"-"` + TargetURL []string `json:"targets"` + Labels model.LabelSet `json:"labels"` + CollectorName string `json:"-"` + hash string +} + +func (t *Item) Hash() string { + return t.hash +} + +// NewItem Creates a new target item. +// INVARIANTS: +// * Item fields must not be modified after creation. +// * Item should only be made via its constructor, never directly. +func NewItem(jobName string, targetURL string, label model.LabelSet, collectorName string) *Item { + return &Item{ + JobName: jobName, + Link: LinkJSON{Link: fmt.Sprintf("/jobs/%s/targets", url.QueryEscape(jobName))}, + hash: jobName + targetURL + label.Fingerprint().String(), + TargetURL: []string{targetURL}, + Labels: label, + CollectorName: collectorName, + } +} diff --git a/cmd/otel-allocator/discovery/testdata/test.yaml b/cmd/otel-allocator/target/testdata/test.yaml similarity index 68% rename from cmd/otel-allocator/discovery/testdata/test.yaml rename to cmd/otel-allocator/target/testdata/test.yaml index 54902342c6..d38b0183a7 100644 --- a/cmd/otel-allocator/discovery/testdata/test.yaml +++ b/cmd/otel-allocator/target/testdata/test.yaml @@ -5,11 +5,13 @@ config: scrape_configs: - job_name: prometheus + file_sd_configs: + - files: + - ../config/testdata/file_sd_test.json static_configs: - targets: ["prom.domain:9001", "prom.domain:9002", "prom.domain:9003"] labels: my: label - - file_sd_configs: - - files: - - ../config/testdata/file_sd_test.json + - job_name: prometheus2 + static_configs: + - targets: ["prom.domain:8001"] diff --git a/cmd/otel-allocator/target/testdata/test_update.yaml b/cmd/otel-allocator/target/testdata/test_update.yaml new file mode 100644 index 0000000000..3a7ec60f3b --- /dev/null +++ b/cmd/otel-allocator/target/testdata/test_update.yaml @@ -0,0 +1,14 @@ +label_selector: + app.kubernetes.io/instance: default.test + app.kubernetes.io/managed-by: opentelemetry-operator +config: + scrape_configs: + - job_name: prometheus + + file_sd_configs: + - files: + - ../config/testdata/file_sd_test.json + static_configs: + - targets: ["prom.domain:9004", "prom.domain:9005"] + labels: + my: other-label diff --git a/cmd/otel-allocator/watcher/file.go b/cmd/otel-allocator/watcher/file.go deleted file mode 100644 index 069b9ac483..0000000000 --- a/cmd/otel-allocator/watcher/file.go +++ /dev/null @@ -1,75 +0,0 @@ -// 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 watcher - -import ( - "path/filepath" - - "github.com/fsnotify/fsnotify" - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/config" -) - -type FileWatcher struct { - configFilePath string - watcher *fsnotify.Watcher - closer chan bool -} - -func newConfigMapWatcher(logger logr.Logger, config config.CLIConfig) (FileWatcher, error) { - fileWatcher, err := fsnotify.NewWatcher() - if err != nil { - logger.Error(err, "Can't start the watcher") - return FileWatcher{}, err - } - - return FileWatcher{ - configFilePath: *config.ConfigFilePath, - watcher: fileWatcher, - closer: make(chan bool), - }, nil -} - -func (f *FileWatcher) Start(upstreamEvents chan Event, upstreamErrors chan error) error { - err := f.watcher.Add(filepath.Dir(f.configFilePath)) - if err != nil { - return err - } - - // translate and copy to central event channel - go func() { - for { - select { - case <-f.closer: - return - case fileEvent := <-f.watcher.Events: - if fileEvent.Op == fsnotify.Create { - upstreamEvents <- Event{ - Source: EventSourceConfigMap, - } - } - case err := <-f.watcher.Errors: - upstreamErrors <- err - } - } - }() - return nil -} - -func (f *FileWatcher) Close() error { - f.closer <- true - return f.watcher.Close() -} diff --git a/cmd/otel-allocator/watcher/main.go b/cmd/otel-allocator/watcher/main.go deleted file mode 100644 index 8dbd71036f..0000000000 --- a/cmd/otel-allocator/watcher/main.go +++ /dev/null @@ -1,118 +0,0 @@ -// 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 watcher - -import ( - "fmt" - - "github.com/go-logr/logr" - - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/allocation" - "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/config" -) - -type Manager struct { - Events chan Event - Errors chan error - allocator allocation.Allocator - watchers []Watcher -} - -type Watcher interface { - // Start watcher and supply channels which will receive change events - Start(upstreamEvents chan Event, upstreamErrors chan error) error - Close() error -} - -type Event struct { - Source EventSource - Watcher *Watcher -} - -type EventSource int - -const ( - EventSourceConfigMap EventSource = iota - EventSourcePrometheusCR -) - -var ( - eventSourceToString = map[EventSource]string{ - EventSourceConfigMap: "EventSourceConfigMap", - EventSourcePrometheusCR: "EventSourcePrometheusCR", - } -) - -func (e EventSource) String() string { - return eventSourceToString[e] -} - -func NewWatcher(logger logr.Logger, config config.CLIConfig, allocator allocation.Allocator) (*Manager, error) { - watcher := Manager{ - allocator: allocator, - Events: make(chan Event), - Errors: make(chan error), - } - - fileWatcher, err := newConfigMapWatcher(logger, config) - if err != nil { - return nil, err - } - watcher.watchers = append(watcher.watchers, &fileWatcher) - - if *config.PromCRWatcherConf.Enabled { - promWatcher, err := newCRDMonitorWatcher(config) - if err != nil { - return nil, err - } - watcher.watchers = append(watcher.watchers, promWatcher) - } - - startErr := watcher.Start() - return &watcher, startErr -} - -func (manager *Manager) Close() error { - var errors []error - for _, watcher := range manager.watchers { - err := watcher.Close() - if err != nil { - errors = append(errors, err) - } - } - - close(manager.Events) - close(manager.Errors) - - if len(errors) > 0 { - return fmt.Errorf("closing errors: %+v", errors) - } - return nil -} - -func (manager *Manager) Start() error { - var errors []error - for _, watcher := range manager.watchers { - err := watcher.Start(manager.Events, manager.Errors) - if err != nil { - errors = append(errors, err) - } - } - - if len(errors) > 0 { - return fmt.Errorf("closing errors: %+v", errors) - } - return nil -} diff --git a/cmd/otel-allocator/watcher/promOperator.go b/cmd/otel-allocator/watcher/promOperator.go index 09eee6f50f..9f916c881e 100644 --- a/cmd/otel-allocator/watcher/promOperator.go +++ b/cmd/otel-allocator/watcher/promOperator.go @@ -15,12 +15,16 @@ package watcher import ( + "context" "fmt" - - allocatorconfig "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/config" + "os" + "time" "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/go-logr/logr" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + promv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" "github.com/prometheus-operator/prometheus-operator/pkg/assets" monitoringclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned" "github.com/prometheus-operator/prometheus-operator/pkg/informers" @@ -30,60 +34,110 @@ import ( "gopkg.in/yaml.v2" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" + + allocatorconfig "github.com/open-telemetry/opentelemetry-operator/cmd/otel-allocator/config" ) -func newCRDMonitorWatcher(config allocatorconfig.CLIConfig) (*PrometheusCRWatcher, error) { - mClient, err := monitoringclient.NewForConfig(config.ClusterConfig) +const minEventInterval = time.Second * 5 + +func NewPrometheusCRWatcher(logger logr.Logger, cfg allocatorconfig.Config) (*PrometheusCRWatcher, error) { + mClient, err := monitoringclient.NewForConfig(cfg.ClusterConfig) if err != nil { return nil, err } - factory := informers.NewMonitoringInformerFactories(map[string]struct{}{v1.NamespaceAll: {}}, map[string]struct{}{}, mClient, allocatorconfig.DefaultResyncTime, nil) //TODO decide what strategy to use regarding namespaces - - serviceMonitorInformers, err := informers.NewInformersForResource(factory, monitoringv1.SchemeGroupVersion.WithResource(monitoringv1.ServiceMonitorName)) + clientset, err := kubernetes.NewForConfig(cfg.ClusterConfig) if err != nil { return nil, err } - podMonitorInformers, err := informers.NewInformersForResource(factory, monitoringv1.SchemeGroupVersion.WithResource(monitoringv1.PodMonitorName)) + factory := informers.NewMonitoringInformerFactories(map[string]struct{}{v1.NamespaceAll: {}}, map[string]struct{}{}, mClient, allocatorconfig.DefaultResyncTime, nil) //TODO decide what strategy to use regarding namespaces + + monitoringInformers, err := getInformers(factory) if err != nil { return nil, err } - monitoringInformers := map[string]*informers.ForResource{ - monitoringv1.ServiceMonitorName: serviceMonitorInformers, - monitoringv1.PodMonitorName: podMonitorInformers, + // TODO: We should make these durations configurable + prom := &monitoringv1.Prometheus{ + Spec: monitoringv1.PrometheusSpec{ + CommonPrometheusFields: monitoringv1.CommonPrometheusFields{ + ScrapeInterval: monitoringv1.Duration(cfg.PrometheusCR.ScrapeInterval.String()), + }, + }, } - generator, err := prometheus.NewConfigGenerator(log.NewNopLogger(), &monitoringv1.Prometheus{}) // TODO replace Nop? + promOperatorLogger := level.NewFilter(log.NewLogfmtLogger(os.Stderr), level.AllowWarn()) + generator, err := prometheus.NewConfigGenerator(promOperatorLogger, prom, true) + if err != nil { return nil, err } + servMonSelector := getSelector(cfg.ServiceMonitorSelector) + + podMonSelector := getSelector(cfg.PodMonitorSelector) + return &PrometheusCRWatcher{ - kubeMonitoringClient: mClient, - informers: monitoringInformers, - stopChannel: make(chan struct{}), - configGenerator: generator, + logger: logger, + kubeMonitoringClient: mClient, + k8sClient: clientset, + informers: monitoringInformers, + stopChannel: make(chan struct{}), + eventInterval: minEventInterval, + configGenerator: generator, + kubeConfigPath: cfg.KubeConfigFilePath, + serviceMonitorSelector: servMonSelector, + podMonitorSelector: podMonSelector, }, nil } type PrometheusCRWatcher struct { - kubeMonitoringClient *monitoringclient.Clientset + logger logr.Logger + kubeMonitoringClient monitoringclient.Interface + k8sClient kubernetes.Interface informers map[string]*informers.ForResource + eventInterval time.Duration stopChannel chan struct{} configGenerator *prometheus.ConfigGenerator + kubeConfigPath string + + serviceMonitorSelector labels.Selector + podMonitorSelector labels.Selector } -// Start wrapped informers and wait for an initial sync. -func (w *PrometheusCRWatcher) Start(upstreamEvents chan Event, upstreamErrors chan error) error { - watcher := Watcher(w) - event := Event{ - Source: EventSourcePrometheusCR, - Watcher: &watcher, +func getSelector(s map[string]string) labels.Selector { + if s == nil { + return labels.NewSelector() + } + return labels.SelectorFromSet(s) +} + +// getInformers returns a map of informers for the given resources. +func getInformers(factory informers.FactoriesForNamespaces) (map[string]*informers.ForResource, error) { + serviceMonitorInformers, err := informers.NewInformersForResource(factory, monitoringv1.SchemeGroupVersion.WithResource(monitoringv1.ServiceMonitorName)) + if err != nil { + return nil, err } + + podMonitorInformers, err := informers.NewInformersForResource(factory, monitoringv1.SchemeGroupVersion.WithResource(monitoringv1.PodMonitorName)) + if err != nil { + return nil, err + } + + return map[string]*informers.ForResource{ + monitoringv1.ServiceMonitorName: serviceMonitorInformers, + monitoringv1.PodMonitorName: podMonitorInformers, + }, nil +} + +// Watch wrapped informers and wait for an initial sync. +func (w *PrometheusCRWatcher) Watch(upstreamEvents chan Event, upstreamErrors chan error) error { success := true + // this channel needs to be buffered because notifications are asynchronous and neither producers nor consumers wait + notifyEvents := make(chan struct{}, 1) for name, resource := range w.informers { resource.Start(w.stopChannel) @@ -92,15 +146,27 @@ func (w *PrometheusCRWatcher) Start(upstreamEvents chan Event, upstreamErrors ch success = false } + // only send an event notification if there isn't one already resource.AddEventHandler(cache.ResourceEventHandlerFuncs{ + // these functions only write to the notification channel if it's empty to avoid blocking + // if scrape config updates are being rate-limited AddFunc: func(obj interface{}) { - upstreamEvents <- event + select { + case notifyEvents <- struct{}{}: + default: + } }, UpdateFunc: func(oldObj, newObj interface{}) { - upstreamEvents <- event + select { + case notifyEvents <- struct{}{}: + default: + } }, DeleteFunc: func(obj interface{}) { - upstreamEvents <- event + select { + case notifyEvents <- struct{}{}: + default: + } }, }) } @@ -108,19 +174,57 @@ func (w *PrometheusCRWatcher) Start(upstreamEvents chan Event, upstreamErrors ch return fmt.Errorf("failed to sync cache") } + // limit the rate of outgoing events + w.rateLimitedEventSender(upstreamEvents, notifyEvents) + + <-w.stopChannel return nil } +// rateLimitedEventSender sends events to the upstreamEvents channel whenever it gets a notification on the notifyEvents channel, +// but not more frequently than once per w.eventPeriod. +func (w *PrometheusCRWatcher) rateLimitedEventSender(upstreamEvents chan Event, notifyEvents chan struct{}) { + ticker := time.NewTicker(w.eventInterval) + defer ticker.Stop() + + event := Event{ + Source: EventSourcePrometheusCR, + Watcher: Watcher(w), + } + + for { + select { + case <-w.stopChannel: + return + case <-ticker.C: // throttle events to avoid excessive updates + select { + case <-notifyEvents: + select { + case upstreamEvents <- event: + default: // put the notification back in the queue if we can't send it upstream + select { + case notifyEvents <- struct{}{}: + default: + } + } + default: + } + } + } +} + func (w *PrometheusCRWatcher) Close() error { - w.stopChannel <- struct{}{} + close(w.stopChannel) return nil } -func (w *PrometheusCRWatcher) CreatePromConfig(kubeConfigPath string) (*promconfig.Config, error) { +func (w *PrometheusCRWatcher) LoadConfig(ctx context.Context) (*promconfig.Config, error) { + store := assets.NewStore(w.k8sClient.CoreV1(), w.k8sClient.CoreV1()) serviceMonitorInstances := make(map[string]*monitoringv1.ServiceMonitor) - smRetrieveErr := w.informers[monitoringv1.ServiceMonitorName].ListAll(labels.NewSelector(), func(sm interface{}) { + smRetrieveErr := w.informers[monitoringv1.ServiceMonitorName].ListAll(w.serviceMonitorSelector, func(sm interface{}) { monitor := sm.(*monitoringv1.ServiceMonitor) key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(monitor) + w.addStoreAssetsForServiceMonitor(ctx, monitor.Name, monitor.Namespace, monitor.Spec.Endpoints, store) serviceMonitorInstances[key] = monitor }) if smRetrieveErr != nil { @@ -128,23 +232,34 @@ func (w *PrometheusCRWatcher) CreatePromConfig(kubeConfigPath string) (*promconf } podMonitorInstances := make(map[string]*monitoringv1.PodMonitor) - pmRetrieveErr := w.informers[monitoringv1.PodMonitorName].ListAll(labels.NewSelector(), func(pm interface{}) { + pmRetrieveErr := w.informers[monitoringv1.PodMonitorName].ListAll(w.podMonitorSelector, func(pm interface{}) { monitor := pm.(*monitoringv1.PodMonitor) key, _ := cache.DeletionHandlingMetaNamespaceKeyFunc(monitor) + w.addStoreAssetsForPodMonitor(ctx, monitor.Name, monitor.Namespace, monitor.Spec.PodMetricsEndpoints, store) podMonitorInstances[key] = monitor }) if pmRetrieveErr != nil { return nil, pmRetrieveErr } - store := assets.Store{ - TLSAssets: nil, - TokenAssets: nil, - BasicAuthAssets: nil, - OAuth2Assets: nil, - SigV4Assets: nil, - } - generatedConfig, err := w.configGenerator.Generate(&monitoringv1.Prometheus{}, serviceMonitorInstances, podMonitorInstances, map[string]*monitoringv1.Probe{}, &store, nil, nil, nil, []string{}) + generatedConfig, err := w.configGenerator.GenerateServerConfiguration( + ctx, + "30s", + "", + nil, + nil, + monitoringv1.TSDBSpec{}, + nil, + nil, + serviceMonitorInstances, + podMonitorInstances, + map[string]*monitoringv1.Probe{}, + map[string]*promv1alpha1.ScrapeConfig{}, + store, + nil, + nil, + nil, + []string{}) if err != nil { return nil, err } @@ -161,9 +276,95 @@ func (w *PrometheusCRWatcher) CreatePromConfig(kubeConfigPath string) (*promconf for _, serviceDiscoveryConfig := range scrapeConfig.ServiceDiscoveryConfigs { if serviceDiscoveryConfig.Name() == "kubernetes" { sdConfig := interface{}(serviceDiscoveryConfig).(*kubeDiscovery.SDConfig) - sdConfig.KubeConfig = kubeConfigPath + sdConfig.KubeConfig = w.kubeConfigPath } } } return promCfg, nil } + +// addStoreAssetsForServiceMonitor adds authentication / authorization related information to the assets store, +// based on the service monitor and endpoints specs. +// This code borrows from +// https://github.com/prometheus-operator/prometheus-operator/blob/06b5c4189f3f72737766d86103d049115c3aff48/pkg/prometheus/resource_selector.go#L73. +func (w *PrometheusCRWatcher) addStoreAssetsForServiceMonitor( + ctx context.Context, + smName, smNamespace string, + endps []monitoringv1.Endpoint, + store *assets.Store, +) { + var err error + for i, endp := range endps { + objKey := fmt.Sprintf("serviceMonitor/%s/%s/%d", smNamespace, smName, i) + + if err = store.AddBearerToken(ctx, smNamespace, endp.BearerTokenSecret, objKey); err != nil { + break + } + + if err = store.AddBasicAuth(ctx, smNamespace, endp.BasicAuth, objKey); err != nil { + break + } + + if endp.TLSConfig != nil { + if err = store.AddTLSConfig(ctx, smNamespace, endp.TLSConfig); err != nil { + break + } + } + + if err = store.AddOAuth2(ctx, smNamespace, endp.OAuth2, objKey); err != nil { + break + } + + smAuthKey := fmt.Sprintf("serviceMonitor/auth/%s/%s/%d", smNamespace, smName, i) + if err = store.AddSafeAuthorizationCredentials(ctx, smNamespace, endp.Authorization, smAuthKey); err != nil { + break + } + } + + if err != nil { + w.logger.Error(err, "Failed to obtain credentials for a ServiceMonitor", "serviceMonitor", smName) + } +} + +// addStoreAssetsForServiceMonitor adds authentication / authorization related information to the assets store, +// based on the service monitor and pod metrics endpoints specs. +// This code borrows from +// https://github.com/prometheus-operator/prometheus-operator/blob/06b5c4189f3f72737766d86103d049115c3aff48/pkg/prometheus/resource_selector.go#L314. +func (w *PrometheusCRWatcher) addStoreAssetsForPodMonitor( + ctx context.Context, + pmName, pmNamespace string, + podMetricsEndps []monitoringv1.PodMetricsEndpoint, + store *assets.Store, +) { + var err error + for i, endp := range podMetricsEndps { + objKey := fmt.Sprintf("podMonitor/%s/%s/%d", pmNamespace, pmName, i) + + if err = store.AddBearerToken(ctx, pmNamespace, &endp.BearerTokenSecret, objKey); err != nil { + break + } + + if err = store.AddBasicAuth(ctx, pmNamespace, endp.BasicAuth, objKey); err != nil { + break + } + + if endp.TLSConfig != nil { + if err = store.AddSafeTLSConfig(ctx, pmNamespace, &endp.TLSConfig.SafeTLSConfig); err != nil { + break + } + } + + if err = store.AddOAuth2(ctx, pmNamespace, endp.OAuth2, objKey); err != nil { + break + } + + smAuthKey := fmt.Sprintf("podMonitor/auth/%s/%s/%d", pmNamespace, pmName, i) + if err = store.AddSafeAuthorizationCredentials(ctx, pmNamespace, endp.Authorization, smAuthKey); err != nil { + break + } + } + + if err != nil { + w.logger.Error(err, "Failed to obtain credentials for a PodMonitor", "podMonitor", pmName) + } +} diff --git a/cmd/otel-allocator/watcher/promOperator_test.go b/cmd/otel-allocator/watcher/promOperator_test.go new file mode 100644 index 0000000000..5aa05c23f8 --- /dev/null +++ b/cmd/otel-allocator/watcher/promOperator_test.go @@ -0,0 +1,427 @@ +// 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 watcher + +import ( + "context" + "testing" + "time" + + "github.com/go-kit/log" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + fakemonitoringclient "github.com/prometheus-operator/prometheus-operator/pkg/client/versioned/fake" + "github.com/prometheus-operator/prometheus-operator/pkg/informers" + "github.com/prometheus-operator/prometheus-operator/pkg/prometheus" + "github.com/prometheus/common/config" + "github.com/prometheus/common/model" + promconfig "github.com/prometheus/prometheus/config" + "github.com/prometheus/prometheus/discovery" + kubeDiscovery "github.com/prometheus/prometheus/discovery/kubernetes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/tools/cache" +) + +func TestLoadConfig(t *testing.T) { + tests := []struct { + name string + serviceMonitor *monitoringv1.ServiceMonitor + podMonitor *monitoringv1.PodMonitor + want *promconfig.Config + wantErr bool + }{ + { + name: "simple test", + serviceMonitor: &monitoringv1.ServiceMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "simple", + Namespace: "test", + }, + Spec: monitoringv1.ServiceMonitorSpec{ + JobLabel: "test", + Endpoints: []monitoringv1.Endpoint{ + { + Port: "web", + }, + }, + }, + }, + podMonitor: &monitoringv1.PodMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "simple", + Namespace: "test", + }, + Spec: monitoringv1.PodMonitorSpec{ + JobLabel: "test", + PodMetricsEndpoints: []monitoringv1.PodMetricsEndpoint{ + { + Port: "web", + }, + }, + }, + }, + want: &promconfig.Config{ + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "serviceMonitor/test/simple/0", + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(10 * time.Second), + HonorTimestamps: true, + HonorLabels: false, + Scheme: "http", + MetricsPath: "/metrics", + ServiceDiscoveryConfigs: []discovery.Config{ + &kubeDiscovery.SDConfig{ + Role: "endpointslice", + NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{ + Names: []string{"test"}, + IncludeOwnNamespace: false, + }, + HTTPClientConfig: config.DefaultHTTPClientConfig, + }, + }, + HTTPClientConfig: config.DefaultHTTPClientConfig, + }, + { + JobName: "podMonitor/test/simple/0", + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(10 * time.Second), + HonorTimestamps: true, + HonorLabels: false, + Scheme: "http", + MetricsPath: "/metrics", + ServiceDiscoveryConfigs: []discovery.Config{ + &kubeDiscovery.SDConfig{ + Role: "pod", + NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{ + Names: []string{"test"}, + IncludeOwnNamespace: false, + }, + HTTPClientConfig: config.DefaultHTTPClientConfig, + }, + }, + HTTPClientConfig: config.DefaultHTTPClientConfig, + }, + }, + }, + }, + { + name: "basic auth (serviceMonitor)", + serviceMonitor: &monitoringv1.ServiceMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "auth", + Namespace: "test", + }, + Spec: monitoringv1.ServiceMonitorSpec{ + JobLabel: "auth", + Endpoints: []monitoringv1.Endpoint{ + { + Port: "web", + BasicAuth: &monitoringv1.BasicAuth{ + Username: v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "basic-auth", + }, + Key: "username", + }, + Password: v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "basic-auth", + }, + Key: "password", + }, + }, + }, + }, + Selector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "auth", + }, + }, + }, + }, + want: &promconfig.Config{ + GlobalConfig: promconfig.GlobalConfig{}, + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "serviceMonitor/test/auth/0", + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(10 * time.Second), + HonorTimestamps: true, + HonorLabels: false, + Scheme: "http", + MetricsPath: "/metrics", + ServiceDiscoveryConfigs: []discovery.Config{ + &kubeDiscovery.SDConfig{ + Role: "endpointslice", + NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{ + Names: []string{"test"}, + IncludeOwnNamespace: false, + }, + HTTPClientConfig: config.DefaultHTTPClientConfig, + }, + }, + HTTPClientConfig: config.HTTPClientConfig{ + FollowRedirects: true, + EnableHTTP2: true, + BasicAuth: &config.BasicAuth{ + Username: "admin", + Password: "password", + }, + }, + }, + }, + }, + }, + { + name: "bearer token (podMonitor)", + podMonitor: &monitoringv1.PodMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bearer", + Namespace: "test", + }, + Spec: monitoringv1.PodMonitorSpec{ + JobLabel: "bearer", + PodMetricsEndpoints: []monitoringv1.PodMetricsEndpoint{ + { + Port: "web", + BearerTokenSecret: v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "bearer", + }, + Key: "token", + }, + }, + }, + }, + }, + want: &promconfig.Config{ + GlobalConfig: promconfig.GlobalConfig{}, + ScrapeConfigs: []*promconfig.ScrapeConfig{ + { + JobName: "podMonitor/test/bearer/0", + ScrapeInterval: model.Duration(30 * time.Second), + ScrapeTimeout: model.Duration(10 * time.Second), + HonorTimestamps: true, + HonorLabels: false, + Scheme: "http", + MetricsPath: "/metrics", + ServiceDiscoveryConfigs: []discovery.Config{ + &kubeDiscovery.SDConfig{ + Role: "pod", + NamespaceDiscovery: kubeDiscovery.NamespaceDiscovery{ + Names: []string{"test"}, + IncludeOwnNamespace: false, + }, + HTTPClientConfig: config.DefaultHTTPClientConfig, + }, + }, + HTTPClientConfig: config.HTTPClientConfig{ + FollowRedirects: true, + EnableHTTP2: true, + Authorization: &config.Authorization{ + Type: "Bearer", + Credentials: "bearer-token", + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + w := getTestPrometheusCRWatcher(t, tt.serviceMonitor, tt.podMonitor) + for _, informer := range w.informers { + // Start informers in order to populate cache. + informer.Start(w.stopChannel) + } + + // Wait for informers to sync. + for _, informer := range w.informers { + for !informer.HasSynced() { + time.Sleep(50 * time.Millisecond) + } + } + + got, err := w.LoadConfig(context.Background()) + assert.NoError(t, err) + + sanitizeScrapeConfigsForTest(got.ScrapeConfigs) + assert.Equal(t, tt.want.ScrapeConfigs, got.ScrapeConfigs) + }) + } +} + +func TestRateLimit(t *testing.T) { + var err error + serviceMonitor := &monitoringv1.ServiceMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Name: "simple", + Namespace: "test", + }, + Spec: monitoringv1.ServiceMonitorSpec{ + JobLabel: "test", + Endpoints: []monitoringv1.Endpoint{ + { + Port: "web", + }, + }, + }, + } + events := make(chan Event, 1) + eventInterval := 5 * time.Millisecond + + w := getTestPrometheusCRWatcher(t, nil, nil) + defer w.Close() + w.eventInterval = eventInterval + + go func() { + watchErr := w.Watch(events, make(chan error)) + require.NoError(t, watchErr) + }() + // we don't have a simple way to wait for the watch to actually add event handlers to the informer, + // instead, we just update a ServiceMonitor periodically and wait until we get a notification + _, err = w.kubeMonitoringClient.MonitoringV1().ServiceMonitors("test").Create(context.Background(), serviceMonitor, metav1.CreateOptions{}) + require.NoError(t, err) + + // wait for cache sync first + for _, informer := range w.informers { + success := cache.WaitForCacheSync(w.stopChannel, informer.HasSynced) + require.True(t, success) + } + + require.Eventually(t, func() bool { + _, createErr := w.kubeMonitoringClient.MonitoringV1().ServiceMonitors("test").Update(context.Background(), serviceMonitor, metav1.UpdateOptions{}) + if createErr != nil { + return false + } + select { + case <-events: + return true + default: + return false + } + }, eventInterval*2, time.Millisecond) + + // it's difficult to measure the rate precisely + // what we do, is send two updates, and then assert that the elapsed time is between eventInterval and 3*eventInterval + startTime := time.Now() + _, err = w.kubeMonitoringClient.MonitoringV1().ServiceMonitors("test").Update(context.Background(), serviceMonitor, metav1.UpdateOptions{}) + require.NoError(t, err) + require.Eventually(t, func() bool { + select { + case <-events: + return true + default: + return false + } + }, eventInterval*2, time.Millisecond) + _, err = w.kubeMonitoringClient.MonitoringV1().ServiceMonitors("test").Update(context.Background(), serviceMonitor, metav1.UpdateOptions{}) + require.NoError(t, err) + require.Eventually(t, func() bool { + select { + case <-events: + return true + default: + return false + } + }, eventInterval*2, time.Millisecond) + elapsedTime := time.Since(startTime) + assert.Less(t, eventInterval, elapsedTime) + assert.GreaterOrEqual(t, eventInterval*3, elapsedTime) + +} + +// getTestPrometheuCRWatcher creates a test instance of PrometheusCRWatcher with fake clients +// and test secrets. +func getTestPrometheusCRWatcher(t *testing.T, sm *monitoringv1.ServiceMonitor, pm *monitoringv1.PodMonitor) *PrometheusCRWatcher { + mClient := fakemonitoringclient.NewSimpleClientset() + if sm != nil { + _, err := mClient.MonitoringV1().ServiceMonitors("test").Create(context.Background(), sm, metav1.CreateOptions{}) + if err != nil { + t.Fatal(t, err) + } + } + if pm != nil { + _, err := mClient.MonitoringV1().PodMonitors("test").Create(context.Background(), pm, metav1.CreateOptions{}) + if err != nil { + t.Fatal(t, err) + } + } + + k8sClient := fake.NewSimpleClientset() + _, err := k8sClient.CoreV1().Secrets("test").Create(context.Background(), &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "basic-auth", + Namespace: "test", + }, + Data: map[string][]byte{"username": []byte("admin"), "password": []byte("password")}, + }, metav1.CreateOptions{}) + if err != nil { + t.Fatal(t, err) + } + _, err = k8sClient.CoreV1().Secrets("test").Create(context.Background(), &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bearer", + Namespace: "test", + }, + Data: map[string][]byte{"token": []byte("bearer-token")}, + }, metav1.CreateOptions{}) + if err != nil { + t.Fatal(t, err) + } + + factory := informers.NewMonitoringInformerFactories(map[string]struct{}{v1.NamespaceAll: {}}, map[string]struct{}{}, mClient, 0, nil) + informers, err := getInformers(factory) + if err != nil { + t.Fatal(t, err) + } + + prom := &monitoringv1.Prometheus{ + Spec: monitoringv1.PrometheusSpec{ + CommonPrometheusFields: monitoringv1.CommonPrometheusFields{ + ScrapeInterval: monitoringv1.Duration("30s"), + }, + }, + } + + generator, err := prometheus.NewConfigGenerator(log.NewNopLogger(), prom, true) + if err != nil { + t.Fatal(t, err) + } + + return &PrometheusCRWatcher{ + kubeMonitoringClient: mClient, + k8sClient: k8sClient, + informers: informers, + configGenerator: generator, + serviceMonitorSelector: getSelector(nil), + podMonitorSelector: getSelector(nil), + stopChannel: make(chan struct{}), + } +} + +// Remove relable configs fields from scrape configs for testing, +// since these are mutated and tested down the line with the hook(s). +func sanitizeScrapeConfigsForTest(scs []*promconfig.ScrapeConfig) { + for _, sc := range scs { + sc.RelabelConfigs = nil + sc.MetricRelabelConfigs = nil + } +} diff --git a/cmd/otel-allocator/watcher/watcher.go b/cmd/otel-allocator/watcher/watcher.go new file mode 100644 index 0000000000..c970c7e47f --- /dev/null +++ b/cmd/otel-allocator/watcher/watcher.go @@ -0,0 +1,51 @@ +// 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 watcher + +import ( + "context" + + promconfig "github.com/prometheus/prometheus/config" +) + +type Watcher interface { + // Watch watcher and supply channels which will receive change events + Watch(upstreamEvents chan Event, upstreamErrors chan error) error + LoadConfig(ctx context.Context) (*promconfig.Config, error) + Close() error +} + +type Event struct { + Source EventSource + Watcher Watcher +} + +type EventSource int + +const ( + EventSourceConfigMap EventSource = iota + EventSourcePrometheusCR +) + +var ( + eventSourceToString = map[EventSource]string{ + EventSourceConfigMap: "EventSourceConfigMap", + EventSourcePrometheusCR: "EventSourcePrometheusCR", + } +) + +func (e EventSource) String() string { + return eventSourceToString[e] +} diff --git a/config/crd/bases/opentelemetry.io_instrumentations.yaml b/config/crd/bases/opentelemetry.io_instrumentations.yaml index ba2ad97b84..d54c45347f 100644 --- a/config/crd/bases/opentelemetry.io_instrumentations.yaml +++ b/config/crd/bases/opentelemetry.io_instrumentations.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: instrumentations.opentelemetry.io spec: group: opentelemetry.io @@ -37,14 +36,14 @@ spec: description: Instrumentation is the spec for OpenTelemetry instrumentation. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation + description: APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + internal value, and may reject unrecognized values. type: string kind: - description: 'Kind is a string value representing the REST resource this + description: Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + submits requests to. Cannot be updated. In CamelCase. type: string metadata: type: object @@ -52,15 +51,739 @@ spec: description: InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumentation. properties: + apacheHttpd: + description: ApacheHttpd defines configuration for Apache HTTPD auto-instrumentation. + properties: + attrs: + description: 'Attrs defines Apache HTTPD agent specific attributes. + The precedence is: `agent default attributes` > `instrument + spec attributes` . Attributes are documented at https://github.' + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + configPath: + description: Location of Apache HTTPD server configuration. Needed + only if different from default "/usr/local/apache2/conf" + type: string + env: + description: Env defines Apache HTTPD specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Apache SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + version: + description: Apache HTTPD server version. One of 2.4 or 2.2. Default + is 2.4 + type: string + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object dotnet: description: DotNet defines configuration for DotNet auto-instrumentation. properties: env: - description: 'Env defines DotNet specific env vars. There are - four layers for env vars'' definitions and the precedence order - is: `original container env vars` > `language specific env vars` - > `common env vars` > `instrument spec configs'' vars`. If the - former var had been defined, then the other vars would be ignored.' + description: Env defines DotNet specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with DotNet SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + env: + description: Env defines common env vars. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + exporter: + description: Exporter defines exporter configuration. + properties: + endpoint: + description: Endpoint is address of the collector with OTLP endpoint. + type: string + type: object + go: + description: Go defines configuration for Go auto-instrumentation. + properties: + env: + description: Env defines Go specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Go SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + java: + description: Java defines configuration for java auto-instrumentation. + properties: + env: + description: Env defines java specific env vars. items: description: EnvVar represents an environment variable present in a Container. @@ -70,15 +793,9 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded + description: Variable references $(VAR_NAME) are expanded using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + the container and any service environment variables. type: string valueFrom: description: Source for the environment variable's value. @@ -107,8 +824,7 @@ spec: description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + spec.serviceAccountName, status.hostIP, status.' properties: apiVersion: description: Version of the schema the FieldPath @@ -126,7 +842,7 @@ spec: description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + and requests.' properties: containerName: description: 'Container name: required for volumes, @@ -174,138 +890,181 @@ spec: type: object type: array image: - description: Image is a container image with DotNet SDK and auto-instrumentation. + description: Image is a container image with javaagent auto-instrumentation + JAR. type: string - type: object - env: - description: 'Env defines common env vars. There are four layers for - env vars'' definitions and the precedence order is: `original container - env vars` > `language specific env vars` > `common env vars` > `instrument - spec configs'' vars`. If the former var had been defined, then the - other vars would be ignored.' - items: - description: EnvVar represents an environment variable present in - a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. + resources: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. properties: - key: - description: The key to select. - type: string name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified - API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. type: string required: - - resource + - name type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's namespace + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + nginx: + description: Nginx defines configuration for Nginx auto-instrumentation. + properties: + attrs: + description: 'Attrs defines Nginx agent specific attributes. The + precedence order is: `agent default attributes` > `instrument + spec attributes` . Attributes are documented at https://github.' + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean - required: - - key + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic type: object - x-kubernetes-map-type: atomic + required: + - name type: object - required: - - name - type: object - type: array - exporter: - description: Exporter defines exporter configuration. - properties: - endpoint: - description: Endpoint is address of the collector with OTLP endpoint. + type: array + configFile: + description: Location of Nginx configuration file. Needed only + if different from default "/etx/nginx/nginx.conf" type: string - type: object - java: - description: Java defines configuration for java auto-instrumentation. - properties: env: - description: 'Env defines java specific env vars. There are four - layers for env vars'' definitions and the precedence order is: - `original container env vars` > `language specific env vars` - > `common env vars` > `instrument spec configs'' vars`. If the - former var had been defined, then the other vars would be ignored.' + description: Env defines Nginx specific env vars. items: description: EnvVar represents an environment variable present in a Container. @@ -315,15 +1074,9 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded + description: Variable references $(VAR_NAME) are expanded using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + the container and any service environment variables. type: string valueFrom: description: Source for the environment variable's value. @@ -352,8 +1105,7 @@ spec: description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + spec.serviceAccountName, status.hostIP, status.' properties: apiVersion: description: Version of the schema the FieldPath @@ -371,7 +1123,7 @@ spec: description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + and requests.' properties: containerName: description: 'Container name: required for volumes, @@ -419,19 +1171,67 @@ spec: type: object type: array image: - description: Image is a container image with javaagent auto-instrumentation - JAR. + description: Image is a container image with Nginx SDK and auto-instrumentation. type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object nodejs: description: NodeJS defines configuration for nodejs auto-instrumentation. properties: env: - description: 'Env defines nodejs specific env vars. There are - four layers for env vars'' definitions and the precedence order - is: `original container env vars` > `language specific env vars` - > `common env vars` > `instrument spec configs'' vars`. If the - former var had been defined, then the other vars would be ignored.' + description: Env defines nodejs specific env vars. items: description: EnvVar represents an environment variable present in a Container. @@ -441,15 +1241,9 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded + description: Variable references $(VAR_NAME) are expanded using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + the container and any service environment variables. type: string valueFrom: description: Source for the environment variable's value. @@ -478,8 +1272,7 @@ spec: description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + spec.serviceAccountName, status.hostIP, status.' properties: apiVersion: description: Version of the schema the FieldPath @@ -497,7 +1290,7 @@ spec: description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + and requests.' properties: containerName: description: 'Container name: required for volumes, @@ -547,10 +1340,64 @@ spec: image: description: Image is a container image with NodeJS SDK and auto-instrumentation. type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object propagators: description: Propagators defines inter-process context propagation - configuration. + configuration. Values in this list will be set in the OTEL_PROPAGATORS + env var. Enum=tracecontext;baggage;b3;b3multi;jaeger;xray;ottrace;none items: description: Propagator represents the propagation type. enum: @@ -568,11 +1415,7 @@ spec: description: Python defines configuration for python auto-instrumentation. properties: env: - description: 'Env defines python specific env vars. There are - four layers for env vars'' definitions and the precedence order - is: `original container env vars` > `language specific env vars` - > `common env vars` > `instrument spec configs'' vars`. If the - former var had been defined, then the other vars would be ignored.' + description: Env defines python specific env vars. items: description: EnvVar represents an environment variable present in a Container. @@ -582,15 +1425,9 @@ spec: C_IDENTIFIER. type: string value: - description: 'Variable references $(VAR_NAME) are expanded + description: Variable references $(VAR_NAME) are expanded using the previously defined environment variables in - the container and any service environment variables. If - a variable cannot be resolved, the reference in the input - string will be unchanged. Double $$ are reduced to a single - $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless - of whether the variable exists or not. Defaults to "".' + the container and any service environment variables. type: string valueFrom: description: Source for the environment variable's value. @@ -619,8 +1456,7 @@ spec: description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, - spec.serviceAccountName, status.hostIP, status.podIP, - status.podIPs.' + spec.serviceAccountName, status.hostIP, status.' properties: apiVersion: description: Version of the schema the FieldPath @@ -638,7 +1474,7 @@ spec: description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + and requests.' properties: containerName: description: 'Container name: required for volumes, @@ -688,6 +1524,59 @@ spec: image: description: Image is a container image with Python SDK and auto-instrumentation. type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 200Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true type: object resource: description: Resource defines the configuration for the resource attributes, @@ -713,7 +1602,8 @@ spec: sampler type it is a number in range [0..1] e.g. 0.25. type: string type: - description: Type defines sampler type. The value can be for instance + description: Type defines sampler type. The value will be set + in the OTEL_TRACES_SAMPLER env var. The value can be for instance parentbased_always_on, parentbased_always_off, parentbased_traceidratio... enum: - always_on diff --git a/config/crd/bases/opentelemetry.io_opampbridges.yaml b/config/crd/bases/opentelemetry.io_opampbridges.yaml new file mode 100644 index 0000000000..439d923de3 --- /dev/null +++ b/config/crd/bases/opentelemetry.io_opampbridges.yaml @@ -0,0 +1,2830 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + name: opampbridges.opentelemetry.io +spec: + group: opentelemetry.io + names: + kind: OpAMPBridge + listKind: OpAMPBridgeList + plural: opampbridges + singular: opampbridge + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - description: OpenTelemetry Version + jsonPath: .status.version + name: Version + type: string + - jsonPath: .spec.endpoint + name: Endpoint + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: OpAMPBridge is the Schema for the opampbridges API. + properties: + apiVersion: + description: APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. + type: string + kind: + description: Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. + type: string + metadata: + type: object + spec: + description: OpAMPBridgeSpec defines the desired state of OpAMPBridge. + properties: + affinity: + description: If specified, indicates the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + capabilities: + additionalProperties: + type: boolean + description: Capabilities supported by the OpAMP Bridge + type: object + componentsAllowed: + additionalProperties: + items: + type: string + type: array + description: ComponentsAllowed is a list of allowed OpenTelemetry + components for each pipeline type (receiver, processor, etc.) + type: object + endpoint: + description: OpAMP backend Server endpoint + type: string + env: + description: ENV vars to set on the OpAMPBridge Pods. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables on + the OpAMPBridge Pods. + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + headers: + additionalProperties: + type: string + description: Headers is an optional map of headers to use when connecting + to the OpAMP Server, typically used to set access tokens or other + authorization headers. + type: object + hostNetwork: + description: HostNetwork indicates if the pod should run in the host + networking namespace. + type: boolean + image: + description: Image indicates the container image to use for the OpAMPBridge. + type: string + imagePullPolicy: + description: ImagePullPolicy indicates the pull policy to be used + for retrieving the container image (Always, Never, IfNotPresent) + type: string + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to schedule OpAMPBridge pods. + type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations is the set of annotations that will be + attached to OpAMPBridge pods. + type: object + podSecurityContext: + description: PodSecurityContext will be set as the pod security context. + properties: + fsGroup: + description: "A special supplemental group that applies to all + containers in a pod. Some volume types allow the Kubelet to + change the ownership of that volume to be owned by the pod: + \n 1." + format: int64 + type: integer + fsGroupChangePolicy: + description: fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. + type: string + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in SecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in SecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers in this + pod. Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process run + in each container, in addition to the container's primary GID, + the fsGroup (if specified), and group memberships defined in + the container image for th + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used for + the pod. Pods with unsupported sysctls (by the container runtime) + might fail to launch. Note that this field cannot be set when + spec.os. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + ports: + description: Ports allows a set of ports to be exposed by the underlying + v1.Service. + items: + description: ServicePort contains information on service's port. + properties: + appProtocol: + description: The application protocol for this port. This is + used as a hint for implementations to offer richer behavior + for protocols that they understand. This field follows standard + Kubernetes label syntax. + type: string + name: + description: The name of this port within the service. This + must be a DNS_LABEL. All ports within a ServiceSpec must have + unique names. + type: string + nodePort: + description: The port on each node on which this service is + exposed when type is NodePort or LoadBalancer. Usually assigned + by the system. + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the pods + targeted by the service. Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-type: atomic + priorityClassName: + description: If specified, indicates the pod's priority. If not specified, + the pod priority will be default or zero if there is no default. + type: string + replicas: + description: Replicas is the number of pod instances for the OpAMPBridge. + format: int32 + maximum: 1 + type: integer + resources: + description: Resources to set on the OpAMPBridge pods. + properties: + claims: + description: "Claims lists the names of resources, defined in + spec.resourceClaims, that are used by this container. \n This + is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims + of the Pod where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + securityContext: + description: SecurityContext will be set as the container security + context. + properties: + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a process + can gain more privileges than its parent process. This bool + directly controls if the no_new_privs flag will be set on the + container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container + runtime. Note that this field cannot be set when spec.os.name + is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in privileged + containers are essentially equivalent to root on the host. Defaults + to false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use for + the containers. The default is DefaultProcMount which uses the + container runtime defaults for readonly paths and masked paths. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. If + seccomp options are provided at both the pod & container level, + the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will + be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + serviceAccount: + description: ServiceAccount indicates the name of an existing service + account to use with this instance. When set, the operator will not + automatically create a ServiceAccount for the OpAMPBridge. + type: string + tolerations: + description: Toleration to schedule OpAMPBridge pods. + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match all + values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to Equal. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints embedded kubernetes pod configuration + option, controls how pods are spread across your cluster among failure-domains + such as regions, zones, nodes, and other user-defined top + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: MatchLabelKeys is a set of pod label keys to select + the pods over which spreading will be calculated. + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: MaxSkew describes the degree to which pods may + be unevenly distributed. + format: int32 + type: integer + minDomains: + description: MinDomains indicates a minimum number of eligible + domains. + format: int32 + type: integer + nodeAffinityPolicy: + description: NodeAffinityPolicy indicates how we will treat + Pod's nodeAffinity/nodeSelector when calculating pod topology + spread skew. + type: string + nodeTaintsPolicy: + description: NodeTaintsPolicy indicates how we will treat node + taints when calculating pod topology spread skew. + type: string + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with a + pod if it doesn't satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + upgradeStrategy: + description: UpgradeStrategy represents how the operator will handle + upgrades to the CR when a newer version of the operator is deployed + enum: + - automatic + - none + type: string + volumeMounts: + description: VolumeMounts represents the mount points to use in the + underlying OpAMPBridge deployment(s) + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: Path within the container at which the volume should + be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are propagated + from the host to container and the other way around. When + not set, MountPropagationNone is used. This field is beta + in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which the + container's volume should be mounted. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-type: atomic + volumes: + description: Volumes represents which volumes to use in the underlying + OpAMPBridge deployment(s). + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs".' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1".' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent disk + resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set).' + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is the + path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified.' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: 'volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + items: + description: items if unspecified, each key-value pair in + the Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and content + is the value. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated + CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: readOnly specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a Optional: mode bits used to set + permissions on created files by default.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used to set permissions + on this file, must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: medium represents what type of storage medium + should back this directory. The default is "" which means + to use the node's default medium. Must be an empty string + (default) or Memory. + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: sizeLimit is the total amount of local storage + required for this EmptyDir volume. The size limit is also + applicable for memory medium. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: ephemeral represents a volume that is handled by + a cluster storage driver. + properties: + volumeClaimTemplate: + description: Will be used to create a stand-alone PVC to + provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. + properties: + metadata: + description: May contain labels and annotations that + will be copied into the PVC when creating it. No other + fields are allowed and will be rejected during validation. + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the PVC + that gets created from this template. + properties: + accessModes: + description: 'accessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: dataSourceRef specifies the object + from which to populate the volume with data, if + a non-empty volume is desired. + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace + is specified, a gateway.networking.k8s. + type: string + required: + - kind + - name + type: object + resources: + description: resources represents the minimum resources + the volume should have. + properties: + claims: + description: "Claims lists the names of resources, + defined in spec.resourceClaims, that are used + by this container. \n This is an alpha field + and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one + entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name + of one entry in pod.spec.resourceClaims + of the Pod where this field is used. + It makes that resource available inside + a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum + amount of compute resources required. + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: 'storageClassName is the name of the + StorageClass required by the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume + is required by the claim. Value of Filesystem + is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends + on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if no + secret object is specified.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: datasetName is Name of the dataset stored as + metadata -> name on the dataset for Flocker should be + considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.' + properties: + fsType: + description: 'fsType is filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: "ext4", + "xfs", "ntfs".' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1".' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource in + GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated.' + properties: + directory: + description: directory is the target directory name. Must + not contain or start with '..'. If '.' is supplied, the + volume directory will be the git repository. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: hostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + properties: + path: + description: 'path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to + the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs".' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that uses + an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. The + portal is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: targetPortal is iSCSI Target Portal. The Portal + is either an IP or ip_addr:port if the port is other than + default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a DNS_LABEL and unique + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export to + be mounted with read-only permissions. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents a + reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.' + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: defaultMode are the mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu + and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced Secret + will be projected into the volume as a file + whose name is the key and content is the value. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: audience is the intended audience + of the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. + type: string + expirationSeconds: + description: expirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, the + kubelet volume plugin will proactively rotate + the service account token. + format: int64 + type: integer + path: + description: path is the path relative to the + mount point of the file to project the token + into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is no + group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults to + false. + type: boolean + registry: + description: registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs".' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is + nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is the rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage for + a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + items: + description: items If unspecified, each key-value pair in + the Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and content + is the value. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in the + pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: volumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within + a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-type: atomic + required: + - capabilities + - endpoint + type: object + status: + description: OpAMPBridgeStatus defines the observed state of OpAMPBridge. + properties: + version: + description: Version of the managed OpAMP Bridge (operand) + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml b/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml index 06db1901ec..6bc2e810ed 100644 --- a/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml +++ b/config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: opentelemetrycollectors.opentelemetry.io spec: group: opentelemetry.io @@ -27,9 +26,19 @@ spec: jsonPath: .status.version name: Version type: string + - jsonPath: .status.scale.statusReplicas + name: Ready + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date + - jsonPath: .status.image + name: Image + type: string + - description: Management State + jsonPath: .spec.managementState + name: Management + type: string name: v1alpha1 schema: openAPIV3Schema: @@ -37,869 +46,5276 @@ spec: API. properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation + description: APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + internal value, and may reject unrecognized values. type: string kind: - description: 'Kind is a string value representing the REST resource this + description: Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + submits requests to. Cannot be updated. In CamelCase. type: string metadata: type: object spec: description: OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector. properties: - args: - additionalProperties: - type: string - description: Args is the set of arguments to pass to the OpenTelemetry - Collector binary - type: object - autoscaler: - description: Autoscaler specifies the pod autoscaling configuration - to use for the OpenTelemetryCollector workload. - properties: - behavior: - description: HorizontalPodAutoscalerBehavior configures the scaling - behavior of the target in both Up and Down directions (scaleUp - and scaleDown fields respectively). - properties: - scaleDown: - description: scaleDown is scaling policy for scaling Down. - If not set, the default value is to allow to scale down - to minReplicas pods, with a 300 second stabilization window - (i.e., the highest recommendation for the last 300sec is - used). + additionalContainers: + description: AdditionalContainers allows injecting additional containers + into the Collector's pod definition. + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: Arguments to the entrypoint. The container image's + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container's environment. + items: + type: string + type: array + command: + description: Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's + environment. + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. properties: - policies: - description: policies is a list of potential scaling polices - which can be used during scaling. At least one policy - must be specified, otherwise the HPAScalingRules will - be discarded as invalid - items: - description: HPAScalingPolicy is a single policy which - must hold true for a specified past interval. - properties: - periodSeconds: - description: PeriodSeconds specifies the window - of time for which the policy should hold true. - PeriodSeconds must be greater than zero and less - than or equal to 1800 (30 min). - format: int32 - type: integer - type: - description: Type is used to specify the scaling - policy. - type: string - value: - description: Value contains the amount of change - which is permitted by the policy. It must be greater - than zero - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - x-kubernetes-list-type: atomic - selectPolicy: - description: selectPolicy is used to specify which policy - should be used. If not set, the default value Max is - used. + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. type: string - stabilizationWindowSeconds: - description: 'StabilizationWindowSeconds is the number - of seconds for which past recommendations should be - considered while scaling up or scaling down. StabilizationWindowSeconds - must be greater than or equal to zero and less than - or equal to 3600 (one hour). If not set, use the default - values: - For scale up: 0 (i.e. no stabilization is - done). - For scale down: 300 (i.e. the stabilization - window is 300 seconds long).' - format: int32 - type: integer + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name type: object - scaleUp: - description: 'scaleUp is scaling policy for scaling Up. If - not set, the default value is the higher of: * increase - no more than 4 pods per 60 seconds * double the number of - pods per 60 seconds No stabilization is used.' + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be + a C_IDENTIFIER. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps properties: - policies: - description: policies is a list of potential scaling polices - which can be used during scaling. At least one policy - must be specified, otherwise the HPAScalingRules will - be discarded as invalid - items: - description: HPAScalingPolicy is a single policy which - must hold true for a specified past interval. - properties: - periodSeconds: - description: PeriodSeconds specifies the window - of time for which the policy should hold true. - PeriodSeconds must be greater than zero and less - than or equal to 1800 (30 min). - format: int32 - type: integer - type: - description: Type is used to specify the scaling - policy. - type: string - value: - description: Value contains the amount of change - which is permitted by the policy. It must be greater - than zero - format: int32 - type: integer - required: - - periodSeconds - - type - - value - type: object - type: array - x-kubernetes-list-type: atomic - selectPolicy: - description: selectPolicy is used to specify which policy - should be used. If not set, the default value Max is - used. + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. type: string - stabilizationWindowSeconds: - description: 'StabilizationWindowSeconds is the number - of seconds for which past recommendations should be - considered while scaling up or scaling down. StabilizationWindowSeconds - must be greater than or equal to zero and less than - or equal to 3600 (one hour). If not set, use the default - values: - For scale up: 0 (i.e. no stabilization is - done). - For scale down: 300 (i.e. the stabilization - window is 300 seconds long).' - format: int32 - type: integer + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic type: object - type: object - targetCPUUtilization: - description: TargetCPUUtilization sets the target average CPU - used across all replicas. If average CPU exceeds this value, - the HPA will scale up. Defaults to 90 percent. - format: int32 - type: integer - type: object - config: - description: Config is the raw JSON to be used as the collector's - configuration. Refer to the OpenTelemetry Collector documentation - for details. - type: string - env: - description: ENV vars to set on the OpenTelemetry Collector's Pods. - These can then in certain cases be consumed in the config file for - the Collector. - items: - description: EnvVar represents an environment variable present in - a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. + type: array + image: + description: 'Container image name. More info: https://kubernetes.' type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using - the previously defined environment variables in the container - and any service environment variables. If a variable cannot - be resolved, the reference in the input string will be unchanged. - Double $$ are reduced to a single $, which allows for escaping - the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the - string literal "$(VAR_NAME)". Escaped references will never - be expanded, regardless of whether the variable exists or - not. Defaults to "".' + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.' type: string - valueFrom: - description: Source for the environment variable's value. Cannot - be used if value is not empty. + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. + postStart: + description: PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key - must be defined - type: boolean - required: - - key + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object type: object - x-kubernetes-map-type: atomic - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, - status.podIP, status.podIPs.' + preStop: + description: PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. properties: - apiVersion: - description: Version of the schema the FieldPath is - written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified - API version. - type: string - required: - - fieldPath + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: 'Selects a resource of the container: only - resources limits and requests (limits.cpu, limits.memory, - limits.ephemeral-storage, requests.cpu, requests.memory - and requests.ephemeral-storage) are currently supported.' + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). type: string - divisor: + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: anyOf: - type: integer - type: string - description: Specifies the output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. type: string required: - - resource + - port type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's namespace + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not + specifying a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default "0.0.0. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: Number of port to expose on the pod's IP + address. This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If + specified, this must be a valid port number, 0 < x < + 65536. If HostNetwork is specified, this must match + ContainerPort. Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod + must have a unique name. Name for the port that can + be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe + fails. Cannot be updated. More info: https://kubernetes.' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). type: string - optional: - description: Specify whether the Secret or its key must - be defined - type: boolean required: - - key + - port type: object - x-kubernetes-map-type: atomic + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment variables on - the OpenTelemetry Collector's Pods. These can then in certain cases - be consumed in the config file for the Collector. - items: - description: EnvFromSource represents the source of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap must be defined - type: boolean + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object type: object - x-kubernetes-map-type: atomic - prefix: - description: An optional identifier to prepend to each key in - the ConfigMap. Must be a C_IDENTIFIER. + restartPolicy: + description: RestartPolicy defines the restart behavior of individual + containers in a pod. This field may only be set for init containers, + and the only allowed value is "Always". type: string - secretRef: - description: The Secret to select from + securityContext: + description: SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. apiVersion, kind, uid?' + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by + the container runtime. Note that this field cannot be + set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent to + root on the host. Defaults to false. Note that this field + cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to + use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. type: string - optional: - description: Specify whether the Secret must be defined + readOnlyRootFilesystem: + description: Whether this container has a read-only root + filesystem. Default is false. Note that this field cannot + be set when spec.os.name is windows. type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a + non-root user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a + random SELinux context for each container. May also be + set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile + must be preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - + a profile defined in a file on the node should be + used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set + in PodSecurityContext. + type: string + type: object type: object - x-kubernetes-map-type: atomic - type: object - type: array - hostNetwork: - description: HostNetwork indicates if the pod should run in the host - networking namespace. - type: boolean - image: - description: Image indicates the container image to use for the OpenTelemetry - Collector. - type: string - imagePullPolicy: - description: ImagePullPolicy indicates the pull policy to be used - for retrieving the container image (Always, Never, IfNotPresent) - type: string - ingress: - description: 'Ingress is used to specify how OpenTelemetry Collector - is exposed. This functionality is only available if one of the valid - modes is set. Valid modes are: deployment, daemonset and statefulset.' - properties: - annotations: - additionalProperties: - type: string - description: 'Annotations to add to ingress. e.g. ''cert-manager.io/cluster-issuer: - "letsencrypt"''' - type: object - hostname: - description: Hostname by which the ingress proxy can be reached. - type: string - tls: - description: TLS configuration. - items: - description: IngressTLS describes the transport layer security - associated with an Ingress. - properties: - hosts: - description: Hosts are a list of hosts included in the TLS - certificate. The values in this list must match the name/s - used in the tlsSecret. Defaults to the wildcard host setting - for the loadbalancer controller fulfilling this Ingress, - if left unspecified. - items: - type: string - type: array - x-kubernetes-list-type: atomic - secretName: - description: SecretName is the name of the secret used to - terminate TLS traffic on port 443. Field is left optional - to allow TLS routing based on SNI hostname alone. If the - SNI host in a listener conflicts with the "Host" header - field used by an IngressRule, the SNI host is used for - termination and value of the Host header is used for routing. - type: string - type: object - type: array - type: - description: 'Type default value is: "" Supported types are: ingress' - enum: - - ingress - type: string - type: object - maxReplicas: - description: MaxReplicas sets an upper bound to the autoscaling feature. - If MaxReplicas is set autoscaling is enabled. - format: int32 - type: integer - minReplicas: - description: MinReplicas sets a lower bound to the autoscaling feature. Set - this if your are using autoscaling. It must be at least 1 - format: int32 - type: integer - mode: - description: Mode represents how the collector should be deployed - (deployment, daemonset, statefulset or sidecar) - enum: - - daemonset - - deployment - - sidecar - - statefulset - type: string - nodeSelector: - additionalProperties: - type: string - description: NodeSelector to schedule OpenTelemetry Collector pods. - This is only relevant to daemonset, statefulset, and deployment - mode - type: object - podAnnotations: - additionalProperties: - type: string - description: PodAnnotations is the set of annotations that will be - attached to Collector and Target Allocator pods. - type: object - podSecurityContext: - description: PodSecurityContext holds pod-level security attributes - and common container settings. Some fields are also present in container.securityContext. Field - values of container.securityContext take precedence over field values - of PodSecurityContext. - properties: - fsGroup: - description: "A special supplemental group that applies to all - containers in a pod. Some volume types allow the Kubelet to - change the ownership of that volume to be owned by the pod: - \n 1. The owning GID will be the FSGroup 2. The setgid bit is - set (new files created in the volume will be owned by FSGroup) - 3. The permission bits are OR'd with rw-rw---- \n If unset, - the Kubelet will not modify the ownership and permissions of - any volume. Note that this field cannot be set when spec.os.name - is windows." - format: int64 - type: integer - fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of changing - ownership and permission of the volume before being exposed - inside Pod. This field will only apply to volume types which - support fsGroup based ownership(and permissions). It will have - no effect on ephemeral volume types such as: secret, configmaps - and emptydir. Valid values are "OnRootMismatch" and "Always". - If not specified, "Always" is used. Note that this field cannot - be set when spec.os.name is windows.' - type: string - runAsGroup: - description: The GID to run the entrypoint of the container process. - Uses runtime default if unset. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail to start - the container if it does. If unset or false, no such validation - will be performed. May also be set in SecurityContext. If set - in both SecurityContext and PodSecurityContext, the value specified - in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in SecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence for that container. Note that this field cannot - be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to all containers. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in SecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence for that container. - Note that this field cannot be set when spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies to - the container. - type: string - role: - description: Role is a SELinux role label that applies to - the container. - type: string - type: - description: Type is a SELinux type label that applies to - the container. - type: string - user: - description: User is a SELinux user label that applies to - the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by the containers in this - pod. Note that this field cannot be set when spec.os.name is - windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must be - preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a profile - defined in a file on the node should be used. RuntimeDefault - - the container runtime default profile should be used. - Unconfined - no profile should be applied." - type: string - required: - - type - type: object - supplementalGroups: - description: A list of groups applied to the first process run - in each container, in addition to the container's primary GID. If - unspecified, no groups will be added to any container. Note - that this field cannot be set when spec.os.name is windows. - items: - format: int64 - type: integer - type: array - sysctls: - description: Sysctls hold a list of namespaced sysctls used for - the pod. Pods with unsupported sysctls (by the container runtime) - might fail to launch. Note that this field cannot be set when - spec.os.name is windows. - items: - description: Sysctl defines a kernel parameter to be set + startupProbe: + description: StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - required: - - name - - value + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer type: object - type: array - windowsOptions: - description: The Windows specific settings applied to all containers. - If unspecified, the options within a container's SecurityContext - will be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named by - the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the GMSA - credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is alpha-level - and will only be honored by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature flag - will result in errors when validating the Pod. All of a - Pod's containers must have the same effective HostProcess - value (it is not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in PodSecurityContext. - If set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - type: string - type: object - type: object - ports: - description: Ports allows a set of ports to be exposed by the underlying - v1.Service. By default, the operator will attempt to infer the required - ports by parsing the .Spec.Config property but this property can - be used to open additional ports that can't be inferred by the operator, - like for custom receivers. - items: - description: ServicePort contains information on service's port. - properties: - appProtocol: - description: The application protocol for this port. This field - follows standard Kubernetes label syntax. Un-prefixed names - are reserved for IANA standard service names (as per RFC-6335 - and https://www.iana.org/assignments/service-names). Non-standard - protocols should use prefixed names such as mycompany.com/my-custom-protocol. + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, reads + from stdin in the container will always result in EOF. Default + is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the + stdin channel after it has been opened by a single attach. + When stdin is true the stdin stream will remain open across + multiple attach sessions. + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the + container''s termination message will be written is mounted + into the container''s filesystem.' type: string - name: - description: The name of this port within the service. This - must be a DNS_LABEL. All ports within a ServiceSpec must have - unique names. When considering the endpoints for a Service, - this must match the 'name' field in the EndpointPort. Optional - if only one ServicePort is defined on this service. + terminationMessagePolicy: + description: Indicate how the termination message should be + populated. File will use the contents of terminationMessagePath + to populate the container status message on both success and + failure. type: string - nodePort: - description: 'The port on each node on which this service is - exposed when type is NodePort or LoadBalancer. Usually assigned - by the system. If a value is specified, in-range, and not - in use it will be used, otherwise the operation will fail. If - not specified, a port will be allocated if this Service requires - one. If this field is specified when creating a Service which - does not need it, creation will fail. This field will be wiped - when updating a Service to no longer need it (e.g. changing - type from NodePort to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' - format: int32 - type: integer - port: - description: The port that will be exposed by this service. - format: int32 - type: integer - protocol: - default: TCP - description: The IP protocol for this port. Supports "TCP", - "UDP", and "SCTP". Default is TCP. + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other + way around. When not set, MountPropagationNone is used. + This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. type: string - targetPort: - anyOf: - - type: integer - - type: string - description: 'Number or name of the port to access on the pods - targeted by the service. Number must be in the range 1 to - 65535. Name must be an IANA_SVC_NAME. If this is a string, - it will be looked up as a named port in the target Pod''s - container ports. If this is not specified, the value of the - ''port'' field is used (an identity map). This field is ignored - for services with clusterIP=None, and should be omitted or - set equal to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' - x-kubernetes-int-or-string: true required: - - port + - name type: object type: array - x-kubernetes-list-type: atomic - priorityClassName: - description: If specified, indicates the pod's priority. If not specified, - the pod priority will be default or zero if there is no default. - type: string - replicas: - description: Replicas is the number of pod instances for the underlying - OpenTelemetry Collector. Set this if your are not using autoscaling - format: int32 - type: integer - resources: - description: Resources to set on the OpenTelemetry Collector pods. + affinity: + description: If specified, indicates the pod's scheduling constraints properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources - allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic type: object - type: object - securityContext: - description: SecurityContext will be set as the container security - context. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether a process - can gain more privileges than its parent process. This bool - directly controls if the no_new_privs flag will be set on the - container process. AllowPrivilegeEscalation is true always when - the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN - Note that this field cannot be set when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. - Defaults to the default set of capabilities granted by the container - runtime. Note that this field cannot be set when spec.os.name - is windows. + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). properties: - add: - description: Added capabilities + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. items: - description: Capability represent POSIX capabilities type - type: string + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object type: array - drop: - description: Removed capabilities + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. items: - description: Capability represent POSIX capabilities type - type: string + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object type: array type: object - privileged: - description: Run container in privileged mode. Processes in privileged - containers are essentially equivalent to root on the host. Defaults - to false. Note that this field cannot be set when spec.os.name - is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to use for - the containers. The default is DefaultProcMount which uses the - container runtime defaults for readonly paths and masked paths. - This requires the ProcMountType feature flag to be enabled. - Note that this field cannot be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root filesystem. - Default is false. Note that this field cannot be set when spec.os.name - is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container process. - Uses runtime default if unset. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence. Note that this - field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root - user. If true, the Kubelet will validate the image at runtime - to ensure that it does not run as UID 0 (root) and fail to start - the container if it does. If unset or false, no such validation - will be performed. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container process. - Defaults to user specified in image metadata if unspecified. - May also be set in PodSecurityContext. If set in both SecurityContext - and PodSecurityContext, the value specified in SecurityContext - takes precedence. Note that this field cannot be set when spec.os.name - is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. - If unspecified, the container runtime will allocate a random - SELinux context for each container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, the value - specified in SecurityContext takes precedence. Note that this - field cannot be set when spec.os.name is windows. + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). properties: - level: - description: Level is SELinux level label that applies to - the container. - type: string - role: - description: Role is a SELinux role label that applies to - the container. - type: string - type: - description: Type is a SELinux type label that applies to - the container. - type: string - user: - description: User is a SELinux user label that applies to - the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. If - seccomp options are provided at both the pod & container level, - the container options override the pod options. Note that this - field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined - in a file on the node should be used. The profile must be - preconfigured on the node to work. Must be a descending - path, relative to the kubelet's configured seccomp profile - location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile - will be applied. Valid options are: \n Localhost - a profile - defined in a file on the node should be used. RuntimeDefault - - the container runtime default profile should be used. - Unconfined - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all containers. - If unspecified, the options from the PodSecurityContext will - be used. If set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext takes precedence. Note - that this field cannot be set when spec.os.name is linux. + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + args: + additionalProperties: + type: string + description: Args is the set of arguments to pass to the OpenTelemetry + Collector binary + type: object + autoscaler: + description: Autoscaler specifies the pod autoscaling configuration + to use for the OpenTelemetryCollector workload. + properties: + behavior: + description: HorizontalPodAutoscalerBehavior configures the scaling + behavior of the target in both Up and Down directions (scaleUp + and scaleDown fields respectively). properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission - webhook (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA credential spec named by - the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the GMSA - credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should - be run as a 'Host Process' container. This field is alpha-level - and will only be honored by components that enable the WindowsHostProcessContainers - feature flag. Setting this field without the feature flag - will result in errors when validating the Pod. All of a - Pod's containers must have the same effective HostProcess - value (it is not allowed to have a mix of HostProcess containers - and non-HostProcess containers). In addition, if HostProcess - is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint - of the container process. Defaults to the user specified - in image metadata if unspecified. May also be set in PodSecurityContext. - If set in both SecurityContext and PodSecurityContext, the - value specified in SecurityContext takes precedence. - type: string + scaleDown: + description: scaleDown is scaling policy for scaling Down. + If not set, the default value is to allow to scale down + to minReplicas pods, with a 300 second stabilization window + (i.e. + properties: + policies: + description: policies is a list of potential scaling polices + which can be used during scaling. At least one policy + must be specified, otherwise the HPAScalingRules will + be discarded as invalid + items: + description: HPAScalingPolicy is a single policy which + must hold true for a specified past interval. + properties: + periodSeconds: + description: periodSeconds specifies the window + of time for which the policy should hold true. + PeriodSeconds must be greater than zero and less + than or equal to 1800 (30 min). + format: int32 + type: integer + type: + description: type is used to specify the scaling + policy. + type: string + value: + description: value contains the amount of change + which is permitted by the policy. It must be greater + than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + x-kubernetes-list-type: atomic + selectPolicy: + description: selectPolicy is used to specify which policy + should be used. If not set, the default value Max is + used. + type: string + stabilizationWindowSeconds: + description: stabilizationWindowSeconds is the number + of seconds for which past recommendations should be + considered while scaling up or scaling down. + format: int32 + type: integer + type: object + scaleUp: + description: scaleUp is scaling policy for scaling Up. + properties: + policies: + description: policies is a list of potential scaling polices + which can be used during scaling. At least one policy + must be specified, otherwise the HPAScalingRules will + be discarded as invalid + items: + description: HPAScalingPolicy is a single policy which + must hold true for a specified past interval. + properties: + periodSeconds: + description: periodSeconds specifies the window + of time for which the policy should hold true. + PeriodSeconds must be greater than zero and less + than or equal to 1800 (30 min). + format: int32 + type: integer + type: + description: type is used to specify the scaling + policy. + type: string + value: + description: value contains the amount of change + which is permitted by the policy. It must be greater + than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + x-kubernetes-list-type: atomic + selectPolicy: + description: selectPolicy is used to specify which policy + should be used. If not set, the default value Max is + used. + type: string + stabilizationWindowSeconds: + description: stabilizationWindowSeconds is the number + of seconds for which past recommendations should be + considered while scaling up or scaling down. + format: int32 + type: integer + type: object + type: object + maxReplicas: + description: MaxReplicas sets an upper bound to the autoscaling + feature. If MaxReplicas is set autoscaling is enabled. + format: int32 + type: integer + metrics: + description: Metrics is meant to provide a customizable way to + configure HPA metrics. currently the only supported custom metrics + is type=Pod. + items: + description: MetricSpec defines a subset of metrics to be defined + for the HPA's metric array more metric type can be supported + as needed. See https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec + for reference. + properties: + pods: + description: PodsMetricSource indicates how to scale on + a metric describing each pod in the current scale target + (for example, transactions-processed-per-second). + properties: + metric: + description: metric identifies the target metric by + name and selector + properties: + name: + description: name is the name of the given metric + type: string + selector: + description: selector is the string-encoded form + of a standard kubernetes label selector for the + given metric When set, it is passed as an additional + parameter to the metrics server for more specific + metrics scopi + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - name + type: object + target: + description: target specifies the target value for the + given metric + properties: + averageUtilization: + description: averageUtilization is the target value + of the average of the resource metric across all + relevant pods, represented as a percentage of + the requested value of the resource for the pods. + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + description: averageValue is the target value of + the average of the metric across all relevant + pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + description: type represents whether the metric + type is Utilization, Value, or AverageValue + type: string + value: + anyOf: + - type: integer + - type: string + description: value is the target value of the metric + (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - metric + - target + type: object + type: + description: MetricSourceType indicates the type of metric. + type: string + required: + - type + type: object + type: array + minReplicas: + description: MinReplicas sets a lower bound to the autoscaling + feature. Set this if your are using autoscaling. It must be + at least 1 + format: int32 + type: integer + targetCPUUtilization: + description: TargetCPUUtilization sets the target average CPU + used across all replicas. If average CPU exceeds this value, + the HPA will scale up. Defaults to 90 percent. + format: int32 + type: integer + targetMemoryUtilization: + description: TargetMemoryUtilization sets the target average memory + utilization across all replicas + format: int32 + type: integer + type: object + config: + description: Config is the raw JSON to be used as the collector's + configuration. Refer to the OpenTelemetry Collector documentation + for details. + type: string + configmaps: + description: ConfigMaps is a list of ConfigMaps in the same namespace + as the OpenTelemetryCollector object, which shall be mounted into + the Collector Pods. + items: + properties: + mountpath: + type: string + name: + description: Configmap defines name and path where the configMaps + should be mounted. + type: string + required: + - mountpath + - name + type: object + type: array + env: + description: ENV vars to set on the OpenTelemetry Collector's Pods. + These can then in certain cases be consumed in the config file for + the Collector. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables on + the OpenTelemetry Collector's Pods. These can then in certain cases + be consumed in the config file for the Collector. + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + hostNetwork: + description: HostNetwork indicates if the pod should run in the host + networking namespace. + type: boolean + image: + description: Image indicates the container image to use for the OpenTelemetry + Collector. + type: string + imagePullPolicy: + description: ImagePullPolicy indicates the pull policy to be used + for retrieving the container image (Always, Never, IfNotPresent) + type: string + ingress: + description: 'Ingress is used to specify how OpenTelemetry Collector + is exposed. This functionality is only available if one of the valid + modes is set. Valid modes are: deployment, daemonset and statefulset.' + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations to add to ingress. e.g. ''cert-manager.io/cluster-issuer: + "letsencrypt"''' + type: object + hostname: + description: Hostname by which the ingress proxy can be reached. + type: string + ingressClassName: + description: IngressClassName is the name of an IngressClass cluster + resource. Ingress controller implementations use this field + to know whether they should be serving this Ingress resource. + type: string + route: + description: Route is an OpenShift specific section that is only + considered when type "route" is used. + properties: + termination: + description: Termination indicates termination type. By default + "edge" is used. + enum: + - insecure + - edge + - passthrough + - reencrypt + type: string + type: object + ruleType: + description: RuleType defines how Ingress exposes collector receivers. + IngressRuleTypePath ("path") exposes each receiver port on a + unique path on single domain defined in Hostname. + enum: + - path + - subdomain + type: string + tls: + description: TLS configuration. + items: + description: IngressTLS describes the transport layer security + associated with an ingress. + properties: + hosts: + description: hosts is a list of hosts included in the TLS + certificate. The values in this list must match the name/s + used in the tlsSecret. + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: secretName is the name of the secret used to + terminate TLS traffic on port 443. Field is left optional + to allow TLS routing based on SNI hostname alone. + type: string + type: object + type: array + type: + description: 'Type default value is: "" Supported types are: ingress, + route' + enum: + - ingress + - route + type: string + type: object + initContainers: + description: InitContainers allows injecting initContainers to the + Collector's pod definition. + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: Arguments to the entrypoint. The container image's + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container's environment. + items: + type: string + type: array + command: + description: Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's + environment. + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be + a C_IDENTIFIER. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + description: 'Container image name. More info: https://kubernetes.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not + specifying a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default "0.0.0. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: Number of port to expose on the pod's IP + address. This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If + specified, this must be a valid port number, 0 < x < + 65536. If HostNetwork is specified, this must match + ContainerPort. Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod + must have a unique name. Name for the port that can + be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe + fails. Cannot be updated. More info: https://kubernetes.' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + restartPolicy: + description: RestartPolicy defines the restart behavior of individual + containers in a pod. This field may only be set for init containers, + and the only allowed value is "Always". + type: string + securityContext: + description: SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. + properties: + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by + the container runtime. Note that this field cannot be + set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent to + root on the host. Defaults to false. Note that this field + cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to + use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root + filesystem. Default is false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a + non-root user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a + random SELinux context for each container. May also be + set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile + must be preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - + a profile defined in a file on the node should be + used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set + in PodSecurityContext. + type: string + type: object + type: object + startupProbe: + description: StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, reads + from stdin in the container will always result in EOF. Default + is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the + stdin channel after it has been opened by a single attach. + When stdin is true the stdin stream will remain open across + multiple attach sessions. + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the + container''s termination message will be written is mounted + into the container''s filesystem.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be + populated. File will use the contents of terminationMessagePath + to populate the container status message on both success and + failure. + type: string + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other + way around. When not set, MountPropagationNone is used. + This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + lifecycle: + description: Actions that the management system should take in response + to container lifecycle events. Cannot be updated. + properties: + postStart: + description: PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name. This will be + canonicalized upon output, so case-variant names + will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler + and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: PreStop is called immediately before a container + is terminated due to an API request or management event such + as liveness/startup probe failure, preemption, resource contention, + etc. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name. This will be + canonicalized upon output, so case-variant names + will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler + and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: Liveness config for the OpenTelemetry Collector except + the probe handler which is auto generated from the health extension + of the collector. + properties: + failureThreshold: + description: Minimum consecutive failures for the probe to be + considered failed after having succeeded. Defaults to 3. Minimum + value is 1. + format: int32 + type: integer + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. Defaults to 0 seconds. + Minimum value is 0. More info: https://kubernetes.' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. Default + to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to be + considered successful after having failed. Defaults to 1. Must + be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate + gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + managementState: + default: managed + description: ManagementState defines if the CR should be managed by + the operator or not. Default is managed. + enum: + - managed + - unmanaged + type: string + maxReplicas: + description: 'MaxReplicas sets an upper bound to the autoscaling feature. + If MaxReplicas is set autoscaling is enabled. Deprecated: use "OpenTelemetryCollector.Spec.Autoscaler.MaxReplicas" + instead.' + format: int32 + type: integer + minReplicas: + description: 'MinReplicas sets a lower bound to the autoscaling feature. Set + this if you are using autoscaling. It must be at least 1 Deprecated: + use "OpenTelemetryCollector.Spec.Autoscaler.MinReplicas" instead.' + format: int32 + type: integer + mode: + description: Mode represents how the collector should be deployed + (deployment, daemonset, statefulset or sidecar) + enum: + - daemonset + - deployment + - sidecar + - statefulset + type: string + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to schedule OpenTelemetry Collector pods. + This is only relevant to daemonset, statefulset, and deployment + mode + type: object + observability: + description: ObservabilitySpec defines how telemetry data gets handled. + properties: + metrics: + description: Metrics defines the metrics configuration for operands. + properties: + enableMetrics: + description: EnableMetrics specifies if ServiceMonitor or + PodMonitor(for sidecar mode) should be created for the OpenTelemetry + Collector and Prometheus Exporters. The operator.observability. + type: boolean + type: object + type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations is the set of annotations that will be + attached to Collector and Target Allocator pods. + type: object + podDisruptionBudget: + description: PodDisruptionBudget specifies the pod disruption budget + configuration to use for the OpenTelemetryCollector workload. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at most "maxUnavailable" + pods selected by "selector" are unavailable after the eviction, + i.e. even in absence of the evicted pod. + x-kubernetes-int-or-string: true + minAvailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at least "minAvailable" + pods selected by "selector" will still be available after the + eviction, i.e. even in the absence of the evicted pod. + x-kubernetes-int-or-string: true + type: object + podSecurityContext: + description: PodSecurityContext configures the pod security context + for the opentelemetry-collector pod, when running as a deployment, + daemonset, or statefulset. + properties: + fsGroup: + description: "A special supplemental group that applies to all + containers in a pod. Some volume types allow the Kubelet to + change the ownership of that volume to be owned by the pod: + \n 1." + format: int64 + type: integer + fsGroupChangePolicy: + description: fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. + type: string + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in SecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in SecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers in this + pod. Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process run + in each container, in addition to the container's primary GID, + the fsGroup (if specified), and group memberships defined in + the container image for th + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used for + the pod. Pods with unsupported sysctls (by the container runtime) + might fail to launch. Note that this field cannot be set when + spec.os. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + ports: + description: Ports allows a set of ports to be exposed by the underlying + v1.Service. By default, the operator will attempt to infer the required + ports by parsing the .Spec. + items: + description: ServicePort contains information on service's port. + properties: + appProtocol: + description: The application protocol for this port. This is + used as a hint for implementations to offer richer behavior + for protocols that they understand. This field follows standard + Kubernetes label syntax. + type: string + name: + description: The name of this port within the service. This + must be a DNS_LABEL. All ports within a ServiceSpec must have + unique names. + type: string + nodePort: + description: The port on each node on which this service is + exposed when type is NodePort or LoadBalancer. Usually assigned + by the system. + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the pods + targeted by the service. Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-type: atomic + priorityClassName: + description: If specified, indicates the pod's priority. If not specified, + the pod priority will be default or zero if there is no default. + type: string + replicas: + description: Replicas is the number of pod instances for the underlying + OpenTelemetry Collector. Set this if your are not using autoscaling + format: int32 + type: integer + resources: + description: Resources to set on the OpenTelemetry Collector pods. + properties: + claims: + description: "Claims lists the names of resources, defined in + spec.resourceClaims, that are used by this container. \n This + is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims + of the Pod where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + securityContext: + description: SecurityContext configures the container security context + for the opentelemetry-collector container. + properties: + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a process + can gain more privileges than its parent process. This bool + directly controls if the no_new_privs flag will be set on the + container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container + runtime. Note that this field cannot be set when spec.os.name + is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in privileged + containers are essentially equivalent to root on the host. Defaults + to false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use for + the containers. The default is DefaultProcMount which uses the + container runtime defaults for readonly paths and masked paths. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. If + seccomp options are provided at both the pod & container level, + the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will + be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + serviceAccount: + description: ServiceAccount indicates the name of an existing service + account to use with this instance. When set, the operator will not + automatically create a ServiceAccount for the collector. + type: string + targetAllocator: + description: TargetAllocator indicates a value which determines whether + to spawn a target allocation resource or not. + properties: + affinity: + description: If specified, indicates the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector in + the specified namespaces, where co-located + is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector in + the specified namespaces, where co-located + is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object type: object - type: object - serviceAccount: - description: ServiceAccount indicates the name of an existing service - account to use with this instance. - type: string - targetAllocator: - description: TargetAllocator indicates a value which determines whether - to spawn a target allocation resource or not. - properties: allocationStrategy: description: AllocationStrategy determines which strategy the target allocator should use for allocation. The current options are least-weighted and consistent-hashing. The default option is least-weighted + enum: + - least-weighted + - consistent-hashing type: string enabled: description: Enabled indicates whether to use a target allocation mechanism for Prometheus targets or not. type: boolean + env: + description: ENV vars to set on the OpenTelemetry TargetAllocator's + Pods. These can then in certain cases be consumed in the config + file for the TargetAllocator. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + filterStrategy: + description: FilterStrategy determines how to filter targets before + allocating them among the collectors. The only current option + is relabel-config (drops targets based on prom relabel_config). + type: string image: description: Image indicates the container image to use for the OpenTelemetry TargetAllocator. type: string + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to schedule OpenTelemetry TargetAllocator + pods. + type: object prometheusCR: description: PrometheusCR defines the configuration for the retrieval of PrometheusOperator CRDs ( servicemonitor.monitoring.coreos.com/v1 - and podmonitor.monitoring.coreos.com/v1 ) retrieval. All CR - instances which the ServiceAccount has access to will be retrieved. - This includes other namespaces. + and podmonitor.monitoring.coreos.com/v1 ) retrieval. properties: enabled: description: Enabled indicates whether to use a PrometheusOperator custom resources as targets or not. type: boolean + podMonitorSelector: + additionalProperties: + type: string + description: PodMonitors to be selected for target discovery. + This is a map of {key,value} pairs. Each {key,value} in + the map is going to exactly match a label in a PodMonitor's + meta labels. + type: object + scrapeInterval: + default: 30s + description: "Interval between consecutive scrapes. Equivalent + to the same setting on the Prometheus CRD. \n Default: \"30s\"" + format: duration + type: string + serviceMonitorSelector: + additionalProperties: + type: string + description: ServiceMonitors to be selected for target discovery. + This is a map of {key,value} pairs. Each {key,value} in + the map is going to exactly match a label in a ServiceMonitor's + meta labels. + type: object type: object replicas: description: Replicas is the number of pod instances for the underlying TargetAllocator. This should only be set to a value other than 1 if a strategy that allows for high availability is chosen. - Currently, the only allocation strategy that can be run in a - high availability mode is consistent-hashing. format: int32 type: integer + resources: + description: Resources to set on the OpenTelemetryTargetAllocator + containers. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + securityContext: + description: SecurityContext configures the container security + context for the targetallocator. + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1." + format: int64 + type: integer + fsGroupChangePolicy: + description: fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers + in this pod. Note that this field cannot be set when spec.os.name + is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID, the fsGroup (if specified), and group memberships defined + in the container image for th + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. Note that this field cannot + be set when spec.os. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. + type: string + type: object + type: object serviceAccount: description: ServiceAccount indicates the name of an existing - service account to use with this instance. + service account to use with this instance. When set, the operator + will not automatically create a ServiceAccount for the TargetAllocator. type: string + tolerations: + description: Toleration embedded kubernetes pod configuration + option, controls how pods can be scheduled with matching taints + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints embedded kubernetes pod + configuration option, controls how pods are spread across your + cluster among failure-domains such as regions, zones, nodes, + and other user-defined top + items: + description: TopologySpreadConstraint specifies how to spread + matching pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine + the number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: MatchLabelKeys is a set of pod label keys to + select the pods over which spreading will be calculated. + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: MaxSkew describes the degree to which pods + may be unevenly distributed. + format: int32 + type: integer + minDomains: + description: MinDomains indicates a minimum number of eligible + domains. + format: int32 + type: integer + nodeAffinityPolicy: + description: NodeAffinityPolicy indicates how we will treat + Pod's nodeAffinity/nodeSelector when calculating pod topology + spread skew. + type: string + nodeTaintsPolicy: + description: NodeTaintsPolicy indicates how we will treat + node taints when calculating pod topology spread skew. + type: string + topologyKey: + description: TopologyKey is the key of node labels. Nodes + that have a label with this key and identical values are + considered to be in the same topology. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with + a pod if it doesn't satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array type: object + terminationGracePeriodSeconds: + description: Duration in seconds the pod needs to terminate gracefully + upon probe failure. + format: int64 + type: integer tolerations: description: Toleration to schedule OpenTelemetry Collector pods. This is only relevant to daemonset, statefulset, and deployment @@ -923,16 +5339,11 @@ spec: operator: description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. - Exists is equivalent to wildcard for value, so that a pod - can tolerate all taints of a particular category. type: string tolerationSeconds: description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise - this field is ignored) tolerates the taint. By default, it - is not set, which means tolerate the taint forever (do not - evict). Zero and negative values will be treated as 0 (evict - immediately) by the system. + this field is ignored) tolerates the taint. format: int64 type: integer value: @@ -942,6 +5353,128 @@ spec: type: string type: object type: array + topologySpreadConstraints: + description: TopologySpreadConstraints embedded kubernetes pod configuration + option, controls how pods are spread across your cluster among failure-domains + such as regions, zones, nodes, and other user-defined top + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: MatchLabelKeys is a set of pod label keys to select + the pods over which spreading will be calculated. + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: MaxSkew describes the degree to which pods may + be unevenly distributed. + format: int32 + type: integer + minDomains: + description: MinDomains indicates a minimum number of eligible + domains. + format: int32 + type: integer + nodeAffinityPolicy: + description: NodeAffinityPolicy indicates how we will treat + Pod's nodeAffinity/nodeSelector when calculating pod topology + spread skew. + type: string + nodeTaintsPolicy: + description: NodeTaintsPolicy indicates how we will treat node + taints when calculating pod topology spread skew. + type: string + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with a + pod if it doesn't satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + updateStrategy: + description: UpdateStrategy represents the strategy the operator will + take replacing existing DaemonSet pods with new pods https://kubernetes. + properties: + rollingUpdate: + description: 'Rolling update config params. Present only if type + = "RollingUpdate". --- TODO: Update this to follow our convention + for oneOf, whatever we decide it to be. Same as Deployment `strategy.' + properties: + maxSurge: + anyOf: + - type: integer + - type: string + description: The maximum number of nodes with an existing + available DaemonSet pod that can have an updated DaemonSet + pod during during an update. + x-kubernetes-int-or-string: true + maxUnavailable: + anyOf: + - type: integer + - type: string + description: The maximum number of DaemonSet pods that can + be unavailable during the update. + x-kubernetes-int-or-string: true + type: object + type: + description: Type of daemon set update. Can be "RollingUpdate" + or "OnDelete". Default is RollingUpdate. + type: string + type: object upgradeStrategy: description: UpgradeStrategy represents how the operator will handle upgrades to the CR when a newer version of the operator is deployed @@ -957,16 +5490,15 @@ spec: to a persistent volume properties: apiVersion: - description: 'APIVersion defines the versioned schema of this + description: APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized - values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + values. type: string kind: - description: 'Kind is a string value representing the REST resource + description: Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' @@ -1000,13 +5532,7 @@ spec: type: array dataSource: description: 'dataSource field can be used to specify either: - * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the provisioner - or an external controller can support the specified data - source, it will create a new volume based on the contents - of the specified data source. If the AnyVolumeDataSource - feature gate is enabled, this field will always have the - same contents as the DataSourceRef field.' + * An existing VolumeSnapshot object (snapshot.storage.k8s.' properties: apiGroup: description: APIGroup is the group for the resource @@ -1026,27 +5552,9 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object from which + description: dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume - is desired. This may be any local object from a non-empty - API group (non core object) or a PersistentVolumeClaim - object. When this field is specified, volume binding will - only succeed if the type of the specified object matches - some installed volume populator or dynamic provisioner. - This field will replace the functionality of the DataSource - field and as such if both fields are non-empty, they must - have the same value. For backwards compatibility, both - fields (DataSource and DataSourceRef) will be set to the - same value automatically if one of them is empty and the - other is non-empty. There are two important differences - between DataSource and DataSourceRef: * While DataSource - only allows two specific types of objects, DataSourceRef - allows any non-core object, as well as PersistentVolumeClaim - objects. * While DataSource ignores disallowed values - (dropping them), DataSourceRef preserves all values, and - generates an error if a disallowed value is specified. - (Beta) Using this field requires the AnyVolumeDataSource - feature gate to be enabled.' + is desired. properties: apiGroup: description: APIGroup is the group for the resource @@ -1060,19 +5568,41 @@ spec: name: description: Name is the name of resource being referenced type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace is specified, + a gateway.networking.k8s. + type: string required: - kind - name type: object - x-kubernetes-map-type: atomic resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify resource - requirements that are lower than previous value but must - still be higher than capacity recorded in the status field - of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: resources represents the minimum resources + the volume should have. properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate." + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where + this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1090,11 +5620,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount - of compute resources required. If Requests is omitted - for a container, it defaults to Limits if that is - explicitly specified, otherwise to an implementation-defined - value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: Requests describes the minimum amount of + compute resources required. type: object type: object selector: @@ -1123,8 +5650,6 @@ spec: If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. - This array is replaced during a strategic merge - patch. items: type: string type: array @@ -1137,10 +5662,6 @@ spec: additionalProperties: type: string description: matchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field - is "key", the operator is "In", and the values array - contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic @@ -1168,6 +5689,18 @@ spec: items: type: string type: array + allocatedResourceStatuses: + additionalProperties: + description: When a controller receives persistentvolume + claim update with ClaimResourceStatus for a resource + that it does not recognizes, then it should ignore that + update and let other controllers handle it. + type: string + description: allocatedResourceStatuses stores status of + resource being resized for the given PVC. Key names follow + standard Kubernetes label syntax. + type: object + x-kubernetes-map-type: granular allocatedResources: additionalProperties: anyOf: @@ -1175,18 +5708,9 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: allocatedResources is the storage resource - within AllocatedResources tracks the capacity allocated - to a PVC. It may be larger than the actual capacity when - a volume expansion operation is requested. For storage - quota, the larger value from allocatedResources and PVC.spec.resources - is used. If allocatedResources is not set, PVC.spec.resources - alone is used for quota calculation. If a volume expansion - capacity request is lowered, allocatedResources is only - lowered if there are no expansion operations in progress - and if the actual volume capacity is equal or lower than - the requested capacity. This is an alpha field and requires - enabling RecoverVolumeExpansionFailure feature. + description: allocatedResources tracks the resources allocated + to a PVC including its capacity. Key names follow standard + Kubernetes label syntax. type: object capacity: additionalProperties: @@ -1203,7 +5727,7 @@ spec: volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'. items: - description: PersistentVolumeClaimCondition contails details + description: PersistentVolumeClaimCondition contains details about state of pvc properties: lastProbeTime: @@ -1223,9 +5747,7 @@ spec: reason: description: reason is a unique, this should be a short, machine understandable string that gives - the reason for condition's last transition. If it - reports "ResizeStarted" that means the underlying - persistent volume is being resized. + the reason for condition's last transition. type: string status: type: string @@ -1241,13 +5763,6 @@ spec: phase: description: phase represents the current phase of PersistentVolumeClaim. type: string - resizeStatus: - description: resizeStatus stores status of resize operation. - ResizeStatus is not set by default but when expansion - is complete resizeStatus is set to empty string by resize - controller or kubelet. This is an alpha field and requires - enabling RecoverVolumeExpansionFailure feature. - type: string type: object type: object type: array @@ -1282,10 +5797,7 @@ spec: type: string subPathExpr: description: Expanded path within the volume from which the - container's volume should be mounted. Behaves similarly to - SubPath but environment variable references $(VAR_NAME) are - expanded using the container's environment. Defaults to "" - (volume's root). SubPathExpr and SubPath are mutually exclusive. + container's volume should be mounted. type: string required: - mountPath @@ -1303,23 +5815,19 @@ spec: awsElasticBlockStore: description: 'awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + to the pod. More info: https://kubernetes.' properties: fsType: description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore - TODO: how do we prevent errors in the filesystem from - compromising the machine' + "ext4", "xfs", "ntfs".' type: string partition: description: 'partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property empty).' + the partition as "1".' format: int32 type: integer readOnly: @@ -1359,7 +5867,7 @@ spec: description: 'kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data - disk (only in managed availability set). defaults to shared' + disk (only in managed availability set).' type: string readOnly: description: readOnly Defaults to false (read/write). ReadOnly @@ -1438,7 +5946,7 @@ spec: description: 'fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to - be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + be "ext4" if unspecified.' type: string readOnly: description: 'readOnly defaults to false (read/write). ReadOnly @@ -1470,24 +5978,14 @@ spec: description: 'defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. Defaults to - 0644. Directories within the path are not affected by - this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set.' + 0 and 511.' format: int32 type: integer items: description: items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content - is the value. If specified, the listed keys will be projected - into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in - the ConfigMap, the volume setup will error unless it is - marked optional. Paths must be relative and may not contain - the '..' path or start with '..'. + is the value. items: description: Maps a string key to a path within a volume. properties: @@ -1498,12 +5996,7 @@ spec: description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. If not - specified, the volume defaultMode will be used. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' + 0 and 511.' format: int32 type: integer path: @@ -1547,10 +6040,7 @@ spec: description: nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume - and NodeUnpublishVolume calls. This field is optional, - and may be empty if no secret is required. If the secret - object contains more than one secret, all secret references - are passed. + and NodeUnpublishVolume calls. properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names @@ -1579,14 +6069,7 @@ spec: defaultMode: description: 'Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set - permissions on created files by default. Must be an octal - value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. Defaults to - 0644. Directories within the path are not affected by - this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set.' + permissions on created files by default.' format: int32 type: integer items: @@ -1615,13 +6098,7 @@ spec: mode: description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 - and 0777 or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON requires - decimal values for mode bits. If not specified, - the volume defaultMode will be used. This might - be in conflict with other options that affect the - file mode, like fsGroup, and the result can be other - mode bits set.' + and 0777 or a decimal value between 0 and 511.' format: int32 type: integer path: @@ -1666,64 +6143,29 @@ spec: shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' properties: medium: - description: 'medium represents what type of storage medium + description: medium represents what type of storage medium should back this directory. The default is "" which means - to use the node''s default medium. Must be an empty string - (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + to use the node's default medium. Must be an empty string + (default) or Memory. type: string sizeLimit: anyOf: - type: integer - type: string - description: 'sizeLimit is the total amount of local storage + description: sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also - applicable for memory medium. The maximum usage on memory - medium EmptyDir would be the minimum value between the - SizeLimit specified here and the sum of memory limits - of all containers in a pod. The default is nil which means - that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + applicable for memory medium. pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object ephemeral: - description: "ephemeral represents a volume that is handled - by a cluster storage driver. The volume's lifecycle is tied - to the pod that defines it - it will be created before the - pod starts, and deleted when the pod is removed. \n Use this - if: a) the volume is only needed while the pod runs, b) features - of normal volumes like restoring from snapshot or capacity - tracking are needed, c) the storage driver is specified through - a storage class, and d) the storage driver supports dynamic - volume provisioning through a PersistentVolumeClaim (see EphemeralVolumeSource - for more information on the connection between this volume - type and PersistentVolumeClaim). \n Use PersistentVolumeClaim - or one of the vendor-specific APIs for volumes that persist - for longer than the lifecycle of an individual pod. \n Use - CSI for light-weight local ephemeral volumes if the CSI driver - is meant to be used that way - see the documentation of the - driver for more information. \n A pod can use both types of - ephemeral volumes and persistent volumes at the same time." + description: ephemeral represents a volume that is handled by + a cluster storage driver. properties: volumeClaimTemplate: - description: "Will be used to create a stand-alone PVC to + description: Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource - is embedded will be the owner of the PVC, i.e. the PVC - will be deleted together with the pod. The name of the - PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. - Pod validation will reject the pod if the concatenated - name is not valid for a PVC (for example, too long). \n - An existing PVC with that name that is not owned by the - pod will *not* be used for the pod to avoid using an unrelated - volume by mistake. Starting the pod is then blocked until - the unrelated PVC is removed. If such a pre-created PVC - is meant to be used by the pod, the PVC has to updated - with an owner reference to the pod once the pod exists. - Normally this should not be necessary, but it may be useful - when manually reconstructing a broken cluster. \n This - field is read-only and no changes will be made by Kubernetes - to the PVC after it has been created. \n Required, must - not be nil." + is embedded will be the owner of the PVC, i.e. properties: metadata: description: May contain labels and annotations that @@ -1750,8 +6192,7 @@ spec: spec: description: The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC - that gets created from this template. The same fields - as in a PersistentVolumeClaim are also valid here. + that gets created from this template. properties: accessModes: description: 'accessModes contains the desired access @@ -1761,14 +6202,7 @@ spec: type: array dataSource: description: 'dataSource field can be used to specify - either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) - * An existing PVC (PersistentVolumeClaim) If the - provisioner or an external controller can support - the specified data source, it will create a new - volume based on the contents of the specified - data source. If the AnyVolumeDataSource feature - gate is enabled, this field will always have the - same contents as the DataSourceRef field.' + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.' properties: apiGroup: description: APIGroup is the group for the resource @@ -1791,30 +6225,9 @@ spec: type: object x-kubernetes-map-type: atomic dataSourceRef: - description: 'dataSourceRef specifies the object + description: dataSourceRef specifies the object from which to populate the volume with data, if - a non-empty volume is desired. This may be any - local object from a non-empty API group (non core - object) or a PersistentVolumeClaim object. When - this field is specified, volume binding will only - succeed if the type of the specified object matches - some installed volume populator or dynamic provisioner. - This field will replace the functionality of the - DataSource field and as such if both fields are - non-empty, they must have the same value. For - backwards compatibility, both fields (DataSource - and DataSourceRef) will be set to the same value - automatically if one of them is empty and the - other is non-empty. There are two important differences - between DataSource and DataSourceRef: * While - DataSource only allows two specific types of objects, - DataSourceRef allows any non-core object, as well - as PersistentVolumeClaim objects. * While DataSource - ignores disallowed values (dropping them), DataSourceRef - preserves all values, and generates an error if - a disallowed value is specified. (Beta) Using - this field requires the AnyVolumeDataSource feature - gate to be enabled.' + a non-empty volume is desired. properties: apiGroup: description: APIGroup is the group for the resource @@ -1831,19 +6244,43 @@ spec: description: Name is the name of resource being referenced type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace + is specified, a gateway.networking.k8s. + type: string required: - kind - name type: object - x-kubernetes-map-type: atomic resources: - description: 'resources represents the minimum resources - the volume should have. If RecoverVolumeExpansionFailure - feature is enabled users are allowed to specify - resource requirements that are lower than previous - value but must still be higher than capacity recorded - in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + description: resources represents the minimum resources + the volume should have. properties: + claims: + description: "Claims lists the names of resources, + defined in spec.resourceClaims, that are used + by this container. \n This is an alpha field + and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one + entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name + of one entry in pod.spec.resourceClaims + of the Pod where this field is used. + It makes that resource available inside + a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1861,12 +6298,8 @@ spec: - type: string pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true - description: 'Requests describes the minimum - amount of compute resources required. If Requests - is omitted for a container, it defaults to - Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: - https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + description: Requests describes the minimum + amount of compute resources required. type: object type: object selector: @@ -1898,9 +6331,7 @@ spec: values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. + the values array must be empty. items: type: string type: array @@ -1913,11 +6344,7 @@ spec: additionalProperties: type: string description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. + pairs. type: object type: object x-kubernetes-map-type: atomic @@ -1946,11 +6373,10 @@ spec: pod. properties: fsType: - description: 'fsType is the filesystem type to mount. Must + description: fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. TODO: how do we prevent errors in the - filesystem from compromising the machine' + if unspecified. type: string lun: description: 'lun is Optional: FC target lun number' @@ -2002,9 +6428,7 @@ spec: description: 'secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no - secret object is specified. If the secret object contains - more than one secret, all secrets are passed to the plugin - scripts.' + secret object is specified.' properties: name: description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names @@ -2033,24 +6457,19 @@ spec: gcePersistentDisk: description: 'gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet''s host machine and then exposed - to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + to the pod. More info: https://kubernetes.' properties: fsType: description: 'fsType is filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", - "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk - TODO: how do we prevent errors in the filesystem from - compromising the machine' + "xfs", "ntfs".' type: string partition: description: 'partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify - the partition as "1". Similarly, the volume partition - for /dev/sda is "0" (or you can leave the property empty). - More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + the partition as "1".' format: int32 type: integer pdName: @@ -2066,17 +6485,12 @@ spec: type: object gitRepo: description: 'gitRepo represents a git repository at a particular - revision. DEPRECATED: GitRepo is deprecated. To provision - a container with a git repo, mount an EmptyDir into an InitContainer - that clones the repo using git, then mount the EmptyDir into - the Pod''s container.' + revision. DEPRECATED: GitRepo is deprecated.' properties: directory: description: directory is the target directory name. Must not contain or start with '..'. If '.' is supplied, the - volume directory will be the git repository. Otherwise, - if specified, the volume will contain the git repository - in the subdirectory with the given name. + volume directory will be the git repository. type: string repository: description: repository is the URL @@ -2110,13 +6524,8 @@ spec: - path type: object hostPath: - description: 'hostPath represents a pre-existing file or directory + description: hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. - This is generally used for system agents or other privileged - things that are allowed to see the host machine. Most containers - will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath - --- TODO(jonesdl) We need to restrict who can use host directory - mounts and who can/can not mount host directories as read/write.' properties: path: description: 'path of the directory on the host. If the @@ -2147,16 +6556,11 @@ spec: description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi - TODO: how do we prevent errors in the filesystem from - compromising the machine' + "ext4", "xfs", "ntfs".' type: string initiatorName: description: initiatorName is the custom iSCSI Initiator - Name. If initiatorName is specified with iscsiInterface - simultaneously, new iSCSI interface : will be created for the connection. + Name. type: string iqn: description: iqn is the target iSCSI Qualified Name. @@ -2228,7 +6632,7 @@ spec: persistentVolumeClaim: description: 'persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + More info: https://kubernetes.' properties: claimName: description: 'claimName is the name of a PersistentVolumeClaim @@ -2286,12 +6690,7 @@ spec: defaultMode: description: defaultMode are the mode bits used to set permissions on created files by default. Must be an octal value between - 0000 and 0777 or a decimal value between 0 and 511. YAML - accepts both octal and decimal values, JSON requires decimal - values for mode bits. Directories within the path are - not affected by this setting. This might be in conflict - with other options that affect the file mode, like fsGroup, - and the result can be other mode bits set. + 0000 and 0777 or a decimal value between 0 and 511. format: int32 type: integer sources: @@ -2309,13 +6708,6 @@ spec: pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. - If specified, the listed keys will be projected - into the specified paths, and unlisted keys - will not be present. If a key is specified which - is not present in the ConfigMap, the volume - setup will error unless it is marked optional. - Paths must be relative and may not contain the - '..' path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -2328,13 +6720,7 @@ spec: used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and - 511. YAML accepts both octal and decimal - values, JSON requires decimal values for - mode bits. If not specified, the volume - defaultMode will be used. This might be - in conflict with other options that affect - the file mode, like fsGroup, and the result - can be other mode bits set.' + 511.' format: int32 type: integer path: @@ -2395,14 +6781,7 @@ spec: description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or - a decimal value between 0 and 511. YAML - accepts both octal and decimal values, - JSON requires decimal values for mode - bits. If not specified, the volume defaultMode - will be used. This might be in conflict - with other options that affect the file - mode, like fsGroup, and the result can - be other mode bits set.' + a decimal value between 0 and 511.' format: int32 type: integer path: @@ -2454,13 +6833,6 @@ spec: pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. - If specified, the listed keys will be projected - into the specified paths, and unlisted keys - will not be present. If a key is specified which - is not present in the Secret, the volume setup - will error unless it is marked optional. Paths - must be relative and may not contain the '..' - path or start with '..'. items: description: Maps a string key to a path within a volume. @@ -2473,13 +6845,7 @@ spec: used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and - 511. YAML accepts both octal and decimal - values, JSON requires decimal values for - mode bits. If not specified, the volume - defaultMode will be used. This might be - in conflict with other options that affect - the file mode, like fsGroup, and the result - can be other mode bits set.' + 511.' format: int32 type: integer path: @@ -2515,19 +6881,14 @@ spec: of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the - token. The audience defaults to the identifier - of the apiserver. + token. type: string expirationSeconds: description: expirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate - the service account token. The kubelet will - start trying to rotate the token if the token - is older than 80 percent of its time to live - or if the token is older than 24 hours.Defaults - to 1 hour and must be at least 10 minutes. + the service account token. format: int64 type: integer path: @@ -2585,10 +6946,7 @@ spec: description: 'fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: - "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" - if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd - TODO: how do we prevent errors in the filesystem from - compromising the machine' + "ext4", "xfs", "ntfs".' type: string image: description: 'image is the rados image name. More info: @@ -2698,24 +7056,14 @@ spec: description: 'defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. Defaults to - 0644. Directories within the path are not affected by - this setting. This might be in conflict with other options - that affect the file mode, like fsGroup, and the result - can be other mode bits set.' + 0 and 511.' format: int32 type: integer items: description: items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content - is the value. If specified, the listed keys will be projected - into the specified paths, and unlisted keys will not be - present. If a key is specified which is not present in - the Secret, the volume setup will error unless it is marked - optional. Paths must be relative and may not contain the - '..' path or start with '..'. + is the value. items: description: Maps a string key to a path within a volume. properties: @@ -2726,12 +7074,7 @@ spec: description: 'mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between - 0 and 511. YAML accepts both octal and decimal values, - JSON requires decimal values for mode bits. If not - specified, the volume defaultMode will be used. - This might be in conflict with other options that - affect the file mode, like fsGroup, and the result - can be other mode bits set.' + 0 and 511.' format: int32 type: integer path: @@ -2787,12 +7130,7 @@ spec: volumeNamespace: description: volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified - then the Pod's namespace will be used. This allows the - Kubernetes name scoping to be mirrored within StorageOS - for tighter integration. Set VolumeName to any name to - override the default behaviour. Set to "default" if you - are not using namespaces within StorageOS. Namespaces - that do not pre-exist within StorageOS will be created. + then the Pod's namespace will be used. type: string type: object vsphereVolume: @@ -2830,6 +7168,10 @@ spec: description: OpenTelemetryCollectorStatus defines the observed state of OpenTelemetryCollector. properties: + image: + description: Image indicates the container image to use for the OpenTelemetry + Collector. + type: string messages: description: 'Messages about actions performed by the operator on this resource. Deprecated: use Kubernetes events instead.' @@ -2856,6 +7198,12 @@ spec: description: The selector used to match the OpenTelemetryCollector's deployment or statefulSet pods. type: string + statusReplicas: + description: StatusReplicas is the number of pods targeted by + this OpenTelemetryCollector's with a Ready Condition / Total + number of non-terminated pods targeted by this OpenTelemetryCollector's + (their labels matc + type: string type: object version: description: Version of the managed OpenTelemetry Collector (operand) diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 09cc1462e5..c8ddc32df9 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -4,16 +4,21 @@ resources: - bases/opentelemetry.io_opentelemetrycollectors.yaml - bases/opentelemetry.io_instrumentations.yaml +- bases/opentelemetry.io_opampbridges.yaml # +kubebuilder:scaffold:crdkustomizeresource -patchesStrategicMerge: # patches here are for enabling the conversion webhook for each CRD # +kubebuilder:scaffold:crdkustomizewebhookpatch # patches here are for enabling the CA injection for each CRD +patchesStrategicMerge: +# - patches/webhook_in_opampbridges.yaml - patches/cainjection_in_opentelemetrycollectors.yaml +- patches/cainjection_in_opampbridges.yaml # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. configurations: - kustomizeconfig.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization diff --git a/config/crd/patches/cainjection_in_opampbridges.yaml b/config/crd/patches/cainjection_in_opampbridges.yaml new file mode 100644 index 0000000000..736bd5f170 --- /dev/null +++ b/config/crd/patches/cainjection_in_opampbridges.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: opampbridges.opentelemetry.io diff --git a/config/crd/patches/webhook_in_opampbridges.yaml b/config/crd/patches/webhook_in_opampbridges.yaml new file mode 100644 index 0000000000..8bf7a8144d --- /dev/null +++ b/config/crd/patches/webhook_in_opampbridges.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: opampbridges.opentelemetry.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index f0f06c1ff6..918d8a41a4 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -10,7 +10,7 @@ spec: spec: containers: - name: kube-rbac-proxy - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 args: - "--secure-listen-address=0.0.0.0:8443" - "--upstream=http://127.0.0.1:8080/" @@ -31,3 +31,6 @@ spec: args: - "--metrics-addr=127.0.0.1:8080" - "--enable-leader-election" + - "--zap-log-level=info" + - "--zap-time-encoding=rfc3339nano" + - "--feature-gates=+operator.autoinstrumentation.go,+operator.autoinstrumentation.nginx" diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2e6cc79764..5c5f0b84cb 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,2 +1,2 @@ resources: -- manager.yaml \ No newline at end of file +- manager.yaml diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 52c3cd62e4..e8a8e5e37d 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -44,9 +44,6 @@ spec: initialDelaySeconds: 5 periodSeconds: 10 resources: - limits: - cpu: 200m - memory: 256Mi requests: cpu: 100m memory: 64Mi diff --git a/config/manifests/bases/opentelemetry-operator.clusterserviceversion.yaml b/config/manifests/bases/opentelemetry-operator.clusterserviceversion.yaml index b36f42e8e6..006062ba80 100644 --- a/config/manifests/bases/opentelemetry-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/opentelemetry-operator.clusterserviceversion.yaml @@ -3,8 +3,8 @@ kind: ClusterServiceVersion metadata: annotations: alm-examples: '[]' - capabilities: Basic Install - categories: Logging & Tracing + capabilities: Deep Insights + categories: Logging & Tracing,Monitoring certified: "false" containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator createdAt: "2020-12-16T13:37:00+00:00" @@ -32,6 +32,24 @@ spec: name: "" version: v1 version: v1alpha1 + - description: OpAMPBridge is the Schema for the opampbridges API. + displayName: OpAMP Bridge + kind: OpAMPBridge + name: opampbridges.opentelemetry.io + resources: + - kind: ConfigMaps + name: "" + version: v1 + - kind: Deployment + name: "" + version: apps/v1 + - kind: Pod + name: "" + version: v1 + - kind: Service + name: "" + version: v1 + version: v1alpha1 - description: OpenTelemetryCollector is the Schema for the opentelemetrycollectors API. displayName: OpenTelemetry Collector @@ -56,6 +74,19 @@ spec: - kind: StatefulSets name: "" version: apps/v1 + specDescriptors: + - description: ObservabilitySpec defines how telemetry data gets handled. + displayName: Observability + path: observability + - description: Metrics defines the metrics configuration for operands. + displayName: Metrics Config + path: observability.metrics + - description: EnableMetrics specifies if ServiceMonitor or PodMonitor(for sidecar + mode) should be created for the OpenTelemetry Collector and Prometheus Exporters. + The operator.observability.prometheus feature gate must be enabled to use + this feature. + displayName: Create ServiceMonitors for OpenTelemetry Collector + path: observability.metrics.enableMetrics version: v1alpha1 description: |- OpenTelemetry is a collection of tools, APIs, and SDKs. You use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior. @@ -97,6 +128,7 @@ spec: - email: jpkroehling@redhat.com name: Juraci Paixão Kröhling maturity: alpha + minKubeVersion: 1.23.0 provider: name: OpenTelemetry Community version: 0.0.0 diff --git a/config/rbac/_opampbridge_editor_role.yaml b/config/rbac/_opampbridge_editor_role.yaml new file mode 100644 index 0000000000..d98049e3ba --- /dev/null +++ b/config/rbac/_opampbridge_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit opampbridges. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: opampbridge-editor-role +rules: +- apiGroups: + - opentelemetry.io + resources: + - opampbridges + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opentelemetry.io + resources: + - opampbridges/status + verbs: + - get diff --git a/config/rbac/_opampbridge_viewer_role.yaml b/config/rbac/_opampbridge_viewer_role.yaml new file mode 100644 index 0000000000..780c4f7f88 --- /dev/null +++ b/config/rbac/_opampbridge_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view opampbridges. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: opampbridge-viewer-role +rules: +- apiGroups: + - opentelemetry.io + resources: + - opampbridges + verbs: + - get + - list + - watch +- apiGroups: + - opentelemetry.io + resources: + - opampbridges/status + verbs: + - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 5655552356..09d018a856 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,13 +2,15 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: - "" resources: - configmaps + - pods + - serviceaccounts + - services verbs: - create - delete @@ -32,9 +34,11 @@ rules: - list - watch - apiGroups: - - "" + - apps resources: - - serviceaccounts + - daemonsets + - deployments + - statefulsets verbs: - create - delete @@ -44,21 +48,17 @@ rules: - update - watch - apiGroups: - - "" + - apps resources: - - services + - replicasets verbs: - - create - - delete - get - list - - patch - - update - watch - apiGroups: - - apps + - autoscaling resources: - - daemonsets + - horizontalpodautoscalers verbs: - create - delete @@ -68,29 +68,19 @@ rules: - update - watch - apiGroups: - - apps + - coordination.k8s.io resources: - - deployments + - leases verbs: - create - - delete - get - list - - patch - update - - watch -- apiGroups: - - apps - resources: - - replicasets - verbs: - - get - - list - - watch - apiGroups: - - apps + - monitoring.coreos.com resources: - - statefulsets + - podmonitors + - servicemonitors verbs: - create - delete @@ -100,9 +90,9 @@ rules: - update - watch - apiGroups: - - autoscaling + - networking.k8s.io resources: - - horizontalpodautoscalers + - ingresses verbs: - create - delete @@ -112,18 +102,19 @@ rules: - update - watch - apiGroups: - - coordination.k8s.io + - opentelemetry.io resources: - - leases + - instrumentations verbs: - - create - get - list + - patch - update + - watch - apiGroups: - - networking.k8s.io + - opentelemetry.io resources: - - ingresses + - opampbridges verbs: - create - delete @@ -135,20 +126,22 @@ rules: - apiGroups: - opentelemetry.io resources: - - instrumentations + - opampbridges/finalizers + verbs: + - update +- apiGroups: + - opentelemetry.io + resources: + - opampbridges/status verbs: - get - - list - patch - update - - watch - apiGroups: - opentelemetry.io resources: - opentelemetrycollectors verbs: - - create - - delete - get - list - patch @@ -170,3 +163,28 @@ rules: - get - patch - update +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - route.openshift.io + resources: + - routes + - routes/custom-host + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/config/samples/_v1alpha1_opampbridge.yaml b/config/samples/_v1alpha1_opampbridge.yaml new file mode 100644 index 0000000000..7e18e320f4 --- /dev/null +++ b/config/samples/_v1alpha1_opampbridge.yaml @@ -0,0 +1,25 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpAMPBridge +metadata: + name: opampbridge-sample +spec: + endpoint: ws://opamp-server:4320/v1/opamp + capabilities: + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRemoteConfig: true + AcceptsRestartCommand: true + ReportsEffectiveConfig: true + ReportsHealth: true + ReportsOwnLogs: true + ReportsOwnMetrics: true + ReportsOwnTraces: true + ReportsRemoteConfig: true + ReportsStatus: true + componentsAllowed: + receivers: + - otlp + processors: + - memory_limiter + exporters: + - logging diff --git a/config/samples/core_v1alpha1_opentelemetrycollector.yaml b/config/samples/core_v1alpha1_opentelemetrycollector.yaml index 2bd048caf0..b9c7a88402 100644 --- a/config/samples/core_v1alpha1_opentelemetrycollector.yaml +++ b/config/samples/core_v1alpha1_opentelemetrycollector.yaml @@ -11,10 +11,10 @@ spec: http: exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] - exporters: [logging] + exporters: [debug] diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 6bdf18d210..5bca251052 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,4 +1,6 @@ -## This file is auto-generated, do not modify ## +## Append samples you want in your CSV to this file as resources ## resources: - core_v1alpha1_opentelemetrycollector.yaml - instrumentation_v1alpha1_instrumentation.yaml +- _v1alpha1_opampbridge.yaml +#+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/sidecar.yaml b/config/samples/sidecar.yaml index 55c2ed4865..5d6ede289b 100644 --- a/config/samples/sidecar.yaml +++ b/config/samples/sidecar.yaml @@ -12,10 +12,10 @@ spec: http: exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] - exporters: [logging] + exporters: [debug] diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml index 90f7ef77db..fb742649cc 100644 --- a/config/scorecard/patches/basic.config.yaml +++ b/config/scorecard/patches/basic.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.23.0 + image: quay.io/operator-framework/scorecard-test:v1.27.0 labels: suite: basic test: basic-check-spec-test diff --git a/config/scorecard/patches/olm.config.yaml b/config/scorecard/patches/olm.config.yaml index c4153b56f1..adaaf65dea 100644 --- a/config/scorecard/patches/olm.config.yaml +++ b/config/scorecard/patches/olm.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.23.0 + image: quay.io/operator-framework/scorecard-test:v1.27.0 labels: suite: olm test: olm-bundle-validation-test @@ -14,7 +14,7 @@ entrypoint: - scorecard-test - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.23.0 + image: quay.io/operator-framework/scorecard-test:v1.27.0 labels: suite: olm test: olm-crds-have-validation-test @@ -24,7 +24,7 @@ entrypoint: - scorecard-test - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.23.0 + image: quay.io/operator-framework/scorecard-test:v1.27.0 labels: suite: olm test: olm-crds-have-resources-test diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 1fd17b825e..ac743c4765 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -2,7 +2,6 @@ apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: - creationTimestamp: null name: mutating-webhook-configuration webhooks: - admissionReviewVersions: @@ -25,6 +24,26 @@ webhooks: resources: - instrumentations sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-opentelemetry-io-v1alpha1-opampbridge + failurePolicy: Fail + name: mopampbridge.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - opampbridges + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -69,7 +88,6 @@ webhooks: apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: - creationTimestamp: null name: validating-webhook-configuration webhooks: - admissionReviewVersions: @@ -111,6 +129,45 @@ webhooks: resources: - instrumentations sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-opentelemetry-io-v1alpha1-opampbridge + failurePolicy: Fail + name: vopampbridgecreateupdate.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - opampbridges + sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-opentelemetry-io-v1alpha1-opampbridge + failurePolicy: Ignore + name: vopampbridgedelete.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - opampbridges + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/controllers/builder_test.go b/controllers/builder_test.go new file mode 100644 index 0000000000..344dc45a0e --- /dev/null +++ b/controllers/builder_test.go @@ -0,0 +1,1058 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controllers + +import ( + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +var ( + selectorLabels = map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "test.test", + } + basePolicy = corev1.ServiceInternalTrafficPolicyCluster + pathTypePrefix = networkingv1.PathTypePrefix +) + +var ( + opampbridgeSelectorLabels = map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/component": "opentelemetry-opamp-bridge", + "app.kubernetes.io/instance": "test.test", + } +) + +func TestBuildCollector(t *testing.T) { + var goodConfig = `receivers: + examplereceiver: + endpoint: "0.0.0.0:12345" +exporters: + logging: +service: + pipelines: + metrics: + receivers: [examplereceiver] + exporters: [logging] +` + one := int32(1) + type args struct { + instance v1alpha1.OpenTelemetryCollector + } + tests := []struct { + name string + args args + want []client.Object + wantErr bool + }{ + { + name: "base case", + args: args{ + instance: v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Replicas: &one, + Mode: "deployment", + Image: "test", + Config: goodConfig, + }, + }, + }, + want: []client.Object{ + &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: map[string]string{ + "opentelemetry-operator-config/sha256": "8188c85abd4aa5e9c798874b4f47d37f08d4778933154e3f7d60246510ed9dd2", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + }, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &one, + Selector: &metav1.LabelSelector{ + MatchLabels: selectorLabels, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: map[string]string{ + "opentelemetry-operator-config/sha256": "8188c85abd4aa5e9c798874b4f47d37f08d4778933154e3f7d60246510ed9dd2", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "otc-internal", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-collector", + }, + Items: []corev1.KeyToPath{ + { + Key: "collector.yaml", + Path: "collector.yaml", + }, + }, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "otc-container", + Image: "test", + Args: []string{ + "--config=/conf/collector.yaml", + }, + Ports: []corev1.ContainerPort{ + { + Name: "examplereceiver", + HostPort: 0, + ContainerPort: 12345, + }, + { + Name: "metrics", + HostPort: 0, + ContainerPort: 8888, + Protocol: "TCP", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "otc-internal", + MountPath: "/conf", + }, + }, + }, + }, + DNSPolicy: "ClusterFirst", + ServiceAccountName: "test-collector", + }, + }, + }, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: nil, + }, + Data: map[string]string{ + "collector.yaml": "receivers:\n examplereceiver:\n endpoint: \"0.0.0.0:12345\"\nexporters:\n logging:\nservice:\n pipelines:\n metrics:\n receivers: [examplereceiver]\n exporters: [logging]\n", + }, + }, + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: nil, + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: nil, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "examplereceiver", + Port: 12345, + }, + }, + Selector: selectorLabels, + InternalTrafficPolicy: &basePolicy, + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-collector-headless", + 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", + "operator.opentelemetry.io/collector-headless-service": "Exists", + }, + Annotations: map[string]string{ + "service.beta.openshift.io/serving-cert-secret-name": "test-collector-headless-tls", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "examplereceiver", + Port: 12345, + }, + }, + Selector: selectorLabels, + InternalTrafficPolicy: &basePolicy, + ClusterIP: "None", + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-collector-monitoring", + 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-monitoring", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + Annotations: nil, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "monitoring", + Port: 8888, + }, + }, + Selector: selectorLabels, + }, + }, + }, + wantErr: false, + }, + { + name: "ingress", + args: args{ + instance: v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Replicas: &one, + Mode: "deployment", + Image: "test", + Ingress: v1alpha1.Ingress{ + Type: v1alpha1.IngressTypeNginx, + Hostname: "example.com", + Annotations: map[string]string{ + "something": "true", + }, + }, + Config: goodConfig, + }, + }, + }, + want: []client.Object{ + &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: map[string]string{ + "opentelemetry-operator-config/sha256": "8188c85abd4aa5e9c798874b4f47d37f08d4778933154e3f7d60246510ed9dd2", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + }, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &one, + Selector: &metav1.LabelSelector{ + MatchLabels: selectorLabels, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: map[string]string{ + "opentelemetry-operator-config/sha256": "8188c85abd4aa5e9c798874b4f47d37f08d4778933154e3f7d60246510ed9dd2", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "otc-internal", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-collector", + }, + Items: []corev1.KeyToPath{ + { + Key: "collector.yaml", + Path: "collector.yaml", + }, + }, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "otc-container", + Image: "test", + Args: []string{ + "--config=/conf/collector.yaml", + }, + Ports: []corev1.ContainerPort{ + { + Name: "examplereceiver", + HostPort: 0, + ContainerPort: 12345, + }, + { + Name: "metrics", + HostPort: 0, + ContainerPort: 8888, + Protocol: "TCP", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "otc-internal", + MountPath: "/conf", + }, + }, + }, + }, + DNSPolicy: "ClusterFirst", + ServiceAccountName: "test-collector", + }, + }, + }, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: nil, + }, + Data: map[string]string{ + "collector.yaml": "receivers:\n examplereceiver:\n endpoint: \"0.0.0.0:12345\"\nexporters:\n logging:\nservice:\n pipelines:\n metrics:\n receivers: [examplereceiver]\n exporters: [logging]\n", + }, + }, + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: nil, + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: nil, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "examplereceiver", + Port: 12345, + }, + }, + Selector: selectorLabels, + InternalTrafficPolicy: &basePolicy, + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-collector-headless", + 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", + "operator.opentelemetry.io/collector-headless-service": "Exists", + }, + Annotations: map[string]string{ + "service.beta.openshift.io/serving-cert-secret-name": "test-collector-headless-tls", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "examplereceiver", + Port: 12345, + }, + }, + Selector: selectorLabels, + InternalTrafficPolicy: &basePolicy, + ClusterIP: "None", + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-collector-monitoring", + 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-monitoring", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + Annotations: nil, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "monitoring", + Port: 8888, + }, + }, + Selector: selectorLabels, + }, + }, + &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + Namespace: "test", + Labels: map[string]string{ + "app.kubernetes.io/instance": "test.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "test-ingress", + }, + Annotations: map[string]string{ + "something": "true", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "example.com", + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/examplereceiver", + PathType: &pathTypePrefix, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-collector", + Port: networkingv1.ServiceBackendPort{ + Name: "examplereceiver", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + wantErr: false, + }, + { + name: "specified service account case", + args: args{ + instance: v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Replicas: &one, + Mode: "deployment", + Image: "test", + Config: goodConfig, + ServiceAccount: "my-special-sa", + }, + }, + }, + want: []client.Object{ + &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: map[string]string{ + "opentelemetry-operator-config/sha256": "8188c85abd4aa5e9c798874b4f47d37f08d4778933154e3f7d60246510ed9dd2", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + }, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &one, + Selector: &metav1.LabelSelector{ + MatchLabels: selectorLabels, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: map[string]string{ + "opentelemetry-operator-config/sha256": "8188c85abd4aa5e9c798874b4f47d37f08d4778933154e3f7d60246510ed9dd2", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "otc-internal", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-collector", + }, + Items: []corev1.KeyToPath{ + { + Key: "collector.yaml", + Path: "collector.yaml", + }, + }, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "otc-container", + Image: "test", + Args: []string{ + "--config=/conf/collector.yaml", + }, + Ports: []corev1.ContainerPort{ + { + Name: "examplereceiver", + HostPort: 0, + ContainerPort: 12345, + }, + { + Name: "metrics", + HostPort: 0, + ContainerPort: 8888, + Protocol: "TCP", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "otc-internal", + MountPath: "/conf", + }, + }, + }, + }, + DNSPolicy: "ClusterFirst", + ServiceAccountName: "my-special-sa", + }, + }, + }, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: nil, + }, + Data: map[string]string{ + "collector.yaml": "receivers:\n examplereceiver:\n endpoint: \"0.0.0.0:12345\"\nexporters:\n logging:\nservice:\n pipelines:\n metrics:\n receivers: [examplereceiver]\n exporters: [logging]\n", + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + 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", + }, + Annotations: nil, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "examplereceiver", + Port: 12345, + }, + }, + Selector: selectorLabels, + InternalTrafficPolicy: &basePolicy, + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-collector-headless", + 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", + "operator.opentelemetry.io/collector-headless-service": "Exists", + }, + Annotations: map[string]string{ + "service.beta.openshift.io/serving-cert-secret-name": "test-collector-headless-tls", + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "examplereceiver", + Port: 12345, + }, + }, + Selector: selectorLabels, + InternalTrafficPolicy: &basePolicy, + ClusterIP: "None", + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-collector-monitoring", + 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-monitoring", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + Annotations: nil, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "monitoring", + Port: 8888, + }, + }, + Selector: selectorLabels, + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := config.New( + config.WithCollectorImage("default-collector"), + config.WithTargetAllocatorImage("default-ta-allocator"), + ) + params := manifests.Params{ + Log: logr.Discard(), + Config: cfg, + OtelCol: tt.args.instance, + } + got, err := BuildCollector(params) + if (err != nil) != tt.wantErr { + t.Errorf("BuildAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + require.Equal(t, tt.want, got) + + }) + } +} + +func TestBuildAll_OpAMPBridge(t *testing.T) { + one := int32(1) + type args struct { + instance v1alpha1.OpAMPBridge + } + tests := []struct { + name string + args args + want []client.Object + wantErr bool + }{ + { + name: "base case", + args: args{ + instance: v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "test", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + Replicas: &one, + Image: "test", + Endpoint: "ws://opamp-server:4320/v1/opamp", + Capabilities: map[v1alpha1.OpAMPBridgeCapability]bool{ + v1alpha1.OpAMPBridgeCapabilityReportsStatus: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsRemoteConfig: true, + v1alpha1.OpAMPBridgeCapabilityReportsEffectiveConfig: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnTraces: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnMetrics: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnLogs: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsOtherConnectionSettings: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsRestartCommand: true, + v1alpha1.OpAMPBridgeCapabilityReportsHealth: true, + v1alpha1.OpAMPBridgeCapabilityReportsRemoteConfig: true, + }, + ComponentsAllowed: map[string][]string{"receivers": {"otlp"}, "processors": {"memory_limiter"}, "exporters": {"logging"}}, + }, + }, + }, + want: []client.Object{ + &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-opamp-bridge", + Namespace: "test", + Labels: map[string]string{ + "app.kubernetes.io/component": "opentelemetry-opamp-bridge", + "app.kubernetes.io/instance": "test.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "test-opamp-bridge", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &one, + Selector: &metav1.LabelSelector{ + MatchLabels: opampbridgeSelectorLabels, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app.kubernetes.io/component": "opentelemetry-opamp-bridge", + "app.kubernetes.io/instance": "test.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "test-opamp-bridge", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "opamp-bridge-internal", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-opamp-bridge", + }, + Items: []corev1.KeyToPath{ + { + Key: "remoteconfiguration.yaml", + Path: "remoteconfiguration.yaml", + }, + }, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "opamp-bridge-container", + Image: "test", + Env: []corev1.EnvVar{ + { + Name: "OTELCOL_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "opamp-bridge-internal", + MountPath: "/conf", + }, + }, + }, + }, + DNSPolicy: "ClusterFirst", + ServiceAccountName: "test-opamp-bridge", + }, + }, + }, + }, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-opamp-bridge", + Namespace: "test", + Labels: map[string]string{ + "app.kubernetes.io/component": "opentelemetry-opamp-bridge", + "app.kubernetes.io/instance": "test.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "test-opamp-bridge", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + Annotations: nil, + }, + Data: map[string]string{ + "remoteconfiguration.yaml": `capabilities: + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRemoteConfig: true + AcceptsRestartCommand: true + ReportsEffectiveConfig: true + ReportsHealth: true + ReportsOwnLogs: true + ReportsOwnMetrics: true + ReportsOwnTraces: true + ReportsRemoteConfig: true + ReportsStatus: true +componentsAllowed: + exporters: + - logging + processors: + - memory_limiter + receivers: + - otlp +endpoint: ws://opamp-server:4320/v1/opamp +`}, + }, + &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-opamp-bridge", + Namespace: "test", + Labels: map[string]string{ + "app.kubernetes.io/component": "opentelemetry-opamp-bridge", + "app.kubernetes.io/instance": "test.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "test-opamp-bridge", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + Annotations: nil, + }, + }, + &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-opamp-bridge", + Namespace: "test", + Labels: map[string]string{ + "app.kubernetes.io/component": "opentelemetry-opamp-bridge", + "app.kubernetes.io/instance": "test.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "test-opamp-bridge", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + }, + Annotations: nil, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: "opamp-bridge", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + Selector: opampbridgeSelectorLabels, + }, + }, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := config.New( + config.WithOperatorOpAMPBridgeImage("default-collector"), + config.WithTargetAllocatorImage("default-ta-allocator"), + config.WithOperatorOpAMPBridgeImage("default-opamp-bridge"), + ) + reconciler := NewOpAMPBridgeReconciler(OpAMPBridgeReconcilerParams{ + Log: logr.Discard(), + Config: cfg, + }) + params := reconciler.getParams(tt.args.instance) + got, err := BuildOpAMPBridge(params) + if (err != nil) != tt.wantErr { + t.Errorf("BuildAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + require.Equal(t, tt.want, got) + }) + } +} diff --git a/controllers/common.go b/controllers/common.go new file mode 100644 index 0000000000..2a7ff1d751 --- /dev/null +++ b/controllers/common.go @@ -0,0 +1,124 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controllers + +import ( + "context" + "errors" + "fmt" + + "github.com/go-logr/logr" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/util/retry" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/opampbridge" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator" +) + +func isNamespaceScoped(obj client.Object) bool { + switch obj.(type) { + case *rbacv1.ClusterRole, *rbacv1.ClusterRoleBinding: + return false + default: + return true + } +} + +// BuildCollector returns the generation and collected errors of all manifests for a given instance. +func BuildCollector(params manifests.Params) ([]client.Object, error) { + builders := []manifests.Builder{ + collector.Build, + targetallocator.Build, + } + var resources []client.Object + for _, builder := range builders { + objs, err := builder(params) + if err != nil { + return nil, err + } + resources = append(resources, objs...) + } + return resources, nil +} + +// BuildOpAMPBridge returns the generation and collected errors of all manifests for a given instance. +func BuildOpAMPBridge(params manifests.Params) ([]client.Object, error) { + builders := []manifests.Builder{ + opampbridge.Build, + } + var resources []client.Object + for _, builder := range builders { + objs, err := builder(params) + if err != nil { + return nil, err + } + resources = append(resources, objs...) + } + return resources, nil +} + +// reconcileDesiredObjects runs the reconcile process using the mutateFn over the given list of objects. +func reconcileDesiredObjects(ctx context.Context, kubeClient client.Client, logger logr.Logger, owner metav1.Object, scheme *runtime.Scheme, desiredObjects ...client.Object) error { + var errs []error + for _, desired := range desiredObjects { + l := logger.WithValues( + "object_name", desired.GetName(), + "object_kind", desired.GetObjectKind(), + ) + if isNamespaceScoped(desired) { + if setErr := ctrl.SetControllerReference(owner, desired, scheme); setErr != nil { + l.Error(setErr, "failed to set controller owner reference to desired") + errs = append(errs, setErr) + continue + } + } + + // existing is an object the controller runtime will hydrate for us + // we obtain the existing object by deep copying the desired object because it's the most convenient way + existing := desired.DeepCopyObject().(client.Object) + mutateFn := manifests.MutateFuncFor(existing, desired) + var op controllerutil.OperationResult + crudErr := retry.RetryOnConflict(retry.DefaultRetry, func() error { + result, createOrUpdateErr := ctrl.CreateOrUpdate(ctx, kubeClient, existing, mutateFn) + op = result + return createOrUpdateErr + }) + if crudErr != nil && errors.Is(crudErr, manifests.ImmutableChangeErr) { + l.Error(crudErr, "detected immutable field change, trying to delete, new object will be created on next reconcile", "existing", existing.GetName()) + delErr := kubeClient.Delete(ctx, existing) + if delErr != nil { + return delErr + } + continue + } else if crudErr != nil { + l.Error(crudErr, "failed to configure desired") + errs = append(errs, crudErr) + continue + } + + l.V(1).Info(fmt.Sprintf("desired has been %s", op)) + } + if len(errs) > 0 { + return fmt.Errorf("failed to create objects for %s: %w", owner.GetName(), errors.Join(errs...)) + } + return nil +} diff --git a/controllers/opampbridge_controller.go b/controllers/opampbridge_controller.go new file mode 100644 index 0000000000..670418ad5c --- /dev/null +++ b/controllers/opampbridge_controller.go @@ -0,0 +1,119 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controllers + +import ( + "context" + + "github.com/go-logr/logr" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + opampbridgeStatus "github.com/open-telemetry/opentelemetry-operator/internal/status/opampbridge" +) + +// OpAMPBridgeReconciler reconciles a OpAMPBridge object. +type OpAMPBridgeReconciler struct { + client.Client + scheme *runtime.Scheme + log logr.Logger + recorder record.EventRecorder + config config.Config +} + +// OpAMPBridgeReconcilerParams is the set of options to build a new OpAMPBridgeReconciler. +type OpAMPBridgeReconcilerParams struct { + client.Client + Recorder record.EventRecorder + Scheme *runtime.Scheme + Log logr.Logger + Config config.Config +} + +func (r *OpAMPBridgeReconciler) getParams(instance v1alpha1.OpAMPBridge) manifests.Params { + return manifests.Params{ + Config: r.config, + Client: r.Client, + OpAMPBridge: instance, + Log: r.log, + Scheme: r.scheme, + Recorder: r.recorder, + } +} + +func NewOpAMPBridgeReconciler(params OpAMPBridgeReconcilerParams) *OpAMPBridgeReconciler { + reconciler := &OpAMPBridgeReconciler{ + Client: params.Client, + scheme: params.Scheme, + log: params.Log, + recorder: params.Recorder, + config: params.Config, + } + return reconciler +} + +//+kubebuilder:rbac:groups=opentelemetry.io,resources=opampbridges,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=opentelemetry.io,resources=opampbridges/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=opentelemetry.io,resources=opampbridges/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.11.2/pkg/reconcile +func (r *OpAMPBridgeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.log.WithValues("opamp-bridge", req.NamespacedName) + var instance v1alpha1.OpAMPBridge + if err := r.Client.Get(ctx, req.NamespacedName, &instance); err != nil { + if !apierrors.IsNotFound(err) { + log.Error(err, "unable to fetch OpAMPBridge") + } + // we'll ignore not-found errors, since they can't be fixed by an immediate + // requeue (we'll need to wait for a new notification), and we can get them + // on deleted requests. + return ctrl.Result{}, client.IgnoreNotFound(err) + } + // We have a deletion, short circuit and let the deletion happen + if deletionTimestamp := instance.GetDeletionTimestamp(); deletionTimestamp != nil { + return ctrl.Result{}, nil + } + + params := r.getParams(instance) + + desiredObjects, buildErr := BuildOpAMPBridge(params) + if buildErr != nil { + return ctrl.Result{}, buildErr + } + err := reconcileDesiredObjects(ctx, r.Client, log, ¶ms.OpAMPBridge, params.Scheme, desiredObjects...) + return opampbridgeStatus.HandleReconcileStatus(ctx, log, params, err) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *OpAMPBridgeReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&v1alpha1.OpAMPBridge{}). + Owns(&corev1.ConfigMap{}). + Owns(&corev1.ServiceAccount{}). + Owns(&corev1.Service{}). + Owns(&appsv1.Deployment{}). + Complete(r) +} diff --git a/controllers/opampbridge_controller_test.go b/controllers/opampbridge_controller_test.go new file mode 100644 index 0000000000..127fddc149 --- /dev/null +++ b/controllers/opampbridge_controller_test.go @@ -0,0 +1,173 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controllers_test + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + "k8s.io/kubectl/pkg/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + k8sconfig "sigs.k8s.io/controller-runtime/pkg/client/config" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/manager" + k8sreconcile "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/controllers" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" + "github.com/open-telemetry/opentelemetry-operator/internal/config" +) + +var opampBridgeLogger = logf.Log.WithName("opamp-bridge-controller-unit-tests") +var opampBridgeMockAutoDetector = &mockAutoDetect{ + OpenShiftRoutesAvailabilityFunc: func() (openshift.RoutesAvailability, error) { + return openshift.RoutesAvailable, nil + }, +} + +func TestNewObjectsOnReconciliation_OpAMPBridge(t *testing.T) { + // prepare + cfg := config.New( + config.WithOperatorOpAMPBridgeImage("default-opamp-bridge"), + config.WithAutoDetect(opampBridgeMockAutoDetector), + ) + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} + reconciler := controllers.NewOpAMPBridgeReconciler(controllers.OpAMPBridgeReconcilerParams{ + Client: k8sClient, + Log: opampBridgeLogger, + Scheme: testScheme, + Recorder: record.NewFakeRecorder(10), + Config: cfg, + }) + require.NoError(t, cfg.AutoDetect()) + created := &v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + Endpoint: "ws://opamp-server:4320/v1/opamp", + Capabilities: map[v1alpha1.OpAMPBridgeCapability]bool{ + v1alpha1.OpAMPBridgeCapabilityReportsStatus: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsRemoteConfig: true, + v1alpha1.OpAMPBridgeCapabilityReportsEffectiveConfig: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnTraces: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnMetrics: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnLogs: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsOtherConnectionSettings: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsRestartCommand: true, + v1alpha1.OpAMPBridgeCapabilityReportsHealth: true, + v1alpha1.OpAMPBridgeCapabilityReportsRemoteConfig: true, + }, + }, + } + err := k8sClient.Create(context.Background(), created) + require.NoError(t, err) + + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err = reconciler.Reconcile(context.Background(), req) + + // verify + require.NoError(t, err) + + // the base query for the underlying objects + opts := []client.ListOption{ + client.InNamespace(nsn.Namespace), + client.MatchingLabels(map[string]string{ + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", nsn.Namespace, nsn.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-opamp-bridge", + }), + } + + // verify that we have at least one object for each of the types we create + // whether we have the right ones is up to the specific tests for each type + { + list := &corev1.ConfigMapList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &corev1.ServiceAccountList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &corev1.ServiceList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &appsv1.DeploymentList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + // cleanup + require.NoError(t, k8sClient.Delete(context.Background(), created)) +} + +func TestSkipWhenInstanceDoesNotExist_OpAMPBridge(t *testing.T) { + // prepare + cfg := config.New() + nsn := types.NamespacedName{Name: "non-existing-my-instance", Namespace: "default"} + reconciler := controllers.NewOpAMPBridgeReconciler(controllers.OpAMPBridgeReconcilerParams{ + Client: k8sClient, + Log: opampBridgeLogger, + Scheme: scheme.Scheme, + Config: cfg, + }) + + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err := reconciler.Reconcile(context.Background(), req) + + // verify + assert.NoError(t, err) +} + +func TestRegisterWithManager_OpAMPBridge(t *testing.T) { + t.Skip("this test requires a real cluster, otherwise the GetConfigOrDie will die") + + // prepare + mgr, err := manager.New(k8sconfig.GetConfigOrDie(), manager.Options{}) + require.NoError(t, err) + + reconciler := controllers.NewReconciler(controllers.Params{}) + + // test + err = reconciler.SetupWithManager(mgr) + + // verify + assert.NoError(t, err) +} diff --git a/controllers/opentelemetrycollector_controller.go b/controllers/opentelemetrycollector_controller.go index 11483b6fb9..0cca79c1d6 100644 --- a/controllers/opentelemetrycollector_controller.go +++ b/controllers/opentelemetrycollector_controller.go @@ -17,13 +17,13 @@ package controllers import ( "context" - "fmt" "github.com/go-logr/logr" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" autoscalingv2 "k8s.io/api/autoscaling/v2" - autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" corev1 "k8s.io/api/core/v1" + policyV1 "k8s.io/api/policy/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" @@ -32,8 +32,9 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/reconcile" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + collectorStatus "github.com/open-telemetry/opentelemetry-operator/internal/status/collector" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" ) // OpenTelemetryCollectorReconciler reconciles a OpenTelemetryCollector object. @@ -42,95 +43,53 @@ type OpenTelemetryCollectorReconciler struct { recorder record.EventRecorder scheme *runtime.Scheme log logr.Logger - tasks []Task config config.Config } -// Task represents a reconciliation task to be executed by the reconciler. -type Task struct { - Do func(context.Context, reconcile.Params) error - Name string - BailOnError bool -} - -// Params is the set of options to build a new openTelemetryCollectorReconciler. +// Params is the set of options to build a new OpenTelemetryCollectorReconciler. type Params struct { client.Client Recorder record.EventRecorder Scheme *runtime.Scheme Log logr.Logger - Tasks []Task Config config.Config } -// NewReconciler creates a new reconciler for OpenTelemetryCollector objects. -func NewReconciler(p Params) *OpenTelemetryCollectorReconciler { - if len(p.Tasks) == 0 { - p.Tasks = []Task{ - { - reconcile.ConfigMaps, - "config maps", - true, - }, - { - reconcile.ServiceAccounts, - "service accounts", - true, - }, - { - reconcile.Services, - "services", - true, - }, - { - reconcile.Deployments, - "deployments", - true, - }, - { - reconcile.HorizontalPodAutoscalers, - "horizontal pod autoscalers", - true, - }, - { - reconcile.DaemonSets, - "daemon sets", - true, - }, - { - reconcile.StatefulSets, - "stateful sets", - true, - }, - { - reconcile.Ingresses, - "ingresses", - true, - }, - { - reconcile.Self, - "opentelemetry", - true, - }, - } +func (r *OpenTelemetryCollectorReconciler) getParams(instance v1alpha1.OpenTelemetryCollector) manifests.Params { + return manifests.Params{ + Config: r.config, + Client: r.Client, + OtelCol: instance, + Log: r.log, + Scheme: r.scheme, + Recorder: r.recorder, } +} - return &OpenTelemetryCollectorReconciler{ +// NewReconciler creates a new reconciler for OpenTelemetryCollector objects. +func NewReconciler(p Params) *OpenTelemetryCollectorReconciler { + r := &OpenTelemetryCollectorReconciler{ Client: p.Client, log: p.Log, scheme: p.Scheme, config: p.Config, - tasks: p.Tasks, recorder: p.Recorder, } + return r } -// +kubebuilder:rbac:groups=opentelemetry.io,resources=opentelemetrycollectors,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=pods;configmaps;services;serviceaccounts,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch +// +kubebuilder:rbac:groups=apps,resources=daemonsets;deployments;statefulsets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=autoscaling,resources=horizontalpodautoscalers,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=policy,resources=poddisruptionbudgets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;create;update +// +kubebuilder:rbac:groups=monitoring.coreos.com,resources=servicemonitors;podmonitors,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=route.openshift.io,resources=routes;routes/custom-host,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=opentelemetry.io,resources=opentelemetrycollectors,verbs=get;list;watch;update;patch // +kubebuilder:rbac:groups=opentelemetry.io,resources=opentelemetrycollectors/status,verbs=get;update;patch // +kubebuilder:rbac:groups=opentelemetry.io,resources=opentelemetrycollectors/finalizers,verbs=get;update;patch -// +kubebuilder:rbac:groups=networking.k8s.io,resources=ingresses,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;create;update -// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch // Reconcile the current state of an OpenTelemetry collector resource with the desired state. func (r *OpenTelemetryCollectorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { @@ -147,43 +106,29 @@ func (r *OpenTelemetryCollectorReconciler) Reconcile(ctx context.Context, req ct // on deleted requests. return ctrl.Result{}, client.IgnoreNotFound(err) } - - params := reconcile.Params{ - Config: r.config, - Client: r.Client, - Instance: instance, - Log: log, - Scheme: r.scheme, - Recorder: r.recorder, + // We have a deletion, short circuit and let the deletion happen + if deletionTimestamp := instance.GetDeletionTimestamp(); deletionTimestamp != nil { + return ctrl.Result{}, nil } - if err := r.RunTasks(ctx, params); err != nil { - return ctrl.Result{}, err + if instance.Spec.ManagementState == v1alpha1.ManagementStateUnmanaged { + log.Info("Skipping reconciliation for unmanaged OpenTelemetryCollector resource", "name", req.String()) + // Stop requeueing for unmanaged OpenTelemetryCollector custom resources + return ctrl.Result{}, nil } - return ctrl.Result{}, nil -} + params := r.getParams(instance) -// RunTasks runs all the tasks associated with this reconciler. -func (r *OpenTelemetryCollectorReconciler) RunTasks(ctx context.Context, params reconcile.Params) error { - for _, task := range r.tasks { - if err := task.Do(ctx, params); err != nil { - r.log.Error(err, fmt.Sprintf("failed to reconcile %s", task.Name)) - if task.BailOnError { - return err - } - } + desiredObjects, buildErr := BuildCollector(params) + if buildErr != nil { + return ctrl.Result{}, buildErr } - - return nil + err := reconcileDesiredObjects(ctx, r.Client, log, ¶ms.OtelCol, params.Scheme, desiredObjects...) + return collectorStatus.HandleReconcileStatus(ctx, log, params, err) } // SetupWithManager tells the manager what our controller is interested in. func (r *OpenTelemetryCollectorReconciler) SetupWithManager(mgr ctrl.Manager) error { - err := r.config.AutoDetect() // We need to call this so we can get the correct autodetect version - if err != nil { - return err - } builder := ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.OpenTelemetryCollector{}). Owns(&corev1.ConfigMap{}). @@ -191,13 +136,13 @@ func (r *OpenTelemetryCollectorReconciler) SetupWithManager(mgr ctrl.Manager) er Owns(&corev1.Service{}). Owns(&appsv1.Deployment{}). Owns(&appsv1.DaemonSet{}). - Owns(&appsv1.StatefulSet{}) + Owns(&appsv1.StatefulSet{}). + Owns(&autoscalingv2.HorizontalPodAutoscaler{}). + Owns(&policyV1.PodDisruptionBudget{}) - autoscalingVersion := r.config.AutoscalingVersion() - if autoscalingVersion == autodetect.AutoscalingVersionV2 { - builder = builder.Owns(&autoscalingv2.HorizontalPodAutoscaler{}) - } else { - builder = builder.Owns(&autoscalingv2beta2.HorizontalPodAutoscaler{}) + if featuregate.PrometheusOperatorIsAvailable.IsEnabled() { + builder.Owns(&monitoringv1.ServiceMonitor{}) + builder.Owns(&monitoringv1.PodMonitor{}) } return builder.Complete(r) diff --git a/controllers/opentelemetrycollector_controller_test.go b/controllers/opentelemetrycollector_controller_test.go deleted file mode 100644 index b9324225b1..0000000000 --- a/controllers/opentelemetrycollector_controller_test.go +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package controllers_test - -import ( - "context" - "errors" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/kubectl/pkg/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - k8sconfig "sigs.k8s.io/controller-runtime/pkg/client/config" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/manager" - k8sreconcile "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/controllers" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/reconcile" - "github.com/open-telemetry/opentelemetry-operator/pkg/platform" -) - -var logger = logf.Log.WithName("unit-tests") -var mockAutoDetector = &mockAutoDetect{ - HPAVersionFunc: func() (autodetect.AutoscalingVersion, error) { - return autodetect.AutoscalingVersionV2Beta2, nil - }, -} - -func TestNewObjectsOnReconciliation(t *testing.T) { - // prepare - cfg := config.New(config.WithCollectorImage("default-collector"), config.WithTargetAllocatorImage("default-ta-allocator"), config.WithAutoDetect(mockAutoDetector)) - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - reconciler := controllers.NewReconciler(controllers.Params{ - Client: k8sClient, - Log: logger, - Scheme: testScheme, - Config: cfg, - }) - created := &v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsn.Name, - Namespace: nsn.Namespace, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeDeployment, - }, - } - err := k8sClient.Create(context.Background(), created) - require.NoError(t, err) - - // test - req := k8sreconcile.Request{ - NamespacedName: nsn, - } - _, err = reconciler.Reconcile(context.Background(), req) - - // verify - require.NoError(t, err) - - // the base query for the underlying objects - opts := []client.ListOption{ - client.InNamespace(nsn.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", nsn.Namespace, nsn.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - - // verify that we have at least one object for each of the types we create - // whether we have the right ones is up to the specific tests for each type - { - list := &corev1.ConfigMapList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - assert.NotEmpty(t, list.Items) - } - { - list := &corev1.ServiceAccountList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - assert.NotEmpty(t, list.Items) - } - { - list := &corev1.ServiceList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - assert.NotEmpty(t, list.Items) - } - { - list := &appsv1.DeploymentList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - assert.NotEmpty(t, list.Items) - } - { - list := &appsv1.DaemonSetList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - // attention! we expect daemonsets to be empty in the default configuration - assert.Empty(t, list.Items) - } - { - list := &appsv1.StatefulSetList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - // attention! we expect statefulsets to be empty in the default configuration - assert.Empty(t, list.Items) - } - - // cleanup - require.NoError(t, k8sClient.Delete(context.Background(), created)) - -} - -func TestNewStatefulSetObjectsOnReconciliation(t *testing.T) { - // prepare - cfg := config.New(config.WithAutoDetect(mockAutoDetector)) - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - reconciler := controllers.NewReconciler(controllers.Params{ - Client: k8sClient, - Log: logger, - Scheme: testScheme, - Config: cfg, - }) - created := &v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsn.Name, - Namespace: nsn.Namespace, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeStatefulSet, - }, - } - err := k8sClient.Create(context.Background(), created) - require.NoError(t, err) - - // test - req := k8sreconcile.Request{ - NamespacedName: nsn, - } - _, err = reconciler.Reconcile(context.Background(), req) - - // verify - require.NoError(t, err) - - // the base query for the underlying objects - opts := []client.ListOption{ - client.InNamespace(nsn.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", nsn.Namespace, nsn.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - - // verify that we have at least one object for each of the types we create - // whether we have the right ones is up to the specific tests for each type - { - list := &corev1.ConfigMapList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - assert.NotEmpty(t, list.Items) - } - { - list := &corev1.ServiceAccountList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - assert.NotEmpty(t, list.Items) - } - { - list := &corev1.ServiceList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - assert.NotEmpty(t, list.Items) - } - { - list := &appsv1.StatefulSetList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - assert.NotEmpty(t, list.Items) - } - { - list := &appsv1.DeploymentList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - // attention! we expect deployments to be empty when starting in the statefulset mode. - assert.Empty(t, list.Items) - } - { - list := &appsv1.DaemonSetList{} - err = k8sClient.List(context.Background(), list, opts...) - assert.NoError(t, err) - // attention! we expect daemonsets to be empty when starting in the statefulset mode. - assert.Empty(t, list.Items) - } - - // cleanup - require.NoError(t, k8sClient.Delete(context.Background(), created)) - -} - -func TestContinueOnRecoverableFailure(t *testing.T) { - // prepare - taskCalled := false - reconciler := controllers.NewReconciler(controllers.Params{ - Log: logger, - Tasks: []controllers.Task{ - { - Name: "should-fail", - Do: func(context.Context, reconcile.Params) error { - return errors.New("should fail!") - }, - BailOnError: false, - }, - { - Name: "should-be-called", - Do: func(context.Context, reconcile.Params) error { - taskCalled = true - return nil - }, - }, - }, - }) - - // test - err := reconciler.RunTasks(context.Background(), reconcile.Params{}) - - // verify - assert.NoError(t, err) - assert.True(t, taskCalled) -} - -func TestBreakOnUnrecoverableError(t *testing.T) { - // prepare - cfg := config.New() - taskCalled := false - expectedErr := errors.New("should fail!") - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - reconciler := controllers.NewReconciler(controllers.Params{ - Client: k8sClient, - Log: logger, - Scheme: scheme.Scheme, - Config: cfg, - Tasks: []controllers.Task{ - { - Name: "should-fail", - Do: func(context.Context, reconcile.Params) error { - taskCalled = true - return expectedErr - }, - BailOnError: true, - }, - { - Name: "should-not-be-called", - Do: func(context.Context, reconcile.Params) error { - assert.Fail(t, "should not have been called") - return nil - }, - }, - }, - }) - created := &v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsn.Name, - Namespace: nsn.Namespace, - }, - } - err := k8sClient.Create(context.Background(), created) - require.NoError(t, err) - - // test - req := k8sreconcile.Request{ - NamespacedName: nsn, - } - _, err = reconciler.Reconcile(context.Background(), req) - - // verify - assert.Equal(t, expectedErr, err) - assert.True(t, taskCalled) - - // cleanup - assert.NoError(t, k8sClient.Delete(context.Background(), created)) -} - -func TestSkipWhenInstanceDoesNotExist(t *testing.T) { - // prepare - cfg := config.New() - nsn := types.NamespacedName{Name: "non-existing-my-instance", Namespace: "default"} - reconciler := controllers.NewReconciler(controllers.Params{ - Client: k8sClient, - Log: logger, - Scheme: scheme.Scheme, - Config: cfg, - Tasks: []controllers.Task{ - { - Name: "should-not-be-called", - Do: func(context.Context, reconcile.Params) error { - assert.Fail(t, "should not have been called") - return nil - }, - }, - }, - }) - - // test - req := k8sreconcile.Request{ - NamespacedName: nsn, - } - _, err := reconciler.Reconcile(context.Background(), req) - - // verify - assert.NoError(t, err) -} - -func TestRegisterWithManager(t *testing.T) { - t.Skip("this test requires a real cluster, otherwise the GetConfigOrDie will die") - - // prepare - mgr, err := manager.New(k8sconfig.GetConfigOrDie(), manager.Options{}) - require.NoError(t, err) - - reconciler := controllers.NewReconciler(controllers.Params{}) - - // test - err = reconciler.SetupWithManager(mgr) - - // verify - assert.NoError(t, err) -} - -var _ autodetect.AutoDetect = (*mockAutoDetect)(nil) - -type mockAutoDetect struct { - PlatformFunc func() (platform.Platform, error) - HPAVersionFunc func() (autodetect.AutoscalingVersion, error) -} - -func (m *mockAutoDetect) HPAVersion() (autodetect.AutoscalingVersion, error) { - return m.HPAVersionFunc() -} - -func (m *mockAutoDetect) Platform() (platform.Platform, error) { - if m.PlatformFunc != nil { - return m.PlatformFunc() - } - return platform.Unknown, nil -} diff --git a/controllers/reconcile_test.go b/controllers/reconcile_test.go new file mode 100644 index 0000000000..13fe816898 --- /dev/null +++ b/controllers/reconcile_test.go @@ -0,0 +1,799 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controllers_test + +import ( + "context" + "testing" + "time" + + routev1 "github.com/openshift/api/route/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" + appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + v1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + policyV1 "k8s.io/api/policy/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + controllerruntime "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + k8sconfig "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/manager" + k8sreconcile "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/controllers" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + ta "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +const ( + baseTaImage = "something:tag" + updatedTaImage = "another:tag" + expectHostname = "something-else.com" + labelName = "something" + labelVal = "great" + annotationName = "io.opentelemetry/test" + annotationVal = "true" +) + +var ( + extraPorts = v1.ServicePort{ + Name: "port-web", + Protocol: "TCP", + Port: 8080, + TargetPort: intstr.FromInt32(8080), + } +) + +type check func(t *testing.T, params manifests.Params) + +func newParamsAssertNoErr(t *testing.T, taContainerImage string, file string) manifests.Params { + p, err := newParams(taContainerImage, file) + assert.NoError(t, err) + if len(taContainerImage) == 0 { + p.OtelCol.Spec.TargetAllocator.Enabled = false + } + return p +} + +func TestOpenTelemetryCollectorReconciler_Reconcile(t *testing.T) { + addedMetadataDeployment := paramsWithMode(v1alpha1.ModeDeployment) + addedMetadataDeployment.OtelCol.Labels = map[string]string{ + labelName: labelVal, + } + addedMetadataDeployment.OtelCol.Annotations = map[string]string{ + annotationName: annotationVal, + } + deploymentExtraPorts := paramsWithModeAndReplicas(v1alpha1.ModeDeployment, 3) + deploymentExtraPorts.OtelCol.Spec.Ports = append(deploymentExtraPorts.OtelCol.Spec.Ports, extraPorts) + ingressParams := newParamsAssertNoErr(t, "", testFileIngress) + ingressParams.OtelCol.Spec.Ingress.Type = "ingress" + updatedIngressParams := newParamsAssertNoErr(t, "", testFileIngress) + updatedIngressParams.OtelCol.Spec.Ingress.Type = "ingress" + updatedIngressParams.OtelCol.Spec.Ingress.Annotations = map[string]string{"blub": "blob"} + updatedIngressParams.OtelCol.Spec.Ingress.Hostname = expectHostname + routeParams := newParamsAssertNoErr(t, "", testFileIngress) + routeParams.OtelCol.Spec.Ingress.Type = v1alpha1.IngressTypeRoute + routeParams.OtelCol.Spec.Ingress.Route.Termination = v1alpha1.TLSRouteTerminationTypeInsecure + updatedRouteParams := newParamsAssertNoErr(t, "", testFileIngress) + updatedRouteParams.OtelCol.Spec.Ingress.Type = v1alpha1.IngressTypeRoute + updatedRouteParams.OtelCol.Spec.Ingress.Route.Termination = v1alpha1.TLSRouteTerminationTypeInsecure + updatedRouteParams.OtelCol.Spec.Ingress.Hostname = expectHostname + deletedParams := paramsWithMode(v1alpha1.ModeDeployment) + now := metav1.NewTime(time.Now()) + deletedParams.OtelCol.DeletionTimestamp = &now + + type args struct { + params manifests.Params + // an optional list of updates to supply after the initial object + updates []manifests.Params + } + type want struct { + // result check + result controllerruntime.Result + // a check to run against the current state applied + checks []check + // if an error from creation validation is expected + validateErr assert.ErrorAssertionFunc + // if an error from reconciliation is expected + wantErr assert.ErrorAssertionFunc + } + tests := []struct { + name string + args args + want []want + }{ + { + name: "deployment collector", + args: args{ + params: addedMetadataDeployment, + updates: []manifests.Params{deploymentExtraPorts}, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + d := appsv1.Deployment{} + exists, err := populateObjectIfExists(t, &d, namespacedObjectName(naming.Collector(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + assert.Equal(t, int32(2), *d.Spec.Replicas) + assert.Contains(t, d.Annotations, annotationName) + assert.Contains(t, d.Labels, labelName) + exists, err = populateObjectIfExists(t, &v1.Service{}, namespacedObjectName(naming.Service(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + exists, err = populateObjectIfExists(t, &v1.ServiceAccount{}, namespacedObjectName(naming.ServiceAccount(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + d := appsv1.Deployment{} + exists, err := populateObjectIfExists(t, &d, namespacedObjectName(naming.Collector(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + assert.Equal(t, int32(3), *d.Spec.Replicas) + // confirm that we don't remove annotations and labels even if we don't set them + assert.Contains(t, d.Annotations, annotationName) + assert.Contains(t, d.Labels, labelName) + actual := v1.Service{} + exists, err = populateObjectIfExists(t, &actual, namespacedObjectName(naming.Service(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + assert.Contains(t, actual.Spec.Ports, extraPorts) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + }, + }, + { + name: "invalid mode", + args: args{ + params: paramsWithMode("bad"), + updates: []manifests.Params{}, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{}, + wantErr: assert.NoError, + validateErr: func(t assert.TestingT, err2 error, msgAndArgs ...interface{}) bool { + return assert.ErrorContains(t, err2, "Unsupported value: \"bad\"", msgAndArgs) + }, + }, + }, + }, + { + name: "invalid prometheus configuration", + args: args{ + params: newParamsAssertNoErr(t, baseTaImage, testFileIngress), + updates: []manifests.Params{}, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{}, + wantErr: assert.NoError, + validateErr: func(t assert.TestingT, err2 error, msgAndArgs ...interface{}) bool { + return assert.ErrorContains(t, err2, "no prometheus available as part of the configuration", msgAndArgs) + }, + }, + }, + }, + { + name: "deployment collector with ingress", + args: args{ + params: ingressParams, + updates: []manifests.Params{updatedIngressParams}, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + d := networkingv1.Ingress{} + exists, err := populateObjectIfExists(t, &d, namespacedObjectName(naming.Ingress(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + d := networkingv1.Ingress{} + exists, err := populateObjectIfExists(t, &d, namespacedObjectName(naming.Ingress(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + assert.Equal(t, "something-else.com", d.Spec.Rules[0].Host) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + }, + }, + { + name: "deployment collector with routes", + args: args{ + params: routeParams, + updates: []manifests.Params{updatedRouteParams}, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + got := routev1.Route{} + nsn := types.NamespacedName{Namespace: params.OtelCol.Namespace, Name: "otlp-grpc-test-route"} + exists, err := populateObjectIfExists(t, &got, nsn) + assert.NoError(t, err) + assert.True(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + got := routev1.Route{} + nsn := types.NamespacedName{Namespace: params.OtelCol.Namespace, Name: "otlp-grpc-test-route"} + exists, err := populateObjectIfExists(t, &got, nsn) + assert.NoError(t, err) + assert.True(t, exists) + assert.Equal(t, "otlp-grpc.something-else.com", got.Spec.Host) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + }, + }, + { + name: "hpa v2 deployment collector", + args: args{ + params: paramsWithHPA(3, 5), + updates: []manifests.Params{paramsWithHPA(1, 9)}, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + actual := autoscalingv2.HorizontalPodAutoscaler{} + exists, hpaErr := populateObjectIfExists(t, &actual, namespacedObjectName(naming.HorizontalPodAutoscaler(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, hpaErr) + require.Len(t, actual.Spec.Metrics, 1) + assert.Equal(t, int32(90), *actual.Spec.Metrics[0].Resource.Target.AverageUtilization) + assert.Equal(t, int32(3), *actual.Spec.MinReplicas) + assert.Equal(t, int32(5), actual.Spec.MaxReplicas) + assert.True(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + actual := autoscalingv2.HorizontalPodAutoscaler{} + exists, hpaErr := populateObjectIfExists(t, &actual, namespacedObjectName(naming.HorizontalPodAutoscaler(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, hpaErr) + require.Len(t, actual.Spec.Metrics, 1) + assert.Equal(t, int32(90), *actual.Spec.Metrics[0].Resource.Target.AverageUtilization) + assert.Equal(t, int32(1), *actual.Spec.MinReplicas) + assert.Equal(t, int32(9), actual.Spec.MaxReplicas) + assert.True(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + }, + }, + { + name: "policy v1 deployment collector", + args: args{ + params: paramsWithPolicy(1, 0), + updates: []manifests.Params{paramsWithPolicy(0, 1)}, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + actual := policyV1.PodDisruptionBudget{} + exists, pdbErr := populateObjectIfExists(t, &actual, namespacedObjectName(naming.HorizontalPodAutoscaler(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, pdbErr) + assert.Equal(t, int32(1), actual.Spec.MinAvailable.IntVal) + assert.Nil(t, actual.Spec.MaxUnavailable) + assert.True(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + actual := policyV1.PodDisruptionBudget{} + exists, pdbErr := populateObjectIfExists(t, &actual, namespacedObjectName(naming.HorizontalPodAutoscaler(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, pdbErr) + assert.Nil(t, actual.Spec.MinAvailable) + assert.Equal(t, int32(1), actual.Spec.MaxUnavailable.IntVal) + assert.True(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + }, + }, + { + name: "daemonset collector", + args: args{ + params: paramsWithMode(v1alpha1.ModeDaemonSet), + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + exists, err := populateObjectIfExists(t, &appsv1.DaemonSet{}, namespacedObjectName(naming.Collector(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + }, + }, + { + name: "stateful should update collector with TA", + args: args{ + params: paramsWithMode(v1alpha1.ModeStatefulSet), + updates: []manifests.Params{ + newParamsAssertNoErr(t, baseTaImage, promFile), + newParamsAssertNoErr(t, baseTaImage, updatedPromFile), + newParamsAssertNoErr(t, updatedTaImage, updatedPromFile), + }, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + exists, err := populateObjectIfExists(t, &v1.ConfigMap{}, namespacedObjectName(naming.Collector(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + exists, err = populateObjectIfExists(t, &appsv1.StatefulSet{}, namespacedObjectName(naming.Collector(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + // Check the TA doesn't exist + exists, err = populateObjectIfExists(t, &v1.ConfigMap{}, namespacedObjectName(naming.TargetAllocator(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.False(t, exists) + exists, err = populateObjectIfExists(t, &appsv1.Deployment{}, namespacedObjectName(naming.TargetAllocator(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.False(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + exists, err := populateObjectIfExists(t, &v1.ConfigMap{}, namespacedObjectName(naming.Collector(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + actual := v1.ConfigMap{} + exists, err = populateObjectIfExists(t, &appsv1.Deployment{}, namespacedObjectName(naming.TargetAllocator(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + exists, err = populateObjectIfExists(t, &actual, namespacedObjectName(naming.TargetAllocator(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + exists, err = populateObjectIfExists(t, &v1.ServiceAccount{}, namespacedObjectName(naming.TargetAllocatorServiceAccount(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + + promConfig, err := ta.ConfigToPromConfig(newParamsAssertNoErr(t, baseTaImage, promFile).OtelCol.Spec.Config) + assert.NoError(t, err) + + taConfig := make(map[interface{}]interface{}) + taConfig["label_selector"] = map[string]string{ + "app.kubernetes.io/instance": "default.test", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/part-of": "opentelemetry", + } + taConfig["config"] = promConfig["config"] + taConfig["allocation_strategy"] = "least-weighted" + taConfig["prometheus_cr"] = map[string]string{ + "scrape_interval": "30s", + } + taConfigYAML, _ := yaml.Marshal(taConfig) + assert.Equal(t, string(taConfigYAML), actual.Data["targetallocator.yaml"]) + assert.NotContains(t, actual.Data["targetallocator.yaml"], "0.0.0.0:10100") + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + exists, err := populateObjectIfExists(t, &v1.ConfigMap{}, namespacedObjectName(naming.Collector(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + actual := v1.ConfigMap{} + exists, err = populateObjectIfExists(t, &appsv1.Deployment{}, namespacedObjectName(naming.TargetAllocator(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + exists, err = populateObjectIfExists(t, &actual, namespacedObjectName(naming.TargetAllocator(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + assert.Contains(t, actual.Data["targetallocator.yaml"], "0.0.0.0:10100") + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + actual := appsv1.Deployment{} + exists, err := populateObjectIfExists(t, &actual, namespacedObjectName(naming.TargetAllocator(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + assert.Equal(t, actual.Spec.Template.Spec.Containers[0].Image, updatedTaImage) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + }, + }, + { + name: "collector is being deleted", + args: args{ + params: deletedParams, + updates: []manifests.Params{}, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + o := v1alpha1.OpenTelemetryCollector{} + exists, err := populateObjectIfExists(t, &o, namespacedObjectName(naming.Collector(params.OtelCol.Name), params.OtelCol.Namespace)) + assert.NoError(t, err) + assert.False(t, exists) // There should be no collector anymore + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + testContext := context.Background() + nsn := types.NamespacedName{Name: tt.args.params.OtelCol.Name, Namespace: tt.args.params.OtelCol.Namespace} + reconciler := controllers.NewReconciler(controllers.Params{ + Client: k8sClient, + Log: logger, + Scheme: testScheme, + Recorder: record.NewFakeRecorder(20), + Config: config.New( + config.WithCollectorImage("default-collector"), + config.WithTargetAllocatorImage("default-ta-allocator"), + config.WithOpenShiftRoutesAvailability(openshift.RoutesAvailable), + ), + }) + + assert.True(t, len(tt.want) > 0, "must have at least one group of checks to run") + firstCheck := tt.want[0] + // Check for this before create, otherwise it's blown away. + deletionTimestamp := tt.args.params.OtelCol.GetDeletionTimestamp() + createErr := k8sClient.Create(testContext, &tt.args.params.OtelCol) + if !firstCheck.validateErr(t, createErr) { + return + } + if deletionTimestamp != nil { + err := k8sClient.Delete(testContext, &tt.args.params.OtelCol, client.PropagationPolicy(metav1.DeletePropagationForeground)) + assert.NoError(t, err) + } + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + got, reconcileErr := reconciler.Reconcile(testContext, req) + if !firstCheck.wantErr(t, reconcileErr) { + require.NoError(t, k8sClient.Delete(testContext, &tt.args.params.OtelCol)) + return + } + assert.Equal(t, firstCheck.result, got) + for _, check := range firstCheck.checks { + check(t, tt.args.params) + } + // run the next set of checks + for pid, updateParam := range tt.args.updates { + updateParam := updateParam + existing := v1alpha1.OpenTelemetryCollector{} + found, err := populateObjectIfExists(t, &existing, nsn) + assert.True(t, found) + assert.NoError(t, err) + + updateParam.OtelCol.SetResourceVersion(existing.ResourceVersion) + updateParam.OtelCol.SetUID(existing.UID) + err = k8sClient.Update(testContext, &updateParam.OtelCol) + assert.NoError(t, err) + if err != nil { + continue + } + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err = reconciler.Reconcile(testContext, req) + // account for already checking the initial group + checkGroup := tt.want[pid+1] + if !checkGroup.wantErr(t, err) { + return + } + assert.Equal(t, checkGroup.result, got) + for _, check := range checkGroup.checks { + check(t, updateParam) + } + } + // Only delete upon a successful creation + if createErr == nil { + require.NoError(t, k8sClient.Delete(testContext, &tt.args.params.OtelCol)) + } + }) + } +} + +func TestOpAMPBridgeReconciler_Reconcile(t *testing.T) { + addedMetadataDeployment := opampBridgeParams() + addedMetadataDeployment.OpAMPBridge.Labels = map[string]string{ + labelName: labelVal, + } + addedMetadataDeployment.OpAMPBridge.Spec.PodAnnotations = map[string]string{ + annotationName: annotationVal, + } + deploymentExtraPorts := opampBridgeParams() + deploymentExtraPorts.OpAMPBridge.Spec.Ports = append(deploymentExtraPorts.OpAMPBridge.Spec.Ports, extraPorts) + + type args struct { + params manifests.Params + // an optional list of updates to supply after the initial object + updates []manifests.Params + } + type want struct { + // result check + result controllerruntime.Result + // a check to run against the current state applied + checks []check + // if an error from creation validation is expected + validateErr assert.ErrorAssertionFunc + // if an error from reconciliation is expected + wantErr assert.ErrorAssertionFunc + } + tests := []struct { + name string + args args + want []want + }{ + { + name: "deployment opamp-bridge", + args: args{ + params: addedMetadataDeployment, + updates: []manifests.Params{deploymentExtraPorts}, + }, + want: []want{ + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + d := appsv1.Deployment{} + exists, err := populateObjectIfExists(t, &d, namespacedObjectName(naming.OpAMPBridge(params.OpAMPBridge.Name), params.OpAMPBridge.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + assert.Equal(t, int32(1), *d.Spec.Replicas) + assert.Contains(t, d.Spec.Template.Annotations, annotationName) + assert.Contains(t, d.Labels, labelName) + exists, err = populateObjectIfExists(t, &v1.Service{}, namespacedObjectName(naming.OpAMPBridgeService(params.OpAMPBridge.Name), params.OpAMPBridge.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + exists, err = populateObjectIfExists(t, &v1.ServiceAccount{}, namespacedObjectName(naming.ServiceAccount(params.OpAMPBridge.Name), params.OpAMPBridge.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + { + result: controllerruntime.Result{}, + checks: []check{ + func(t *testing.T, params manifests.Params) { + d := appsv1.Deployment{} + exists, err := populateObjectIfExists(t, &d, namespacedObjectName(naming.OpAMPBridge(params.OpAMPBridge.Name), params.OpAMPBridge.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + // confirm that we don't remove annotations and labels even if we don't set them + assert.Contains(t, d.Spec.Template.Annotations, annotationName) + assert.Contains(t, d.Labels, labelName) + actual := v1.Service{} + exists, err = populateObjectIfExists(t, &actual, namespacedObjectName(naming.OpAMPBridgeService(params.OpAMPBridge.Name), params.OpAMPBridge.Namespace)) + assert.NoError(t, err) + assert.True(t, exists) + assert.Contains(t, actual.Spec.Ports, extraPorts) + }, + }, + wantErr: assert.NoError, + validateErr: assert.NoError, + }, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + testContext := context.Background() + nsn := types.NamespacedName{Name: tt.args.params.OpAMPBridge.Name, Namespace: tt.args.params.OpAMPBridge.Namespace} + reconciler := controllers.NewOpAMPBridgeReconciler(controllers.OpAMPBridgeReconcilerParams{ + Client: k8sClient, + Log: logger, + Scheme: testScheme, + Recorder: record.NewFakeRecorder(20), + Config: config.New( + config.WithCollectorImage("default-collector"), + config.WithTargetAllocatorImage("default-ta-allocator"), + config.WithOperatorOpAMPBridgeImage("default-opamp-bridge"), + ), + }) + assert.True(t, len(tt.want) > 0, "must have at least one group of checks to run") + firstCheck := tt.want[0] + createErr := k8sClient.Create(testContext, &tt.args.params.OpAMPBridge) + if !firstCheck.validateErr(t, createErr) { + return + } + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + got, reconcileErr := reconciler.Reconcile(testContext, req) + if !firstCheck.wantErr(t, reconcileErr) { + require.NoError(t, k8sClient.Delete(testContext, &tt.args.params.OpAMPBridge)) + return + } + assert.Equal(t, firstCheck.result, got) + for _, check := range firstCheck.checks { + check(t, tt.args.params) + } + // run the next set of checks + for pid, updateParam := range tt.args.updates { + updateParam := updateParam + existing := v1alpha1.OpAMPBridge{} + found, err := populateObjectIfExists(t, &existing, nsn) + assert.True(t, found) + assert.NoError(t, err) + + updateParam.OpAMPBridge.SetResourceVersion(existing.ResourceVersion) + updateParam.OpAMPBridge.SetUID(existing.UID) + err = k8sClient.Update(testContext, &updateParam.OpAMPBridge) + assert.NoError(t, err) + if err != nil { + continue + } + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err = reconciler.Reconcile(testContext, req) + // account for already checking the initial group + checkGroup := tt.want[pid+1] + if !checkGroup.wantErr(t, err) { + return + } + assert.Equal(t, checkGroup.result, got) + for _, check := range checkGroup.checks { + check(t, updateParam) + } + } + // Only delete upon a successful creation + if createErr == nil { + require.NoError(t, k8sClient.Delete(testContext, &tt.args.params.OpAMPBridge)) + } + }) + } +} + +func TestSkipWhenInstanceDoesNotExist(t *testing.T) { + // prepare + cfg := config.New() + nsn := types.NamespacedName{Name: "non-existing-my-instance", Namespace: "default"} + reconciler := controllers.NewReconciler(controllers.Params{ + Client: k8sClient, + Log: logger, + Scheme: scheme.Scheme, + Config: cfg, + }) + + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err := reconciler.Reconcile(context.Background(), req) + + // verify + assert.NoError(t, err) +} + +func TestRegisterWithManager(t *testing.T) { + t.Skip("this test requires a real cluster, otherwise the GetConfigOrDie will die") + + // prepare + mgr, err := manager.New(k8sconfig.GetConfigOrDie(), manager.Options{}) + require.NoError(t, err) + + reconciler := controllers.NewReconciler(controllers.Params{}) + + // test + err = reconciler.SetupWithManager(mgr) + + // verify + assert.NoError(t, err) +} + +func namespacedObjectName(name string, namespace string) types.NamespacedName { + return types.NamespacedName{ + Namespace: namespace, + Name: name, + } +} diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 1068a79be3..51fe86d5a1 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -25,15 +25,33 @@ import ( "testing" "time" + routev1 "github.com/openshift/api/route/v1" + v1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" "k8s.io/client-go/util/retry" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/testdata" // +kubebuilder:scaffold:imports ) @@ -43,25 +61,63 @@ var ( testScheme *runtime.Scheme = scheme.Scheme ctx context.Context cancel context.CancelFunc + err error + cfg *rest.Config + logger = logf.Log.WithName("unit-tests") + + instanceUID = uuid.NewUUID() + mockAutoDetector = &mockAutoDetect{ + OpenShiftRoutesAvailabilityFunc: func() (openshift.RoutesAvailability, error) { + return openshift.RoutesAvailable, nil + }, + } +) + +const ( + defaultCollectorImage = "default-collector" + defaultTaAllocationImage = "default-ta-allocator" + defaultOpAMPBridgeImage = "default-opamp-bridge" + promFile = "testdata/test.yaml" + updatedPromFile = "testdata/test_ta_update.yaml" + testFileIngress = "testdata/ingress_testdata.yaml" ) +var _ autodetect.AutoDetect = (*mockAutoDetect)(nil) + +type mockAutoDetect struct { + OpenShiftRoutesAvailabilityFunc func() (openshift.RoutesAvailability, error) +} + +func (m *mockAutoDetect) OpenShiftRoutesAvailability() (openshift.RoutesAvailability, error) { + if m.OpenShiftRoutesAvailabilityFunc != nil { + return m.OpenShiftRoutesAvailabilityFunc() + } + return openshift.RoutesNotAvailable, nil +} + func TestMain(m *testing.M) { ctx, cancel = context.WithCancel(context.TODO()) defer cancel() testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + CRDs: []*apiextensionsv1.CustomResourceDefinition{testdata.OpenShiftRouteCRD}, WebhookInstallOptions: envtest.WebhookInstallOptions{ Paths: []string{filepath.Join("..", "config", "webhook")}, }, } - cfg, err := testEnv.Start() + cfg, err = testEnv.Start() if err != nil { fmt.Printf("failed to start testEnv: %v", err) os.Exit(1) } - if err := v1alpha1.AddToScheme(testScheme); err != nil { + if err = routev1.AddToScheme(testScheme); err != nil { + fmt.Printf("failed to register scheme: %v", err) + os.Exit(1) + } + + if err = v1alpha1.AddToScheme(testScheme); err != nil { fmt.Printf("failed to register scheme: %v", err) os.Exit(1) } @@ -75,20 +131,29 @@ func TestMain(m *testing.M) { // start webhook server using Manager webhookInstallOptions := &testEnv.WebhookInstallOptions - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: testScheme, - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - LeaderElection: false, - MetricsBindAddress: "0", + mgr, mgrErr := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: testScheme, + LeaderElection: false, + WebhookServer: webhook.NewServer(webhook.Options{ + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), + Metrics: metricsserver.Options{ + BindAddress: "0", + }, }) - if err != nil { - fmt.Printf("failed to start webhook server: %v", err) + if mgrErr != nil { + fmt.Printf("failed to start webhook server: %v", mgrErr) + os.Exit(1) + } + + if err = v1alpha1.SetupCollectorWebhook(mgr, config.New()); err != nil { + fmt.Printf("failed to SetupWebhookWithManager: %v", err) os.Exit(1) } - if err := (&v1alpha1.OpenTelemetryCollector{}).SetupWebhookWithManager(mgr); err != nil { + if err = v1alpha1.SetupOpAMPBridgeWebhook(mgr, config.New()); err != nil { fmt.Printf("failed to SetupWebhookWithManager: %v", err) os.Exit(1) } @@ -119,9 +184,9 @@ func TestMain(m *testing.M) { return true }, func() error { // #nosec G402 - conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) - if err != nil { - return err + conn, tlsErr := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) + if tlsErr != nil { + return tlsErr } _ = conn.Close() return nil @@ -142,3 +207,269 @@ func TestMain(m *testing.M) { os.Exit(code) } + +func paramsWithMode(mode v1alpha1.Mode) manifests.Params { + replicas := int32(2) + return paramsWithModeAndReplicas(mode, replicas) +} + +func paramsWithModeAndReplicas(mode v1alpha1.Mode, replicas int32) manifests.Params { + configYAML, err := os.ReadFile("testdata/test.yaml") + if err != nil { + fmt.Printf("Error getting yaml file: %v", err) + } + return manifests.Params{ + Config: config.New(config.WithCollectorImage(defaultCollectorImage), config.WithTargetAllocatorImage(defaultTaAllocationImage)), + Client: k8sClient, + OtelCol: v1alpha1.OpenTelemetryCollector{ + TypeMeta: metav1.TypeMeta{ + Kind: "opentelemetry.io", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.47.0", + Ports: []v1.ServicePort{{ + Name: "web", + Port: 80, + TargetPort: intstr.IntOrString{ + Type: intstr.Int, + IntVal: 80, + }, + NodePort: 0, + }}, + Replicas: &replicas, + Config: string(configYAML), + Mode: mode, + }, + }, + Scheme: testScheme, + Log: logger, + Recorder: record.NewFakeRecorder(10), + } +} + +func newParams(taContainerImage string, file string) (manifests.Params, error) { + replicas := int32(1) + var configYAML []byte + var err error + + if file == "" { + configYAML, err = os.ReadFile("testdata/test.yaml") + } else { + configYAML, err = os.ReadFile(file) + } + if err != nil { + return manifests.Params{}, fmt.Errorf("Error getting yaml file: %w", err) + } + + cfg := config.New(config.WithCollectorImage(defaultCollectorImage), config.WithTargetAllocatorImage(defaultTaAllocationImage)) + + return manifests.Params{ + Config: cfg, + Client: k8sClient, + OtelCol: v1alpha1.OpenTelemetryCollector{ + TypeMeta: metav1.TypeMeta{ + Kind: "opentelemetry.io", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeStatefulSet, + Ports: []v1.ServicePort{{ + Name: "web", + Port: 80, + TargetPort: intstr.IntOrString{ + Type: intstr.Int, + IntVal: 80, + }, + NodePort: 0, + }}, + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + Image: taContainerImage, + }, + Replicas: &replicas, + Config: string(configYAML), + }, + }, + Scheme: testScheme, + Log: logger, + }, nil +} + +func paramsWithHPA(minReps, maxReps int32) manifests.Params { + configYAML, err := os.ReadFile("testdata/test.yaml") + if err != nil { + fmt.Printf("Error getting yaml file: %v", err) + } + + cpuUtilization := int32(90) + + configuration := config.New(config.WithCollectorImage(defaultCollectorImage), config.WithTargetAllocatorImage(defaultTaAllocationImage)) + + return manifests.Params{ + Config: configuration, + Client: k8sClient, + OtelCol: v1alpha1.OpenTelemetryCollector{ + TypeMeta: metav1.TypeMeta{ + Kind: "opentelemetry.io", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "hpatest", + Namespace: "default", + UID: instanceUID, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Ports: []v1.ServicePort{{ + Name: "web", + Port: 80, + TargetPort: intstr.IntOrString{ + Type: intstr.Int, + IntVal: 80, + }, + NodePort: 0, + }}, + Config: string(configYAML), + Autoscaler: &v1alpha1.AutoscalerSpec{ + MinReplicas: &minReps, + MaxReplicas: &maxReps, + TargetCPUUtilization: &cpuUtilization, + }, + }, + }, + Scheme: testScheme, + Log: logger, + Recorder: record.NewFakeRecorder(10), + } +} + +func paramsWithPolicy(minAvailable, maxUnavailable int32) manifests.Params { + configYAML, err := os.ReadFile("testdata/test.yaml") + if err != nil { + fmt.Printf("Error getting yaml file: %v", err) + } + + configuration := config.New(config.WithAutoDetect(mockAutoDetector), config.WithCollectorImage(defaultCollectorImage), config.WithTargetAllocatorImage(defaultTaAllocationImage)) + err = configuration.AutoDetect() + if err != nil { + logger.Error(err, "configuration.autodetect failed") + } + + pdb := &v1alpha1.PodDisruptionBudgetSpec{} + + if maxUnavailable > 0 && minAvailable > 0 { + fmt.Printf("worng configuration: %v", fmt.Errorf("minAvailable and maxUnavailable cannot be both set")) + } + if maxUnavailable > 0 { + pdb.MaxUnavailable = &intstr.IntOrString{ + Type: intstr.Int, + IntVal: maxUnavailable, + } + } else { + pdb.MinAvailable = &intstr.IntOrString{ + Type: intstr.Int, + IntVal: minAvailable, + } + } + + return manifests.Params{ + Config: configuration, + Client: k8sClient, + OtelCol: v1alpha1.OpenTelemetryCollector{ + TypeMeta: metav1.TypeMeta{ + Kind: "opentelemetry.io", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "policytest", + Namespace: "default", + UID: instanceUID, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Ports: []v1.ServicePort{{ + Name: "web", + Port: 80, + TargetPort: intstr.IntOrString{ + Type: intstr.Int, + IntVal: 80, + }, + NodePort: 0, + }}, + Config: string(configYAML), + PodDisruptionBudget: pdb, + }, + }, + Scheme: testScheme, + Log: logger, + Recorder: record.NewFakeRecorder(10), + } +} + +func opampBridgeParams() manifests.Params { + return manifests.Params{ + Config: config.New(config.WithOperatorOpAMPBridgeImage(defaultOpAMPBridgeImage)), + Client: k8sClient, + OpAMPBridge: v1alpha1.OpAMPBridge{ + TypeMeta: metav1.TypeMeta{ + Kind: "opentelemetry.io", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + UID: instanceUID, + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + Image: "ghcr.io/open-telemetry/opentelemetry-operator/operator-opamp-bridge:0.69.0", + Ports: []v1.ServicePort{ + { + Name: "metrics", + Port: 8081, + TargetPort: intstr.IntOrString{ + Type: intstr.Int, + IntVal: 8081, + }, + }, + }, + Endpoint: "ws://127.0.0.1:4320/v1/opamp", + Capabilities: map[v1alpha1.OpAMPBridgeCapability]bool{ + v1alpha1.OpAMPBridgeCapabilityReportsStatus: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsRemoteConfig: true, + v1alpha1.OpAMPBridgeCapabilityReportsEffectiveConfig: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnTraces: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnMetrics: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnLogs: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsOtherConnectionSettings: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsRestartCommand: true, + v1alpha1.OpAMPBridgeCapabilityReportsHealth: true, + v1alpha1.OpAMPBridgeCapabilityReportsRemoteConfig: true, + }, + ComponentsAllowed: map[string][]string{"receivers": {"otlp"}, "processors": {"memory_limiter"}, "exporters": {"logging"}}, + }, + }, + Scheme: testScheme, + Log: logger, + Recorder: record.NewFakeRecorder(10), + } +} + +func populateObjectIfExists(t testing.TB, object client.Object, namespacedName types.NamespacedName) (bool, error) { + t.Helper() + err := k8sClient.Get(context.Background(), namespacedName, object) + if errors.IsNotFound(err) { + return false, nil + } + if err != nil { + return false, err + } + return true, nil +} diff --git a/controllers/testdata/ingress_testdata.yaml b/controllers/testdata/ingress_testdata.yaml new file mode 100644 index 0000000000..4d6ba26c18 --- /dev/null +++ b/controllers/testdata/ingress_testdata.yaml @@ -0,0 +1,17 @@ +--- +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:12345 + otlp/test: + protocols: + grpc: + endpoint: 0.0.0.0:12346 +exporters: + debug: +service: + pipelines: + traces: + receivers: [otlp, otlp/test] + exporters: [debug] diff --git a/controllers/testdata/test.yaml b/controllers/testdata/test.yaml new file mode 100644 index 0000000000..abba475c08 --- /dev/null +++ b/controllers/testdata/test.yaml @@ -0,0 +1,22 @@ +processors: +receivers: + jaeger: + protocols: + grpc: + prometheus: + config: + scrape_configs: + - job_name: otel-collector + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888', '0.0.0.0:9999' ] + +exporters: + logging: + +service: + pipelines: + metrics: + receivers: [prometheus, jaeger] + processors: [] + exporters: [logging] \ No newline at end of file diff --git a/controllers/testdata/test_ta_update.yaml b/controllers/testdata/test_ta_update.yaml new file mode 100644 index 0000000000..b024fede61 --- /dev/null +++ b/controllers/testdata/test_ta_update.yaml @@ -0,0 +1,22 @@ +processors: +receivers: + jaeger: + protocols: + grpc: + prometheus: + config: + scrape_configs: + - job_name: otel-collector + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888', '0.0.0.0:9999', '0.0.0.0:10100' ] + +exporters: + logging: + +service: + pipelines: + metrics: + receivers: [prometheus, jaeger] + processors: [] + exporters: [logging] \ No newline at end of file diff --git a/docs/api.md b/docs/api.md index 1bb6512d3c..12a06d1a64 100644 --- a/docs/api.md +++ b/docs/api.md @@ -10,6 +10,8 @@ Resource Types: - [Instrumentation](#instrumentation) +- [OpAMPBridge](#opampbridge) + - [OpenTelemetryCollector](#opentelemetrycollector) @@ -86,6 +88,13 @@ InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumen + apacheHttpd + object + + ApacheHttpd defines configuration for Apache HTTPD auto-instrumentation.
+ + false + dotnet object @@ -96,7 +105,7 @@ InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumen env []object - Env defines common env vars. There are four layers for env vars' definitions and the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. If the former var had been defined, then the other vars would be ignored.
+ Env defines common env vars.
false @@ -106,6 +115,13 @@ InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumen Exporter defines exporter configuration.
false + + go + object + + Go defines configuration for Go auto-instrumentation.
+ + false java object @@ -113,6 +129,13 @@ InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumen Java defines configuration for java auto-instrumentation.
false + + nginx + object + + Nginx defines configuration for Nginx auto-instrumentation.
+ + false nodejs object @@ -124,7 +147,7 @@ InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumen propagators []enum - Propagators defines inter-process context propagation configuration.
+ Propagators defines inter-process context propagation configuration. Values in this list will be set in the OTEL_PROPAGATORS env var. Enum=tracecontext;baggage;b3;b3multi;jaeger;xray;ottrace;none
false @@ -152,12 +175,12 @@ InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumen -### Instrumentation.spec.dotnet +### Instrumentation.spec.apacheHttpd [↩ Parent](#instrumentationspec) -DotNet defines configuration for DotNet auto-instrumentation. +ApacheHttpd defines configuration for Apache HTTPD auto-instrumentation. @@ -169,25 +192,60 @@ DotNet defines configuration for DotNet auto-instrumentation. - + + + + + + + + + + + + + + + + + + + + + + + + + +
envattrs[]object + Attrs defines Apache HTTPD agent specific attributes. The precedence is: `agent default attributes` > `instrument spec attributes` . Attributes are documented at https://github.
+
false
configPathstring + Location of Apache HTTPD server configuration. Needed only if different from default "/usr/local/apache2/conf"
+
false
env []object - Env defines DotNet specific env vars. There are four layers for env vars' definitions and the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. If the former var had been defined, then the other vars would be ignored.
+ Env defines Apache HTTPD specific env vars.
false
image string - Image is a container image with DotNet SDK and auto-instrumentation.
+ Image is a container image with Apache SDK and auto-instrumentation.
+
false
resourceRequirementsobject + Resources describes the compute resource requirements.
+
false
versionstring + Apache HTTPD server version. One of 2.4 or 2.2. Default is 2.4
+
false
volumeLimitSizeint or string + VolumeSizeLimit defines size limit for volume used for auto-instrumentation. The default size is 200Mi.
false
-### Instrumentation.spec.dotnet.env[index] -[↩ Parent](#instrumentationspecdotnet) +### Instrumentation.spec.apacheHttpd.attrs[index] +[↩ Parent](#instrumentationspecapachehttpd) @@ -213,11 +271,11 @@ EnvVar represents an environment variable present in a Container. value string - Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".
+ Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
false - valueFrom + valueFrom object Source for the environment variable's value. Cannot be used if value is not empty.
@@ -227,8 +285,8 @@ EnvVar represents an environment variable present in a Container. -### Instrumentation.spec.dotnet.env[index].valueFrom -[↩ Parent](#instrumentationspecdotnetenvindex) +### Instrumentation.spec.apacheHttpd.attrs[index].valueFrom +[↩ Parent](#instrumentationspecapachehttpdattrsindex) @@ -244,28 +302,28 @@ Source for the environment variable's value. Cannot be used if value is not empt - configMapKeyRef + configMapKeyRef object Selects a key of a ConfigMap.
false - fieldRef + fieldRef object - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
false - resourceFieldRef + resourceFieldRef object - Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+ Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
false - secretKeyRef + secretKeyRef object Selects a key of a secret in the pod's namespace
@@ -275,8 +333,8 @@ Source for the environment variable's value. Cannot be used if value is not empt -### Instrumentation.spec.dotnet.env[index].valueFrom.configMapKeyRef -[↩ Parent](#instrumentationspecdotnetenvindexvaluefrom) +### Instrumentation.spec.apacheHttpd.attrs[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecapachehttpdattrsindexvaluefrom) @@ -316,12 +374,12 @@ Selects a key of a ConfigMap. -### Instrumentation.spec.dotnet.env[index].valueFrom.fieldRef -[↩ Parent](#instrumentationspecdotnetenvindexvaluefrom) +### Instrumentation.spec.apacheHttpd.attrs[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecapachehttpdattrsindexvaluefrom) -Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. @@ -350,12 +408,12 @@ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadat
-### Instrumentation.spec.dotnet.env[index].valueFrom.resourceFieldRef -[↩ Parent](#instrumentationspecdotnetenvindexvaluefrom) +### Instrumentation.spec.apacheHttpd.attrs[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecapachehttpdattrsindexvaluefrom) -Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. @@ -391,8 +449,8 @@ Selects a resource of the container: only resources limits and requests (limits.
-### Instrumentation.spec.dotnet.env[index].valueFrom.secretKeyRef -[↩ Parent](#instrumentationspecdotnetenvindexvaluefrom) +### Instrumentation.spec.apacheHttpd.attrs[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecapachehttpdattrsindexvaluefrom) @@ -432,8 +490,8 @@ Selects a key of a secret in the pod's namespace -### Instrumentation.spec.env[index] -[↩ Parent](#instrumentationspec) +### Instrumentation.spec.apacheHttpd.env[index] +[↩ Parent](#instrumentationspecapachehttpd) @@ -459,11 +517,11 @@ EnvVar represents an environment variable present in a Container. value string - Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".
+ Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
false - valueFrom + valueFrom object Source for the environment variable's value. Cannot be used if value is not empty.
@@ -473,8 +531,8 @@ EnvVar represents an environment variable present in a Container. -### Instrumentation.spec.env[index].valueFrom -[↩ Parent](#instrumentationspecenvindex) +### Instrumentation.spec.apacheHttpd.env[index].valueFrom +[↩ Parent](#instrumentationspecapachehttpdenvindex) @@ -490,28 +548,28 @@ Source for the environment variable's value. Cannot be used if value is not empt - configMapKeyRef + configMapKeyRef object Selects a key of a ConfigMap.
false - fieldRef + fieldRef object - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
false - resourceFieldRef + resourceFieldRef object - Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+ Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
false - secretKeyRef + secretKeyRef object Selects a key of a secret in the pod's namespace
@@ -521,8 +579,8 @@ Source for the environment variable's value. Cannot be used if value is not empt -### Instrumentation.spec.env[index].valueFrom.configMapKeyRef -[↩ Parent](#instrumentationspecenvindexvaluefrom) +### Instrumentation.spec.apacheHttpd.env[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecapachehttpdenvindexvaluefrom) @@ -562,12 +620,12 @@ Selects a key of a ConfigMap. -### Instrumentation.spec.env[index].valueFrom.fieldRef -[↩ Parent](#instrumentationspecenvindexvaluefrom) +### Instrumentation.spec.apacheHttpd.env[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecapachehttpdenvindexvaluefrom) -Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. @@ -596,12 +654,12 @@ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadat
-### Instrumentation.spec.env[index].valueFrom.resourceFieldRef -[↩ Parent](#instrumentationspecenvindexvaluefrom) +### Instrumentation.spec.apacheHttpd.env[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecapachehttpdenvindexvaluefrom) -Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. @@ -637,8 +695,8 @@ Selects a resource of the container: only resources limits and requests (limits.
-### Instrumentation.spec.env[index].valueFrom.secretKeyRef -[↩ Parent](#instrumentationspecenvindexvaluefrom) +### Instrumentation.spec.apacheHttpd.env[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecapachehttpdenvindexvaluefrom) @@ -678,12 +736,12 @@ Selects a key of a secret in the pod's namespace -### Instrumentation.spec.exporter -[↩ Parent](#instrumentationspec) +### Instrumentation.spec.apacheHttpd.resourceRequirements +[↩ Parent](#instrumentationspecapachehttpd) -Exporter defines exporter configuration. +Resources describes the compute resource requirements. @@ -695,22 +753,64 @@ Exporter defines exporter configuration. - - + + + + + + + + + + + +
endpointstringclaims[]object - Endpoint is address of the collector with OTLP endpoint.
+ Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limitsmap[string]int or string + Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
false
-### Instrumentation.spec.java +### Instrumentation.spec.apacheHttpd.resourceRequirements.claims[index] +[↩ Parent](#instrumentationspecapachehttpdresourcerequirements) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### Instrumentation.spec.dotnet [↩ Parent](#instrumentationspec) -Java defines configuration for java auto-instrumentation. +DotNet defines configuration for DotNet auto-instrumentation. @@ -722,25 +822,39 @@ Java defines configuration for java auto-instrumentation. - + + + + + + + + + + +
envenv []object - Env defines java specific env vars. There are four layers for env vars' definitions and the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. If the former var had been defined, then the other vars would be ignored.
+ Env defines DotNet specific env vars.
false
image string - Image is a container image with javaagent auto-instrumentation JAR.
+ Image is a container image with DotNet SDK and auto-instrumentation.
+
false
resourceRequirementsobject + Resources describes the compute resource requirements.
+
false
volumeLimitSizeint or string + VolumeSizeLimit defines size limit for volume used for auto-instrumentation. The default size is 200Mi.
false
-### Instrumentation.spec.java.env[index] -[↩ Parent](#instrumentationspecjava) +### Instrumentation.spec.dotnet.env[index] +[↩ Parent](#instrumentationspecdotnet) @@ -766,11 +880,11 @@ EnvVar represents an environment variable present in a Container. value string - Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".
+ Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
false - valueFrom + valueFrom object Source for the environment variable's value. Cannot be used if value is not empty.
@@ -780,8 +894,8 @@ EnvVar represents an environment variable present in a Container. -### Instrumentation.spec.java.env[index].valueFrom -[↩ Parent](#instrumentationspecjavaenvindex) +### Instrumentation.spec.dotnet.env[index].valueFrom +[↩ Parent](#instrumentationspecdotnetenvindex) @@ -797,28 +911,28 @@ Source for the environment variable's value. Cannot be used if value is not empt - configMapKeyRef + configMapKeyRef object Selects a key of a ConfigMap.
false - fieldRef + fieldRef object - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
false - resourceFieldRef + resourceFieldRef object - Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+ Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
false - secretKeyRef + secretKeyRef object Selects a key of a secret in the pod's namespace
@@ -828,8 +942,8 @@ Source for the environment variable's value. Cannot be used if value is not empt -### Instrumentation.spec.java.env[index].valueFrom.configMapKeyRef -[↩ Parent](#instrumentationspecjavaenvindexvaluefrom) +### Instrumentation.spec.dotnet.env[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecdotnetenvindexvaluefrom) @@ -869,12 +983,12 @@ Selects a key of a ConfigMap. -### Instrumentation.spec.java.env[index].valueFrom.fieldRef -[↩ Parent](#instrumentationspecjavaenvindexvaluefrom) +### Instrumentation.spec.dotnet.env[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecdotnetenvindexvaluefrom) -Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. @@ -903,12 +1017,12 @@ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadat
-### Instrumentation.spec.java.env[index].valueFrom.resourceFieldRef -[↩ Parent](#instrumentationspecjavaenvindexvaluefrom) +### Instrumentation.spec.dotnet.env[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecdotnetenvindexvaluefrom) -Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. @@ -944,8 +1058,8 @@ Selects a resource of the container: only resources limits and requests (limits.
-### Instrumentation.spec.java.env[index].valueFrom.secretKeyRef -[↩ Parent](#instrumentationspecjavaenvindexvaluefrom) +### Instrumentation.spec.dotnet.env[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecdotnetenvindexvaluefrom) @@ -985,12 +1099,12 @@ Selects a key of a secret in the pod's namespace -### Instrumentation.spec.nodejs -[↩ Parent](#instrumentationspec) +### Instrumentation.spec.dotnet.resourceRequirements +[↩ Parent](#instrumentationspecdotnet) -NodeJS defines configuration for nodejs auto-instrumentation. +Resources describes the compute resource requirements. @@ -1002,25 +1116,60 @@ NodeJS defines configuration for nodejs auto-instrumentation. - + - - + + + + + + +
envclaims []object - Env defines nodejs specific env vars. There are four layers for env vars' definitions and the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. If the former var had been defined, then the other vars would be ignored.
+ Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
false
imagestringlimitsmap[string]int or string - Image is a container image with NodeJS SDK and auto-instrumentation.
+ Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
false
-### Instrumentation.spec.nodejs.env[index] -[↩ Parent](#instrumentationspecnodejs) +### Instrumentation.spec.dotnet.resourceRequirements.claims[index] +[↩ Parent](#instrumentationspecdotnetresourcerequirements) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### Instrumentation.spec.env[index] +[↩ Parent](#instrumentationspec) @@ -1046,11 +1195,11 @@ EnvVar represents an environment variable present in a Container. value string - Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".
+ Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
false - valueFrom + valueFrom object Source for the environment variable's value. Cannot be used if value is not empty.
@@ -1060,8 +1209,8 @@ EnvVar represents an environment variable present in a Container. -### Instrumentation.spec.nodejs.env[index].valueFrom -[↩ Parent](#instrumentationspecnodejsenvindex) +### Instrumentation.spec.env[index].valueFrom +[↩ Parent](#instrumentationspecenvindex) @@ -1077,28 +1226,28 @@ Source for the environment variable's value. Cannot be used if value is not empt - configMapKeyRef + configMapKeyRef object Selects a key of a ConfigMap.
false - fieldRef + fieldRef object - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
false - resourceFieldRef + resourceFieldRef object - Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+ Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
false - secretKeyRef + secretKeyRef object Selects a key of a secret in the pod's namespace
@@ -1108,8 +1257,8 @@ Source for the environment variable's value. Cannot be used if value is not empt -### Instrumentation.spec.nodejs.env[index].valueFrom.configMapKeyRef -[↩ Parent](#instrumentationspecnodejsenvindexvaluefrom) +### Instrumentation.spec.env[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecenvindexvaluefrom) @@ -1149,12 +1298,12 @@ Selects a key of a ConfigMap. -### Instrumentation.spec.nodejs.env[index].valueFrom.fieldRef -[↩ Parent](#instrumentationspecnodejsenvindexvaluefrom) +### Instrumentation.spec.env[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecenvindexvaluefrom) -Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. @@ -1183,12 +1332,12 @@ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadat
-### Instrumentation.spec.nodejs.env[index].valueFrom.resourceFieldRef -[↩ Parent](#instrumentationspecnodejsenvindexvaluefrom) +### Instrumentation.spec.env[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecenvindexvaluefrom) -Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. @@ -1224,8 +1373,8 @@ Selects a resource of the container: only resources limits and requests (limits.
-### Instrumentation.spec.nodejs.env[index].valueFrom.secretKeyRef -[↩ Parent](#instrumentationspecnodejsenvindexvaluefrom) +### Instrumentation.spec.env[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecenvindexvaluefrom) @@ -1265,12 +1414,12 @@ Selects a key of a secret in the pod's namespace -### Instrumentation.spec.python +### Instrumentation.spec.exporter [↩ Parent](#instrumentationspec) -Python defines configuration for python auto-instrumentation. +Exporter defines exporter configuration. @@ -1282,25 +1431,66 @@ Python defines configuration for python auto-instrumentation. - - - + + + + +
env[]object - Env defines python specific env vars. There are four layers for env vars' definitions and the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`. If the former var had been defined, then the other vars would be ignored.
+
endpointstring + Endpoint is address of the collector with OTLP endpoint.
+
false
+ + +### Instrumentation.spec.go +[↩ Parent](#instrumentationspec) + + + +Go defines configuration for Go auto-instrumentation. + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
env[]object + Env defines Go specific env vars.
false
image string - Image is a container image with Python SDK and auto-instrumentation.
+ Image is a container image with Go SDK and auto-instrumentation.
+
false
resourceRequirementsobject + Resources describes the compute resource requirements.
+
false
volumeLimitSizeint or string + VolumeSizeLimit defines size limit for volume used for auto-instrumentation. The default size is 200Mi.
false
-### Instrumentation.spec.python.env[index] -[↩ Parent](#instrumentationspecpython) +### Instrumentation.spec.go.env[index] +[↩ Parent](#instrumentationspecgo) @@ -1326,11 +1516,11 @@ EnvVar represents an environment variable present in a Container. value string - Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".
+ Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
false - valueFrom + valueFrom object Source for the environment variable's value. Cannot be used if value is not empty.
@@ -1340,8 +1530,8 @@ EnvVar represents an environment variable present in a Container. -### Instrumentation.spec.python.env[index].valueFrom -[↩ Parent](#instrumentationspecpythonenvindex) +### Instrumentation.spec.go.env[index].valueFrom +[↩ Parent](#instrumentationspecgoenvindex) @@ -1357,28 +1547,28 @@ Source for the environment variable's value. Cannot be used if value is not empt - configMapKeyRef + configMapKeyRef object Selects a key of a ConfigMap.
false - fieldRef + fieldRef object - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
false - resourceFieldRef + resourceFieldRef object - Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+ Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
false - secretKeyRef + secretKeyRef object Selects a key of a secret in the pod's namespace
@@ -1388,8 +1578,8 @@ Source for the environment variable's value. Cannot be used if value is not empt -### Instrumentation.spec.python.env[index].valueFrom.configMapKeyRef -[↩ Parent](#instrumentationspecpythonenvindexvaluefrom) +### Instrumentation.spec.go.env[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecgoenvindexvaluefrom) @@ -1429,12 +1619,12 @@ Selects a key of a ConfigMap. -### Instrumentation.spec.python.env[index].valueFrom.fieldRef -[↩ Parent](#instrumentationspecpythonenvindexvaluefrom) +### Instrumentation.spec.go.env[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecgoenvindexvaluefrom) -Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. @@ -1463,12 +1653,12 @@ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadat
-### Instrumentation.spec.python.env[index].valueFrom.resourceFieldRef -[↩ Parent](#instrumentationspecpythonenvindexvaluefrom) +### Instrumentation.spec.go.env[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecgoenvindexvaluefrom) -Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. @@ -1504,8 +1694,8 @@ Selects a resource of the container: only resources limits and requests (limits.
-### Instrumentation.spec.python.env[index].valueFrom.secretKeyRef -[↩ Parent](#instrumentationspecpythonenvindexvaluefrom) +### Instrumentation.spec.go.env[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecgoenvindexvaluefrom) @@ -1545,12 +1735,12 @@ Selects a key of a secret in the pod's namespace -### Instrumentation.spec.resource -[↩ Parent](#instrumentationspec) +### Instrumentation.spec.go.resourceRequirements +[↩ Parent](#instrumentationspecgo) -Resource defines the configuration for the resource attributes, as defined by the OpenTelemetry specification. +Resources describes the compute resource requirements. @@ -1562,29 +1752,64 @@ Resource defines the configuration for the resource attributes, as defined by th - - + + - - + + + + + + +
addK8sUIDAttributesbooleanclaims[]object - AddK8sUIDAttributes defines whether K8s UID attributes should be collected (e.g. k8s.deployment.uid).
+ Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
false
resourceAttributesmap[string]stringlimitsmap[string]int or string - Attributes defines attributes that are added to the resource. For example environment: dev
+ Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
false
-### Instrumentation.spec.sampler +### Instrumentation.spec.go.resourceRequirements.claims[index] +[↩ Parent](#instrumentationspecgoresourcerequirements) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### Instrumentation.spec.java [↩ Parent](#instrumentationspec) -Sampler defines sampling configuration. +Java defines configuration for java auto-instrumentation. @@ -1596,33 +1821,43 @@ Sampler defines sampling configuration. - + + + + + + - - + + + + + + +
argumentenv[]object + Env defines java specific env vars.
+
false
image string - Argument defines sampler argument. The value depends on the sampler type. For instance for parentbased_traceidratio sampler type it is a number in range [0..1] e.g. 0.25.
+ Image is a container image with javaagent auto-instrumentation JAR.
false
typeenumresourcesobject - Type defines sampler type. The value can be for instance parentbased_always_on, parentbased_always_off, parentbased_traceidratio...
-
- Enum: always_on, always_off, traceidratio, parentbased_always_on, parentbased_always_off, parentbased_traceidratio, jaeger_remote, xray
+ Resources describes the compute resource requirements.
+
false
volumeLimitSizeint or string + VolumeSizeLimit defines size limit for volume used for auto-instrumentation. The default size is 200Mi.
false
-## OpenTelemetryCollector -[↩ Parent](#opentelemetryiov1alpha1 ) - - +### Instrumentation.spec.java.env[index] +[↩ Parent](#instrumentationspecjava) -OpenTelemetryCollector is the Schema for the opentelemetrycollectors API. +EnvVar represents an environment variable present in a Container. @@ -1634,46 +1869,36 @@ OpenTelemetryCollector is the Schema for the opentelemetrycollectors API. - - - - - - - - - - - - - - - - + + + + - - + + - +
apiVersionstringopentelemetry.io/v1alpha1true
kindstringOpenTelemetryCollectortrue
metadataobjectRefer to the Kubernetes API documentation for the fields of the `metadata` field.truenamestring + Name of the environment variable. Must be a C_IDENTIFIER.
+
true
specobjectvaluestring - OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector.
+ Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
false
statusvalueFrom object - OpenTelemetryCollectorStatus defines the observed state of OpenTelemetryCollector.
+ Source for the environment variable's value. Cannot be used if value is not empty.
false
-### OpenTelemetryCollector.spec -[↩ Parent](#opentelemetrycollector) +### Instrumentation.spec.java.env[index].valueFrom +[↩ Parent](#instrumentationspecjavaenvindex) -OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector. +Source for the environment variable's value. Cannot be used if value is not empty. @@ -1685,214 +1910,17287 @@ OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector. - - + + - + - - + + - - + + + +
argsmap[string]stringconfigMapKeyRefobject - Args is the set of arguments to pass to the OpenTelemetry Collector binary
+ Selects a key of a ConfigMap.
false
autoscalerfieldRef object - Autoscaler specifies the pod autoscaling configuration to use for the OpenTelemetryCollector workload.
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
false
configstringresourceFieldRefobject - Config is the raw JSON to be used as the collector's configuration. Refer to the OpenTelemetry Collector documentation for details.
+ Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
false
env[]objectsecretKeyRefobject - ENV vars to set on the OpenTelemetry Collector's Pods. These can then in certain cases be consumed in the config file for the Collector.
+ Selects a key of a secret in the pod's namespace
false
+ + +### Instrumentation.spec.java.env[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecjavaenvindexvaluefrom) + + + +Selects a key of a ConfigMap. + + + + + + + + + + + + + + + - - + + - + - - + +
NameTypeDescriptionRequired
keystring + The key to select.
+
true
envFrom[]objectnamestring - List of sources to populate environment variables on the OpenTelemetry Collector's Pods. These can then in certain cases be consumed in the config file for the Collector.
+ Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
false
hostNetworkoptional boolean - HostNetwork indicates if the pod should run in the host networking namespace.
+ Specify whether the ConfigMap or its key must be defined
false
image
+ + +### Instrumentation.spec.java.env[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecjavaenvindexvaluefrom) + + + +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. + + + + + + + + + + + + - + - + - - - + +
NameTypeDescriptionRequired
fieldPath string - Image indicates the container image to use for the OpenTelemetry Collector.
+ Path of the field to select in the specified API version.
falsetrue
imagePullPolicyapiVersion string - ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent)
+ Version of the schema the FieldPath is written in terms of, defaults to "v1".
false
ingressobject
+ + +### Instrumentation.spec.java.env[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecjavaenvindexvaluefrom) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. + + + + + + + + + + + + + - + - - + + - - + + + +
NameTypeDescriptionRequired
resourcestring - Ingress is used to specify how OpenTelemetry Collector is exposed. This functionality is only available if one of the valid modes is set. Valid modes are: deployment, daemonset and statefulset.
+ Required: resource to select
falsetrue
maxReplicasintegercontainerNamestring - MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled.
-
- Format: int32
+ Container name: required for volumes, optional for env vars
false
minReplicasintegerdivisorint or string - MinReplicas sets a lower bound to the autoscaling feature. Set this if your are using autoscaling. It must be at least 1
-
- Format: int32
+ Specifies the output format of the exposed resources, defaults to "1"
false
+ + +### Instrumentation.spec.java.env[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecjavaenvindexvaluefrom) + + + +Selects a key of a secret in the pod's namespace + + + + + + + + + + + + + + + - - + + - - + + - - - + +
NameTypeDescriptionRequired
keystring + The key of the secret to select from. Must be a valid secret key.
+
true
modeenumnamestring - Mode represents how the collector should be deployed (deployment, daemonset, statefulset or sidecar)
-
- Enum: daemonset, deployment, sidecar, statefulset
+ Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
false
nodeSelectormap[string]stringoptionalboolean - NodeSelector to schedule OpenTelemetry Collector pods. This is only relevant to daemonset, statefulset, and deployment mode
+ Specify whether the Secret or its key must be defined
false
podAnnotationsmap[string]string
+ + +### Instrumentation.spec.java.resources +[↩ Parent](#instrumentationspecjava) + + + +Resources describes the compute resource requirements. + + + + + + + + + + + + + - - + + - + + + + + +
NameTypeDescriptionRequired
claims[]object - PodAnnotations is the set of annotations that will be attached to Collector and Target Allocator pods.
+ Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
false
podSecurityContextobjectlimitsmap[string]int or string - PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.
+ Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
false
portsrequestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
+
false
+ + +### Instrumentation.spec.java.resources.claims[index] +[↩ Parent](#instrumentationspecjavaresources) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### Instrumentation.spec.nginx +[↩ Parent](#instrumentationspec) + + + +Nginx defines configuration for Nginx auto-instrumentation. + + + + + + + + + + + + - + - - + + - - + + - + - + + + + + +
NameTypeDescriptionRequired
attrs []object - Ports allows a set of ports to be exposed by the underlying v1.Service. By default, the operator will attempt to infer the required ports by parsing the .Spec.Config property but this property can be used to open additional ports that can't be inferred by the operator, like for custom receivers.
+ Attrs defines Nginx agent specific attributes. The precedence order is: `agent default attributes` > `instrument spec attributes` . Attributes are documented at https://github.
false
priorityClassNameconfigFile string - If specified, indicates the pod's priority. If not specified, the pod priority will be default or zero if there is no default.
+ Location of Nginx configuration file. Needed only if different from default "/etx/nginx/nginx.conf"
false
replicasintegerenv[]object - Replicas is the number of pod instances for the underlying OpenTelemetry Collector. Set this if your are not using autoscaling
-
- Format: int32
+ Env defines Nginx specific env vars.
false
resourcesobjectimagestring - Resources to set on the OpenTelemetry Collector pods.
+ Image is a container image with Nginx SDK and auto-instrumentation.
false
securityContextresourceRequirements object - SecurityContext will be set as the container security context.
+ Resources describes the compute resource requirements.
false
serviceAccountvolumeLimitSizeint or string + VolumeSizeLimit defines size limit for volume used for auto-instrumentation. The default size is 200Mi.
+
false
+ + +### Instrumentation.spec.nginx.attrs[index] +[↩ Parent](#instrumentationspecnginx) + + + +EnvVar represents an environment variable present in a Container. + + + + + + + + + + + + + + + + + - + + + +
NameTypeDescriptionRequired
namestring + Name of the environment variable. Must be a C_IDENTIFIER.
+
true
value string - ServiceAccount indicates the name of an existing service account to use with this instance.
+ Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
false
targetAllocatorvalueFrom object - TargetAllocator indicates a value which determines whether to spawn a target allocation resource or not.
+ Source for the environment variable's value. Cannot be used if value is not empty.
+
false
+ + +### Instrumentation.spec.nginx.attrs[index].valueFrom +[↩ Parent](#instrumentationspecnginxattrsindex) + + + +Source for the environment variable's value. Cannot be used if value is not empty. + + + + + + + + + + + + + + - - + + - - + + + + + + + + + +
NameTypeDescriptionRequired
configMapKeyRefobject + Selects a key of a ConfigMap.
false
tolerations[]objectfieldRefobject - Toleration to schedule OpenTelemetry Collector pods. This is only relevant to daemonset, statefulset, and deployment mode
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
false
upgradeStrategyenumresourceFieldRefobject - UpgradeStrategy represents how the operator will handle upgrades to the CR when a newer version of the operator is deployed
-
- Enum: automatic, none
+ Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
+
false
secretKeyRefobject + Selects a key of a secret in the pod's namespace
+
false
+ + +### Instrumentation.spec.nginx.attrs[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecnginxattrsindexvaluefrom) + + + +Selects a key of a ConfigMap. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key to select.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap or its key must be defined
+
false
+ + +### Instrumentation.spec.nginx.attrs[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecnginxattrsindexvaluefrom) + + + +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### Instrumentation.spec.nginx.attrs[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecnginxattrsindexvaluefrom) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### Instrumentation.spec.nginx.attrs[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecnginxattrsindexvaluefrom) + + + +Selects a key of a secret in the pod's namespace + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key of the secret to select from. Must be a valid secret key.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret or its key must be defined
+
false
+ + +### Instrumentation.spec.nginx.env[index] +[↩ Parent](#instrumentationspecnginx) + + + +EnvVar represents an environment variable present in a Container. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the environment variable. Must be a C_IDENTIFIER.
+
true
valuestring + Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
+
false
valueFromobject + Source for the environment variable's value. Cannot be used if value is not empty.
+
false
+ + +### Instrumentation.spec.nginx.env[index].valueFrom +[↩ Parent](#instrumentationspecnginxenvindex) + + + +Source for the environment variable's value. Cannot be used if value is not empty. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapKeyRefobject + Selects a key of a ConfigMap.
+
false
fieldRefobject + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
+
false
resourceFieldRefobject + Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
+
false
secretKeyRefobject + Selects a key of a secret in the pod's namespace
+
false
+ + +### Instrumentation.spec.nginx.env[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecnginxenvindexvaluefrom) + + + +Selects a key of a ConfigMap. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key to select.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap or its key must be defined
+
false
+ + +### Instrumentation.spec.nginx.env[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecnginxenvindexvaluefrom) + + + +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### Instrumentation.spec.nginx.env[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecnginxenvindexvaluefrom) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### Instrumentation.spec.nginx.env[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecnginxenvindexvaluefrom) + + + +Selects a key of a secret in the pod's namespace + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key of the secret to select from. Must be a valid secret key.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret or its key must be defined
+
false
+ + +### Instrumentation.spec.nginx.resourceRequirements +[↩ Parent](#instrumentationspecnginx) + + + +Resources describes the compute resource requirements. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limitsmap[string]int or string + Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
+
false
+ + +### Instrumentation.spec.nginx.resourceRequirements.claims[index] +[↩ Parent](#instrumentationspecnginxresourcerequirements) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### Instrumentation.spec.nodejs +[↩ Parent](#instrumentationspec) + + + +NodeJS defines configuration for nodejs auto-instrumentation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
env[]object + Env defines nodejs specific env vars.
+
false
imagestring + Image is a container image with NodeJS SDK and auto-instrumentation.
+
false
resourceRequirementsobject + Resources describes the compute resource requirements.
+
false
volumeLimitSizeint or string + VolumeSizeLimit defines size limit for volume used for auto-instrumentation. The default size is 200Mi.
+
false
+ + +### Instrumentation.spec.nodejs.env[index] +[↩ Parent](#instrumentationspecnodejs) + + + +EnvVar represents an environment variable present in a Container. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the environment variable. Must be a C_IDENTIFIER.
+
true
valuestring + Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
+
false
valueFromobject + Source for the environment variable's value. Cannot be used if value is not empty.
+
false
+ + +### Instrumentation.spec.nodejs.env[index].valueFrom +[↩ Parent](#instrumentationspecnodejsenvindex) + + + +Source for the environment variable's value. Cannot be used if value is not empty. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapKeyRefobject + Selects a key of a ConfigMap.
+
false
fieldRefobject + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
+
false
resourceFieldRefobject + Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
+
false
secretKeyRefobject + Selects a key of a secret in the pod's namespace
+
false
+ + +### Instrumentation.spec.nodejs.env[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecnodejsenvindexvaluefrom) + + + +Selects a key of a ConfigMap. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key to select.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap or its key must be defined
+
false
+ + +### Instrumentation.spec.nodejs.env[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecnodejsenvindexvaluefrom) + + + +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### Instrumentation.spec.nodejs.env[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecnodejsenvindexvaluefrom) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### Instrumentation.spec.nodejs.env[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecnodejsenvindexvaluefrom) + + + +Selects a key of a secret in the pod's namespace + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key of the secret to select from. Must be a valid secret key.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret or its key must be defined
+
false
+ + +### Instrumentation.spec.nodejs.resourceRequirements +[↩ Parent](#instrumentationspecnodejs) + + + +Resources describes the compute resource requirements. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limitsmap[string]int or string + Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
+
false
+ + +### Instrumentation.spec.nodejs.resourceRequirements.claims[index] +[↩ Parent](#instrumentationspecnodejsresourcerequirements) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### Instrumentation.spec.python +[↩ Parent](#instrumentationspec) + + + +Python defines configuration for python auto-instrumentation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
env[]object + Env defines python specific env vars.
+
false
imagestring + Image is a container image with Python SDK and auto-instrumentation.
+
false
resourceRequirementsobject + Resources describes the compute resource requirements.
+
false
volumeLimitSizeint or string + VolumeSizeLimit defines size limit for volume used for auto-instrumentation. The default size is 200Mi.
+
false
+ + +### Instrumentation.spec.python.env[index] +[↩ Parent](#instrumentationspecpython) + + + +EnvVar represents an environment variable present in a Container. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the environment variable. Must be a C_IDENTIFIER.
+
true
valuestring + Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
+
false
valueFromobject + Source for the environment variable's value. Cannot be used if value is not empty.
+
false
+ + +### Instrumentation.spec.python.env[index].valueFrom +[↩ Parent](#instrumentationspecpythonenvindex) + + + +Source for the environment variable's value. Cannot be used if value is not empty. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapKeyRefobject + Selects a key of a ConfigMap.
+
false
fieldRefobject + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
+
false
resourceFieldRefobject + Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
+
false
secretKeyRefobject + Selects a key of a secret in the pod's namespace
+
false
+ + +### Instrumentation.spec.python.env[index].valueFrom.configMapKeyRef +[↩ Parent](#instrumentationspecpythonenvindexvaluefrom) + + + +Selects a key of a ConfigMap. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key to select.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap or its key must be defined
+
false
+ + +### Instrumentation.spec.python.env[index].valueFrom.fieldRef +[↩ Parent](#instrumentationspecpythonenvindexvaluefrom) + + + +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### Instrumentation.spec.python.env[index].valueFrom.resourceFieldRef +[↩ Parent](#instrumentationspecpythonenvindexvaluefrom) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### Instrumentation.spec.python.env[index].valueFrom.secretKeyRef +[↩ Parent](#instrumentationspecpythonenvindexvaluefrom) + + + +Selects a key of a secret in the pod's namespace + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key of the secret to select from. Must be a valid secret key.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret or its key must be defined
+
false
+ + +### Instrumentation.spec.python.resourceRequirements +[↩ Parent](#instrumentationspecpython) + + + +Resources describes the compute resource requirements. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limitsmap[string]int or string + Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
+
false
+ + +### Instrumentation.spec.python.resourceRequirements.claims[index] +[↩ Parent](#instrumentationspecpythonresourcerequirements) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### Instrumentation.spec.resource +[↩ Parent](#instrumentationspec) + + + +Resource defines the configuration for the resource attributes, as defined by the OpenTelemetry specification. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
addK8sUIDAttributesboolean + AddK8sUIDAttributes defines whether K8s UID attributes should be collected (e.g. k8s.deployment.uid).
+
false
resourceAttributesmap[string]string + Attributes defines attributes that are added to the resource. For example environment: dev
+
false
+ + +### Instrumentation.spec.sampler +[↩ Parent](#instrumentationspec) + + + +Sampler defines sampling configuration. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
argumentstring + Argument defines sampler argument. The value depends on the sampler type. For instance for parentbased_traceidratio sampler type it is a number in range [0..1] e.g. 0.25.
+
false
typeenum + Type defines sampler type. The value will be set in the OTEL_TRACES_SAMPLER env var. The value can be for instance parentbased_always_on, parentbased_always_off, parentbased_traceidratio...
+
+ Enum: always_on, always_off, traceidratio, parentbased_always_on, parentbased_always_off, parentbased_traceidratio, jaeger_remote, xray
+
false
+ +## OpAMPBridge +[↩ Parent](#opentelemetryiov1alpha1 ) + + + + + + +OpAMPBridge is the Schema for the opampbridges API. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
apiVersionstringopentelemetry.io/v1alpha1true
kindstringOpAMPBridgetrue
metadataobjectRefer to the Kubernetes API documentation for the fields of the `metadata` field.true
specobject + OpAMPBridgeSpec defines the desired state of OpAMPBridge.
+
false
statusobject + OpAMPBridgeStatus defines the observed state of OpAMPBridge.
+
false
+ + +### OpAMPBridge.spec +[↩ Parent](#opampbridge) + + + +OpAMPBridgeSpec defines the desired state of OpAMPBridge. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
capabilitiesmap[string]boolean + Capabilities supported by the OpAMP Bridge
+
true
endpointstring + OpAMP backend Server endpoint
+
true
affinityobject + If specified, indicates the pod's scheduling constraints
+
false
componentsAllowedmap[string][]string + ComponentsAllowed is a list of allowed OpenTelemetry components for each pipeline type (receiver, processor, etc.)
+
false
env[]object + ENV vars to set on the OpAMPBridge Pods.
+
false
envFrom[]object + List of sources to populate environment variables on the OpAMPBridge Pods.
+
false
headersmap[string]string + Headers is an optional map of headers to use when connecting to the OpAMP Server, typically used to set access tokens or other authorization headers.
+
false
hostNetworkboolean + HostNetwork indicates if the pod should run in the host networking namespace.
+
false
imagestring + Image indicates the container image to use for the OpAMPBridge.
+
false
imagePullPolicystring + ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent)
+
false
nodeSelectormap[string]string + NodeSelector to schedule OpAMPBridge pods.
+
false
podAnnotationsmap[string]string + PodAnnotations is the set of annotations that will be attached to OpAMPBridge pods.
+
false
podSecurityContextobject + PodSecurityContext will be set as the pod security context.
+
false
ports[]object + Ports allows a set of ports to be exposed by the underlying v1.Service.
+
false
priorityClassNamestring + If specified, indicates the pod's priority. If not specified, the pod priority will be default or zero if there is no default.
+
false
replicasinteger + Replicas is the number of pod instances for the OpAMPBridge.
+
+ Format: int32
+ Maximum: 1
+
false
resourcesobject + Resources to set on the OpAMPBridge pods.
+
false
securityContextobject + SecurityContext will be set as the container security context.
+
false
serviceAccountstring + ServiceAccount indicates the name of an existing service account to use with this instance. When set, the operator will not automatically create a ServiceAccount for the OpAMPBridge.
+
false
tolerations[]object + Toleration to schedule OpAMPBridge pods.
+
false
topologySpreadConstraints[]object + TopologySpreadConstraints embedded kubernetes pod configuration option, controls how pods are spread across your cluster among failure-domains such as regions, zones, nodes, and other user-defined top
+
false
upgradeStrategyenum + UpgradeStrategy represents how the operator will handle upgrades to the CR when a newer version of the operator is deployed
+
+ Enum: automatic, none
+
false
volumeMounts[]object + VolumeMounts represents the mount points to use in the underlying OpAMPBridge deployment(s)
+
false
volumes[]object + Volumes represents which volumes to use in the underlying OpAMPBridge deployment(s).
+
false
+ + +### OpAMPBridge.spec.affinity +[↩ Parent](#opampbridgespec) + + + +If specified, indicates the pod's scheduling constraints + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
nodeAffinityobject + Describes node affinity scheduling rules for the pod.
+
false
podAffinityobject + Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)).
+
false
podAntiAffinityobject + Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)).
+
false
+ + +### OpAMPBridge.spec.affinity.nodeAffinity +[↩ Parent](#opampbridgespecaffinity) + + + +Describes node affinity scheduling rules for the pod. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferredDuringSchedulingIgnoredDuringExecution[]object + The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions.
+
false
requiredDuringSchedulingIgnoredDuringExecutionobject + If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node.
+
false
+ + +### OpAMPBridge.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opampbridgespecaffinitynodeaffinity) + + + +An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferenceobject + A node selector term, associated with the corresponding weight.
+
true
weightinteger + Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.
+
+ Format: int32
+
true
+ + +### OpAMPBridge.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].preference +[↩ Parent](#opampbridgespecaffinitynodeaffinitypreferredduringschedulingignoredduringexecutionindex) + + + +A node selector term, associated with the corresponding weight. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + A list of node selector requirements by node's labels.
+
false
matchFields[]object + A list of node selector requirements by node's fields.
+
false
+ + +### OpAMPBridge.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].preference.matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitynodeaffinitypreferredduringschedulingignoredduringexecutionindexpreference) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].preference.matchFields[index] +[↩ Parent](#opampbridgespecaffinitynodeaffinitypreferredduringschedulingignoredduringexecutionindexpreference) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution +[↩ Parent](#opampbridgespecaffinitynodeaffinity) + + + +If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
nodeSelectorTerms[]object + Required. A list of node selector terms. The terms are ORed.
+
true
+ + +### OpAMPBridge.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[index] +[↩ Parent](#opampbridgespecaffinitynodeaffinityrequiredduringschedulingignoredduringexecution) + + + +A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + A list of node selector requirements by node's labels.
+
false
matchFields[]object + A list of node selector requirements by node's fields.
+
false
+ + +### OpAMPBridge.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[index].matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitynodeaffinityrequiredduringschedulingignoredduringexecutionnodeselectortermsindex) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[index].matchFields[index] +[↩ Parent](#opampbridgespecaffinitynodeaffinityrequiredduringschedulingignoredduringexecutionnodeselectortermsindex) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity +[↩ Parent](#opampbridgespecaffinity) + + + +Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferredDuringSchedulingIgnoredDuringExecution[]object + The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions.
+
false
requiredDuringSchedulingIgnoredDuringExecution[]object + If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opampbridgespecaffinitypodaffinity) + + + +The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
podAffinityTermobject + Required. A pod affinity term, associated with the corresponding weight.
+
true
weightinteger + weight associated with matching the corresponding podAffinityTerm, in the range 1-100.
+
+ Format: int32
+
true
+ + +### OpAMPBridge.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm +[↩ Parent](#opampbridgespecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindex) + + + +Required. A pod affinity term, associated with the corresponding weight. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector +[↩ Parent](#opampbridgespecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector.matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector +[↩ Parent](#opampbridgespecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector.matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opampbridgespecaffinitypodaffinity) + + + +Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-locate + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector +[↩ Parent](#opampbridgespecaffinitypodaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector.matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitypodaffinityrequiredduringschedulingignoredduringexecutionindexlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector +[↩ Parent](#opampbridgespecaffinitypodaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector.matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitypodaffinityrequiredduringschedulingignoredduringexecutionindexnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity +[↩ Parent](#opampbridgespecaffinity) + + + +Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferredDuringSchedulingIgnoredDuringExecution[]object + The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions.
+
false
requiredDuringSchedulingIgnoredDuringExecution[]object + If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opampbridgespecaffinitypodantiaffinity) + + + +The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
podAffinityTermobject + Required. A pod affinity term, associated with the corresponding weight.
+
true
weightinteger + weight associated with matching the corresponding podAffinityTerm, in the range 1-100.
+
+ Format: int32
+
true
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm +[↩ Parent](#opampbridgespecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindex) + + + +Required. A pod affinity term, associated with the corresponding weight. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector +[↩ Parent](#opampbridgespecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector.matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector +[↩ Parent](#opampbridgespecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector.matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opampbridgespecaffinitypodantiaffinity) + + + +Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-locate + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector +[↩ Parent](#opampbridgespecaffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector.matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindexlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector +[↩ Parent](#opampbridgespecaffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector.matchExpressions[index] +[↩ Parent](#opampbridgespecaffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindexnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.env[index] +[↩ Parent](#opampbridgespec) + + + +EnvVar represents an environment variable present in a Container. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the environment variable. Must be a C_IDENTIFIER.
+
true
valuestring + Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
+
false
valueFromobject + Source for the environment variable's value. Cannot be used if value is not empty.
+
false
+ + +### OpAMPBridge.spec.env[index].valueFrom +[↩ Parent](#opampbridgespecenvindex) + + + +Source for the environment variable's value. Cannot be used if value is not empty. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapKeyRefobject + Selects a key of a ConfigMap.
+
false
fieldRefobject + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
+
false
resourceFieldRefobject + Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
+
false
secretKeyRefobject + Selects a key of a secret in the pod's namespace
+
false
+ + +### OpAMPBridge.spec.env[index].valueFrom.configMapKeyRef +[↩ Parent](#opampbridgespecenvindexvaluefrom) + + + +Selects a key of a ConfigMap. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key to select.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap or its key must be defined
+
false
+ + +### OpAMPBridge.spec.env[index].valueFrom.fieldRef +[↩ Parent](#opampbridgespecenvindexvaluefrom) + + + +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### OpAMPBridge.spec.env[index].valueFrom.resourceFieldRef +[↩ Parent](#opampbridgespecenvindexvaluefrom) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### OpAMPBridge.spec.env[index].valueFrom.secretKeyRef +[↩ Parent](#opampbridgespecenvindexvaluefrom) + + + +Selects a key of a secret in the pod's namespace + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key of the secret to select from. Must be a valid secret key.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret or its key must be defined
+
false
+ + +### OpAMPBridge.spec.envFrom[index] +[↩ Parent](#opampbridgespec) + + + +EnvFromSource represents the source of a set of ConfigMaps + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapRefobject + The ConfigMap to select from
+
false
prefixstring + An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.
+
false
secretRefobject + The Secret to select from
+
false
+ + +### OpAMPBridge.spec.envFrom[index].configMapRef +[↩ Parent](#opampbridgespecenvfromindex) + + + +The ConfigMap to select from + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap must be defined
+
false
+ + +### OpAMPBridge.spec.envFrom[index].secretRef +[↩ Parent](#opampbridgespecenvfromindex) + + + +The Secret to select from + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret must be defined
+
false
+ + +### OpAMPBridge.spec.podSecurityContext +[↩ Parent](#opampbridgespec) + + + +PodSecurityContext will be set as the pod security context. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fsGroupinteger + A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod: + 1.
+
+ Format: int64
+
false
fsGroupChangePolicystring + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod.
+
false
runAsGroupinteger + The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext.
+
+ Format: int64
+
false
runAsNonRootboolean + Indicates that the container must run as a non-root user.
+
false
runAsUserinteger + The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext.
+
+ Format: int64
+
false
seLinuxOptionsobject + The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext.
+
false
seccompProfileobject + The seccomp options to use by the containers in this pod. Note that this field cannot be set when spec.os.name is windows.
+
false
supplementalGroups[]integer + A list of groups applied to the first process run in each container, in addition to the container's primary GID, the fsGroup (if specified), and group memberships defined in the container image for th
+
false
sysctls[]object + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.
+
false
windowsOptionsobject + The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used.
+
false
+ + +### OpAMPBridge.spec.podSecurityContext.seLinuxOptions +[↩ Parent](#opampbridgespecpodsecuritycontext) + + + +The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
levelstring + Level is SELinux level label that applies to the container.
+
false
rolestring + Role is a SELinux role label that applies to the container.
+
false
typestring + Type is a SELinux type label that applies to the container.
+
false
userstring + User is a SELinux user label that applies to the container.
+
false
+ + +### OpAMPBridge.spec.podSecurityContext.seccompProfile +[↩ Parent](#opampbridgespecpodsecuritycontext) + + + +The seccomp options to use by the containers in this pod. Note that this field cannot be set when spec.os.name is windows. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
typestring + type indicates which kind of seccomp profile will be applied. Valid options are: + Localhost - a profile defined in a file on the node should be used.
+
true
localhostProfilestring + localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work.
+
false
+ + +### OpAMPBridge.spec.podSecurityContext.sysctls[index] +[↩ Parent](#opampbridgespecpodsecuritycontext) + + + +Sysctl defines a kernel parameter to be set + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of a property to set
+
true
valuestring + Value of a property to set
+
true
+ + +### OpAMPBridge.spec.podSecurityContext.windowsOptions +[↩ Parent](#opampbridgespecpodsecuritycontext) + + + +The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
gmsaCredentialSpecstring + GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.
+
false
gmsaCredentialSpecNamestring + GMSACredentialSpecName is the name of the GMSA credential spec to use.
+
false
hostProcessboolean + HostProcess determines if a container should be run as a 'Host Process' container.
+
false
runAsUserNamestring + The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
false
+ + +### OpAMPBridge.spec.ports[index] +[↩ Parent](#opampbridgespec) + + + +ServicePort contains information on service's port. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portinteger + The port that will be exposed by this service.
+
+ Format: int32
+
true
appProtocolstring + The application protocol for this port. This is used as a hint for implementations to offer richer behavior for protocols that they understand. This field follows standard Kubernetes label syntax.
+
false
namestring + The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names.
+
false
nodePortinteger + The port on each node on which this service is exposed when type is NodePort or LoadBalancer. Usually assigned by the system.
+
+ Format: int32
+
false
protocolstring + The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". Default is TCP.
+
+ Default: TCP
+
false
targetPortint or string + Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
false
+ + +### OpAMPBridge.spec.resources +[↩ Parent](#opampbridgespec) + + + +Resources to set on the OpAMPBridge pods. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limitsmap[string]int or string + Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
+
false
+ + +### OpAMPBridge.spec.resources.claims[index] +[↩ Parent](#opampbridgespecresources) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### OpAMPBridge.spec.securityContext +[↩ Parent](#opampbridgespec) + + + +SecurityContext will be set as the container security context. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
allowPrivilegeEscalationboolean + AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process.
+
false
capabilitiesobject + The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows.
+
false
privilegedboolean + Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.
+
false
procMountstring + procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths.
+
false
readOnlyRootFilesystemboolean + Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.
+
false
runAsGroupinteger + The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext.
+
+ Format: int64
+
false
runAsNonRootboolean + Indicates that the container must run as a non-root user.
+
false
runAsUserinteger + The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
+ Format: int64
+
false
seLinuxOptionsobject + The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext.
+
false
seccompProfileobject + The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options.
+
false
windowsOptionsobject + The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used.
+
false
+ + +### OpAMPBridge.spec.securityContext.capabilities +[↩ Parent](#opampbridgespecsecuritycontext) + + + +The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
add[]string + Added capabilities
+
false
drop[]string + Removed capabilities
+
false
+ + +### OpAMPBridge.spec.securityContext.seLinuxOptions +[↩ Parent](#opampbridgespecsecuritycontext) + + + +The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
levelstring + Level is SELinux level label that applies to the container.
+
false
rolestring + Role is a SELinux role label that applies to the container.
+
false
typestring + Type is a SELinux type label that applies to the container.
+
false
userstring + User is a SELinux user label that applies to the container.
+
false
+ + +### OpAMPBridge.spec.securityContext.seccompProfile +[↩ Parent](#opampbridgespecsecuritycontext) + + + +The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
typestring + type indicates which kind of seccomp profile will be applied. Valid options are: + Localhost - a profile defined in a file on the node should be used.
+
true
localhostProfilestring + localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work.
+
false
+ + +### OpAMPBridge.spec.securityContext.windowsOptions +[↩ Parent](#opampbridgespecsecuritycontext) + + + +The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
gmsaCredentialSpecstring + GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.
+
false
gmsaCredentialSpecNamestring + GMSACredentialSpecName is the name of the GMSA credential spec to use.
+
false
hostProcessboolean + HostProcess determines if a container should be run as a 'Host Process' container.
+
false
runAsUserNamestring + The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
false
+ + +### OpAMPBridge.spec.tolerations[index] +[↩ Parent](#opampbridgespec) + + + +The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
effectstring + Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
+
false
keystring + Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.
+
false
operatorstring + Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal.
+
false
tolerationSecondsinteger + TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint.
+
+ Format: int64
+
false
valuestring + Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.
+
false
+ + +### OpAMPBridge.spec.topologySpreadConstraints[index] +[↩ Parent](#opampbridgespec) + + + +TopologySpreadConstraint specifies how to spread matching pods among the given topology. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
maxSkewinteger + MaxSkew describes the degree to which pods may be unevenly distributed.
+
+ Format: int32
+
true
topologyKeystring + TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology.
+
true
whenUnsatisfiablestring + WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it.
+
true
labelSelectorobject + LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain.
+
false
matchLabelKeys[]string + MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated.
+
false
minDomainsinteger + MinDomains indicates a minimum number of eligible domains.
+
+ Format: int32
+
false
nodeAffinityPolicystring + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew.
+
false
nodeTaintsPolicystring + NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew.
+
false
+ + +### OpAMPBridge.spec.topologySpreadConstraints[index].labelSelector +[↩ Parent](#opampbridgespectopologyspreadconstraintsindex) + + + +LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.topologySpreadConstraints[index].labelSelector.matchExpressions[index] +[↩ Parent](#opampbridgespectopologyspreadconstraintsindexlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.volumeMounts[index] +[↩ Parent](#opampbridgespec) + + + +VolumeMount describes a mounting of a Volume within a container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
mountPathstring + Path within the container at which the volume should be mounted. Must not contain ':'.
+
true
namestring + This must match the Name of a Volume.
+
true
mountPropagationstring + mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.
+
false
readOnlyboolean + Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.
+
false
subPathstring + Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root).
+
false
subPathExprstring + Expanded path within the volume from which the container's volume should be mounted.
+
false
+ + +### OpAMPBridge.spec.volumes[index] +[↩ Parent](#opampbridgespec) + + + +Volume represents a named volume in a pod that may be accessed by any container in the pod. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + name of the volume. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+
true
awsElasticBlockStoreobject + awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.
+
false
azureDiskobject + azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
+
false
azureFileobject + azureFile represents an Azure File Service mount on the host and bind mount to the pod.
+
false
cephfsobject + cephFS represents a Ceph FS mount on the host that shares a pod's lifetime
+
false
cinderobject + cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md
+
false
configMapobject + configMap represents a configMap that should populate this volume
+
false
csiobject + csi (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature).
+
false
downwardAPIobject + downwardAPI represents downward API about the pod that should populate this volume
+
false
emptyDirobject + emptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir
+
false
ephemeralobject + ephemeral represents a volume that is handled by a cluster storage driver.
+
false
fcobject + fc represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod.
+
false
flexVolumeobject + flexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin.
+
false
flockerobject + flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running
+
false
gcePersistentDiskobject + gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.
+
false
gitRepoobject + gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated.
+
false
glusterfsobject + glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md
+
false
hostPathobject + hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container.
+
false
iscsiobject + iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md
+
false
nfsobject + nfs represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs
+
false
persistentVolumeClaimobject + persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.
+
false
photonPersistentDiskobject + photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine
+
false
portworxVolumeobject + portworxVolume represents a portworx volume attached and mounted on kubelets host machine
+
false
projectedobject + projected items for all in one resources secrets, configmaps, and downward API
+
false
quobyteobject + quobyte represents a Quobyte mount on the host that shares a pod's lifetime
+
false
rbdobject + rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md
+
false
scaleIOobject + scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
+
false
secretobject + secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret
+
false
storageosobject + storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
+
false
vsphereVolumeobject + vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine
+
false
+ + +### OpAMPBridge.spec.volumes[index].awsElasticBlockStore +[↩ Parent](#opampbridgespecvolumesindex) + + + +awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
volumeIDstring + volumeID is unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
+
true
fsTypestring + fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs".
+
false
partitioninteger + partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1".
+
+ Format: int32
+
false
readOnlyboolean + readOnly value true will force the readOnly setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
+
false
+ + +### OpAMPBridge.spec.volumes[index].azureDisk +[↩ Parent](#opampbridgespecvolumesindex) + + + +azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
diskNamestring + diskName is the Name of the data disk in the blob storage
+
true
diskURIstring + diskURI is the URI of data disk in the blob storage
+
true
cachingModestring + cachingMode is the Host Caching mode: None, Read Only, Read Write.
+
false
fsTypestring + fsType is Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
+
false
kindstring + kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set).
+
false
readOnlyboolean + readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.
+
false
+ + +### OpAMPBridge.spec.volumes[index].azureFile +[↩ Parent](#opampbridgespecvolumesindex) + + + +azureFile represents an Azure File Service mount on the host and bind mount to the pod. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
secretNamestring + secretName is the name of secret that contains Azure Storage Account Name and Key
+
true
shareNamestring + shareName is the azure share Name
+
true
readOnlyboolean + readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.
+
false
+ + +### OpAMPBridge.spec.volumes[index].cephfs +[↩ Parent](#opampbridgespecvolumesindex) + + + +cephFS represents a Ceph FS mount on the host that shares a pod's lifetime + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
monitors[]string + monitors is Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it
+
true
pathstring + path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /
+
false
readOnlyboolean + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it
+
false
secretFilestring + secretFile is Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it
+
false
secretRefobject + secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it
+
false
userstring + user is optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it
+
false
+ + +### OpAMPBridge.spec.volumes[index].cephfs.secretRef +[↩ Parent](#opampbridgespecvolumesindexcephfs) + + + +secretRef is Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
+ + +### OpAMPBridge.spec.volumes[index].cinder +[↩ Parent](#opampbridgespecvolumesindex) + + + +cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
volumeIDstring + volumeID used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md
+
true
fsTypestring + fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
+
false
readOnlyboolean + readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md
+
false
secretRefobject + secretRef is optional: points to a secret object containing parameters used to connect to OpenStack.
+
false
+ + +### OpAMPBridge.spec.volumes[index].cinder.secretRef +[↩ Parent](#opampbridgespecvolumesindexcinder) + + + +secretRef is optional: points to a secret object containing parameters used to connect to OpenStack. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
+ + +### OpAMPBridge.spec.volumes[index].configMap +[↩ Parent](#opampbridgespecvolumesindex) + + + +configMap represents a configMap that should populate this volume + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
defaultModeinteger + defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+
+ Format: int32
+
false
items[]object + items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value.
+
false
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + optional specify whether the ConfigMap or its keys must be defined
+
false
+ + +### OpAMPBridge.spec.volumes[index].configMap.items[index] +[↩ Parent](#opampbridgespecvolumesindexconfigmap) + + + +Maps a string key to a path within a volume. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the key to project.
+
true
pathstring + path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.
+
true
modeinteger + mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+
+ Format: int32
+
false
+ + +### OpAMPBridge.spec.volumes[index].csi +[↩ Parent](#opampbridgespecvolumesindex) + + + +csi (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
driverstring + driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.
+
true
fsTypestring + fsType to mount. Ex. "ext4", "xfs", "ntfs". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.
+
false
nodePublishSecretRefobject + nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls.
+
false
readOnlyboolean + readOnly specifies a read-only configuration for the volume. Defaults to false (read/write).
+
false
volumeAttributesmap[string]string + volumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.
+
false
+ + +### OpAMPBridge.spec.volumes[index].csi.nodePublishSecretRef +[↩ Parent](#opampbridgespecvolumesindexcsi) + + + +nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
+ + +### OpAMPBridge.spec.volumes[index].downwardAPI +[↩ Parent](#opampbridgespecvolumesindex) + + + +downwardAPI represents downward API about the pod that should populate this volume + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
defaultModeinteger + Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set permissions on created files by default.
+
+ Format: int32
+
false
items[]object + Items is a list of downward API volume file
+
false
+ + +### OpAMPBridge.spec.volumes[index].downwardAPI.items[index] +[↩ Parent](#opampbridgespecvolumesindexdownwardapi) + + + +DownwardAPIVolumeFile represents information to create the file containing the pod field + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
pathstring + Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'
+
true
fieldRefobject + Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.
+
false
modeinteger + Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+
+ Format: int32
+
false
resourceFieldRefobject + Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
+
false
+ + +### OpAMPBridge.spec.volumes[index].downwardAPI.items[index].fieldRef +[↩ Parent](#opampbridgespecvolumesindexdownwardapiitemsindex) + + + +Required: Selects a field of the pod: only annotations, labels, name and namespace are supported. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### OpAMPBridge.spec.volumes[index].downwardAPI.items[index].resourceFieldRef +[↩ Parent](#opampbridgespecvolumesindexdownwardapiitemsindex) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### OpAMPBridge.spec.volumes[index].emptyDir +[↩ Parent](#opampbridgespecvolumesindex) + + + +emptyDir represents a temporary directory that shares a pod's lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
mediumstring + medium represents what type of storage medium should back this directory. The default is "" which means to use the node's default medium. Must be an empty string (default) or Memory.
+
false
sizeLimitint or string + sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium.
+
false
+ + +### OpAMPBridge.spec.volumes[index].ephemeral +[↩ Parent](#opampbridgespecvolumesindex) + + + +ephemeral represents a volume that is handled by a cluster storage driver. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
volumeClaimTemplateobject + Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e.
+
false
+ + +### OpAMPBridge.spec.volumes[index].ephemeral.volumeClaimTemplate +[↩ Parent](#opampbridgespecvolumesindexephemeral) + + + +Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
specobject + The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template.
+
true
metadataobject + May contain labels and annotations that will be copied into the PVC when creating it. No other fields are allowed and will be rejected during validation.
+
false
+ + +### OpAMPBridge.spec.volumes[index].ephemeral.volumeClaimTemplate.spec +[↩ Parent](#opampbridgespecvolumesindexephemeralvolumeclaimtemplate) + + + +The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
accessModes[]string + accessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1
+
false
dataSourceobject + dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.
+
false
dataSourceRefobject + dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired.
+
false
resourcesobject + resources represents the minimum resources the volume should have.
+
false
selectorobject + selector is a label query over volumes to consider for binding.
+
false
storageClassNamestring + storageClassName is the name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1
+
false
volumeModestring + volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec.
+
false
volumeNamestring + volumeName is the binding reference to the PersistentVolume backing this claim.
+
false
+ + +### OpAMPBridge.spec.volumes[index].ephemeral.volumeClaimTemplate.spec.dataSource +[↩ Parent](#opampbridgespecvolumesindexephemeralvolumeclaimtemplatespec) + + + +dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
kindstring + Kind is the type of resource being referenced
+
true
namestring + Name is the name of resource being referenced
+
true
apiGroupstring + APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.
+
false
+ + +### OpAMPBridge.spec.volumes[index].ephemeral.volumeClaimTemplate.spec.dataSourceRef +[↩ Parent](#opampbridgespecvolumesindexephemeralvolumeclaimtemplatespec) + + + +dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
kindstring + Kind is the type of resource being referenced
+
true
namestring + Name is the name of resource being referenced
+
true
apiGroupstring + APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.
+
false
namespacestring + Namespace is the namespace of resource being referenced Note that when a namespace is specified, a gateway.networking.k8s.
+
false
+ + +### OpAMPBridge.spec.volumes[index].ephemeral.volumeClaimTemplate.spec.resources +[↩ Parent](#opampbridgespecvolumesindexephemeralvolumeclaimtemplatespec) + + + +resources represents the minimum resources the volume should have. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limitsmap[string]int or string + Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
+
false
+ + +### OpAMPBridge.spec.volumes[index].ephemeral.volumeClaimTemplate.spec.resources.claims[index] +[↩ Parent](#opampbridgespecvolumesindexephemeralvolumeclaimtemplatespecresources) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### OpAMPBridge.spec.volumes[index].ephemeral.volumeClaimTemplate.spec.selector +[↩ Parent](#opampbridgespecvolumesindexephemeralvolumeclaimtemplatespec) + + + +selector is a label query over volumes to consider for binding. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpAMPBridge.spec.volumes[index].ephemeral.volumeClaimTemplate.spec.selector.matchExpressions[index] +[↩ Parent](#opampbridgespecvolumesindexephemeralvolumeclaimtemplatespecselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpAMPBridge.spec.volumes[index].ephemeral.volumeClaimTemplate.metadata +[↩ Parent](#opampbridgespecvolumesindexephemeralvolumeclaimtemplate) + + + +May contain labels and annotations that will be copied into the PVC when creating it. No other fields are allowed and will be rejected during validation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
annotationsmap[string]string +
+
false
finalizers[]string +
+
false
labelsmap[string]string +
+
false
namestring +
+
false
namespacestring +
+
false
+ + +### OpAMPBridge.spec.volumes[index].fc +[↩ Parent](#opampbridgespecvolumesindex) + + + +fc represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fsTypestring + fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
+
false
luninteger + lun is Optional: FC target lun number
+
+ Format: int32
+
false
readOnlyboolean + readOnly is Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.
+
false
targetWWNs[]string + targetWWNs is Optional: FC target worldwide names (WWNs)
+
false
wwids[]string + wwids Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.
+
false
+ + +### OpAMPBridge.spec.volumes[index].flexVolume +[↩ Parent](#opampbridgespecvolumesindex) + + + +flexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
driverstring + driver is the name of the driver to use for this volume.
+
true
fsTypestring + fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script.
+
false
optionsmap[string]string + options is Optional: this field holds extra command options if any.
+
false
readOnlyboolean + readOnly is Optional: defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.
+
false
secretRefobject + secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified.
+
false
+ + +### OpAMPBridge.spec.volumes[index].flexVolume.secretRef +[↩ Parent](#opampbridgespecvolumesindexflexvolume) + + + +secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
+ + +### OpAMPBridge.spec.volumes[index].flocker +[↩ Parent](#opampbridgespecvolumesindex) + + + +flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
datasetNamestring + datasetName is Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated
+
false
datasetUUIDstring + datasetUUID is the UUID of the dataset. This is unique identifier of a Flocker dataset
+
false
+ + +### OpAMPBridge.spec.volumes[index].gcePersistentDisk +[↩ Parent](#opampbridgespecvolumesindex) + + + +gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
pdNamestring + pdName is unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
+
true
fsTypestring + fsType is filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs".
+
false
partitioninteger + partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1".
+
+ Format: int32
+
false
readOnlyboolean + readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
+
false
+ + +### OpAMPBridge.spec.volumes[index].gitRepo +[↩ Parent](#opampbridgespecvolumesindex) + + + +gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
repositorystring + repository is the URL
+
true
directorystring + directory is the target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository.
+
false
revisionstring + revision is the commit hash for the specified revision.
+
false
+ + +### OpAMPBridge.spec.volumes[index].glusterfs +[↩ Parent](#opampbridgespecvolumesindex) + + + +glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
endpointsstring + endpoints is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod
+
true
pathstring + path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod
+
true
readOnlyboolean + readOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod
+
false
+ + +### OpAMPBridge.spec.volumes[index].hostPath +[↩ Parent](#opampbridgespecvolumesindex) + + + +hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
pathstring + path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath
+
true
typestring + type for HostPath Volume Defaults to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath
+
false
+ + +### OpAMPBridge.spec.volumes[index].iscsi +[↩ Parent](#opampbridgespecvolumesindex) + + + +iscsi represents an ISCSI Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
iqnstring + iqn is the target iSCSI Qualified Name.
+
true
luninteger + lun represents iSCSI Target Lun number.
+
+ Format: int32
+
true
targetPortalstring + targetPortal is iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).
+
true
chapAuthDiscoveryboolean + chapAuthDiscovery defines whether support iSCSI Discovery CHAP authentication
+
false
chapAuthSessionboolean + chapAuthSession defines whether support iSCSI Session CHAP authentication
+
false
fsTypestring + fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs".
+
false
initiatorNamestring + initiatorName is the custom iSCSI Initiator Name.
+
false
iscsiInterfacestring + iscsiInterface is the interface Name that uses an iSCSI transport. Defaults to 'default' (tcp).
+
false
portals[]string + portals is the iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260).
+
false
readOnlyboolean + readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false.
+
false
secretRefobject + secretRef is the CHAP Secret for iSCSI target and initiator authentication
+
false
+ + +### OpAMPBridge.spec.volumes[index].iscsi.secretRef +[↩ Parent](#opampbridgespecvolumesindexiscsi) + + + +secretRef is the CHAP Secret for iSCSI target and initiator authentication + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
+ + +### OpAMPBridge.spec.volumes[index].nfs +[↩ Parent](#opampbridgespecvolumesindex) + + + +nfs represents an NFS mount on the host that shares a pod's lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
pathstring + path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs
+
true
serverstring + server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs
+
true
readOnlyboolean + readOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs
+
false
+ + +### OpAMPBridge.spec.volumes[index].persistentVolumeClaim +[↩ Parent](#opampbridgespecvolumesindex) + + + +persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
claimNamestring + claimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims
+
true
readOnlyboolean + readOnly Will force the ReadOnly setting in VolumeMounts. Default false.
+
false
+ + +### OpAMPBridge.spec.volumes[index].photonPersistentDisk +[↩ Parent](#opampbridgespecvolumesindex) + + + +photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
pdIDstring + pdID is the ID that identifies Photon Controller persistent disk
+
true
fsTypestring + fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
+
false
+ + +### OpAMPBridge.spec.volumes[index].portworxVolume +[↩ Parent](#opampbridgespecvolumesindex) + + + +portworxVolume represents a portworx volume attached and mounted on kubelets host machine + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
volumeIDstring + volumeID uniquely identifies a Portworx volume
+
true
fsTypestring + fSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified.
+
false
readOnlyboolean + readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected +[↩ Parent](#opampbridgespecvolumesindex) + + + +projected items for all in one resources secrets, configmaps, and downward API + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
defaultModeinteger + defaultMode are the mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+
+ Format: int32
+
false
sources[]object + sources is the list of volume projections
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index] +[↩ Parent](#opampbridgespecvolumesindexprojected) + + + +Projection that may be projected along with other supported volume types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapobject + configMap information about the configMap data to project
+
false
downwardAPIobject + downwardAPI information about the downwardAPI data to project
+
false
secretobject + secret information about the secret data to project
+
false
serviceAccountTokenobject + serviceAccountToken is information about the serviceAccountToken data to project
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index].configMap +[↩ Parent](#opampbridgespecvolumesindexprojectedsourcesindex) + + + +configMap information about the configMap data to project + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
items[]object + items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value.
+
false
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + optional specify whether the ConfigMap or its keys must be defined
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index].configMap.items[index] +[↩ Parent](#opampbridgespecvolumesindexprojectedsourcesindexconfigmap) + + + +Maps a string key to a path within a volume. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the key to project.
+
true
pathstring + path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.
+
true
modeinteger + mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+
+ Format: int32
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index].downwardAPI +[↩ Parent](#opampbridgespecvolumesindexprojectedsourcesindex) + + + +downwardAPI information about the downwardAPI data to project + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
items[]object + Items is a list of DownwardAPIVolume file
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index].downwardAPI.items[index] +[↩ Parent](#opampbridgespecvolumesindexprojectedsourcesindexdownwardapi) + + + +DownwardAPIVolumeFile represents information to create the file containing the pod field + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
pathstring + Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'
+
true
fieldRefobject + Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.
+
false
modeinteger + Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+
+ Format: int32
+
false
resourceFieldRefobject + Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index].downwardAPI.items[index].fieldRef +[↩ Parent](#opampbridgespecvolumesindexprojectedsourcesindexdownwardapiitemsindex) + + + +Required: Selects a field of the pod: only annotations, labels, name and namespace are supported. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index].downwardAPI.items[index].resourceFieldRef +[↩ Parent](#opampbridgespecvolumesindexprojectedsourcesindexdownwardapiitemsindex) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index].secret +[↩ Parent](#opampbridgespecvolumesindexprojectedsourcesindex) + + + +secret information about the secret data to project + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
items[]object + items if unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value.
+
false
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + optional field specify whether the Secret or its key must be defined
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index].secret.items[index] +[↩ Parent](#opampbridgespecvolumesindexprojectedsourcesindexsecret) + + + +Maps a string key to a path within a volume. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the key to project.
+
true
pathstring + path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.
+
true
modeinteger + mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+
+ Format: int32
+
false
+ + +### OpAMPBridge.spec.volumes[index].projected.sources[index].serviceAccountToken +[↩ Parent](#opampbridgespecvolumesindexprojectedsourcesindex) + + + +serviceAccountToken is information about the serviceAccountToken data to project + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
pathstring + path is the path relative to the mount point of the file to project the token into.
+
true
audiencestring + audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token.
+
false
expirationSecondsinteger + expirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token.
+
+ Format: int64
+
false
+ + +### OpAMPBridge.spec.volumes[index].quobyte +[↩ Parent](#opampbridgespecvolumesindex) + + + +quobyte represents a Quobyte mount on the host that shares a pod's lifetime + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
registrystring + registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes
+
true
volumestring + volume is a string that references an already created Quobyte volume by name.
+
true
groupstring + group to map volume access to Default is no group
+
false
readOnlyboolean + readOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false.
+
false
tenantstring + tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin
+
false
userstring + user to map volume access to Defaults to serivceaccount user
+
false
+ + +### OpAMPBridge.spec.volumes[index].rbd +[↩ Parent](#opampbridgespecvolumesindex) + + + +rbd represents a Rados Block Device mount on the host that shares a pod's lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
imagestring + image is the rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it
+
true
monitors[]string + monitors is a collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it
+
true
fsTypestring + fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs".
+
false
keyringstring + keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it
+
false
poolstring + pool is the rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it
+
false
readOnlyboolean + readOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it
+
false
secretRefobject + secretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it
+
false
userstring + user is the rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it
+
false
+ + +### OpAMPBridge.spec.volumes[index].rbd.secretRef +[↩ Parent](#opampbridgespecvolumesindexrbd) + + + +secretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
+ + +### OpAMPBridge.spec.volumes[index].scaleIO +[↩ Parent](#opampbridgespecvolumesindex) + + + +scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
gatewaystring + gateway is the host address of the ScaleIO API Gateway.
+
true
secretRefobject + secretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail.
+
true
systemstring + system is the name of the storage system as configured in ScaleIO.
+
true
fsTypestring + fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Default is "xfs".
+
false
protectionDomainstring + protectionDomain is the name of the ScaleIO Protection Domain for the configured storage.
+
false
readOnlyboolean + readOnly Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.
+
false
sslEnabledboolean + sslEnabled Flag enable/disable SSL communication with Gateway, default false
+
false
storageModestring + storageMode indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned.
+
false
storagePoolstring + storagePool is the ScaleIO Storage Pool associated with the protection domain.
+
false
volumeNamestring + volumeName is the name of a volume already created in the ScaleIO system that is associated with this volume source.
+
false
+ + +### OpAMPBridge.spec.volumes[index].scaleIO.secretRef +[↩ Parent](#opampbridgespecvolumesindexscaleio) + + + +secretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
+ + +### OpAMPBridge.spec.volumes[index].secret +[↩ Parent](#opampbridgespecvolumesindex) + + + +secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
defaultModeinteger + defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+
+ Format: int32
+
false
items[]object + items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value.
+
false
optionalboolean + optional field specify whether the Secret or its keys must be defined
+
false
secretNamestring + secretName is the name of the secret in the pod's namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret
+
false
+ + +### OpAMPBridge.spec.volumes[index].secret.items[index] +[↩ Parent](#opampbridgespecvolumesindexsecret) + + + +Maps a string key to a path within a volume. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the key to project.
+
true
pathstring + path is the relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'.
+
true
modeinteger + mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.
+
+ Format: int32
+
false
+ + +### OpAMPBridge.spec.volumes[index].storageos +[↩ Parent](#opampbridgespecvolumesindex) + + + +storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fsTypestring + fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
+
false
readOnlyboolean + readOnly defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.
+
false
secretRefobject + secretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted.
+
false
volumeNamestring + volumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace.
+
false
volumeNamespacestring + volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used.
+
false
+ + +### OpAMPBridge.spec.volumes[index].storageos.secretRef +[↩ Parent](#opampbridgespecvolumesindexstorageos) + + + +secretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
+ + +### OpAMPBridge.spec.volumes[index].vsphereVolume +[↩ Parent](#opampbridgespecvolumesindex) + + + +vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
volumePathstring + volumePath is the path that identifies vSphere volume vmdk
+
true
fsTypestring + fsType is filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
+
false
storagePolicyIDstring + storagePolicyID is the storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName.
+
false
storagePolicyNamestring + storagePolicyName is the storage Policy Based Management (SPBM) profile name.
+
false
+ + +### OpAMPBridge.status +[↩ Parent](#opampbridge) + + + +OpAMPBridgeStatus defines the observed state of OpAMPBridge. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
versionstring + Version of the managed OpAMP Bridge (operand)
+
false
+ +## OpenTelemetryCollector +[↩ Parent](#opentelemetryiov1alpha1 ) + + + + + + +OpenTelemetryCollector is the Schema for the opentelemetrycollectors API. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
apiVersionstringopentelemetry.io/v1alpha1true
kindstringOpenTelemetryCollectortrue
metadataobjectRefer to the Kubernetes API documentation for the fields of the `metadata` field.true
specobject + OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector.
+
false
statusobject + OpenTelemetryCollectorStatus defines the observed state of OpenTelemetryCollector.
+
false
+ + +### OpenTelemetryCollector.spec +[↩ Parent](#opentelemetrycollector) + + + +OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
additionalContainers[]object + AdditionalContainers allows injecting additional containers into the Collector's pod definition.
+
false
affinityobject + If specified, indicates the pod's scheduling constraints
+
false
argsmap[string]string + Args is the set of arguments to pass to the OpenTelemetry Collector binary
+
false
autoscalerobject + Autoscaler specifies the pod autoscaling configuration to use for the OpenTelemetryCollector workload.
+
false
configstring + Config is the raw JSON to be used as the collector's configuration. Refer to the OpenTelemetry Collector documentation for details.
+
false
configmaps[]object + ConfigMaps is a list of ConfigMaps in the same namespace as the OpenTelemetryCollector object, which shall be mounted into the Collector Pods.
+
false
env[]object + ENV vars to set on the OpenTelemetry Collector's Pods. These can then in certain cases be consumed in the config file for the Collector.
+
false
envFrom[]object + List of sources to populate environment variables on the OpenTelemetry Collector's Pods. These can then in certain cases be consumed in the config file for the Collector.
+
false
hostNetworkboolean + HostNetwork indicates if the pod should run in the host networking namespace.
+
false
imagestring + Image indicates the container image to use for the OpenTelemetry Collector.
+
false
imagePullPolicystring + ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent)
+
false
ingressobject + Ingress is used to specify how OpenTelemetry Collector is exposed. This functionality is only available if one of the valid modes is set. Valid modes are: deployment, daemonset and statefulset.
+
false
initContainers[]object + InitContainers allows injecting initContainers to the Collector's pod definition.
+
false
lifecycleobject + Actions that the management system should take in response to container lifecycle events. Cannot be updated.
+
false
livenessProbeobject + Liveness config for the OpenTelemetry Collector except the probe handler which is auto generated from the health extension of the collector.
+
false
managementStateenum + ManagementState defines if the CR should be managed by the operator or not. Default is managed.
+
+ Enum: managed, unmanaged
+ Default: managed
+
false
maxReplicasinteger + MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled. Deprecated: use "OpenTelemetryCollector.Spec.Autoscaler.MaxReplicas" instead.
+
+ Format: int32
+
false
minReplicasinteger + MinReplicas sets a lower bound to the autoscaling feature. Set this if you are using autoscaling. It must be at least 1 Deprecated: use "OpenTelemetryCollector.Spec.Autoscaler.MinReplicas" instead.
+
+ Format: int32
+
false
modeenum + Mode represents how the collector should be deployed (deployment, daemonset, statefulset or sidecar)
+
+ Enum: daemonset, deployment, sidecar, statefulset
+
false
nodeSelectormap[string]string + NodeSelector to schedule OpenTelemetry Collector pods. This is only relevant to daemonset, statefulset, and deployment mode
+
false
observabilityobject + ObservabilitySpec defines how telemetry data gets handled.
+
false
podAnnotationsmap[string]string + PodAnnotations is the set of annotations that will be attached to Collector and Target Allocator pods.
+
false
podDisruptionBudgetobject + PodDisruptionBudget specifies the pod disruption budget configuration to use for the OpenTelemetryCollector workload.
+
false
podSecurityContextobject + PodSecurityContext configures the pod security context for the opentelemetry-collector pod, when running as a deployment, daemonset, or statefulset.
+
false
ports[]object + Ports allows a set of ports to be exposed by the underlying v1.Service. By default, the operator will attempt to infer the required ports by parsing the .Spec.
+
false
priorityClassNamestring + If specified, indicates the pod's priority. If not specified, the pod priority will be default or zero if there is no default.
+
false
replicasinteger + Replicas is the number of pod instances for the underlying OpenTelemetry Collector. Set this if your are not using autoscaling
+
+ Format: int32
+
false
resourcesobject + Resources to set on the OpenTelemetry Collector pods.
+
false
securityContextobject + SecurityContext configures the container security context for the opentelemetry-collector container.
+
false
serviceAccountstring + ServiceAccount indicates the name of an existing service account to use with this instance. When set, the operator will not automatically create a ServiceAccount for the collector.
+
false
targetAllocatorobject + TargetAllocator indicates a value which determines whether to spawn a target allocation resource or not.
+
false
terminationGracePeriodSecondsinteger + Duration in seconds the pod needs to terminate gracefully upon probe failure.
+
+ Format: int64
+
false
tolerations[]object + Toleration to schedule OpenTelemetry Collector pods. This is only relevant to daemonset, statefulset, and deployment mode
+
false
topologySpreadConstraints[]object + TopologySpreadConstraints embedded kubernetes pod configuration option, controls how pods are spread across your cluster among failure-domains such as regions, zones, nodes, and other user-defined top
+
false
updateStrategyobject + UpdateStrategy represents the strategy the operator will take replacing existing DaemonSet pods with new pods https://kubernetes.
+
false
upgradeStrategyenum + UpgradeStrategy represents how the operator will handle upgrades to the CR when a newer version of the operator is deployed
+
+ Enum: automatic, none
+
false
volumeClaimTemplates[]object + VolumeClaimTemplates will provide stable storage using PersistentVolumes. Only available when the mode=statefulset.
+
false
volumeMounts[]object + VolumeMounts represents the mount points to use in the underlying collector deployment(s)
+
false
volumes[]object + Volumes represents which volumes to use in the underlying collector deployment(s).
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index] +[↩ Parent](#opentelemetrycollectorspec) + + + +A single application container that you want to run within a pod. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.
+
true
args[]string + Arguments to the entrypoint. The container image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment.
+
false
command[]string + Entrypoint array. Not executed within a shell. The container image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment.
+
false
env[]object + List of environment variables to set in the container. Cannot be updated.
+
false
envFrom[]object + List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER.
+
false
imagestring + Container image name. More info: https://kubernetes.
+
false
imagePullPolicystring + Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.
+
false
lifecycleobject + Actions that the management system should take in response to container lifecycle events. Cannot be updated.
+
false
livenessProbeobject + Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
false
ports[]object + List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default "0.0.0.
+
false
readinessProbeobject + Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.
+
false
resizePolicy[]object + Resources resize policy for the container.
+
false
resourcesobject + Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
restartPolicystring + RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is "Always".
+
false
securityContextobject + SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext.
+
false
startupProbeobject + StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully.
+
false
stdinboolean + Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.
+
false
stdinOnceboolean + Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions.
+
false
terminationMessagePathstring + Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem.
+
false
terminationMessagePolicystring + Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure.
+
false
ttyboolean + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.
+
false
volumeDevices[]object + volumeDevices is the list of block devices to be used by the container.
+
false
volumeMounts[]object + Pod volumes to mount into the container's filesystem. Cannot be updated.
+
false
workingDirstring + Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].env[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +EnvVar represents an environment variable present in a Container. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the environment variable. Must be a C_IDENTIFIER.
+
true
valuestring + Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
+
false
valueFromobject + Source for the environment variable's value. Cannot be used if value is not empty.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].env[index].valueFrom +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexenvindex) + + + +Source for the environment variable's value. Cannot be used if value is not empty. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapKeyRefobject + Selects a key of a ConfigMap.
+
false
fieldRefobject + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
+
false
resourceFieldRefobject + Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
+
false
secretKeyRefobject + Selects a key of a secret in the pod's namespace
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].env[index].valueFrom.configMapKeyRef +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexenvindexvaluefrom) + + + +Selects a key of a ConfigMap. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key to select.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap or its key must be defined
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].env[index].valueFrom.fieldRef +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexenvindexvaluefrom) + + + +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].env[index].valueFrom.resourceFieldRef +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexenvindexvaluefrom) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].env[index].valueFrom.secretKeyRef +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexenvindexvaluefrom) + + + +Selects a key of a secret in the pod's namespace + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key of the secret to select from. Must be a valid secret key.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret or its key must be defined
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].envFrom[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +EnvFromSource represents the source of a set of ConfigMaps + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapRefobject + The ConfigMap to select from
+
false
prefixstring + An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.
+
false
secretRefobject + The Secret to select from
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].envFrom[index].configMapRef +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexenvfromindex) + + + +The ConfigMap to select from + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap must be defined
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].envFrom[index].secretRef +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexenvfromindex) + + + +The Secret to select from + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret must be defined
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +Actions that the management system should take in response to container lifecycle events. Cannot be updated. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
postStartobject + PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy.
+
false
preStopobject + PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.postStart +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecycle) + + + +PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
tcpSocketobject + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.postStart.exec +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecyclepoststart) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.postStart.httpGet +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecyclepoststart) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.postStart.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecyclepoststarthttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.postStart.tcpSocket +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecyclepoststart) + + + +Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.preStop +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecycle) + + + +PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
tcpSocketobject + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.preStop.exec +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecycleprestop) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.preStop.httpGet +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecycleprestop) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.preStop.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecycleprestophttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].lifecycle.preStop.tcpSocket +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlifecycleprestop) + + + +Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].livenessProbe +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
failureThresholdinteger + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.
+
+ Format: int32
+
false
grpcobject + GRPC specifies an action involving a GRPC port.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
initialDelaySecondsinteger + Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
periodSecondsinteger + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.
+
+ Format: int32
+
false
successThresholdinteger + Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+
+ Format: int32
+
false
tcpSocketobject + TCPSocket specifies an action involving a TCP port.
+
false
terminationGracePeriodSecondsinteger + Optional duration in seconds the pod needs to terminate gracefully upon probe failure.
+
+ Format: int64
+
false
timeoutSecondsinteger + Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].livenessProbe.exec +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlivenessprobe) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].livenessProbe.grpc +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlivenessprobe) + + + +GRPC specifies an action involving a GRPC port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portinteger + Port number of the gRPC service. Number must be in the range 1 to 65535.
+
+ Format: int32
+
true
servicestring + Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].livenessProbe.httpGet +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlivenessprobe) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].livenessProbe.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlivenessprobehttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].livenessProbe.tcpSocket +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexlivenessprobe) + + + +TCPSocket specifies an action involving a TCP port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].ports[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +ContainerPort represents a network port in a single container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
containerPortinteger + Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.
+
+ Format: int32
+
true
hostIPstring + What host IP to bind the external port to.
+
false
hostPortinteger + Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.
+
+ Format: int32
+
false
namestring + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.
+
false
protocolstring + Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP".
+
+ Default: TCP
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].readinessProbe +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
failureThresholdinteger + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.
+
+ Format: int32
+
false
grpcobject + GRPC specifies an action involving a GRPC port.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
initialDelaySecondsinteger + Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
periodSecondsinteger + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.
+
+ Format: int32
+
false
successThresholdinteger + Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+
+ Format: int32
+
false
tcpSocketobject + TCPSocket specifies an action involving a TCP port.
+
false
terminationGracePeriodSecondsinteger + Optional duration in seconds the pod needs to terminate gracefully upon probe failure.
+
+ Format: int64
+
false
timeoutSecondsinteger + Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].readinessProbe.exec +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexreadinessprobe) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].readinessProbe.grpc +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexreadinessprobe) + + + +GRPC specifies an action involving a GRPC port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portinteger + Port number of the gRPC service. Number must be in the range 1 to 65535.
+
+ Format: int32
+
true
servicestring + Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].readinessProbe.httpGet +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexreadinessprobe) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].readinessProbe.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexreadinessprobehttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].readinessProbe.tcpSocket +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexreadinessprobe) + + + +TCPSocket specifies an action involving a TCP port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].resizePolicy[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +ContainerResizePolicy represents resource resize policy for the container. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourceNamestring + Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.
+
true
restartPolicystring + Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired.
+
true
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].resources +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limitsmap[string]int or string + Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].resources.claims[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexresources) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].securityContext +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
allowPrivilegeEscalationboolean + AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process.
+
false
capabilitiesobject + The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows.
+
false
privilegedboolean + Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.
+
false
procMountstring + procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths.
+
false
readOnlyRootFilesystemboolean + Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.
+
false
runAsGroupinteger + The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext.
+
+ Format: int64
+
false
runAsNonRootboolean + Indicates that the container must run as a non-root user.
+
false
runAsUserinteger + The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
+ Format: int64
+
false
seLinuxOptionsobject + The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext.
+
false
seccompProfileobject + The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options.
+
false
windowsOptionsobject + The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].securityContext.capabilities +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexsecuritycontext) + + + +The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
add[]string + Added capabilities
+
false
drop[]string + Removed capabilities
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].securityContext.seLinuxOptions +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexsecuritycontext) + + + +The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
levelstring + Level is SELinux level label that applies to the container.
+
false
rolestring + Role is a SELinux role label that applies to the container.
+
false
typestring + Type is a SELinux type label that applies to the container.
+
false
userstring + User is a SELinux user label that applies to the container.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].securityContext.seccompProfile +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexsecuritycontext) + + + +The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
typestring + type indicates which kind of seccomp profile will be applied. Valid options are: + Localhost - a profile defined in a file on the node should be used.
+
true
localhostProfilestring + localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].securityContext.windowsOptions +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexsecuritycontext) + + + +The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
gmsaCredentialSpecstring + GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.
+
false
gmsaCredentialSpecNamestring + GMSACredentialSpecName is the name of the GMSA credential spec to use.
+
false
hostProcessboolean + HostProcess determines if a container should be run as a 'Host Process' container.
+
false
runAsUserNamestring + The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].startupProbe +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
failureThresholdinteger + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.
+
+ Format: int32
+
false
grpcobject + GRPC specifies an action involving a GRPC port.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
initialDelaySecondsinteger + Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
periodSecondsinteger + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.
+
+ Format: int32
+
false
successThresholdinteger + Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+
+ Format: int32
+
false
tcpSocketobject + TCPSocket specifies an action involving a TCP port.
+
false
terminationGracePeriodSecondsinteger + Optional duration in seconds the pod needs to terminate gracefully upon probe failure.
+
+ Format: int64
+
false
timeoutSecondsinteger + Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].startupProbe.exec +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexstartupprobe) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].startupProbe.grpc +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexstartupprobe) + + + +GRPC specifies an action involving a GRPC port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portinteger + Port number of the gRPC service. Number must be in the range 1 to 65535.
+
+ Format: int32
+
true
servicestring + Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].startupProbe.httpGet +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexstartupprobe) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].startupProbe.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexstartupprobehttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].startupProbe.tcpSocket +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindexstartupprobe) + + + +TCPSocket specifies an action involving a TCP port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].volumeDevices[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +volumeDevice describes a mapping of a raw block device within a container. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
devicePathstring + devicePath is the path inside of the container that the device will be mapped to.
+
true
namestring + name must match the name of a persistentVolumeClaim in the pod
+
true
+ + +### OpenTelemetryCollector.spec.additionalContainers[index].volumeMounts[index] +[↩ Parent](#opentelemetrycollectorspecadditionalcontainersindex) + + + +VolumeMount describes a mounting of a Volume within a container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
mountPathstring + Path within the container at which the volume should be mounted. Must not contain ':'.
+
true
namestring + This must match the Name of a Volume.
+
true
mountPropagationstring + mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.
+
false
readOnlyboolean + Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.
+
false
subPathstring + Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root).
+
false
subPathExprstring + Expanded path within the volume from which the container's volume should be mounted.
+
false
+ + +### OpenTelemetryCollector.spec.affinity +[↩ Parent](#opentelemetrycollectorspec) + + + +If specified, indicates the pod's scheduling constraints + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
nodeAffinityobject + Describes node affinity scheduling rules for the pod.
+
false
podAffinityobject + Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)).
+
false
podAntiAffinityobject + Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)).
+
false
+ + +### OpenTelemetryCollector.spec.affinity.nodeAffinity +[↩ Parent](#opentelemetrycollectorspecaffinity) + + + +Describes node affinity scheduling rules for the pod. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferredDuringSchedulingIgnoredDuringExecution[]object + The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions.
+
false
requiredDuringSchedulingIgnoredDuringExecutionobject + If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspecaffinitynodeaffinity) + + + +An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferenceobject + A node selector term, associated with the corresponding weight.
+
true
weightinteger + Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.
+
+ Format: int32
+
true
+ + +### OpenTelemetryCollector.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].preference +[↩ Parent](#opentelemetrycollectorspecaffinitynodeaffinitypreferredduringschedulingignoredduringexecutionindex) + + + +A node selector term, associated with the corresponding weight. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + A list of node selector requirements by node's labels.
+
false
matchFields[]object + A list of node selector requirements by node's fields.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].preference.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitynodeaffinitypreferredduringschedulingignoredduringexecutionindexpreference) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].preference.matchFields[index] +[↩ Parent](#opentelemetrycollectorspecaffinitynodeaffinitypreferredduringschedulingignoredduringexecutionindexpreference) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution +[↩ Parent](#opentelemetrycollectorspecaffinitynodeaffinity) + + + +If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
nodeSelectorTerms[]object + Required. A list of node selector terms. The terms are ORed.
+
true
+ + +### OpenTelemetryCollector.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[index] +[↩ Parent](#opentelemetrycollectorspecaffinitynodeaffinityrequiredduringschedulingignoredduringexecution) + + + +A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + A list of node selector requirements by node's labels.
+
false
matchFields[]object + A list of node selector requirements by node's fields.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[index].matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitynodeaffinityrequiredduringschedulingignoredduringexecutionnodeselectortermsindex) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[index].matchFields[index] +[↩ Parent](#opentelemetrycollectorspecaffinitynodeaffinityrequiredduringschedulingignoredduringexecutionnodeselectortermsindex) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity +[↩ Parent](#opentelemetrycollectorspecaffinity) + + + +Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferredDuringSchedulingIgnoredDuringExecution[]object + The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions.
+
false
requiredDuringSchedulingIgnoredDuringExecution[]object + If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinity) + + + +The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
podAffinityTermobject + Required. A pod affinity term, associated with the corresponding weight.
+
true
weightinteger + weight associated with matching the corresponding podAffinityTerm, in the range 1-100.
+
+ Format: int32
+
true
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindex) + + + +Required. A pod affinity term, associated with the corresponding weight. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinity) + + + +Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-locate + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinityrequiredduringschedulingignoredduringexecutionindexlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodaffinityrequiredduringschedulingignoredduringexecutionindexnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity +[↩ Parent](#opentelemetrycollectorspecaffinity) + + + +Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferredDuringSchedulingIgnoredDuringExecution[]object + The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions.
+
false
requiredDuringSchedulingIgnoredDuringExecution[]object + If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinity) + + + +The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
podAffinityTermobject + Required. A pod affinity term, associated with the corresponding weight.
+
true
weightinteger + weight associated with matching the corresponding podAffinityTerm, in the range 1-100.
+
+ Format: int32
+
true
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindex) + + + +Required. A pod affinity term, associated with the corresponding weight. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinity) + + + +Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-locate + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindexlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecaffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindexnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.autoscaler +[↩ Parent](#opentelemetrycollectorspec) + + + +Autoscaler specifies the pod autoscaling configuration to use for the OpenTelemetryCollector workload. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
behaviorobject + HorizontalPodAutoscalerBehavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively).
+
false
maxReplicasinteger + MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled.
+
+ Format: int32
+
false
metrics[]object + Metrics is meant to provide a customizable way to configure HPA metrics. currently the only supported custom metrics is type=Pod.
+
false
minReplicasinteger + MinReplicas sets a lower bound to the autoscaling feature. Set this if your are using autoscaling. It must be at least 1
+
+ Format: int32
+
false
targetCPUUtilizationinteger + TargetCPUUtilization sets the target average CPU used across all replicas. If average CPU exceeds this value, the HPA will scale up. Defaults to 90 percent.
+
+ Format: int32
+
false
targetMemoryUtilizationinteger + TargetMemoryUtilization sets the target average memory utilization across all replicas
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.autoscaler.behavior +[↩ Parent](#opentelemetrycollectorspecautoscaler) + + + +HorizontalPodAutoscalerBehavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
scaleDownobject + scaleDown is scaling policy for scaling Down. If not set, the default value is to allow to scale down to minReplicas pods, with a 300 second stabilization window (i.e.
+
false
scaleUpobject + scaleUp is scaling policy for scaling Up.
+
false
+ + +### OpenTelemetryCollector.spec.autoscaler.behavior.scaleDown +[↩ Parent](#opentelemetrycollectorspecautoscalerbehavior) + + + +scaleDown is scaling policy for scaling Down. If not set, the default value is to allow to scale down to minReplicas pods, with a 300 second stabilization window (i.e. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
policies[]object + policies is a list of potential scaling polices which can be used during scaling. At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid
+
false
selectPolicystring + selectPolicy is used to specify which policy should be used. If not set, the default value Max is used.
+
false
stabilizationWindowSecondsinteger + stabilizationWindowSeconds is the number of seconds for which past recommendations should be considered while scaling up or scaling down.
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.autoscaler.behavior.scaleDown.policies[index] +[↩ Parent](#opentelemetrycollectorspecautoscalerbehaviorscaledown) + + + +HPAScalingPolicy is a single policy which must hold true for a specified past interval. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
periodSecondsinteger + periodSeconds specifies the window of time for which the policy should hold true. PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min).
+
+ Format: int32
+
true
typestring + type is used to specify the scaling policy.
+
true
valueinteger + value contains the amount of change which is permitted by the policy. It must be greater than zero
+
+ Format: int32
+
true
+ + +### OpenTelemetryCollector.spec.autoscaler.behavior.scaleUp +[↩ Parent](#opentelemetrycollectorspecautoscalerbehavior) + + + +scaleUp is scaling policy for scaling Up. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
policies[]object + policies is a list of potential scaling polices which can be used during scaling. At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid
+
false
selectPolicystring + selectPolicy is used to specify which policy should be used. If not set, the default value Max is used.
+
false
stabilizationWindowSecondsinteger + stabilizationWindowSeconds is the number of seconds for which past recommendations should be considered while scaling up or scaling down.
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.autoscaler.behavior.scaleUp.policies[index] +[↩ Parent](#opentelemetrycollectorspecautoscalerbehaviorscaleup) + + + +HPAScalingPolicy is a single policy which must hold true for a specified past interval. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
periodSecondsinteger + periodSeconds specifies the window of time for which the policy should hold true. PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min).
+
+ Format: int32
+
true
typestring + type is used to specify the scaling policy.
+
true
valueinteger + value contains the amount of change which is permitted by the policy. It must be greater than zero
+
+ Format: int32
+
true
+ + +### OpenTelemetryCollector.spec.autoscaler.metrics[index] +[↩ Parent](#opentelemetrycollectorspecautoscaler) + + + +MetricSpec defines a subset of metrics to be defined for the HPA's metric array more metric type can be supported as needed. See https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec for reference. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
typestring + MetricSourceType indicates the type of metric.
+
true
podsobject + PodsMetricSource indicates how to scale on a metric describing each pod in the current scale target (for example, transactions-processed-per-second).
+
false
+ + +### OpenTelemetryCollector.spec.autoscaler.metrics[index].pods +[↩ Parent](#opentelemetrycollectorspecautoscalermetricsindex) + + + +PodsMetricSource indicates how to scale on a metric describing each pod in the current scale target (for example, transactions-processed-per-second). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
metricobject + metric identifies the target metric by name and selector
+
true
targetobject + target specifies the target value for the given metric
+
true
+ + +### OpenTelemetryCollector.spec.autoscaler.metrics[index].pods.metric +[↩ Parent](#opentelemetrycollectorspecautoscalermetricsindexpods) + + + +metric identifies the target metric by name and selector + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + name is the name of the given metric
+
true
selectorobject + selector is the string-encoded form of a standard kubernetes label selector for the given metric When set, it is passed as an additional parameter to the metrics server for more specific metrics scopi
+
false
+ + +### OpenTelemetryCollector.spec.autoscaler.metrics[index].pods.metric.selector +[↩ Parent](#opentelemetrycollectorspecautoscalermetricsindexpodsmetric) + + + +selector is the string-encoded form of a standard kubernetes label selector for the given metric When set, it is passed as an additional parameter to the metrics server for more specific metrics scopi + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.autoscaler.metrics[index].pods.metric.selector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspecautoscalermetricsindexpodsmetricselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.autoscaler.metrics[index].pods.target +[↩ Parent](#opentelemetrycollectorspecautoscalermetricsindexpods) + + + +target specifies the target value for the given metric + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
typestring + type represents whether the metric type is Utilization, Value, or AverageValue
+
true
averageUtilizationinteger + averageUtilization is the target value of the average of the resource metric across all relevant pods, represented as a percentage of the requested value of the resource for the pods.
+
+ Format: int32
+
false
averageValueint or string + averageValue is the target value of the average of the metric across all relevant pods (as a quantity)
+
false
valueint or string + value is the target value of the metric (as a quantity).
+
false
+ + +### OpenTelemetryCollector.spec.configmaps[index] +[↩ Parent](#opentelemetrycollectorspec) + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
mountpathstring +
+
true
namestring + Configmap defines name and path where the configMaps should be mounted.
+
true
+ + +### OpenTelemetryCollector.spec.env[index] +[↩ Parent](#opentelemetrycollectorspec) + + + +EnvVar represents an environment variable present in a Container. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the environment variable. Must be a C_IDENTIFIER.
+
true
valuestring + Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
+
false
valueFromobject + Source for the environment variable's value. Cannot be used if value is not empty.
+
false
+ + +### OpenTelemetryCollector.spec.env[index].valueFrom +[↩ Parent](#opentelemetrycollectorspecenvindex) + + + +Source for the environment variable's value. Cannot be used if value is not empty. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapKeyRefobject + Selects a key of a ConfigMap.
+
false
fieldRefobject + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
+
false
resourceFieldRefobject + Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
+
false
secretKeyRefobject + Selects a key of a secret in the pod's namespace
+
false
+ + +### OpenTelemetryCollector.spec.env[index].valueFrom.configMapKeyRef +[↩ Parent](#opentelemetrycollectorspecenvindexvaluefrom) + + + +Selects a key of a ConfigMap. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key to select.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap or its key must be defined
+
false
+ + +### OpenTelemetryCollector.spec.env[index].valueFrom.fieldRef +[↩ Parent](#opentelemetrycollectorspecenvindexvaluefrom) + + + +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### OpenTelemetryCollector.spec.env[index].valueFrom.resourceFieldRef +[↩ Parent](#opentelemetrycollectorspecenvindexvaluefrom) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### OpenTelemetryCollector.spec.env[index].valueFrom.secretKeyRef +[↩ Parent](#opentelemetrycollectorspecenvindexvaluefrom) + + + +Selects a key of a secret in the pod's namespace + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key of the secret to select from. Must be a valid secret key.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret or its key must be defined
+
false
+ + +### OpenTelemetryCollector.spec.envFrom[index] +[↩ Parent](#opentelemetrycollectorspec) + + + +EnvFromSource represents the source of a set of ConfigMaps + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapRefobject + The ConfigMap to select from
+
false
prefixstring + An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.
+
false
secretRefobject + The Secret to select from
+
false
+ + +### OpenTelemetryCollector.spec.envFrom[index].configMapRef +[↩ Parent](#opentelemetrycollectorspecenvfromindex) + + + +The ConfigMap to select from + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap must be defined
+
false
+ + +### OpenTelemetryCollector.spec.envFrom[index].secretRef +[↩ Parent](#opentelemetrycollectorspecenvfromindex) + + + +The Secret to select from + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret must be defined
+
false
+ + +### OpenTelemetryCollector.spec.ingress +[↩ Parent](#opentelemetrycollectorspec) + + + +Ingress is used to specify how OpenTelemetry Collector is exposed. This functionality is only available if one of the valid modes is set. Valid modes are: deployment, daemonset and statefulset. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
annotationsmap[string]string + Annotations to add to ingress. e.g. 'cert-manager.io/cluster-issuer: "letsencrypt"'
+
false
hostnamestring + Hostname by which the ingress proxy can be reached.
+
false
ingressClassNamestring + IngressClassName is the name of an IngressClass cluster resource. Ingress controller implementations use this field to know whether they should be serving this Ingress resource.
+
false
routeobject + Route is an OpenShift specific section that is only considered when type "route" is used.
+
false
ruleTypeenum + RuleType defines how Ingress exposes collector receivers. IngressRuleTypePath ("path") exposes each receiver port on a unique path on single domain defined in Hostname.
+
+ Enum: path, subdomain
+
false
tls[]object + TLS configuration.
+
false
typeenum + Type default value is: "" Supported types are: ingress, route
+
+ Enum: ingress, route
+
false
+ + +### OpenTelemetryCollector.spec.ingress.route +[↩ Parent](#opentelemetrycollectorspecingress) + + + +Route is an OpenShift specific section that is only considered when type "route" is used. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
terminationenum + Termination indicates termination type. By default "edge" is used.
+
+ Enum: insecure, edge, passthrough, reencrypt
+
false
+ + +### OpenTelemetryCollector.spec.ingress.tls[index] +[↩ Parent](#opentelemetrycollectorspecingress) + + + +IngressTLS describes the transport layer security associated with an ingress. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
hosts[]string + hosts is a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret.
+
false
secretNamestring + secretName is the name of the secret used to terminate TLS traffic on port 443. Field is left optional to allow TLS routing based on SNI hostname alone.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index] +[↩ Parent](#opentelemetrycollectorspec) + + + +A single application container that you want to run within a pod. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.
+
true
args[]string + Arguments to the entrypoint. The container image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment.
+
false
command[]string + Entrypoint array. Not executed within a shell. The container image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment.
+
false
env[]object + List of environment variables to set in the container. Cannot be updated.
+
false
envFrom[]object + List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER.
+
false
imagestring + Container image name. More info: https://kubernetes.
+
false
imagePullPolicystring + Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.
+
false
lifecycleobject + Actions that the management system should take in response to container lifecycle events. Cannot be updated.
+
false
livenessProbeobject + Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
false
ports[]object + List of ports to expose from the container. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default "0.0.0.
+
false
readinessProbeobject + Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes.
+
false
resizePolicy[]object + Resources resize policy for the container.
+
false
resourcesobject + Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
restartPolicystring + RestartPolicy defines the restart behavior of individual containers in a pod. This field may only be set for init containers, and the only allowed value is "Always".
+
false
securityContextobject + SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext.
+
false
startupProbeobject + StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully.
+
false
stdinboolean + Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.
+
false
stdinOnceboolean + Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions.
+
false
terminationMessagePathstring + Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem.
+
false
terminationMessagePolicystring + Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure.
+
false
ttyboolean + Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.
+
false
volumeDevices[]object + volumeDevices is the list of block devices to be used by the container.
+
false
volumeMounts[]object + Pod volumes to mount into the container's filesystem. Cannot be updated.
+
false
workingDirstring + Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].env[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +EnvVar represents an environment variable present in a Container. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the environment variable. Must be a C_IDENTIFIER.
+
true
valuestring + Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
+
false
valueFromobject + Source for the environment variable's value. Cannot be used if value is not empty.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].env[index].valueFrom +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexenvindex) + + + +Source for the environment variable's value. Cannot be used if value is not empty. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapKeyRefobject + Selects a key of a ConfigMap.
+
false
fieldRefobject + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
+
false
resourceFieldRefobject + Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
+
false
secretKeyRefobject + Selects a key of a secret in the pod's namespace
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].env[index].valueFrom.configMapKeyRef +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexenvindexvaluefrom) + + + +Selects a key of a ConfigMap. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key to select.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap or its key must be defined
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].env[index].valueFrom.fieldRef +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexenvindexvaluefrom) + + + +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fieldPathstring + Path of the field to select in the specified API version.
+
true
apiVersionstring + Version of the schema the FieldPath is written in terms of, defaults to "v1".
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].env[index].valueFrom.resourceFieldRef +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexenvindexvaluefrom) + + + +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourcestring + Required: resource to select
+
true
containerNamestring + Container name: required for volumes, optional for env vars
+
false
divisorint or string + Specifies the output format of the exposed resources, defaults to "1"
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].env[index].valueFrom.secretKeyRef +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexenvindexvaluefrom) + + + +Selects a key of a secret in the pod's namespace + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The key of the secret to select from. Must be a valid secret key.
+
true
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret or its key must be defined
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].envFrom[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +EnvFromSource represents the source of a set of ConfigMaps + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
configMapRefobject + The ConfigMap to select from
+
false
prefixstring + An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.
+
false
secretRefobject + The Secret to select from
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].envFrom[index].configMapRef +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexenvfromindex) + + + +The ConfigMap to select from + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the ConfigMap must be defined
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].envFrom[index].secretRef +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexenvfromindex) + + + +The Secret to select from + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+
false
optionalboolean + Specify whether the Secret must be defined
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +Actions that the management system should take in response to container lifecycle events. Cannot be updated. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
postStartobject + PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy.
+
false
preStopobject + PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.postStart +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecycle) + + + +PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
tcpSocketobject + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.postStart.exec +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecyclepoststart) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.postStart.httpGet +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecyclepoststart) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.postStart.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecyclepoststarthttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.postStart.tcpSocket +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecyclepoststart) + + + +Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.preStop +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecycle) + + + +PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
tcpSocketobject + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.preStop.exec +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecycleprestop) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.preStop.httpGet +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecycleprestop) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.preStop.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecycleprestophttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.initContainers[index].lifecycle.preStop.tcpSocket +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlifecycleprestop) + + + +Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].livenessProbe +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +Periodic probe of container liveness. Container will be restarted if the probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
failureThresholdinteger + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.
+
+ Format: int32
+
false
grpcobject + GRPC specifies an action involving a GRPC port.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
initialDelaySecondsinteger + Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
periodSecondsinteger + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.
+
+ Format: int32
+
false
successThresholdinteger + Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+
+ Format: int32
+
false
tcpSocketobject + TCPSocket specifies an action involving a TCP port.
+
false
terminationGracePeriodSecondsinteger + Optional duration in seconds the pod needs to terminate gracefully upon probe failure.
+
+ Format: int64
+
false
timeoutSecondsinteger + Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].livenessProbe.exec +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlivenessprobe) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].livenessProbe.grpc +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlivenessprobe) + + + +GRPC specifies an action involving a GRPC port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portinteger + Port number of the gRPC service. Number must be in the range 1 to 65535.
+
+ Format: int32
+
true
servicestring + Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].livenessProbe.httpGet +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlivenessprobe) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].livenessProbe.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlivenessprobehttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.initContainers[index].livenessProbe.tcpSocket +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexlivenessprobe) + + + +TCPSocket specifies an action involving a TCP port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].ports[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +ContainerPort represents a network port in a single container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
containerPortinteger + Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.
+
+ Format: int32
+
true
hostIPstring + What host IP to bind the external port to.
+
false
hostPortinteger + Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.
+
+ Format: int32
+
false
namestring + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.
+
false
protocolstring + Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP".
+
+ Default: TCP
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].readinessProbe +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +Periodic probe of container service readiness. Container will be removed from service endpoints if the probe fails. Cannot be updated. More info: https://kubernetes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
failureThresholdinteger + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.
+
+ Format: int32
+
false
grpcobject + GRPC specifies an action involving a GRPC port.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
initialDelaySecondsinteger + Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
periodSecondsinteger + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.
+
+ Format: int32
+
false
successThresholdinteger + Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+
+ Format: int32
+
false
tcpSocketobject + TCPSocket specifies an action involving a TCP port.
+
false
terminationGracePeriodSecondsinteger + Optional duration in seconds the pod needs to terminate gracefully upon probe failure.
+
+ Format: int64
+
false
timeoutSecondsinteger + Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].readinessProbe.exec +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexreadinessprobe) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].readinessProbe.grpc +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexreadinessprobe) + + + +GRPC specifies an action involving a GRPC port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portinteger + Port number of the gRPC service. Number must be in the range 1 to 65535.
+
+ Format: int32
+
true
servicestring + Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].readinessProbe.httpGet +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexreadinessprobe) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].readinessProbe.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexreadinessprobehttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.initContainers[index].readinessProbe.tcpSocket +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexreadinessprobe) + + + +TCPSocket specifies an action involving a TCP port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].resizePolicy[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +ContainerResizePolicy represents resource resize policy for the container. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
resourceNamestring + Name of the resource to which this resource resize policy applies. Supported values: cpu, memory.
+
true
restartPolicystring + Restart policy to apply when specified resource is resized. If not specified, it defaults to NotRequired.
+
true
+ + +### OpenTelemetryCollector.spec.initContainers[index].resources +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +Compute Resources required by this container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limitsmap[string]int or string + Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].resources.claims[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexresources) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### OpenTelemetryCollector.spec.initContainers[index].securityContext +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
allowPrivilegeEscalationboolean + AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process.
+
false
capabilitiesobject + The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows.
+
false
privilegedboolean + Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.
+
false
procMountstring + procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths.
+
false
readOnlyRootFilesystemboolean + Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.
+
false
runAsGroupinteger + The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext.
+
+ Format: int64
+
false
runAsNonRootboolean + Indicates that the container must run as a non-root user.
+
false
runAsUserinteger + The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
+ Format: int64
+
false
seLinuxOptionsobject + The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext.
+
false
seccompProfileobject + The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options.
+
false
windowsOptionsobject + The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].securityContext.capabilities +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexsecuritycontext) + + + +The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
add[]string + Added capabilities
+
false
drop[]string + Removed capabilities
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].securityContext.seLinuxOptions +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexsecuritycontext) + + + +The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
levelstring + Level is SELinux level label that applies to the container.
+
false
rolestring + Role is a SELinux role label that applies to the container.
+
false
typestring + Type is a SELinux type label that applies to the container.
+
false
userstring + User is a SELinux user label that applies to the container.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].securityContext.seccompProfile +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexsecuritycontext) + + + +The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
typestring + type indicates which kind of seccomp profile will be applied. Valid options are: + Localhost - a profile defined in a file on the node should be used.
+
true
localhostProfilestring + localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].securityContext.windowsOptions +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexsecuritycontext) + + + +The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
gmsaCredentialSpecstring + GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.
+
false
gmsaCredentialSpecNamestring + GMSACredentialSpecName is the name of the GMSA credential spec to use.
+
false
hostProcessboolean + HostProcess determines if a container should be run as a 'Host Process' container.
+
false
runAsUserNamestring + The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].startupProbe +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +StartupProbe indicates that the Pod has successfully initialized. If specified, no other probes are executed until this completes successfully. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
failureThresholdinteger + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.
+
+ Format: int32
+
false
grpcobject + GRPC specifies an action involving a GRPC port.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
initialDelaySecondsinteger + Number of seconds after the container has started before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
periodSecondsinteger + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.
+
+ Format: int32
+
false
successThresholdinteger + Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+
+ Format: int32
+
false
tcpSocketobject + TCPSocket specifies an action involving a TCP port.
+
false
terminationGracePeriodSecondsinteger + Optional duration in seconds the pod needs to terminate gracefully upon probe failure.
+
+ Format: int64
+
false
timeoutSecondsinteger + Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].startupProbe.exec +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexstartupprobe) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].startupProbe.grpc +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexstartupprobe) + + + +GRPC specifies an action involving a GRPC port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portinteger + Port number of the gRPC service. Number must be in the range 1 to 65535.
+
+ Format: int32
+
true
servicestring + Service is the name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md).
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].startupProbe.httpGet +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexstartupprobe) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].startupProbe.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexstartupprobehttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.initContainers[index].startupProbe.tcpSocket +[↩ Parent](#opentelemetrycollectorspecinitcontainersindexstartupprobe) + + + +TCPSocket specifies an action involving a TCP port. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.initContainers[index].volumeDevices[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +volumeDevice describes a mapping of a raw block device within a container. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
devicePathstring + devicePath is the path inside of the container that the device will be mapped to.
+
true
namestring + name must match the name of a persistentVolumeClaim in the pod
+
true
+ + +### OpenTelemetryCollector.spec.initContainers[index].volumeMounts[index] +[↩ Parent](#opentelemetrycollectorspecinitcontainersindex) + + + +VolumeMount describes a mounting of a Volume within a container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
mountPathstring + Path within the container at which the volume should be mounted. Must not contain ':'.
+
true
namestring + This must match the Name of a Volume.
+
true
mountPropagationstring + mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10.
+
false
readOnlyboolean + Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false.
+
false
subPathstring + Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root).
+
false
subPathExprstring + Expanded path within the volume from which the container's volume should be mounted.
+
false
+ + +### OpenTelemetryCollector.spec.lifecycle +[↩ Parent](#opentelemetrycollectorspec) + + + +Actions that the management system should take in response to container lifecycle events. Cannot be updated. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
postStartobject + PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy.
+
false
preStopobject + PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc.
+
false
+ + +### OpenTelemetryCollector.spec.lifecycle.postStart +[↩ Parent](#opentelemetrycollectorspeclifecycle) + + + +PostStart is called immediately after a container is created. If the handler fails, the container is terminated and restarted according to its restart policy. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
tcpSocketobject + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility.
+
false
+ + +### OpenTelemetryCollector.spec.lifecycle.postStart.exec +[↩ Parent](#opentelemetrycollectorspeclifecyclepoststart) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.lifecycle.postStart.httpGet +[↩ Parent](#opentelemetrycollectorspeclifecyclepoststart) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.lifecycle.postStart.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspeclifecyclepoststarthttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.lifecycle.postStart.tcpSocket +[↩ Parent](#opentelemetrycollectorspeclifecyclepoststart) + + + +Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.lifecycle.preStop +[↩ Parent](#opentelemetrycollectorspeclifecycle) + + + +PreStop is called immediately before a container is terminated due to an API request or management event such as liveness/startup probe failure, preemption, resource contention, etc. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
execobject + Exec specifies the action to take.
+
false
httpGetobject + HTTPGet specifies the http request to perform.
+
false
tcpSocketobject + Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility.
+
false
+ + +### OpenTelemetryCollector.spec.lifecycle.preStop.exec +[↩ Parent](#opentelemetrycollectorspeclifecycleprestop) + + + +Exec specifies the action to take. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
command[]string + Command is the command line to execute inside the container, the working directory for the command is root ('/') in the container's filesystem.
+
false
+ + +### OpenTelemetryCollector.spec.lifecycle.preStop.httpGet +[↩ Parent](#opentelemetrycollectorspeclifecycleprestop) + + + +HTTPGet specifies the http request to perform. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Name or number of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Host name to connect to, defaults to the pod IP. You probably want to set "Host" in httpHeaders instead.
+
false
httpHeaders[]object + Custom headers to set in the request. HTTP allows repeated headers.
+
false
pathstring + Path to access on the HTTP server.
+
false
schemestring + Scheme to use for connecting to the host. Defaults to HTTP.
+
false
+ + +### OpenTelemetryCollector.spec.lifecycle.preStop.httpGet.httpHeaders[index] +[↩ Parent](#opentelemetrycollectorspeclifecycleprestophttpget) + + + +HTTPHeader describes a custom header to be used in HTTP probes + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + The header field name. This will be canonicalized upon output, so case-variant names will be understood as the same header.
+
true
valuestring + The header field value
+
true
+ + +### OpenTelemetryCollector.spec.lifecycle.preStop.tcpSocket +[↩ Parent](#opentelemetrycollectorspeclifecycleprestop) + + + +Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept for the backward compatibility. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portint or string + Number or name of the port to access on the container. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
true
hoststring + Optional: Host name to connect to, defaults to the pod IP.
+
false
+ + +### OpenTelemetryCollector.spec.livenessProbe +[↩ Parent](#opentelemetrycollectorspec) + + + +Liveness config for the OpenTelemetry Collector except the probe handler which is auto generated from the health extension of the collector. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
failureThresholdinteger + Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.
+
+ Format: int32
+
false
initialDelaySecondsinteger + Number of seconds after the container has started before liveness probes are initiated. Defaults to 0 seconds. Minimum value is 0. More info: https://kubernetes.
+
+ Format: int32
+
false
periodSecondsinteger + How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.
+
+ Format: int32
+
false
successThresholdinteger + Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.
+
+ Format: int32
+
false
terminationGracePeriodSecondsinteger + Optional duration in seconds the pod needs to terminate gracefully upon probe failure.
+
+ Format: int64
+
false
timeoutSecondsinteger + Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
+
+ Format: int32
+
false
+ + +### OpenTelemetryCollector.spec.observability +[↩ Parent](#opentelemetrycollectorspec) + + + +ObservabilitySpec defines how telemetry data gets handled. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
metricsobject + Metrics defines the metrics configuration for operands.
+
false
+ + +### OpenTelemetryCollector.spec.observability.metrics +[↩ Parent](#opentelemetrycollectorspecobservability) + + + +Metrics defines the metrics configuration for operands. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
enableMetricsboolean + EnableMetrics specifies if ServiceMonitor or PodMonitor(for sidecar mode) should be created for the OpenTelemetry Collector and Prometheus Exporters. The operator.observability.
+
false
+ + +### OpenTelemetryCollector.spec.podDisruptionBudget +[↩ Parent](#opentelemetrycollectorspec) + + + +PodDisruptionBudget specifies the pod disruption budget configuration to use for the OpenTelemetryCollector workload. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
maxUnavailableint or string + An eviction is allowed if at most "maxUnavailable" pods selected by "selector" are unavailable after the eviction, i.e. even in absence of the evicted pod.
+
false
minAvailableint or string + An eviction is allowed if at least "minAvailable" pods selected by "selector" will still be available after the eviction, i.e. even in the absence of the evicted pod.
+
false
+ + +### OpenTelemetryCollector.spec.podSecurityContext +[↩ Parent](#opentelemetrycollectorspec) + + + +PodSecurityContext configures the pod security context for the opentelemetry-collector pod, when running as a deployment, daemonset, or statefulset. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
fsGroupinteger + A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod: + 1.
+
+ Format: int64
+
false
fsGroupChangePolicystring + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod.
+
false
runAsGroupinteger + The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext.
+
+ Format: int64
+
false
runAsNonRootboolean + Indicates that the container must run as a non-root user.
+
false
runAsUserinteger + The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext.
+
+ Format: int64
+
false
seLinuxOptionsobject + The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext.
+
false
seccompProfileobject + The seccomp options to use by the containers in this pod. Note that this field cannot be set when spec.os.name is windows.
+
false
supplementalGroups[]integer + A list of groups applied to the first process run in each container, in addition to the container's primary GID, the fsGroup (if specified), and group memberships defined in the container image for th
+
false
sysctls[]object + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.
+
false
windowsOptionsobject + The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used.
+
false
+ + +### OpenTelemetryCollector.spec.podSecurityContext.seLinuxOptions +[↩ Parent](#opentelemetrycollectorspecpodsecuritycontext) + + + +The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
levelstring + Level is SELinux level label that applies to the container.
+
false
rolestring + Role is a SELinux role label that applies to the container.
+
false
typestring + Type is a SELinux type label that applies to the container.
+
false
userstring + User is a SELinux user label that applies to the container.
+
false
+ + +### OpenTelemetryCollector.spec.podSecurityContext.seccompProfile +[↩ Parent](#opentelemetrycollectorspecpodsecuritycontext) + + + +The seccomp options to use by the containers in this pod. Note that this field cannot be set when spec.os.name is windows. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
typestring + type indicates which kind of seccomp profile will be applied. Valid options are: + Localhost - a profile defined in a file on the node should be used.
+
true
localhostProfilestring + localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work.
+
false
+ + +### OpenTelemetryCollector.spec.podSecurityContext.sysctls[index] +[↩ Parent](#opentelemetrycollectorspecpodsecuritycontext) + + + +Sysctl defines a kernel parameter to be set + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name of a property to set
+
true
valuestring + Value of a property to set
+
true
+ + +### OpenTelemetryCollector.spec.podSecurityContext.windowsOptions +[↩ Parent](#opentelemetrycollectorspecpodsecuritycontext) + + + +The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
gmsaCredentialSpecstring + GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.
+
false
gmsaCredentialSpecNamestring + GMSACredentialSpecName is the name of the GMSA credential spec to use.
+
false
hostProcessboolean + HostProcess determines if a container should be run as a 'Host Process' container.
+
false
runAsUserNamestring + The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
false
+ + +### OpenTelemetryCollector.spec.ports[index] +[↩ Parent](#opentelemetrycollectorspec) + + + +ServicePort contains information on service's port. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
portinteger + The port that will be exposed by this service.
+
+ Format: int32
+
true
appProtocolstring + The application protocol for this port. This is used as a hint for implementations to offer richer behavior for protocols that they understand. This field follows standard Kubernetes label syntax.
+
false
namestring + The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names.
+
false
nodePortinteger + The port on each node on which this service is exposed when type is NodePort or LoadBalancer. Usually assigned by the system.
+
+ Format: int32
+
false
protocolstring + The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". Default is TCP.
+
+ Default: TCP
+
false
targetPortint or string + Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+
false
+ + +### OpenTelemetryCollector.spec.resources +[↩ Parent](#opentelemetrycollectorspec) + + + +Resources to set on the OpenTelemetry Collector pods. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limitsmap[string]int or string + Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+
false
requestsmap[string]int or string + Requests describes the minimum amount of compute resources required.
+
false
+ + +### OpenTelemetryCollector.spec.resources.claims[index] +[↩ Parent](#opentelemetrycollectorspecresources) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + +### OpenTelemetryCollector.spec.securityContext +[↩ Parent](#opentelemetrycollectorspec) + + + +SecurityContext configures the container security context for the opentelemetry-collector container. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
allowPrivilegeEscalationboolean + AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process.
+
false
capabilitiesobject + The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows.
+
false
privilegedboolean + Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.
+
false
procMountstring + procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths.
+
false
readOnlyRootFilesystemboolean + Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.
+
false
runAsGroupinteger + The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext.
+
+ Format: int64
+
false
runAsNonRootboolean + Indicates that the container must run as a non-root user.
+
false
runAsUserinteger + The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
+ Format: int64
+
false
seLinuxOptionsobject + The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext.
+
false
seccompProfileobject + The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options.
+
false
windowsOptionsobject + The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used.
+
false
+ + +### OpenTelemetryCollector.spec.securityContext.capabilities +[↩ Parent](#opentelemetrycollectorspecsecuritycontext) + + + +The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
add[]string + Added capabilities
+
false
drop[]string + Removed capabilities
+
false
+ + +### OpenTelemetryCollector.spec.securityContext.seLinuxOptions +[↩ Parent](#opentelemetrycollectorspecsecuritycontext) + + + +The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
levelstring + Level is SELinux level label that applies to the container.
+
false
rolestring + Role is a SELinux role label that applies to the container.
+
false
typestring + Type is a SELinux type label that applies to the container.
+
false
userstring + User is a SELinux user label that applies to the container.
+
false
+ + +### OpenTelemetryCollector.spec.securityContext.seccompProfile +[↩ Parent](#opentelemetrycollectorspecsecuritycontext) + + + +The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
typestring + type indicates which kind of seccomp profile will be applied. Valid options are: + Localhost - a profile defined in a file on the node should be used.
+
true
localhostProfilestring + localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work.
+
false
+ + +### OpenTelemetryCollector.spec.securityContext.windowsOptions +[↩ Parent](#opentelemetrycollectorspecsecuritycontext) + + + +The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
gmsaCredentialSpecstring + GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.
+
false
gmsaCredentialSpecNamestring + GMSACredentialSpecName is the name of the GMSA credential spec to use.
+
false
hostProcessboolean + HostProcess determines if a container should be run as a 'Host Process' container.
+
false
runAsUserNamestring + The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator +[↩ Parent](#opentelemetrycollectorspec) + + + +TargetAllocator indicates a value which determines whether to spawn a target allocation resource or not. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
affinityobject + If specified, indicates the pod's scheduling constraints
+
false
allocationStrategyenum + AllocationStrategy determines which strategy the target allocator should use for allocation. The current options are least-weighted and consistent-hashing. The default option is least-weighted
+
+ Enum: least-weighted, consistent-hashing
+
false
enabledboolean + Enabled indicates whether to use a target allocation mechanism for Prometheus targets or not.
+
false
env[]object + ENV vars to set on the OpenTelemetry TargetAllocator's Pods. These can then in certain cases be consumed in the config file for the TargetAllocator.
+
false
filterStrategystring + FilterStrategy determines how to filter targets before allocating them among the collectors. The only current option is relabel-config (drops targets based on prom relabel_config).
+
false
imagestring + Image indicates the container image to use for the OpenTelemetry TargetAllocator.
+
false
nodeSelectormap[string]string + NodeSelector to schedule OpenTelemetry TargetAllocator pods.
+
false
prometheusCRobject + PrometheusCR defines the configuration for the retrieval of PrometheusOperator CRDs ( servicemonitor.monitoring.coreos.com/v1 and podmonitor.monitoring.coreos.com/v1 ) retrieval.
+
false
replicasinteger + Replicas is the number of pod instances for the underlying TargetAllocator. This should only be set to a value other than 1 if a strategy that allows for high availability is chosen.
+
+ Format: int32
+
false
resourcesobject + Resources to set on the OpenTelemetryTargetAllocator containers.
+
false
securityContextobject + SecurityContext configures the container security context for the targetallocator.
+
false
serviceAccountstring + ServiceAccount indicates the name of an existing service account to use with this instance. When set, the operator will not automatically create a ServiceAccount for the TargetAllocator.
+
false
tolerations[]object + Toleration embedded kubernetes pod configuration option, controls how pods can be scheduled with matching taints
+
false
topologySpreadConstraints[]object + TopologySpreadConstraints embedded kubernetes pod configuration option, controls how pods are spread across your cluster among failure-domains such as regions, zones, nodes, and other user-defined top
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity +[↩ Parent](#opentelemetrycollectorspectargetallocator) + + + +If specified, indicates the pod's scheduling constraints + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
nodeAffinityobject + Describes node affinity scheduling rules for the pod.
+
false
podAffinityobject + Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)).
+
false
podAntiAffinityobject + Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)).
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.nodeAffinity +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinity) + + + +Describes node affinity scheduling rules for the pod. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferredDuringSchedulingIgnoredDuringExecution[]object + The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions.
+
false
requiredDuringSchedulingIgnoredDuringExecutionobject + If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitynodeaffinity) + + + +An empty preferred scheduling term matches all objects with implicit weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferenceobject + A node selector term, associated with the corresponding weight.
+
true
weightinteger + Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.
+
+ Format: int32
+
true
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].preference +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitynodeaffinitypreferredduringschedulingignoredduringexecutionindex) + + + +A node selector term, associated with the corresponding weight. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + A list of node selector requirements by node's labels.
+
false
matchFields[]object + A list of node selector requirements by node's fields.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].preference.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitynodeaffinitypreferredduringschedulingignoredduringexecutionindexpreference) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].preference.matchFields[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitynodeaffinitypreferredduringschedulingignoredduringexecutionindexpreference) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitynodeaffinity) + + + +If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
nodeSelectorTerms[]object + Required. A list of node selector terms. The terms are ORed.
+
true
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitynodeaffinityrequiredduringschedulingignoredduringexecution) + + + +A null or empty node selector term matches no objects. The requirements of them are ANDed. The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + A list of node selector requirements by node's labels.
+
false
matchFields[]object + A list of node selector requirements by node's fields.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[index].matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitynodeaffinityrequiredduringschedulingignoredduringexecutionnodeselectortermsindex) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[index].matchFields[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitynodeaffinityrequiredduringschedulingignoredduringexecutionnodeselectortermsindex) + + + +A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + The label key that the selector applies to.
+
true
operatorstring + Represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+
true
values[]string + An array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinity) + + + +Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s)). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferredDuringSchedulingIgnoredDuringExecution[]object + The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions.
+
false
requiredDuringSchedulingIgnoredDuringExecution[]object + If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinity) + + + +The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
podAffinityTermobject + Required. A pod affinity term, associated with the corresponding weight.
+
true
weightinteger + weight associated with matching the corresponding podAffinityTerm, in the range 1-100.
+
+ Format: int32
+
true
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinitypreferredduringschedulingignoredduringexecutionindex) + + + +Required. A pod affinity term, associated with the corresponding weight. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinity) + + + +Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-locate + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinityrequiredduringschedulingignoredduringexecutionindexlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinityrequiredduringschedulingignoredduringexecutionindex) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodaffinityrequiredduringschedulingignoredduringexecutionindexnamespaceselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinity) + + + +Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod in the same node, zone, etc. as some other pod(s)). + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
preferredDuringSchedulingIgnoredDuringExecution[]object + The scheduler will prefer to schedule pods to nodes that satisfy the anti-affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions.
+
false
requiredDuringSchedulingIgnoredDuringExecution[]object + If the anti-affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinity) + + + +The weights of all of the matched WeightedPodAffinityTerm fields are added per-node to find the most preferred node(s) + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
podAffinityTermobject + Required. A pod affinity term, associated with the corresponding weight.
+
true
weightinteger + weight associated with matching the corresponding podAffinityTerm, in the range 1-100.
+
+ Format: int32
+
true
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindex) + + + +Required. A pod affinity term, associated with the corresponding weight. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
topologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelectorobject + A label query over a set of resources, in this case pods.
+
false
namespaceSelectorobject + A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over a set of resources, in this case pods. + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
matchExpressions[]object + matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
+
false
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermlabelselector) + + + +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. + + + + + + + + + + + + + + + + + + + - + - - + + - - + +
NameTypeDescriptionRequired
keystring + key is the label key that the selector applies to.
+
true
operatorstring + operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
falsetrue
volumeClaimTemplates[]objectvalues[]string - VolumeClaimTemplates will provide stable storage using PersistentVolumes. Only available when the mode=statefulset.
+ values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
false
volumeMounts
+ + +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinityterm) + + + +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. + + + + + + + + + + + + - - + +
NameTypeDescriptionRequired
matchExpressions []object - VolumeMounts represents the mount points to use in the underlying collector deployment(s)
+ matchExpressions is a list of label selector requirements. The requirements are ANDed.
false
volumes[]objectmatchLabelsmap[string]string - Volumes represents which volumes to use in the underlying collector deployment(s).
+ matchLabels is a map of {key,value} pairs.
false
-### OpenTelemetryCollector.spec.autoscaler -[↩ Parent](#opentelemetrycollectorspec) +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[index].podAffinityTerm.namespaceSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinitypreferredduringschedulingignoredduringexecutionindexpodaffinitytermnamespaceselector) -Autoscaler specifies the pod autoscaling configuration to use for the OpenTelemetryCollector workload. +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. @@ -1904,31 +19202,36 @@ Autoscaler specifies the pod autoscaling configuration to use for the OpenTeleme - - + + - + - - + + + + + + +
behaviorobjectkeystring - HorizontalPodAutoscalerBehavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively).
+ key is the label key that the selector applies to.
falsetrue
targetCPUUtilizationintegeroperatorstring - TargetCPUUtilization sets the target average CPU used across all replicas. If average CPU exceeds this value, the HPA will scale up. Defaults to 90 percent.
-
- Format: int32
+ operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
false
-### OpenTelemetryCollector.spec.autoscaler.behavior -[↩ Parent](#opentelemetrycollectorspecautoscaler) +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinity) -HorizontalPodAutoscalerBehavior configures the scaling behavior of the target in both Up and Down directions (scaleUp and scaleDown fields respectively). +Defines a set of pods (namely those matching the labelSelector relative to the given namespace(s)) that this pod should be co-located (affinity) or not co-located (anti-affinity) with, where co-locate @@ -1940,29 +19243,43 @@ HorizontalPodAutoscalerBehavior configures the scaling behavior of the target in - + + + + + + - + + + + + +
scaleDowntopologyKeystring + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose
+
true
labelSelector object - scaleDown is scaling policy for scaling Down. If not set, the default value is to allow to scale down to minReplicas pods, with a 300 second stabilization window (i.e., the highest recommendation for the last 300sec is used).
+ A label query over a set of resources, in this case pods.
false
scaleUpnamespaceSelector object - scaleUp is scaling policy for scaling Up. If not set, the default value is the higher of: * increase no more than 4 pods per 60 seconds * double the number of pods per 60 seconds No stabilization is used.
+ A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field.
+
false
namespaces[]string + namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector.
false
-### OpenTelemetryCollector.spec.autoscaler.behavior.scaleDown -[↩ Parent](#opentelemetrycollectorspecautoscalerbehavior) +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindex) -scaleDown is scaling policy for scaling Down. If not set, the default value is to allow to scale down to minReplicas pods, with a 300 second stabilization window (i.e., the highest recommendation for the last 300sec is used). +A label query over a set of resources, in this case pods. @@ -1974,38 +19291,29 @@ scaleDown is scaling policy for scaling Down. If not set, the default value is t - + - - - - - - - + +
policiesmatchExpressions []object - policies is a list of potential scaling polices which can be used during scaling. At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid
-
false
selectPolicystring - selectPolicy is used to specify which policy should be used. If not set, the default value Max is used.
+ matchExpressions is a list of label selector requirements. The requirements are ANDed.
false
stabilizationWindowSecondsintegermatchLabelsmap[string]string - StabilizationWindowSeconds is the number of seconds for which past recommendations should be considered while scaling up or scaling down. StabilizationWindowSeconds must be greater than or equal to zero and less than or equal to 3600 (one hour). If not set, use the default values: - For scale up: 0 (i.e. no stabilization is done). - For scale down: 300 (i.e. the stabilization window is 300 seconds long).
-
- Format: int32
+ matchLabels is a map of {key,value} pairs.
false
-### OpenTelemetryCollector.spec.autoscaler.behavior.scaleDown.policies[index] -[↩ Parent](#opentelemetrycollectorspecautoscalerbehaviorscaledown) +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindexlabelselector) -HPAScalingPolicy is a single policy which must hold true for a specified past interval. +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. @@ -2017,40 +19325,36 @@ HPAScalingPolicy is a single policy which must hold true for a specified past in - - + + - + - - + + - +
periodSecondsintegerkeystring - PeriodSeconds specifies the window of time for which the policy should hold true. PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min).
-
- Format: int32
+ key is the label key that the selector applies to.
true
typeoperator string - Type is used to specify the scaling policy.
+ operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
true
valueintegervalues[]string - Value contains the amount of change which is permitted by the policy. It must be greater than zero
-
- Format: int32
+ values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
truefalse
-### OpenTelemetryCollector.spec.autoscaler.behavior.scaleUp -[↩ Parent](#opentelemetrycollectorspecautoscalerbehavior) +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindex) -scaleUp is scaling policy for scaling Up. If not set, the default value is the higher of: * increase no more than 4 pods per 60 seconds * double the number of pods per 60 seconds No stabilization is used. +A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. @@ -2062,38 +19366,29 @@ scaleUp is scaling policy for scaling Up. If not set, the default value is the h - + - - - - - - - + +
policiesmatchExpressions []object - policies is a list of potential scaling polices which can be used during scaling. At least one policy must be specified, otherwise the HPAScalingRules will be discarded as invalid
-
false
selectPolicystring - selectPolicy is used to specify which policy should be used. If not set, the default value Max is used.
+ matchExpressions is a list of label selector requirements. The requirements are ANDed.
false
stabilizationWindowSecondsintegermatchLabelsmap[string]string - StabilizationWindowSeconds is the number of seconds for which past recommendations should be considered while scaling up or scaling down. StabilizationWindowSeconds must be greater than or equal to zero and less than or equal to 3600 (one hour). If not set, use the default values: - For scale up: 0 (i.e. no stabilization is done). - For scale down: 300 (i.e. the stabilization window is 300 seconds long).
-
- Format: int32
+ matchLabels is a map of {key,value} pairs.
false
-### OpenTelemetryCollector.spec.autoscaler.behavior.scaleUp.policies[index] -[↩ Parent](#opentelemetrycollectorspecautoscalerbehaviorscaleup) +### OpenTelemetryCollector.spec.targetAllocator.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[index].namespaceSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatoraffinitypodantiaffinityrequiredduringschedulingignoredduringexecutionindexnamespaceselector) -HPAScalingPolicy is a single policy which must hold true for a specified past interval. +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. @@ -2105,36 +19400,32 @@ HPAScalingPolicy is a single policy which must hold true for a specified past in - - + + - + - - + + - +
periodSecondsintegerkeystring - PeriodSeconds specifies the window of time for which the policy should hold true. PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min).
-
- Format: int32
+ key is the label key that the selector applies to.
true
typeoperator string - Type is used to specify the scaling policy.
+ operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
true
valueintegervalues[]string - Value contains the amount of change which is permitted by the policy. It must be greater than zero
-
- Format: int32
+ values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
truefalse
-### OpenTelemetryCollector.spec.env[index] -[↩ Parent](#opentelemetrycollectorspec) +### OpenTelemetryCollector.spec.targetAllocator.env[index] +[↩ Parent](#opentelemetrycollectorspectargetallocator) @@ -2160,11 +19451,11 @@ EnvVar represents an environment variable present in a Container. value string - Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".
+ Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables.
false - valueFrom + valueFrom object Source for the environment variable's value. Cannot be used if value is not empty.
@@ -2174,8 +19465,8 @@ EnvVar represents an environment variable present in a Container. -### OpenTelemetryCollector.spec.env[index].valueFrom -[↩ Parent](#opentelemetrycollectorspecenvindex) +### OpenTelemetryCollector.spec.targetAllocator.env[index].valueFrom +[↩ Parent](#opentelemetrycollectorspectargetallocatorenvindex) @@ -2191,28 +19482,28 @@ Source for the environment variable's value. Cannot be used if value is not empt - configMapKeyRef + configMapKeyRef object Selects a key of a ConfigMap.
false - fieldRef + fieldRef object - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.
false - resourceFieldRef + resourceFieldRef object - Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+ Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.
false - secretKeyRef + secretKeyRef object Selects a key of a secret in the pod's namespace
@@ -2222,8 +19513,8 @@ Source for the environment variable's value. Cannot be used if value is not empt -### OpenTelemetryCollector.spec.env[index].valueFrom.configMapKeyRef -[↩ Parent](#opentelemetrycollectorspecenvindexvaluefrom) +### OpenTelemetryCollector.spec.targetAllocator.env[index].valueFrom.configMapKeyRef +[↩ Parent](#opentelemetrycollectorspectargetallocatorenvindexvaluefrom) @@ -2263,12 +19554,12 @@ Selects a key of a ConfigMap. -### OpenTelemetryCollector.spec.env[index].valueFrom.fieldRef -[↩ Parent](#opentelemetrycollectorspecenvindexvaluefrom) +### OpenTelemetryCollector.spec.targetAllocator.env[index].valueFrom.fieldRef +[↩ Parent](#opentelemetrycollectorspectargetallocatorenvindexvaluefrom) -Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. +Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status. @@ -2297,12 +19588,12 @@ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadat
-### OpenTelemetryCollector.spec.env[index].valueFrom.resourceFieldRef -[↩ Parent](#opentelemetrycollectorspecenvindexvaluefrom) +### OpenTelemetryCollector.spec.targetAllocator.env[index].valueFrom.resourceFieldRef +[↩ Parent](#opentelemetrycollectorspectargetallocatorenvindexvaluefrom) -Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. +Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests. @@ -2338,8 +19629,8 @@ Selects a resource of the container: only resources limits and requests (limits.
-### OpenTelemetryCollector.spec.env[index].valueFrom.secretKeyRef -[↩ Parent](#opentelemetrycollectorspecenvindexvaluefrom) +### OpenTelemetryCollector.spec.targetAllocator.env[index].valueFrom.secretKeyRef +[↩ Parent](#opentelemetrycollectorspectargetallocatorenvindexvaluefrom) @@ -2379,87 +19670,12 @@ Selects a key of a secret in the pod's namespace -### OpenTelemetryCollector.spec.envFrom[index] -[↩ Parent](#opentelemetrycollectorspec) - - - -EnvFromSource represents the source of a set of ConfigMaps - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionRequired
configMapRefobject - The ConfigMap to select from
-
false
prefixstring - An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER.
-
false
secretRefobject - The Secret to select from
-
false
- - -### OpenTelemetryCollector.spec.envFrom[index].configMapRef -[↩ Parent](#opentelemetrycollectorspecenvfromindex) - - - -The ConfigMap to select from - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionRequired
namestring - Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
-
false
optionalboolean - Specify whether the ConfigMap must be defined
-
false
- - -### OpenTelemetryCollector.spec.envFrom[index].secretRef -[↩ Parent](#opentelemetrycollectorspecenvfromindex) +### OpenTelemetryCollector.spec.targetAllocator.prometheusCR +[↩ Parent](#opentelemetrycollectorspectargetallocator) -The Secret to select from +PrometheusCR defines the configuration for the retrieval of PrometheusOperator CRDs ( servicemonitor.monitoring.coreos.com/v1 and podmonitor.monitoring.coreos.com/v1 ) retrieval. @@ -2471,29 +19687,47 @@ The Secret to select from - + + + + + + + + + + + - - + +
nameenabledboolean + Enabled indicates whether to use a PrometheusOperator custom resources as targets or not.
+
false
podMonitorSelectormap[string]string + PodMonitors to be selected for target discovery. This is a map of {key,value} pairs. Each {key,value} in the map is going to exactly match a label in a PodMonitor's meta labels.
+
false
scrapeInterval string - Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?
+ Interval between consecutive scrapes. Equivalent to the same setting on the Prometheus CRD. + Default: "30s"
+
+ Format: duration
+ Default: 30s
false
optionalbooleanserviceMonitorSelectormap[string]string - Specify whether the Secret must be defined
+ ServiceMonitors to be selected for target discovery. This is a map of {key,value} pairs. Each {key,value} in the map is going to exactly match a label in a ServiceMonitor's meta labels.
false
-### OpenTelemetryCollector.spec.ingress -[↩ Parent](#opentelemetrycollectorspec) +### OpenTelemetryCollector.spec.targetAllocator.resources +[↩ Parent](#opentelemetrycollectorspectargetallocator) -Ingress is used to specify how OpenTelemetry Collector is exposed. This functionality is only available if one of the valid modes is set. Valid modes are: deployment, daemonset and statefulset. +Resources to set on the OpenTelemetryTargetAllocator containers. @@ -2505,45 +19739,37 @@ Ingress is used to specify how OpenTelemetry Collector is exposed. This function - - - - - - - + + - - + + - - + +
annotationsmap[string]string - Annotations to add to ingress. e.g. 'cert-manager.io/cluster-issuer: "letsencrypt"'
-
false
hostnamestringclaims[]object - Hostname by which the ingress proxy can be reached.
+ Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
false
tls[]objectlimitsmap[string]int or string - TLS configuration.
+ Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
false
typeenumrequestsmap[string]int or string - Type default value is: "" Supported types are: ingress
-
- Enum: ingress
+ Requests describes the minimum amount of compute resources required.
false
-### OpenTelemetryCollector.spec.ingress.tls[index] -[↩ Parent](#opentelemetrycollectorspecingress) +### OpenTelemetryCollector.spec.targetAllocator.resources.claims[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatorresources) -IngressTLS describes the transport layer security associated with an Ingress. +ResourceClaim references one entry in PodSpec.ResourceClaims. @@ -2555,29 +19781,22 @@ IngressTLS describes the transport layer security associated with an Ingress. - - - - - - + - +
hosts[]string - Hosts are a list of hosts included in the TLS certificate. The values in this list must match the name/s used in the tlsSecret. Defaults to the wildcard host setting for the loadbalancer controller fulfilling this Ingress, if left unspecified.
-
false
secretNamename string - SecretName is the name of the secret used to terminate TLS traffic on port 443. Field is left optional to allow TLS routing based on SNI hostname alone. If the SNI host in a listener conflicts with the "Host" header field used by an IngressRule, the SNI host is used for termination and value of the Host header is used for routing.
+ Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
falsetrue
-### OpenTelemetryCollector.spec.podSecurityContext -[↩ Parent](#opentelemetrycollectorspec) +### OpenTelemetryCollector.spec.targetAllocator.securityContext +[↩ Parent](#opentelemetrycollectorspectargetallocator) -PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext. +SecurityContext configures the container security context for the targetallocator. @@ -2593,8 +19812,7 @@ PodSecurityContext holds pod-level security attributes and common container sett @@ -2603,14 +19821,14 @@ PodSecurityContext holds pod-level security attributes and common container sett @@ -2619,27 +19837,27 @@ PodSecurityContext holds pod-level security attributes and common container sett - + - + - + - +
integer A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod: - 1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw---- - If unset, the Kubelet will not modify the ownership and permissions of any volume. Note that this field cannot be set when spec.os.name is windows.
+ 1.

Format: int64
fsGroupChangePolicy string - fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. This field will only apply to volume types which support fsGroup based ownership(and permissions). It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. Note that this field cannot be set when spec.os.name is windows.
+ fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod.
false
runAsGroup integer - The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows.
+ The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext.

Format: int64
runAsNonRoot boolean - Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.
+ Indicates that the container must run as a non-root user.
false
runAsUser integer - The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows.
+ The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext.

Format: int64
false
seLinuxOptionsseLinuxOptions object - The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows.
+ The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext.
false
seccompProfileseccompProfile object The seccomp options to use by the containers in this pod. Note that this field cannot be set when spec.os.name is windows.
@@ -2649,33 +19867,33 @@ PodSecurityContext holds pod-level security attributes and common container sett
supplementalGroups []integer - A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container. Note that this field cannot be set when spec.os.name is windows.
+ A list of groups applied to the first process run in each container, in addition to the container's primary GID, the fsGroup (if specified), and group memberships defined in the container image for th
false
sysctlssysctls []object - Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.name is windows.
+ Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.
false
windowsOptionswindowsOptions object - The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux.
+ The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used.
false
-### OpenTelemetryCollector.spec.podSecurityContext.seLinuxOptions -[↩ Parent](#opentelemetrycollectorspecpodsecuritycontext) +### OpenTelemetryCollector.spec.targetAllocator.securityContext.seLinuxOptions +[↩ Parent](#opentelemetrycollectorspectargetallocatorsecuritycontext) -The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. +The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. @@ -2718,8 +19936,8 @@ The SELinux context to be applied to all containers. If unspecified, the contain
-### OpenTelemetryCollector.spec.podSecurityContext.seccompProfile -[↩ Parent](#opentelemetrycollectorspecpodsecuritycontext) +### OpenTelemetryCollector.spec.targetAllocator.securityContext.seccompProfile +[↩ Parent](#opentelemetrycollectorspectargetallocatorsecuritycontext) @@ -2739,22 +19957,22 @@ The seccomp options to use by the containers in this pod. Note that this field c string type indicates which kind of seccomp profile will be applied. Valid options are: - Localhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.
+ Localhost - a profile defined in a file on the node should be used.
true localhostProfile string - localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must only be set if type is "Localhost".
+ localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work.
false -### OpenTelemetryCollector.spec.podSecurityContext.sysctls[index] -[↩ Parent](#opentelemetrycollectorspecpodsecuritycontext) +### OpenTelemetryCollector.spec.targetAllocator.securityContext.sysctls[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatorsecuritycontext) @@ -2787,12 +20005,12 @@ Sysctl defines a kernel parameter to be set -### OpenTelemetryCollector.spec.podSecurityContext.windowsOptions -[↩ Parent](#opentelemetrycollectorspecpodsecuritycontext) +### OpenTelemetryCollector.spec.targetAllocator.securityContext.windowsOptions +[↩ Parent](#opentelemetrycollectorspectargetallocatorsecuritycontext) -The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. +The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. @@ -2821,26 +20039,26 @@ The Windows specific settings applied to all containers. If unspecified, the opt
hostProcess boolean - HostProcess determines if a container should be run as a 'Host Process' container. This field is alpha-level and will only be honored by components that enable the WindowsHostProcessContainers feature flag. Setting this field without the feature flag will result in errors when validating the Pod. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.
+ HostProcess determines if a container should be run as a 'Host Process' container.
false
runAsUserName string - The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.
+ The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext.
false
-### OpenTelemetryCollector.spec.ports[index] -[↩ Parent](#opentelemetrycollectorspec) +### OpenTelemetryCollector.spec.targetAllocator.tolerations[index] +[↩ Parent](#opentelemetrycollectorspectargetallocator) -ServicePort contains information on service's port. +The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . @@ -2852,97 +20070,52 @@ ServicePort contains information on service's port. - - + + - + - + - + - + - + - - - - - - - -
portintegereffectstring - The port that will be exposed by this service.
-
- Format: int32
+ Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
truefalse
appProtocolkey string - The application protocol for this port. This field follows standard Kubernetes label syntax. Un-prefixed names are reserved for IANA standard service names (as per RFC-6335 and https://www.iana.org/assignments/service-names). Non-standard protocols should use prefixed names such as mycompany.com/my-custom-protocol.
+ Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.
false
nameoperator string - The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.
+ Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal.
false
nodePorttolerationSeconds integer - The port on each node on which this service is exposed when type is NodePort or LoadBalancer. Usually assigned by the system. If a value is specified, in-range, and not in use it will be used, otherwise the operation will fail. If not specified, a port will be allocated if this Service requires one. If this field is specified when creating a Service which does not need it, creation will fail. This field will be wiped when updating a Service to no longer need it (e.g. changing type from NodePort to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
+ TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint.

- Format: int32
+ Format: int64
false
protocolvalue string - The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". Default is TCP.
-
- Default: TCP
-
false
targetPortint or string - Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service
-
false
- - -### OpenTelemetryCollector.spec.resources -[↩ Parent](#opentelemetrycollectorspec) - - - -Resources to set on the OpenTelemetry Collector pods. - - - - - - - - - - - - - - - - - - -
NameTypeDescriptionRequired
limitsmap[string]int or string - Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
-
false
requestsmap[string]int or string - Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+ Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.
false
-### OpenTelemetryCollector.spec.securityContext -[↩ Parent](#opentelemetrycollectorspec) +### OpenTelemetryCollector.spec.targetAllocator.topologySpreadConstraints[index] +[↩ Parent](#opentelemetrycollectorspectargetallocator) -SecurityContext will be set as the container security context. +TopologySpreadConstraint specifies how to spread matching pods among the given topology. @@ -2954,130 +20127,75 @@ SecurityContext will be set as the container security context. - - - - - - - - - - - - + + - + - + - + - - + + - + - - + + - - + + - + - - - - - - - - - - - - - - - -
allowPrivilegeEscalationboolean - AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.
-
false
capabilitiesobject - The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows.
-
false
privilegedbooleanmaxSkewinteger - Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows.
+ MaxSkew describes the degree to which pods may be unevenly distributed.
+
+ Format: int32
falsetrue
procMounttopologyKey string - procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows.
+ TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology.
falsetrue
readOnlyRootFilesystembooleanwhenUnsatisfiablestring - Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows.
+ WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it.
falsetrue
runAsGroupintegerlabelSelectorobject - The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.
-
- Format: int64
+ LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain.
false
runAsNonRootbooleanmatchLabelKeys[]string - Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.
+ MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated.
false
runAsUserminDomains integer - The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.
+ MinDomains indicates a minimum number of eligible domains.

- Format: int64
-
false
seLinuxOptionsobject - The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows.
-
false
seccompProfileobject - The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. Note that this field cannot be set when spec.os.name is windows.
+ Format: int32
false
windowsOptionsobject - The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux.
-
false
- - -### OpenTelemetryCollector.spec.securityContext.capabilities -[↩ Parent](#opentelemetrycollectorspecsecuritycontext) - - - -The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. - - - - - - - - - - - - - + + - - + +
NameTypeDescriptionRequired
add[]stringnodeAffinityPolicystring - Added capabilities
+ NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew.
false
drop[]stringnodeTaintsPolicystring - Removed capabilities
+ NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew.
false
-### OpenTelemetryCollector.spec.securityContext.seLinuxOptions -[↩ Parent](#opentelemetrycollectorspecsecuritycontext) +### OpenTelemetryCollector.spec.targetAllocator.topologySpreadConstraints[index].labelSelector +[↩ Parent](#opentelemetrycollectorspectargetallocatortopologyspreadconstraintsindex) -The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. +LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain. @@ -3089,43 +20207,29 @@ The SELinux context to be applied to the container. If unspecified, the containe - - - - - - - - - - - - + + - - + +
levelstring - Level is SELinux level label that applies to the container.
-
false
rolestring - Role is a SELinux role label that applies to the container.
-
false
typestringmatchExpressions[]object - Type is a SELinux type label that applies to the container.
+ matchExpressions is a list of label selector requirements. The requirements are ANDed.
false
userstringmatchLabelsmap[string]string - User is a SELinux user label that applies to the container.
+ matchLabels is a map of {key,value} pairs.
false
-### OpenTelemetryCollector.spec.securityContext.seccompProfile -[↩ Parent](#opentelemetrycollectorspecsecuritycontext) +### OpenTelemetryCollector.spec.targetAllocator.topologySpreadConstraints[index].labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectargetallocatortopologyspreadconstraintsindexlabelselector) -The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. Note that this field cannot be set when spec.os.name is windows. +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. @@ -3137,30 +20241,36 @@ The seccomp options to use by this container. If seccomp options are provided at - + - + + + + + +
typekey string - type indicates which kind of seccomp profile will be applied. Valid options are: - Localhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied.
+ key is the label key that the selector applies to.
true
localhostProfileoperator string - localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must only be set if type is "Localhost".
+ operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
false
-### OpenTelemetryCollector.spec.securityContext.windowsOptions -[↩ Parent](#opentelemetrycollectorspecsecuritycontext) +### OpenTelemetryCollector.spec.tolerations[index] +[↩ Parent](#opentelemetrycollectorspec) -The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. +The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . @@ -3172,43 +20282,52 @@ The Windows specific settings applied to all containers. If unspecified, the opt - + - + - - + + - + + + + + +
gmsaCredentialSpeceffect string - GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.
+ Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
false
gmsaCredentialSpecNamekey string - GMSACredentialSpecName is the name of the GMSA credential spec to use.
+ Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.
false
hostProcessbooleanoperatorstring - HostProcess determines if a container should be run as a 'Host Process' container. This field is alpha-level and will only be honored by components that enable the WindowsHostProcessContainers feature flag. Setting this field without the feature flag will result in errors when validating the Pod. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.
+ Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal.
false
runAsUserNametolerationSecondsinteger + TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint.
+
+ Format: int64
+
false
value string - The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.
+ Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.
false
-### OpenTelemetryCollector.spec.targetAllocator +### OpenTelemetryCollector.spec.topologySpreadConstraints[index] [↩ Parent](#opentelemetrycollectorspec) -TargetAllocator indicates a value which determines whether to spawn a target allocation resource or not. +TopologySpreadConstraint specifies how to spread matching pods among the given topology. @@ -3220,59 +20339,75 @@ TargetAllocator indicates a value which determines whether to spawn a target all - - + + - + - - + + - + - + - + - + - + + + + + + - + + + + + +
allocationStrategystringmaxSkewinteger - AllocationStrategy determines which strategy the target allocator should use for allocation. The current options are least-weighted and consistent-hashing. The default option is least-weighted
+ MaxSkew describes the degree to which pods may be unevenly distributed.
+
+ Format: int32
falsetrue
enabledbooleantopologyKeystring - Enabled indicates whether to use a target allocation mechanism for Prometheus targets or not.
+ TopologyKey is the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology.
falsetrue
imagewhenUnsatisfiable string - Image indicates the container image to use for the OpenTelemetry TargetAllocator.
+ WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy the spread constraint. - DoNotSchedule (default) tells the scheduler not to schedule it.
falsetrue
prometheusCRlabelSelector object - PrometheusCR defines the configuration for the retrieval of PrometheusOperator CRDs ( servicemonitor.monitoring.coreos.com/v1 and podmonitor.monitoring.coreos.com/v1 ) retrieval. All CR instances which the ServiceAccount has access to will be retrieved. This includes other namespaces.
+ LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain.
false
replicasmatchLabelKeys[]string + MatchLabelKeys is a set of pod label keys to select the pods over which spreading will be calculated.
+
false
minDomains integer - Replicas is the number of pod instances for the underlying TargetAllocator. This should only be set to a value other than 1 if a strategy that allows for high availability is chosen. Currently, the only allocation strategy that can be run in a high availability mode is consistent-hashing.
+ MinDomains indicates a minimum number of eligible domains.

Format: int32
false
serviceAccountnodeAffinityPolicystring + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew.
+
false
nodeTaintsPolicy string - ServiceAccount indicates the name of an existing service account to use with this instance.
+ NodeTaintsPolicy indicates how we will treat node taints when calculating pod topology spread skew.
false
-### OpenTelemetryCollector.spec.targetAllocator.prometheusCR -[↩ Parent](#opentelemetrycollectorspectargetallocator) +### OpenTelemetryCollector.spec.topologySpreadConstraints[index].labelSelector +[↩ Parent](#opentelemetrycollectorspectopologyspreadconstraintsindex) -PrometheusCR defines the configuration for the retrieval of PrometheusOperator CRDs ( servicemonitor.monitoring.coreos.com/v1 and podmonitor.monitoring.coreos.com/v1 ) retrieval. All CR instances which the ServiceAccount has access to will be retrieved. This includes other namespaces. +LabelSelector is used to find matching pods. Pods that match this label selector are counted to determine the number of pods in their corresponding topology domain. @@ -3284,22 +20419,29 @@ PrometheusCR defines the configuration for the retrieval of PrometheusOperator C - - + + + + + + +
enabledbooleanmatchExpressions[]object - Enabled indicates whether to use a PrometheusOperator custom resources as targets or not.
+ matchExpressions is a list of label selector requirements. The requirements are ANDed.
+
false
matchLabelsmap[string]string + matchLabels is a map of {key,value} pairs.
false
-### OpenTelemetryCollector.spec.tolerations[index] -[↩ Parent](#opentelemetrycollectorspec) +### OpenTelemetryCollector.spec.topologySpreadConstraints[index].labelSelector.matchExpressions[index] +[↩ Parent](#opentelemetrycollectorspectopologyspreadconstraintsindexlabelselector) -The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . +A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. @@ -3311,40 +20453,92 @@ The pod this Toleration is attached to tolerates any taint that matches the trip - + - + - + + + + + + + + +
effectkey string - Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
+ key is the label key that the selector applies to.
falsetrue
keyoperator string - Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.
+ operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+
true
values[]string + values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
+
false
+ + +### OpenTelemetryCollector.spec.updateStrategy +[↩ Parent](#opentelemetrycollectorspec) + + + +UpdateStrategy represents the strategy the operator will take replacing existing DaemonSet pods with new pods https://kubernetes. + + + + + + + + + + + + + + - + - - - + +
NameTypeDescriptionRequired
rollingUpdateobject + Rolling update config params. Present only if type = "RollingUpdate". --- TODO: Update this to follow our convention for oneOf, whatever we decide it to be. Same as Deployment `strategy.
false
operatortype string - Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.
+ Type of daemon set update. Can be "RollingUpdate" or "OnDelete". Default is RollingUpdate.
false
tolerationSecondsinteger
+ + +### OpenTelemetryCollector.spec.updateStrategy.rollingUpdate +[↩ Parent](#opentelemetrycollectorspecupdatestrategy) + + + +Rolling update config params. Present only if type = "RollingUpdate". --- TODO: Update this to follow our convention for oneOf, whatever we decide it to be. Same as Deployment `strategy. + + + + + + + + + + + + + - - + + @@ -3371,14 +20565,14 @@ PersistentVolumeClaim is a user's request for and claim to a persistent volume @@ -3488,21 +20682,21 @@ spec defines the desired characteristics of a volume requested by a pod author. @@ -3542,7 +20736,7 @@ spec defines the desired characteristics of a volume requested by a pod author. -dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. If the AnyVolumeDataSource feature gate is enabled, this field will always have the same contents as the DataSourceRef field. +dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.
NameTypeDescriptionRequired
maxSurgeint or string - TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.
-
- Format: int64
+ The maximum number of nodes with an existing available DaemonSet pod that can have an updated DaemonSet pod during during an update.
false
valuestringmaxUnavailableint or string - Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.
+ The maximum number of DaemonSet pods that can be unavailable during the update.
false
apiVersion string - APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values.
false
kind string - Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase.
false
dataSource object - dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. If the AnyVolumeDataSource feature gate is enabled, this field will always have the same contents as the DataSourceRef field.
+ dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.
false
dataSourceRef object - dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any local object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef allows any non-core object, as well as PersistentVolumeClaim objects. * While DataSource ignores disallowed values (dropping them), DataSourceRef preserves all values, and generates an error if a disallowed value is specified. (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled.
+ dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired.
false
resources object - resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources
+ resources represents the minimum resources the volume should have.
false
@@ -3583,7 +20777,7 @@ dataSource field can be used to specify either: * An existing VolumeSnapshot obj -dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any local object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef allows any non-core object, as well as PersistentVolumeClaim objects. * While DataSource ignores disallowed values (dropping them), DataSourceRef preserves all values, and generates an error if a disallowed value is specified. (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. +dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired.
@@ -3615,6 +20809,13 @@ dataSourceRef specifies the object from which to populate the volume with data, APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.
+ + + + +
false
namespacestring + Namespace is the namespace of resource being referenced Note that when a namespace is specified, a gateway.networking.k8s.
+
false
@@ -3624,7 +20825,7 @@ dataSourceRef specifies the object from which to populate the volume with data, -resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources +resources represents the minimum resources the volume should have. @@ -3636,6 +20837,14 @@ resources represents the minimum resources the volume should have. If RecoverVol + + + + +
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limits map[string]int or string @@ -3646,13 +20855,40 @@ resources represents the minimum resources the volume should have. If RecoverVol requests map[string]int or string - Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+ Requests describes the minimum amount of compute resources required.
false
+### OpenTelemetryCollector.spec.volumeClaimTemplates[index].spec.resources.claims[index] +[↩ Parent](#opentelemetrycollectorspecvolumeclaimtemplatesindexspecresources) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + ### OpenTelemetryCollector.spec.volumeClaimTemplates[index].spec.selector [↩ Parent](#opentelemetrycollectorspecvolumeclaimtemplatesindexspec) @@ -3680,7 +20916,7 @@ selector is a label query over volumes to consider for binding. matchLabels map[string]string - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+ matchLabels is a map of {key,value} pairs.
false @@ -3721,7 +20957,7 @@ A label selector requirement is a selector that contains values, a key, and an o values []string - values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+ values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
false @@ -3751,11 +20987,18 @@ status represents the current information/status of a persistent volume claim. R accessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1
false + + allocatedResourceStatuses + map[string]string + + allocatedResourceStatuses stores status of resource being resized for the given PVC. Key names follow standard Kubernetes label syntax.
+ + false allocatedResources map[string]int or string - allocatedResources is the storage resource within AllocatedResources tracks the capacity allocated to a PVC. It may be larger than the actual capacity when a volume expansion operation is requested. For storage quota, the larger value from allocatedResources and PVC.spec.resources is used. If allocatedResources is not set, PVC.spec.resources alone is used for quota calculation. If a volume expansion capacity request is lowered, allocatedResources is only lowered if there are no expansion operations in progress and if the actual volume capacity is equal or lower than the requested capacity. This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.
+ allocatedResources tracks the resources allocated to a PVC including its capacity. Key names follow standard Kubernetes label syntax.
false @@ -3779,13 +21022,6 @@ status represents the current information/status of a persistent volume claim. R phase represents the current phase of PersistentVolumeClaim.
false - - resizeStatus - string - - resizeStatus stores status of resize operation. ResizeStatus is not set by default but when expansion is complete resizeStatus is set to empty string by resize controller or kubelet. This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature.
- - false @@ -3795,7 +21031,7 @@ status represents the current information/status of a persistent volume claim. R -PersistentVolumeClaimCondition contails details about state of pvc +PersistentVolumeClaimCondition contains details about state of pvc @@ -3849,7 +21085,7 @@ PersistentVolumeClaimCondition contails details about state of pvc @@ -3911,7 +21147,7 @@ VolumeMount describes a mounting of a Volume within a container. @@ -3945,7 +21181,7 @@ Volume represents a named volume in a pod that may be accessed by any container @@ -4008,11 +21244,7 @@ Volume represents a named volume in a pod that may be accessed by any container @@ -4040,14 +21272,14 @@ Volume represents a named volume in a pod that may be accessed by any container @@ -4061,7 +21293,7 @@ Volume represents a named volume in a pod that may be accessed by any container @@ -4082,7 +21314,7 @@ Volume represents a named volume in a pod that may be accessed by any container @@ -4157,7 +21389,7 @@ Volume represents a named volume in a pod that may be accessed by any container -awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore +awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.
reason string - reason is a unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports "ResizeStarted" that means the underlying persistent volume is being resized.
+ reason is a unique, this should be a short, machine understandable string that gives the reason for condition's last transition.
false
subPathExpr string - Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to "" (volume's root). SubPathExpr and SubPath are mutually exclusive.
+ Expanded path within the volume from which the container's volume should be mounted.
false
awsElasticBlockStore object - awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
+ awsElasticBlockStore represents an AWS Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.
false
ephemeral object - ephemeral represents a volume that is handled by a cluster storage driver. The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, and deleted when the pod is removed. - Use this if: a) the volume is only needed while the pod runs, b) features of normal volumes like restoring from snapshot or capacity tracking are needed, c) the storage driver is specified through a storage class, and d) the storage driver supports dynamic volume provisioning through a PersistentVolumeClaim (see EphemeralVolumeSource for more information on the connection between this volume type and PersistentVolumeClaim). - Use PersistentVolumeClaim or one of the vendor-specific APIs for volumes that persist for longer than the lifecycle of an individual pod. - Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to be used that way - see the documentation of the driver for more information. - A pod can use both types of ephemeral volumes and persistent volumes at the same time.
+ ephemeral represents a volume that is handled by a cluster storage driver.
false
gcePersistentDisk object - gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
+ gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.
false
gitRepo object - gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container.
+ gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated.
false
hostPath object - hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath --- TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not mount host directories as read/write.
+ hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container.
false
persistentVolumeClaim object - persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims
+ persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.
false
@@ -4179,14 +21411,14 @@ awsElasticBlockStore represents an AWS Disk resource that is attached to a kubel @@ -4250,7 +21482,7 @@ azureDisk represents an Azure Data Disk mount on the host and bind mount to the @@ -4421,7 +21653,7 @@ cinder represents a cinder volume attached and mounted on kubelets host machine. @@ -4489,7 +21721,7 @@ configMap represents a configMap that should populate this volume @@ -4498,7 +21730,7 @@ configMap represents a configMap that should populate this volume @@ -4553,7 +21785,7 @@ Maps a string key to a path within a volume. @@ -4596,7 +21828,7 @@ csi (Container Storage Interface) represents ephemeral storage that is handled b @@ -4622,7 +21854,7 @@ csi (Container Storage Interface) represents ephemeral storage that is handled b -nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed. +nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls.
fsType string - fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore TODO: how do we prevent errors in the filesystem from compromising the machine
+ fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs".
false
partition integer - partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1". Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty).
+ partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1".

Format: int32
kind string - kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared
+ kind expected values are Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set).
false
fsType string - fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md
+ fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
false
defaultMode integer - defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ defaultMode is optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.

Format: int32
items []object - items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.
+ items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value.
false
mode integer - mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.

Format: int32
nodePublishSecretRef object - nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed.
+ nodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls.
false
@@ -4664,7 +21896,7 @@ downwardAPI represents downward API about the pod that should populate this volu @@ -4714,7 +21946,7 @@ DownwardAPIVolumeFile represents information to create the file containing the p @@ -4825,14 +22057,14 @@ emptyDir represents a temporary directory that shares a pod's lifetime. More inf @@ -4844,11 +22076,7 @@ emptyDir represents a temporary directory that shares a pod's lifetime. More inf -ephemeral represents a volume that is handled by a cluster storage driver. The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, and deleted when the pod is removed. - Use this if: a) the volume is only needed while the pod runs, b) features of normal volumes like restoring from snapshot or capacity tracking are needed, c) the storage driver is specified through a storage class, and d) the storage driver supports dynamic volume provisioning through a PersistentVolumeClaim (see EphemeralVolumeSource for more information on the connection between this volume type and PersistentVolumeClaim). - Use PersistentVolumeClaim or one of the vendor-specific APIs for volumes that persist for longer than the lifecycle of an individual pod. - Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to be used that way - see the documentation of the driver for more information. - A pod can use both types of ephemeral volumes and persistent volumes at the same time. +ephemeral represents a volume that is handled by a cluster storage driver.
defaultMode integer - Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set permissions on created files by default.

Format: int32
mode integer - Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.

Format: int32
medium string - medium represents what type of storage medium should back this directory. The default is "" which means to use the node's default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir
+ medium represents what type of storage medium should back this directory. The default is "" which means to use the node's default medium. Must be an empty string (default) or Memory.
false
sizeLimit int or string - sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir
+ sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium.
false
@@ -4863,10 +22091,7 @@ ephemeral represents a volume that is handled by a cluster storage driver. The v @@ -4878,10 +22103,7 @@ ephemeral represents a volume that is handled by a cluster storage driver. The v -Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e. the PVC will be deleted together with the pod. The name of the PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. Pod validation will reject the pod if the concatenated name is not valid for a PVC (for example, too long). - An existing PVC with that name that is not owned by the pod will *not* be used for the pod to avoid using an unrelated volume by mistake. Starting the pod is then blocked until the unrelated PVC is removed. If such a pre-created PVC is meant to be used by the pod, the PVC has to updated with an owner reference to the pod once the pod exists. Normally this should not be necessary, but it may be useful when manually reconstructing a broken cluster. - This field is read-only and no changes will be made by Kubernetes to the PVC after it has been created. - Required, must not be nil. +Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e.
volumeClaimTemplate object - Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e. the PVC will be deleted together with the pod. The name of the PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. Pod validation will reject the pod if the concatenated name is not valid for a PVC (for example, too long). - An existing PVC with that name that is not owned by the pod will *not* be used for the pod to avoid using an unrelated volume by mistake. Starting the pod is then blocked until the unrelated PVC is removed. If such a pre-created PVC is meant to be used by the pod, the PVC has to updated with an owner reference to the pod once the pod exists. Normally this should not be necessary, but it may be useful when manually reconstructing a broken cluster. - This field is read-only and no changes will be made by Kubernetes to the PVC after it has been created. - Required, must not be nil.
+ Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e.
false
@@ -4896,7 +22118,7 @@ Will be used to create a stand-alone PVC to provision the volume. The pod in whi @@ -4915,7 +22137,7 @@ Will be used to create a stand-alone PVC to provision the volume. The pod in whi -The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template. The same fields as in a PersistentVolumeClaim are also valid here. +The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template.
spec object - The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template. The same fields as in a PersistentVolumeClaim are also valid here.
+ The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template.
true
@@ -4937,21 +22159,21 @@ The specification for the PersistentVolumeClaim. The entire content is copied un @@ -4991,7 +22213,7 @@ The specification for the PersistentVolumeClaim. The entire content is copied un -dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. If the AnyVolumeDataSource feature gate is enabled, this field will always have the same contents as the DataSourceRef field. +dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.
dataSource object - dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. If the AnyVolumeDataSource feature gate is enabled, this field will always have the same contents as the DataSourceRef field.
+ dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.
false
dataSourceRef object - dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any local object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef allows any non-core object, as well as PersistentVolumeClaim objects. * While DataSource ignores disallowed values (dropping them), DataSourceRef preserves all values, and generates an error if a disallowed value is specified. (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled.
+ dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired.
false
resources object - resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources
+ resources represents the minimum resources the volume should have.
false
@@ -5032,7 +22254,7 @@ dataSource field can be used to specify either: * An existing VolumeSnapshot obj -dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any local object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef allows any non-core object, as well as PersistentVolumeClaim objects. * While DataSource ignores disallowed values (dropping them), DataSourceRef preserves all values, and generates an error if a disallowed value is specified. (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled. +dataSourceRef specifies the object from which to populate the volume with data, if a non-empty volume is desired.
@@ -5064,6 +22286,13 @@ dataSourceRef specifies the object from which to populate the volume with data, APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required.
+ + + + +
false
namespacestring + Namespace is the namespace of resource being referenced Note that when a namespace is specified, a gateway.networking.k8s.
+
false
@@ -5073,7 +22302,7 @@ dataSourceRef specifies the object from which to populate the volume with data, -resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources +resources represents the minimum resources the volume should have. @@ -5085,6 +22314,14 @@ resources represents the minimum resources the volume should have. If RecoverVol + + + + +
claims[]object + Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. + This is an alpha field and requires enabling the DynamicResourceAllocation feature gate.
+
false
limits map[string]int or string @@ -5095,13 +22332,40 @@ resources represents the minimum resources the volume should have. If RecoverVol requests map[string]int or string - Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+ Requests describes the minimum amount of compute resources required.
false
+### OpenTelemetryCollector.spec.volumes[index].ephemeral.volumeClaimTemplate.spec.resources.claims[index] +[↩ Parent](#opentelemetrycollectorspecvolumesindexephemeralvolumeclaimtemplatespecresources) + + + +ResourceClaim references one entry in PodSpec.ResourceClaims. + + + + + + + + + + + + + + + + +
NameTypeDescriptionRequired
namestring + Name must match the name of one entry in pod.spec.resourceClaims of the Pod where this field is used. It makes that resource available inside a container.
+
true
+ + ### OpenTelemetryCollector.spec.volumes[index].ephemeral.volumeClaimTemplate.spec.selector [↩ Parent](#opentelemetrycollectorspecvolumesindexephemeralvolumeclaimtemplatespec) @@ -5129,7 +22393,7 @@ selector is a label query over volumes to consider for binding. matchLabels map[string]string - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+ matchLabels is a map of {key,value} pairs.
false @@ -5170,7 +22434,7 @@ A label selector requirement is a selector that contains values, a key, and an o values []string - values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+ values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty.
false @@ -5252,7 +22516,7 @@ fc represents a Fibre Channel resource that is attached to a kubelet's host mach fsType string - fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. TODO: how do we prevent errors in the filesystem from compromising the machine
+ fsType is the filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
false @@ -5337,7 +22601,7 @@ flexVolume represents a generic volume resource that is provisioned/attached usi secretRef object - secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.
+ secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified.
false @@ -5349,7 +22613,7 @@ flexVolume represents a generic volume resource that is provisioned/attached usi -secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts. +secretRef is Optional: secretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. @@ -5410,7 +22674,7 @@ flocker represents a Flocker volume attached to a kubelet's host machine. This d -gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk +gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's host machine and then exposed to the pod. More info: https://kubernetes.
@@ -5432,14 +22696,14 @@ gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's @@ -5460,7 +22724,7 @@ gcePersistentDisk represents a GCE Disk resource that is attached to a kubelet's -gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod's container. +gitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated.
fsType string - fsType is filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk TODO: how do we prevent errors in the filesystem from compromising the machine
+ fsType is filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs".
false
partition integer - partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1". Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
+ partition is the partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1".

Format: int32
@@ -5482,7 +22746,7 @@ gitRepo represents a git repository at a particular revision. DEPRECATED: GitRep @@ -5542,7 +22806,7 @@ glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime. -hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath --- TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not mount host directories as read/write. +hostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container.
directory string - directory is the target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name.
+ directory is the target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository.
false
@@ -5628,14 +22892,14 @@ iscsi represents an ISCSI Disk resource that is attached to a kubelet's host mac @@ -5743,7 +23007,7 @@ nfs represents an NFS mount on the host that shares a pod's lifetime More info: -persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims +persistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.
fsType string - fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi TODO: how do we prevent errors in the filesystem from compromising the machine
+ fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs".
false
initiatorName string - initiatorName is the custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection.
+ initiatorName is the custom iSCSI Initiator Name.
false
@@ -5867,7 +23131,7 @@ projected items for all in one resources secrets, configmaps, and downward API @@ -5951,7 +23215,7 @@ configMap information about the configMap data to project @@ -6006,7 +23270,7 @@ Maps a string key to a path within a volume. @@ -6076,7 +23340,7 @@ DownwardAPIVolumeFile represents information to create the file containing the p @@ -6187,7 +23451,7 @@ secret information about the secret data to project @@ -6242,7 +23506,7 @@ Maps a string key to a path within a volume. @@ -6278,14 +23542,14 @@ serviceAccountToken is information about the serviceAccountToken data to project @@ -6390,7 +23654,7 @@ rbd represents a Rados Block Device mount on the host that shares a pod's lifeti @@ -6596,7 +23860,7 @@ secret represents a secret that should populate this volume. More info: https:// @@ -6605,7 +23869,7 @@ secret represents a secret that should populate this volume. More info: https:// @@ -6660,7 +23924,7 @@ Maps a string key to a path within a volume. @@ -6717,7 +23981,7 @@ storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes @@ -6816,6 +24080,13 @@ OpenTelemetryCollectorStatus defines the observed state of OpenTelemetryCollecto + + + + + + + + + +
defaultMode integer - defaultMode are the mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ defaultMode are the mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.

Format: int32
items []object - items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.
+ items if unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value.
false
mode integer - mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.

Format: int32
mode integer - Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.

Format: int32
items []object - items if unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.
+ items if unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value.
false
mode integer - mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.

Format: int32
audience string - audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver.
+ audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token.
false
expirationSeconds integer - expirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes.
+ expirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token.

Format: int64
fsType string - fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd TODO: how do we prevent errors in the filesystem from compromising the machine
+ fsType is the filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs".
false
defaultMode integer - defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ defaultMode is Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.

Format: int32
items []object - items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'.
+ items If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value.
false
mode integer - mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.
+ mode is Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511.

Format: int32
volumeNamespace string - volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created.
+ volumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used.
false
imagestring + Image indicates the container image to use for the OpenTelemetry Collector.
+
false
messages []string @@ -6881,5 +24152,12 @@ Scale is the OpenTelemetryCollector's scale subresource status. The selector used to match the OpenTelemetryCollector's deployment or statefulSet pods.
false
statusReplicasstring + StatusReplicas is the number of pods targeted by this OpenTelemetryCollector's with a Ready Condition / Total number of non-terminated pods targeted by this OpenTelemetryCollector's (their labels matc
+
false
\ No newline at end of file diff --git a/go.mod b/go.mod index 126bf6b707..96eaaa8714 100644 --- a/go.mod +++ b/go.mod @@ -1,135 +1,160 @@ module github.com/open-telemetry/opentelemetry-operator -go 1.19 +go 1.20 retract v1.51.0 require ( - github.com/Masterminds/semver/v3 v3.1.1 - github.com/go-logr/logr v1.2.3 + dario.cat/mergo v1.0.0 + github.com/Masterminds/semver/v3 v3.2.1 + github.com/go-logr/logr v1.3.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/prometheus/prometheus v1.8.2-0.20210621150501-ff58416a0b02 + github.com/openshift/api v3.9.0+incompatible + github.com/operator-framework/operator-lib v0.11.0 + github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.70.0 + github.com/prometheus/prometheus v0.48.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.1 - go.opentelemetry.io/otel v1.11.1 + github.com/stretchr/testify v1.8.4 + go.opentelemetry.io/collector/featuregate v0.77.0 + go.opentelemetry.io/otel v1.21.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.25.3 - k8s.io/apimachinery v0.25.3 - k8s.io/client-go v0.25.3 - k8s.io/kubectl v0.25.3 - sigs.k8s.io/controller-runtime v0.13.0 + k8s.io/api v0.28.4 + k8s.io/apiextensions-apiserver v0.28.4 + k8s.io/apimachinery v0.28.4 + k8s.io/client-go v0.28.4 + k8s.io/component-base v0.28.4 + k8s.io/kubectl v0.28.4 + k8s.io/utils v0.0.0-20231127182322-b307cd553661 + sigs.k8s.io/controller-runtime v0.16.3 ) require ( - cloud.google.com/go v0.99.0 // indirect - github.com/Azure/azure-sdk-for-go v55.2.0+incompatible // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.27 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/Microsoft/go-winio v0.4.16 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 // indirect - github.com/armon/go-metrics v0.3.3 // indirect - github.com/aws/aws-sdk-go v1.38.60 // indirect + cloud.google.com/go/compute v1.23.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/aws/aws-sdk-go v1.45.25 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/containerd/containerd v1.4.8 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/digitalocean/godo v1.62.0 // indirect - github.com/docker/distribution v2.8.1+incompatible // indirect - github.com/docker/docker v20.10.14+incompatible // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/digitalocean/godo v1.104.1 // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v24.0.7+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.4.0 // indirect - github.com/emicklei/go-restful/v3 v3.8.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/envoyproxy/go-control-plane v0.11.1 // indirect + github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/go-kit/log v0.1.0 // indirect - github.com/go-logfmt/logfmt v0.5.0 // indirect - github.com/go-logr/zapr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.5 // indirect - github.com/go-openapi/swag v0.19.15 // indirect - github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect - github.com/go-zookeeper/zk v1.0.2 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/zapr v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-resty/resty/v2 v2.7.0 // indirect + github.com/go-zookeeper/zk v1.0.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.2.0 // indirect + github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/go-querystring v1.0.0 // indirect - github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.1.2 // indirect - github.com/googleapis/gax-go/v2 v2.1.1 // indirect - github.com/gophercloud/gophercloud v0.18.0 // indirect - github.com/hashicorp/consul/api v1.8.1 // indirect - github.com/hashicorp/go-cleanhttp v0.5.1 // indirect - github.com/hashicorp/go-hclog v0.12.2 // indirect - github.com/hashicorp/go-immutable-radix v1.2.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/gophercloud/gophercloud v1.7.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect + github.com/hashicorp/consul/api v1.25.1 // indirect + github.com/hashicorp/cronexpr v1.1.2 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-retryablehttp v0.7.4 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect - github.com/hashicorp/serf v0.9.5 // indirect - github.com/hetznercloud/hcloud-go v1.26.2 // indirect - github.com/imdario/mergo v0.3.12 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/golang-lru v0.6.0 // indirect + github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/hetznercloud/hcloud-go/v2 v2.4.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/ionos-cloud/sdk-go/v6 v6.1.9 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/linode/linodego v0.28.5 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect - github.com/miekg/dns v1.1.42 // indirect + github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/linode/linodego v1.23.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/miekg/dns v1.1.56 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.1 // indirect + github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/ovh/go-ovh v1.4.3 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.2 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect - go.opencensus.io v0.23.0 // indirect - go.opentelemetry.io/otel/trace v1.11.1 // indirect - go.uber.org/atomic v1.8.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.21.0 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect - golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/api v0.61.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/common/sigv4 v0.1.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/vultr/govultr/v2 v2.17.2 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.25.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/oauth2 v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/term v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.14.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/api v0.147.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect - google.golang.org/grpc v1.47.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect - gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect + google.golang.org/grpc v1.58.3 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.25.0 // indirect - k8s.io/component-base v0.25.3 // indirect - k8s.io/klog/v2 v2.70.1 // indirect - k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index bcfb164917..9daf429454 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,11 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= @@ -15,28 +13,16 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/bigtable v1.3.0/go.mod h1:z5EyKrPE8OQmeg4h5MNdKvuSnI9CCT49Ki3f23aBzio= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -48,415 +34,144 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go v41.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v55.2.0+incompatible h1:TL2/vJWJEPOrmv97nHcbvjXES0Ntlb9P95hqGA1J2dU= -github.com/Azure/azure-sdk-for-go v55.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0 h1:9kDVnTz3vbfweTqAUmk/a/pH5pWFCHtvRpHYC0G/dcA= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 h1:UPeCRD+XY7QlaGQte2EVI2iOcWvUYA2XY8w5T/8v0NQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1/go.mod h1:oGV6NlB0cvi1ZbYRR2UN44QHxWFyGk+iylgD0qaMXjA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1 h1:bWh0Z2rOEDfB/ywv/l0iHN1JgyazE6kW/aIA89+CEK0= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1/go.mod h1:Bzf34hhAE9NSxailk8xVeLEZbUjOXcC+GnU1mMKdhLw= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= -github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest v0.10.1/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= -github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A= -github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= -github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.8.3/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg= -github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM= -github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= -github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= -github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/SAP/go-hdb v0.14.1/go.mod h1:7fdQLVC2lER3urZLjZCm0AuMQfApof92n3aylBPEkMo= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4= -github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= -github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/apache/arrow/go/arrow v0.0.0-20200923215132-ac86123a3f01/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.3 h1:a9F4rlj7EWWrbj7BYw8J8+x+ZZkJeqzNyRk8hdPF+ro= -github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.29.16/go.mod h1:1KvfttTE3SPKMpo8g2c6jL3ZKfXtFvKscTgahTma5Xg= -github.com/aws/aws-sdk-go v1.30.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= -github.com/aws/aws-sdk-go v1.38.60 h1:MgyEsX0IMwivwth1VwEnesBpH0vxbjp5a0w1lurMOXY= -github.com/aws/aws-sdk-go v1.38.60/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.45.25 h1:c4fLlh5sLdK2DCRTY1z0hyuJZU4ygxX8m1FswL6/nF4= +github.com/aws/aws-sdk-go v1.45.25/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/immutable v0.2.1/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= -github.com/benbjohnson/tmpl v1.0.0/go.mod h1:igT620JFIi44B6awvU9IsDhR77IXWtFigTLil/RPdps= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/bonitoo-io/go-sql-bigquery v0.3.4-1.4.0/go.mod h1:J4Y6YJm0qTWB9aFziB7cPeSyc6dOZFyJdteSeybVpXQ= -github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= -github.com/cactus/go-statsd-client/statsd v0.0.0-20191106001114-12b4e2b38748/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v0.0.0-20181003080854-62661b46c409/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.8 h1:H0wkS4AbVKTg9vyvBdCBrxoax8AMObKbNz9Fl2N0i4Y= -github.com/containerd/containerd v1.4.8/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= -github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= -github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/digitalocean/godo v1.62.0 h1:7Gw2KFsWkxl36qJa0s50tgXaE0Cgm51JdRP+MFQvNnM= -github.com/digitalocean/godo v1.62.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= -github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= -github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.14+incompatible h1:+T9/PRYWNDo5SZl5qS1r9Mo/0Q8AwxKKPtu9S1yxM0w= -github.com/docker/docker v20.10.14+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/digitalocean/godo v1.104.1 h1:SZNxjAsskM/su0YW9P8Wx3gU0W1Z13b6tZlYNpl5BnA= +github.com/digitalocean/godo v1.104.1/go.mod h1:VAI/L5YDzMuPRU01lEEUSQ/sp5Z//1HnnFv/RBTEdbg= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.8.0 h1:eCZ8ulSerjdAiaNpF7GxXIE7ZCMo1moN1qX+S609eVw= -github.com/emicklei/go-restful/v3 v3.8.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= +github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/foxcpp/go-mockdns v0.0.0-20201212160233-ede2f9158d15/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= -github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-chi/chi v4.1.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= -github.com/go-kit/log v0.1.0 h1:DGJh0Sm43HbOeYDNnVZFl8BvcYVvjD5bqYJvp0REbwQ= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= -github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk= -github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/errors v0.19.4/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.0/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= -github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= -github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= -github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= -github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= -github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= -github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= -github.com/go-openapi/runtime v0.19.28/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= -github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= -github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= -github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= -github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= -github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= -github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= -github.com/go-openapi/strfmt v0.20.1/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= -github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= -github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= -github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= -github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= -github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= -github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= -github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= -github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= -github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= +github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM= -github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= +github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -469,9 +184,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -487,18 +199,13 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -510,162 +217,110 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200417002340-c6e0a841f49a/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= -github.com/gophercloud/gophercloud v0.10.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss= -github.com/gophercloud/gophercloud v0.18.0 h1:V6hcuMPmjXg+js9flU8T3RIHDCjV7F5CG5GD0MRhP/w= -github.com/gophercloud/gophercloud v0.18.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.14.4/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU= -github.com/hashicorp/consul/api v1.8.1 h1:BOEQaMWoGMhmQ29fC26bi0qb7/rId9JzZP2V0Xmx7m8= -github.com/hashicorp/consul/api v1.8.1/go.mod h1:sDjTOq0yUyv5G4h+BqSea7Fn6BU+XbolEz1952UB+mk= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= -github.com/hashicorp/consul/sdk v0.7.0 h1:H6R9d008jDcHPQPAqPNuydAshJ4v5/8URdFnUvK/+sc= -github.com/hashicorp/consul/sdk v0.7.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/gophercloud/gophercloud v1.7.0 h1:fyJGKh0LBvIZKLvBWvQdIgkaV5yTM3Jh9EYUh+UNCAs= +github.com/gophercloud/gophercloud v1.7.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= +github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= +github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= +github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.12.2 h1:F1fdYblUEsxKiailtkhCCG2g4bipEgaHiDc8vffNpD4= -github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.2.0 h1:l6UW37iCXwZkZoAbEYnptSHVE/cQ5bOTPYG5W3vf9+8= -github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= +github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.2.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.2.3 h1:BwZa5IjREr75J0am7nblP+X5i95Rmp8EEbMI5vkUWdA= -github.com/hashicorp/memberlist v0.2.3/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU= -github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= -github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= -github.com/hetznercloud/hcloud-go v1.26.2 h1:fI8BXAGJI4EFeCDd2a/I4EhqyK32cDdxGeWfYMGUi50= -github.com/hetznercloud/hcloud-go v1.26.2/go.mod h1:2C5uMtBiMoFr3m7lBFPf7wXTdh33CevmZpQIIDPGYJI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c h1:Nc3Mt2BAnq0/VoLEntF/nipX+K1S7pG+RgwiitSv6v0= +github.com/hashicorp/nomad/api v0.0.0-20230721134942-515895c7690c/go.mod h1:O23qLAZuCx4htdY9zBaO4cJPXgleSFEdq6D/sezGgYE= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hetznercloud/hcloud-go/v2 v2.4.0 h1:MqlAE+w125PLvJRCpAJmEwrIxoVdUdOyuFUhE/Ukbok= +github.com/hetznercloud/hcloud-go/v2 v2.4.0/go.mod h1:l7fA5xsncFBzQTyw29/dw5Yr88yEGKKdc6BHf24ONS0= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY= -github.com/influxdata/flux v0.113.0/go.mod h1:3TJtvbm/Kwuo5/PEo5P6HUzwVg4bXWkb2wPQHPtQdlU= -github.com/influxdata/httprouter v1.3.1-0.20191122104820-ee83e2772f69/go.mod h1:pwymjR6SrP3gD3pRj9RJwdl1j5s3doEEV8gS4X9qSzA= -github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ= -github.com/influxdata/influxdb v1.9.2/go.mod h1:UEe3MeD9AaP5rlPIes102IhYua3FhIWZuOXNHxDjSrI= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo= -github.com/influxdata/influxql v1.1.1-0.20210223160523-b6ab99450c93/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= -github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= -github.com/influxdata/pkg-config v0.2.6/go.mod h1:EMS7Ll0S4qkzDk53XS3Z72/egBsPInt+BeRxb0WeSwk= -github.com/influxdata/pkg-config v0.2.7/go.mod h1:EMS7Ll0S4qkzDk53XS3Z72/egBsPInt+BeRxb0WeSwk= -github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= -github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= -github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= -github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= -github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/ionos-cloud/sdk-go/v6 v6.1.9 h1:Iq3VIXzeEbc8EbButuACgfLMiY5TPVWUPNrF+Vsddo4= +github.com/ionos-cloud/sdk-go/v6 v6.1.9/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= +github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -673,104 +328,59 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= -github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= -github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20160406211939-eadb3ce320cb/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/linode/linodego v0.28.5 h1:JaCziTxHJ7a01MYyjHqskRc8zXQxXOddwrDeoQ2rBnw= -github.com/linode/linodego v0.28.5/go.mod h1:BR0gVkCJffEdIGJSl6bHR80Ty+Uvg/2jkjmrWaFectM= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/linode/linodego v1.23.0 h1:s0ReCZtuN9Z1IoUN9w1RLeYO1dMZUGPwOQ/IBFsBHtU= +github.com/linode/linodego v1.23.0/go.mod h1:0U7wj/UQOqBNbKv1FYTXiBUXueR8DY4HvIotwE0ENgg= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.42 h1:gWGe42RGaIqXQZ+r3WUGEKBEtvPHY2SXo4dqixDNxuY= -github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/mileusna/useragent v0.0.0-20190129205925-3e331f0949a5/go.mod h1:JWhYAp2EXqUtsxTKdeGlY8Wp44M7VxThC9FEoNGi2IE= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -778,316 +388,147 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= -github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= -github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= -github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDDLDLFresAeYs= +github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= +github.com/operator-framework/operator-lib v0.11.0 h1:eYzqpiOfq9WBI4Trddisiq/X9BwCisZd3rIzmHRC9Z8= +github.com/operator-framework/operator-lib v0.11.0/go.mod h1:RpyKhFAoG6DmKTDIwMuO6pI3LRc8IE9rxEYWy476o6g= +github.com/ovh/go-ovh v1.4.3 h1:Gs3V823zwTFpzgGLZNI6ILS4rmxZgJwJCz54Er9LwD0= +github.com/ovh/go-ovh v1.4.3/go.mod h1:AkPXVtgwB6xlKblMjRKJJmjRp+ogrE7fz2lVgcQY8SY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/prometheus/alertmanager v0.20.0/go.mod h1:9g2i48FAyZW6BtbsnvHtMHQXl2aVtrORKwKVCQ+nbrg= -github.com/prometheus/alertmanager v0.22.2/go.mod h1:rYinOWxFuCnNssc3iOjn2oMTlhLaPcUuqV5yk5JKUAE= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.70.0 h1:CFTvpkpVP4EXXZuaZuxpikAoma8xVha/IZKMDc9lw+Y= +github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.70.0/go.mod h1:npfc20mPOAu7ViOVnATVMbI7PoXvW99EzgJVqkAomIQ= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= -github.com/prometheus/common v0.23.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/exporter-toolkit v0.5.1/go.mod h1:OCkM4805mmisBhLmVFw858QYi3v0wKdY6/UxrT0pZVg= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= +github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/prometheus v0.0.0-20200609090129-a6600f564e3c/go.mod h1:S5n0C6tSgdnwWshBUceRx5G1OsjLv/EeZ9t3wIfEtsY= -github.com/prometheus/prometheus v1.8.2-0.20210621150501-ff58416a0b02 h1:waKRn/b6LBaXHjQ3dlZd+0li1nIykM34r5XEYr4lTBM= -github.com/prometheus/prometheus v1.8.2-0.20210621150501-ff58416a0b02/go.mod h1:fC6ROpjS/2o+MQTO7X8NSZLhLBSNlDzxaeDMqQm+TUM= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/prometheus v0.48.0 h1:yrBloImGQ7je4h8M10ujGh4R6oxYQJQKlMuETwNskGk= +github.com/prometheus/prometheus v0.48.0/go.mod h1:SRw624aMAxTfryAcP8rOjg4S/sHHaetx2lyJJ2nM83g= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44 h1:3egqo0Vut6daANFm7tOXdNAa8v5/uLU+sgCJrc88Meo= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21 h1:yWfiTPwYxB0l5fGMhl/G+liULugVIHD9AU77iNLrURQ= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/shoenig/test v0.6.6 h1:Oe8TPH9wAbv++YPNDKJWUnI8Q4PPWCx3UbOfH+FxiMU= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/snowflakedb/gosnowflake v1.3.4/go.mod h1:NsRq2QeiMUuoNUJhp5Q6xGC4uBrsS9g6LwZVEkTWgsE= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/uber-go/tally v3.3.15+incompatible/go.mod h1:YDTIBxdXyOU/sCWilKB4bgyufu1cEi0jdVnRdxvjnmU= -github.com/uber/athenadriver v1.1.4/go.mod h1:tQjho4NzXw55LGfSZEcETuYydpY1vtmixUabHkC1K/E= -github.com/uber/jaeger-client-go v2.23.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= -github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= -github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= -github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= +github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= -go.mongodb.org/mongo-driver v1.3.2/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= -go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= -go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= -go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= -go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= -go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4= -go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE= -go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ= -go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/collector/featuregate v0.77.0 h1:m1/IzaXoQh6SgF6CM80vrBOCf5zSJ2GVISfA27fYzGU= +go.opentelemetry.io/collector/featuregate v0.77.0/go.mod h1:/kVAsGUCyJXIDSgHftCN63QiwAEVHRLX2Kh/S+dqgHY= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.8.0 h1:CUhrE4N1rqSE6FM9ecihEjRkLQu8cDfgDyoOs83mEY4= -go.uber.org/atomic v1.8.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= -go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= -golang.org/x/crypto v0.0.0-20180505025534-4ec37c66abab/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= @@ -1097,7 +538,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1110,8 +552,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -1120,23 +560,16 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1145,12 +578,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1158,55 +586,37 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1214,57 +624,38 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1272,112 +663,71 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190813034749-528a2984e271/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1386,45 +736,28 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304024140-c4206d458c3f/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200422205258-72e4a01eba43/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200721032237-77f530d86f9a/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= -gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1441,23 +774,9 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0 h1:TXXKS1slM3b2bZNJwD5DV/Tp6/M2cLzLOLh9PjDhrw8= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= +google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= @@ -1470,17 +789,13 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= @@ -1490,83 +805,35 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200420144010-e5e8543f8aeb/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI= +google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1579,48 +846,32 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/fsnotify/fsnotify.v1 v1.4.7 h1:XNNYLJHt73EyYiCZi6+xjupS9CpvmiDgjPTAjrBlQbo= -gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1628,58 +879,32 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.17.5/go.mod h1:0zV5/ungglgy2Rlm3QK8fbxkXVs+BSJWpJP/+8gUVLY= -k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s= -k8s.io/api v0.25.3 h1:Q1v5UFfYe87vi5H7NU0p4RXC26PPMT8KOpr1TLQbCMQ= -k8s.io/api v0.25.3/go.mod h1:o42gKscFrEVjHdQnyRenACrMtbuJsVdP+WVjqejfzmI= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= -k8s.io/apimachinery v0.17.5/go.mod h1:ioIo1G/a+uONV7Tv+ZmCbMG1/a3kVw5YcDdncd8ugQ0= -k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= -k8s.io/apimachinery v0.25.3 h1:7o9ium4uyUOM76t6aunP0nZuex7gDf8VGwkR5RcJnQc= -k8s.io/apimachinery v0.25.3/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo= -k8s.io/client-go v0.17.5/go.mod h1:S8uZpBpjJJdEH/fEyxcqg7Rn0P5jH+ilkgBHjriSmNo= -k8s.io/client-go v0.21.1/go.mod h1:/kEw4RgW+3xnBGzvp9IWxKSNA+lXn3A7AuH3gdOAzLs= -k8s.io/client-go v0.25.3 h1:oB4Dyl8d6UbfDHD8Bv8evKylzs3BXzzufLiO27xuPs0= -k8s.io/client-go v0.25.3/go.mod h1:t39LPczAIMwycjcXkVc+CB+PZV69jQuNx4um5ORDjQA= -k8s.io/component-base v0.25.3 h1:UrsxciGdrCY03ULT1h/S/gXFCOPnLhUVwSyx+hM/zq4= -k8s.io/component-base v0.25.3/go.mod h1:WYoS8L+IlTZgU7rhAl5Ctpw0WdMxDfCC5dkxcEFa/TI= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.70.1 h1:7aaoSdahviPmR+XkS7FyxlkkXs6tHISSG03RxleQAVQ= -k8s.io/klog/v2 v2.70.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200316234421-82d701f24f9d/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA= -k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/kubectl v0.25.3 h1:HnWJziEtmsm4JaJiKT33kG0kadx68MXxUE8UEbXnN4U= -k8s.io/kubectl v0.25.3/go.mod h1:glU7PiVj/R6Ud4A9FJdTcJjyzOtCJyc0eO7Mrbh3jlI= -k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -k8s.io/utils v0.0.0-20200414100711-2df71ebbae66/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY= +k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0= +k8s.io/apiextensions-apiserver v0.28.4 h1:AZpKY/7wQ8n+ZYDtNHbAJBb+N4AXXJvyZx6ww6yAJvU= +k8s.io/apiextensions-apiserver v0.28.4/go.mod h1:pgQIZ1U8eJSMQcENew/0ShUTlePcSGFq6dxSxf2mwPM= +k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8= +k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg= +k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY= +k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4= +k8s.io/component-base v0.28.4 h1:c/iQLWPdUgI90O+T9TeECg8o7N3YJTiuz2sKxILYcYo= +k8s.io/component-base v0.28.4/go.mod h1:m9hR0uvqXDybiGL2nf/3Lf0MerAfQXzkfWhUY58JUbU= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +k8s.io/kubectl v0.28.4 h1:gWpUXW/T7aFne+rchYeHkyB8eVDl5UZce8G4X//kjUQ= +k8s.io/kubectl v0.28.4/go.mod h1:CKOccVx3l+3MmDbkXtIUtibq93nN2hkDR99XDCn7c/c= +k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI= +k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= -sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v2 v2.0.1/go.mod h1:Wb7vfKAodbKgf6tn1Kl0VvGj7mRH6DGaRcixXEJXTsE= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= +sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/hack/check-operator-ready.go b/hack/check-operator-ready.go new file mode 100644 index 0000000000..4bf6390cdf --- /dev/null +++ b/hack/check-operator-ready.go @@ -0,0 +1,135 @@ +// 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 main + +import ( + "context" + "fmt" + "os" + "path/filepath" + "time" + + appsv1 "k8s.io/api/apps/v1" + k8sruntime "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + + "github.com/spf13/pflag" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" + "sigs.k8s.io/controller-runtime/pkg/client" + + otelv1alpha1 "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +var scheme *k8sruntime.Scheme + +func init() { + scheme = k8sruntime.NewScheme() + utilruntime.Must(otelv1alpha1.AddToScheme(scheme)) + utilruntime.Must(appsv1.AddToScheme(scheme)) +} + +func main() { + var timeout int + var kubeconfigPath string + + defaultKubeconfigPath := filepath.Join(homedir.HomeDir(), ".kube", "config") + + pflag.IntVar(&timeout, "timeout", 300, "The timeout for the check.") + pflag.StringVar(&kubeconfigPath, "kubeconfig-path", defaultKubeconfigPath, "Absolute path to the KubeconfigPath file") + pflag.Parse() + + pollInterval := 500 * time.Millisecond + timeoutPoll := time.Duration(timeout) * time.Second + + config, err := clientcmd.BuildConfigFromFlags("", kubeconfigPath) + if err != nil { + fmt.Printf("Error reading the kubeconfig: %s\n", err.Error()) + os.Exit(1) + } + + clusterClient, err := client.New(config, client.Options{Scheme: scheme}) + if err != nil { + fmt.Printf("Creating the Kubernetes client: %s\n", err) + os.Exit(1) + } + + fmt.Println("Waiting until the OpenTelemetry Operator deployment is created") + operatorDeployment := &appsv1.Deployment{} + + ctx := context.Background() + err = wait.PollUntilContextTimeout(ctx, pollInterval, timeoutPoll, false, func(c context.Context) (done bool, err error) { + err = clusterClient.Get( + c, + client.ObjectKey{ + Name: "opentelemetry-operator-controller-manager", + Namespace: "opentelemetry-operator-system", + }, + operatorDeployment, + ) + if err != nil { + fmt.Printf("Failed to get OpenTelemetry operator deployment: %s\n", err) + return false, nil + } + return true, nil + }) + + if err != nil { + fmt.Println(err) + } + fmt.Println("OpenTelemetry Operator deployment is created. Now checking if it if fully operational.") + + // Sometimes, the deployment of the OTEL Operator is ready but, when + // creating new instances of the OTEL Collector, the webhook is not reachable + // and kubectl apply fails. This code deployes an OTEL Collector instance + // until success (or timeout) + collectorInstance := otelv1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "operator-check", + Namespace: "default", + }, + } + + // Ensure the collector is not there before the check + _ = clusterClient.Delete(context.Background(), &collectorInstance) + + fmt.Println("Check if the OpenTelemetry collector CR can be created.") + collectorCtx := context.Background() + err = wait.PollUntilContextTimeout(collectorCtx, pollInterval, timeoutPoll, false, func(c context.Context) (done bool, err error) { + err = clusterClient.Create( + c, + &collectorInstance, + ) + if err != nil { + fmt.Printf("failed: to create OpenTelemetry collector CR %s\n", err) + return false, nil + } + return true, nil + }) + + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if err := clusterClient.Delete(context.Background(), &collectorInstance); err != nil { + fmt.Printf("Failed to delete OpenTelemetry collector CR: %s\n", err) + os.Exit(1) + } + + fmt.Println("OpenTelemetry operator is ready.") +} diff --git a/hack/ignore-createdAt-bundle.sh b/hack/ignore-createdAt-bundle.sh new file mode 100755 index 0000000000..d94a30a477 --- /dev/null +++ b/hack/ignore-createdAt-bundle.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# Since operator-sdk 1.26.0, `make bundle` changes the `createdAt` field from the bundle +# even if it is patched: +# https://github.com/operator-framework/operator-sdk/pull/6136 +# This code checks if only the createdAt field. If is the only change, it is ignored. +# Else, it will do nothing. +# https://github.com/operator-framework/operator-sdk/issues/6285#issuecomment-1415350333 +git diff --quiet -I'^ createdAt: ' bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml +ret=$? +changes=$(git diff --numstat bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml) +if [ $ret = 0 ] && [ "$changes" = '1 1 bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml' ] ; then + git checkout bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml +fi diff --git a/hack/install-kubebuilder.sh b/hack/install-kubebuilder.sh deleted file mode 100755 index 216de0a329..0000000000 --- a/hack/install-kubebuilder.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -os=$(go env GOOS) -arch=$(go env GOARCH) -curl -L https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.3.1/kubebuilder_2.3.1_${os}_${arch}.tar.gz | tar -xz -C /tmp/ -sudo mv /tmp/kubebuilder_2.3.1_${os}_${arch} /usr/local/kubebuilder -export PATH=$PATH:/usr/local/kubebuilder/bin diff --git a/hack/install-kustomize.sh b/hack/install-kustomize.sh deleted file mode 100755 index 0b0405b436..0000000000 --- a/hack/install-kustomize.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -sudo curl -sL https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv4.2.0/kustomize_v4.2.0_linux_amd64.tar.gz | tar xvz -C /usr/local/bin/ -export PATH=$PATH:/usr/local/bin diff --git a/hack/install-kuttl.sh b/hack/install-kuttl.sh index 760e786fe8..94326b52c0 100755 --- a/hack/install-kuttl.sh +++ b/hack/install-kuttl.sh @@ -1,5 +1,5 @@ #!/bin/bash -sudo curl -Lo /usr/local/bin/kubectl-kuttl https://github.com/kudobuilder/kuttl/releases/download/v0.12.1/kubectl-kuttl_0.12.1_linux_x86_64 +sudo curl -Lo /usr/local/bin/kubectl-kuttl https://github.com/kudobuilder/kuttl/releases/download/v0.15.0/kubectl-kuttl_0.15.0_linux_x86_64 sudo chmod +x /usr/local/bin/kubectl-kuttl export PATH=$PATH:/usr/local/bin diff --git a/hack/install-metrics-server.sh b/hack/install-metrics-server.sh index d63332d77f..e217a3d34b 100755 --- a/hack/install-metrics-server.sh +++ b/hack/install-metrics-server.sh @@ -1,7 +1,16 @@ #!/bin/bash -# Install metrics-server on kind clusters for autoscale tests. Note: This is not needed for minikube, +# Install metrics-server on kind clusters for autoscale tests. +# Note: This is not needed for minikube, # you can just add --addons "metrics-server" to the start command. -kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -kubectl patch deployment -n kube-system metrics-server --type "json" -p '[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": --kubelet-insecure-tls}]' -kubectl wait --for=condition=available deployment/metrics-server -n kube-system --timeout=5m + + +if [[ "$(kubectl api-resources --api-group=operator.openshift.io -o name)" ]]; then + echo "Connected to an OpenShift cluster. metrics-server installation is not needed" +elif [[ "$(kubectl get deployment metrics-server -n kube-system 2>&1 )" =~ "NotFound" ]]; then + kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml + kubectl patch deployment -n kube-system metrics-server --type "json" -p '[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": --kubelet-insecure-tls}]' + kubectl wait --for=condition=available deployment/metrics-server -n kube-system --timeout=5m +else + echo "metrics-server is installed. Skipping installation" +fi diff --git a/hack/install-prometheus-operator.sh b/hack/install-prometheus-operator.sh new file mode 100755 index 0000000000..3809e9f15c --- /dev/null +++ b/hack/install-prometheus-operator.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [[ "$(kubectl api-resources --api-group=monitoring.coreos.com -o name)" ]]; then + echo "Prometheus CRDs are there" +else + kubectl create -f https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.66.0/bundle.yaml +fi diff --git a/hack/install-targetallocator-prometheus-crds.sh b/hack/install-targetallocator-prometheus-crds.sh new file mode 100755 index 0000000000..a905925eb0 --- /dev/null +++ b/hack/install-targetallocator-prometheus-crds.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +if [[ "$(kubectl api-resources --api-group=monitoring.coreos.com -o name)" ]]; then + echo "Prometheus CRDs are there" +else + kubectl create -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/main/example/prometheus-operator-crd/monitoring.coreos.com_servicemonitors.yaml + kubectl create -f https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/main/example/prometheus-operator-crd/monitoring.coreos.com_podmonitors.yaml +fi diff --git a/hack/modify-test-images.sh b/hack/modify-test-images.sh new file mode 100755 index 0000000000..5c98e53984 --- /dev/null +++ b/hack/modify-test-images.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +SED_BIN=${SED_BIN:-sed} + +DEFAULT_TARGETALLOCATOR_IMG=${DEFAULT_TARGETALLOCATOR_IMG:-local/opentelemetry-operator-targetallocator:e2e} +DEFAULT_OPERATOROPAMPBRIDGE_IMG=${DEFAULT_OPERATOROPAMPBRIDGE_IMG:-local/opentelemetry-operator-opamp-bridge:e2e} +DEFAULT_OPERATOR_IMG=${DEFAULT_OPERATOR_IMG:-local/opentelemetry-operator:e2e} + +${SED_BIN} -i "s#${DEFAULT_TARGETALLOCATOR_IMG}#${TARGETALLOCATOR_IMG}#g" tests/e2e/smoke-targetallocator/*.yaml +${SED_BIN} -i "s#${DEFAULT_TARGETALLOCATOR_IMG}#${TARGETALLOCATOR_IMG}#g" tests/e2e/targetallocator-features/00-install.yaml +${SED_BIN} -i "s#${DEFAULT_TARGETALLOCATOR_IMG}#${TARGETALLOCATOR_IMG}#g" tests/e2e/prometheus-config-validation/*.yaml +${SED_BIN} -i "s#${DEFAULT_TARGETALLOCATOR_IMG}#${TARGETALLOCATOR_IMG}#g" tests/e2e/targetallocator-prometheuscr/*.yaml + +${SED_BIN} -i "s#${DEFAULT_OPERATOR_IMG}#${OPERATOR_IMG}#g" tests/e2e-multi-instrumentation/*.yaml + +${SED_BIN} -i "s#${DEFAULT_OPERATOROPAMPBRIDGE_IMG}#${OPERATOROPAMPBRIDGE_IMG}#g" tests/e2e-opampbridge/opampbridge/*.yaml diff --git a/internal/autodetect/main.go b/internal/autodetect/main.go new file mode 100644 index 0000000000..fb71adaa39 --- /dev/null +++ b/internal/autodetect/main.go @@ -0,0 +1,66 @@ +// 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 autodetect is for auto-detecting traits from the environment (platform, APIs, ...). +package autodetect + +import ( + "k8s.io/client-go/discovery" + "k8s.io/client-go/rest" + + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" +) + +var _ AutoDetect = (*autoDetect)(nil) + +// AutoDetect provides an assortment of routines that auto-detect traits based on the runtime. +type AutoDetect interface { + OpenShiftRoutesAvailability() (openshift.RoutesAvailability, error) +} + +type autoDetect struct { + dcl discovery.DiscoveryInterface +} + +// New creates a new auto-detection worker, using the given client when talking to the current cluster. +func New(restConfig *rest.Config) (AutoDetect, error) { + dcl, err := discovery.NewDiscoveryClientForConfig(restConfig) + if err != nil { + // it's pretty much impossible to get into this problem, as most of the + // code branches from the previous call just won't fail at all, + // but let's handle this error anyway... + return nil, err + } + + return &autoDetect{ + dcl: dcl, + }, nil +} + +// OpenShiftRoutesAvailability checks if OpenShift Route are available. +func (a *autoDetect) OpenShiftRoutesAvailability() (openshift.RoutesAvailability, error) { + apiList, err := a.dcl.ServerGroups() + if err != nil { + return openshift.RoutesNotAvailable, err + } + + apiGroups := apiList.Groups + for i := 0; i < len(apiGroups); i++ { + if apiGroups[i].Name == "route.openshift.io" { + return openshift.RoutesAvailable, nil + } + } + + return openshift.RoutesNotAvailable, nil +} diff --git a/pkg/autodetect/main_test.go b/internal/autodetect/main_test.go similarity index 55% rename from pkg/autodetect/main_test.go rename to internal/autodetect/main_test.go index eea043120a..6c18eee59a 100644 --- a/pkg/autodetect/main_test.go +++ b/internal/autodetect/main_test.go @@ -25,18 +25,18 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - "github.com/open-telemetry/opentelemetry-operator/pkg/platform" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" ) func TestDetectPlatformBasedOnAvailableAPIGroups(t *testing.T) { for _, tt := range []struct { apiGroupList *metav1.APIGroupList - expected platform.Platform + expected openshift.RoutesAvailability }{ { &metav1.APIGroupList{}, - platform.Kubernetes, + openshift.RoutesNotAvailable, }, { &metav1.APIGroupList{ @@ -46,7 +46,7 @@ func TestDetectPlatformBasedOnAvailableAPIGroups(t *testing.T) { }, }, }, - platform.OpenShift, + openshift.RoutesAvailable, }, } { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { @@ -64,39 +64,10 @@ func TestDetectPlatformBasedOnAvailableAPIGroups(t *testing.T) { require.NoError(t, err) // test - plt, err := autoDetect.Platform() + ora, err := autoDetect.OpenShiftRoutesAvailability() // verify assert.NoError(t, err) - assert.Equal(t, tt.expected, plt) + assert.Equal(t, tt.expected, ora) } } - -func TestUnknownPlatformOnError(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - })) - defer server.Close() - - autoDetect, err := autodetect.New(&rest.Config{Host: server.URL}) - require.NoError(t, err) - - // test - plt, err := autoDetect.Platform() - - // verify - assert.Error(t, err) - assert.Equal(t, platform.Unknown, plt) -} - -func TestAutoscalingVersionToString(t *testing.T) { - assert.Equal(t, "v2", autodetect.AutoscalingVersionV2.String()) - assert.Equal(t, "v2beta2", autodetect.AutoscalingVersionV2Beta2.String()) - assert.Equal(t, "unknown", autodetect.AutoscalingVersionUnknown.String()) -} - -func TestToAutoScalingVersion(t *testing.T) { - assert.Equal(t, autodetect.AutoscalingVersionV2, autodetect.ToAutoScalingVersion("v2")) - assert.Equal(t, autodetect.AutoscalingVersionV2Beta2, autodetect.ToAutoScalingVersion("v2beta2")) - assert.Equal(t, autodetect.AutoscalingVersionUnknown, autodetect.ToAutoScalingVersion("fred")) -} diff --git a/pkg/platform/main.go b/internal/autodetect/openshift/routes.go similarity index 55% rename from pkg/platform/main.go rename to internal/autodetect/openshift/routes.go index e4791a28eb..1872b9ba10 100644 --- a/pkg/platform/main.go +++ b/internal/autodetect/openshift/routes.go @@ -12,23 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package platform contains target platforms this operator might run on. -package platform +package openshift -// Platform holds the auto-detected platform type. -type Platform int +// RoutesAvailability holds the auto-detected OpenShift Routes availability API. +type RoutesAvailability int const ( - // Unknown is used when the current platform can't be determined. - Unknown Platform = iota + // RoutesAvailable represents the route.openshift.io API is available. + RoutesAvailable RoutesAvailability = iota - // OpenShift represents a platform of type OpenShift. - OpenShift Platform = iota - - // Kubernetes represents a platform of type Kubernetes. - Kubernetes + // RoutesNotAvailable represents the route.openshift.io API is not available. + RoutesNotAvailable ) -func (p Platform) String() string { - return [...]string{"Unknown", "OpenShift", "Kubernetes"}[p] +func (p RoutesAvailability) String() string { + return [...]string{"Available", "NotAvailable"}[p] } diff --git a/internal/config/main.go b/internal/config/main.go index 64aeaa551d..8c16226d6a 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -21,127 +21,84 @@ import ( "github.com/go-logr/logr" logf "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" "github.com/open-telemetry/opentelemetry-operator/internal/version" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - "github.com/open-telemetry/opentelemetry-operator/pkg/platform" ) const ( - defaultAutoDetectFrequency = 5 * time.Second - defaultCollectorConfigMapEntry = "collector.yaml" - defaultTargetAllocatorConfigMapEntry = "targetallocator.yaml" + defaultAutoDetectFrequency = 5 * time.Second + defaultCollectorConfigMapEntry = "collector.yaml" + defaultTargetAllocatorConfigMapEntry = "targetallocator.yaml" + defaultOperatorOpAMPBridgeConfigMapEntry = "remoteconfiguration.yaml" ) // Config holds the static configuration for this operator. type Config struct { - autoDetect autodetect.AutoDetect - OnChange func() error - logger logr.Logger - targetAllocatorImage string - autoInstrumentationPythonImage string - collectorImage string - collectorConfigMapEntry string - autoInstrumentationDotNetImage string - targetAllocatorConfigMapEntry string - autoInstrumentationNodeJSImage string - autoInstrumentationJavaImage string - onChange []func() error - labelsFilter []string - platform platform.Platform - autoDetectFrequency time.Duration - autoscalingVersion autodetect.AutoscalingVersion + autoDetect autodetect.AutoDetect + logger logr.Logger + targetAllocatorImage string + operatorOpAMPBridgeImage string + autoInstrumentationPythonImage string + collectorImage string + collectorConfigMapEntry string + autoInstrumentationDotNetImage string + autoInstrumentationGoImage string + autoInstrumentationApacheHttpdImage string + autoInstrumentationNginxImage string + targetAllocatorConfigMapEntry string + operatorOpAMPBridgeConfigMapEntry string + autoInstrumentationNodeJSImage string + autoInstrumentationJavaImage string + openshiftRoutesAvailability openshift.RoutesAvailability + labelsFilter []string } // New constructs a new configuration based on the given options. func New(opts ...Option) Config { // initialize with the default values o := options{ - autoDetectFrequency: defaultAutoDetectFrequency, - collectorConfigMapEntry: defaultCollectorConfigMapEntry, - targetAllocatorConfigMapEntry: defaultTargetAllocatorConfigMapEntry, - logger: logf.Log.WithName("config"), - platform: platform.Unknown, - version: version.Get(), - autoscalingVersion: autodetect.DefaultAutoscalingVersion, + openshiftRoutesAvailability: openshift.RoutesNotAvailable, + collectorConfigMapEntry: defaultCollectorConfigMapEntry, + targetAllocatorConfigMapEntry: defaultTargetAllocatorConfigMapEntry, + operatorOpAMPBridgeConfigMapEntry: defaultOperatorOpAMPBridgeConfigMapEntry, + logger: logf.Log.WithName("config"), + version: version.Get(), } for _, opt := range opts { opt(&o) } return Config{ - autoDetect: o.autoDetect, - autoDetectFrequency: o.autoDetectFrequency, - collectorImage: o.collectorImage, - collectorConfigMapEntry: o.collectorConfigMapEntry, - targetAllocatorImage: o.targetAllocatorImage, - targetAllocatorConfigMapEntry: o.targetAllocatorConfigMapEntry, - logger: o.logger, - onChange: o.onChange, - platform: o.platform, - autoInstrumentationJavaImage: o.autoInstrumentationJavaImage, - autoInstrumentationNodeJSImage: o.autoInstrumentationNodeJSImage, - autoInstrumentationPythonImage: o.autoInstrumentationPythonImage, - autoInstrumentationDotNetImage: o.autoInstrumentationDotNetImage, - labelsFilter: o.labelsFilter, - autoscalingVersion: o.autoscalingVersion, - } -} - -// StartAutoDetect attempts to automatically detect relevant information for this operator. This will block until the first -// run is executed and will schedule periodic updates. -func (c *Config) StartAutoDetect() error { - err := c.AutoDetect() - go c.periodicAutoDetect() - - return err -} - -func (c *Config) periodicAutoDetect() { - ticker := time.NewTicker(c.autoDetectFrequency) - - for range ticker.C { - if err := c.AutoDetect(); err != nil { - c.logger.Info("auto-detection failed", "error", err) - } + autoDetect: o.autoDetect, + collectorImage: o.collectorImage, + collectorConfigMapEntry: o.collectorConfigMapEntry, + targetAllocatorImage: o.targetAllocatorImage, + operatorOpAMPBridgeImage: o.operatorOpAMPBridgeImage, + targetAllocatorConfigMapEntry: o.targetAllocatorConfigMapEntry, + operatorOpAMPBridgeConfigMapEntry: o.operatorOpAMPBridgeConfigMapEntry, + logger: o.logger, + openshiftRoutesAvailability: o.openshiftRoutesAvailability, + autoInstrumentationJavaImage: o.autoInstrumentationJavaImage, + autoInstrumentationNodeJSImage: o.autoInstrumentationNodeJSImage, + autoInstrumentationPythonImage: o.autoInstrumentationPythonImage, + autoInstrumentationDotNetImage: o.autoInstrumentationDotNetImage, + autoInstrumentationGoImage: o.autoInstrumentationGoImage, + autoInstrumentationApacheHttpdImage: o.autoInstrumentationApacheHttpdImage, + autoInstrumentationNginxImage: o.autoInstrumentationNginxImage, + labelsFilter: o.labelsFilter, } } // AutoDetect attempts to automatically detect relevant information for this operator. func (c *Config) AutoDetect() error { - changed := false c.logger.V(2).Info("auto-detecting the configuration based on the environment") - // TODO: once new things need to be detected, extract this into individual detection routines - if c.platform == platform.Unknown { - plt, err := c.autoDetect.Platform() - if err != nil { - return err - } - - if c.platform != plt { - c.logger.V(1).Info("platform detected", "platform", plt) - c.platform = plt - changed = true - } - } - - if changed { - for _, callback := range c.onChange { - if err := callback(); err != nil { - // we don't fail if the callback failed, as the auto-detection itself - // did work - c.logger.Error(err, "configuration change notification failed for callback") - } - } - } - - hpaVersion, err := c.autoDetect.HPAVersion() + ora, err := c.autoDetect.OpenShiftRoutesAvailability() if err != nil { return err } - c.autoscalingVersion = hpaVersion - c.logger.V(1).Info("autoscaling version detected", "autoscaling-version", c.autoscalingVersion.String()) - + c.openshiftRoutesAvailability = ora return nil } @@ -160,19 +117,24 @@ func (c *Config) TargetAllocatorImage() string { return c.targetAllocatorImage } +// OperatorOpAMPBridgeImage represents the flag to override the OpAMPBridge container image. +func (c *Config) OperatorOpAMPBridgeImage() string { + return c.operatorOpAMPBridgeImage +} + // TargetAllocatorConfigMapEntry represents the configuration file name for the TargetAllocator. Immutable. func (c *Config) TargetAllocatorConfigMapEntry() string { return c.targetAllocatorConfigMapEntry } -// Platform represents the type of the platform this operator is running. -func (c *Config) Platform() platform.Platform { - return c.platform +// OperatorOpAMPBridgeImageConfigMapEntry represents the configuration file name for the OpAMPBridge. Immutable. +func (c *Config) OperatorOpAMPBridgeConfigMapEntry() string { + return c.operatorOpAMPBridgeConfigMapEntry } -// AutoscalingVersion represents the preferred version of autoscaling. -func (c *Config) AutoscalingVersion() autodetect.AutoscalingVersion { - return c.autoscalingVersion +// OpenShiftRoutesAvailability represents the availability of the OpenShift Routes API. +func (c *Config) OpenShiftRoutesAvailability() openshift.RoutesAvailability { + return c.openshiftRoutesAvailability } // AutoInstrumentationJavaImage returns OpenTelemetry Java auto-instrumentation container image. @@ -195,7 +157,22 @@ func (c *Config) AutoInstrumentationDotNetImage() string { return c.autoInstrumentationDotNetImage } -// Returns the filters converted to regex strings used to filter out unwanted labels from propagations. +// AutoInstrumentationGoImage returns OpenTelemetry Go auto-instrumentation container image. +func (c *Config) AutoInstrumentationGoImage() string { + return c.autoInstrumentationGoImage +} + +// AutoInstrumentationApacheHttpdImage returns OpenTelemetry ApacheHttpd auto-instrumentation container image. +func (c *Config) AutoInstrumentationApacheHttpdImage() string { + return c.autoInstrumentationApacheHttpdImage +} + +// AutoInstrumentationNginxImage returns OpenTelemetry Nginx auto-instrumentation container image. +func (c *Config) AutoInstrumentationNginxImage() string { + return c.autoInstrumentationNginxImage +} + +// LabelsFilter Returns the filters converted to regex strings used to filter out unwanted labels from propagations. func (c *Config) LabelsFilter() []string { return c.labelsFilter } diff --git a/internal/config/main_test.go b/internal/config/main_test.go index fbdc6d8995..44345bdf56 100644 --- a/internal/config/main_test.go +++ b/internal/config/main_test.go @@ -15,16 +15,14 @@ package config_test import ( - "sync" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - "github.com/open-telemetry/opentelemetry-operator/pkg/platform" ) func TestNewConfig(t *testing.T) { @@ -32,83 +30,46 @@ func TestNewConfig(t *testing.T) { cfg := config.New( config.WithCollectorImage("some-image"), config.WithCollectorConfigMapEntry("some-config.yaml"), - config.WithPlatform(platform.Kubernetes), + config.WithOpenShiftRoutesAvailability(openshift.RoutesAvailable), ) // test assert.Equal(t, "some-image", cfg.CollectorImage()) assert.Equal(t, "some-config.yaml", cfg.CollectorConfigMapEntry()) - assert.Equal(t, platform.Kubernetes, cfg.Platform()) + assert.Equal(t, openshift.RoutesAvailable, cfg.OpenShiftRoutesAvailability()) } -func TestCallbackOnChanges(t *testing.T) { +func TestConfigChangesOnAutoDetect(t *testing.T) { // prepare - calledBack := false mock := &mockAutoDetect{ - PlatformFunc: func() (platform.Platform, error) { - return platform.OpenShift, nil + OpenShiftRoutesAvailabilityFunc: func() (openshift.RoutesAvailability, error) { + return openshift.RoutesAvailable, nil }, } cfg := config.New( config.WithAutoDetect(mock), - config.WithOnChange(func() error { - calledBack = true - return nil - }), ) // sanity check - require.Equal(t, platform.Unknown, cfg.Platform()) + require.Equal(t, openshift.RoutesNotAvailable, cfg.OpenShiftRoutesAvailability()) // test err := cfg.AutoDetect() require.NoError(t, err) // verify - assert.Equal(t, platform.OpenShift, cfg.Platform()) - assert.True(t, calledBack) -} - -func TestAutoDetectInBackground(t *testing.T) { - // prepare - wg := &sync.WaitGroup{} - wg.Add(2) - mock := &mockAutoDetect{ - PlatformFunc: func() (platform.Platform, error) { - wg.Done() - // returning Unknown will cause the auto-detection to keep trying to detect the platform - return platform.Unknown, nil - }, - } - cfg := config.New( - config.WithAutoDetect(mock), - config.WithAutoDetectFrequency(100*time.Millisecond), - ) - - // sanity check - require.Equal(t, platform.Unknown, cfg.Platform()) - - // test - err := cfg.StartAutoDetect() - require.NoError(t, err) - - // verify - wg.Wait() + assert.Equal(t, openshift.RoutesAvailable, cfg.OpenShiftRoutesAvailability()) } var _ autodetect.AutoDetect = (*mockAutoDetect)(nil) type mockAutoDetect struct { - PlatformFunc func() (platform.Platform, error) -} - -func (m *mockAutoDetect) HPAVersion() (autodetect.AutoscalingVersion, error) { - return autodetect.DefaultAutoscalingVersion, nil + OpenShiftRoutesAvailabilityFunc func() (openshift.RoutesAvailability, error) } -func (m *mockAutoDetect) Platform() (platform.Platform, error) { - if m.PlatformFunc != nil { - return m.PlatformFunc() +func (m *mockAutoDetect) OpenShiftRoutesAvailability() (openshift.RoutesAvailability, error) { + if m.OpenShiftRoutesAvailabilityFunc != nil { + return m.OpenShiftRoutesAvailabilityFunc() } - return platform.Unknown, nil + return openshift.RoutesNotAvailable, nil } diff --git a/internal/config/options.go b/internal/config/options.go index a3846ce156..225692cec4 100644 --- a/internal/config/options.go +++ b/internal/config/options.go @@ -17,35 +17,36 @@ package config import ( "regexp" "strings" - "time" "github.com/go-logr/logr" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" "github.com/open-telemetry/opentelemetry-operator/internal/version" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - "github.com/open-telemetry/opentelemetry-operator/pkg/platform" ) // Option represents one specific configuration option. type Option func(c *options) type options struct { - autoDetect autodetect.AutoDetect - version version.Version - logger logr.Logger - autoInstrumentationDotNetImage string - autoInstrumentationJavaImage string - autoInstrumentationNodeJSImage string - autoInstrumentationPythonImage string - collectorImage string - collectorConfigMapEntry string - targetAllocatorConfigMapEntry string - targetAllocatorImage string - onChange []func() error - labelsFilter []string - platform platform.Platform - autoDetectFrequency time.Duration - autoscalingVersion autodetect.AutoscalingVersion + autoDetect autodetect.AutoDetect + version version.Version + logger logr.Logger + autoInstrumentationDotNetImage string + autoInstrumentationGoImage string + autoInstrumentationJavaImage string + autoInstrumentationNodeJSImage string + autoInstrumentationPythonImage string + autoInstrumentationApacheHttpdImage string + autoInstrumentationNginxImage string + collectorImage string + collectorConfigMapEntry string + targetAllocatorConfigMapEntry string + operatorOpAMPBridgeConfigMapEntry string + targetAllocatorImage string + operatorOpAMPBridgeImage string + openshiftRoutesAvailability openshift.RoutesAvailability + labelsFilter []string } func WithAutoDetect(a autodetect.AutoDetect) Option { @@ -53,17 +54,16 @@ func WithAutoDetect(a autodetect.AutoDetect) Option { o.autoDetect = a } } -func WithAutoDetectFrequency(t time.Duration) Option { +func WithTargetAllocatorImage(s string) Option { return func(o *options) { - o.autoDetectFrequency = t + o.targetAllocatorImage = s } } -func WithTargetAllocatorImage(s string) Option { +func WithOperatorOpAMPBridgeImage(s string) Option { return func(o *options) { - o.targetAllocatorImage = s + o.operatorOpAMPBridgeImage = s } } - func WithCollectorImage(s string) Option { return func(o *options) { o.collectorImage = s @@ -79,22 +79,14 @@ func WithTargetAllocatorConfigMapEntry(s string) Option { o.targetAllocatorConfigMapEntry = s } } -func WithLogger(logger logr.Logger) Option { +func WithOperatorOpAMPBridgeConfigMapEntry(s string) Option { return func(o *options) { - o.logger = logger + o.operatorOpAMPBridgeConfigMapEntry = s } } -func WithOnChange(f func() error) Option { - return func(o *options) { - if o.onChange == nil { - o.onChange = []func() error{} - } - o.onChange = append(o.onChange, f) - } -} -func WithPlatform(plt platform.Platform) Option { +func WithLogger(logger logr.Logger) Option { return func(o *options) { - o.platform = plt + o.logger = logger } } func WithVersion(v version.Version) Option { @@ -127,6 +119,30 @@ func WithAutoInstrumentationDotNetImage(s string) Option { } } +func WithAutoInstrumentationGoImage(s string) Option { + return func(o *options) { + o.autoInstrumentationGoImage = s + } +} + +func WithAutoInstrumentationApacheHttpdImage(s string) Option { + return func(o *options) { + o.autoInstrumentationApacheHttpdImage = s + } +} + +func WithAutoInstrumentationNginxImage(s string) Option { + return func(o *options) { + o.autoInstrumentationNginxImage = s + } +} + +func WithOpenShiftRoutesAvailability(os openshift.RoutesAvailability) Option { + return func(o *options) { + o.openshiftRoutesAvailability = os + } +} + func WithLabelFilters(labelFilters []string) Option { return func(o *options) { diff --git a/internal/manifests/builder.go b/internal/manifests/builder.go new file mode 100644 index 0000000000..88a47ed277 --- /dev/null +++ b/internal/manifests/builder.go @@ -0,0 +1,46 @@ +// 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 manifests + +import ( + "reflect" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type Builder func(params Params) ([]client.Object, error) + +type ManifestFactory[T client.Object] func(params Params) (T, error) +type SimpleManifestFactory[T client.Object] func(params Params) T +type K8sManifestFactory ManifestFactory[client.Object] + +func FactoryWithoutError[T client.Object](f SimpleManifestFactory[T]) K8sManifestFactory { + return func(params Params) (client.Object, error) { + return f(params), nil + } +} + +func Factory[T client.Object](f ManifestFactory[T]) K8sManifestFactory { + return func(params Params) (client.Object, error) { + return f(params) + } +} + +// ObjectIsNotNil ensures that we only create an object IFF it isn't nil, +// and it's concrete type isn't nil either. This works around the Go type system +// by using reflection to verify its concrete type isn't nil. +func ObjectIsNotNil(obj client.Object) bool { + return obj != nil && !reflect.ValueOf(obj).IsNil() +} diff --git a/pkg/collector/adapters/config_from.go b/internal/manifests/collector/adapters/config_from.go similarity index 100% rename from pkg/collector/adapters/config_from.go rename to internal/manifests/collector/adapters/config_from.go diff --git a/pkg/collector/adapters/config_from_test.go b/internal/manifests/collector/adapters/config_from_test.go similarity index 91% rename from pkg/collector/adapters/config_from_test.go rename to internal/manifests/collector/adapters/config_from_test.go index 92fb0569b3..8099387083 100644 --- a/pkg/collector/adapters/config_from_test.go +++ b/internal/manifests/collector/adapters/config_from_test.go @@ -19,7 +19,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" ) func TestInvalidYAML(t *testing.T) { diff --git a/internal/manifests/collector/adapters/config_to_ports.go b/internal/manifests/collector/adapters/config_to_ports.go new file mode 100644 index 0000000000..a4cffde4d4 --- /dev/null +++ b/internal/manifests/collector/adapters/config_to_ports.go @@ -0,0 +1,178 @@ +// 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 adapters + +import ( + "fmt" + "net" + "sort" + "strconv" + "strings" + + "github.com/go-logr/logr" + "github.com/mitchellh/mapstructure" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + exporterParser "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/exporter" + receiverParser "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/receiver" +) + +type ComponentType int + +const ( + ComponentTypeReceiver ComponentType = iota + ComponentTypeExporter +) + +func (c ComponentType) String() string { + return [...]string{"receiver", "exporter"}[c] +} + +// ConfigToComponentPorts converts the incoming configuration object into a set of service ports required by the exporters. +func ConfigToComponentPorts(logger logr.Logger, cType ComponentType, config map[interface{}]interface{}) ([]corev1.ServicePort, error) { + // now, we gather which ports we might need to open + // for that, we get all the exporters and check their `endpoint` properties, + // extracting the port from it. The port name has to be a "DNS_LABEL", so, we try to make it follow the pattern: + // ${instance.Name}-${exporter.name}-${exporter.qualifier} + // the exporter-name is typically the node name from the exporters map + // the exporter-qualifier is what comes after the slash in the exporter name, but typically nil + // examples: + // ```yaml + // components: + // componentexample: + // endpoint: 0.0.0.0:12345 + // componentexample/settings: + // endpoint: 0.0.0.0:12346 + // in this case, we have 2 ports, named: "componentexample" and "componentexample-settings" + componentsProperty, ok := config[fmt.Sprintf("%ss", cType.String())] + if !ok { + return nil, fmt.Errorf("no %ss available as part of the configuration", cType) + } + + components, ok := componentsProperty.(map[interface{}]interface{}) + if !ok { + return nil, fmt.Errorf("%ss doesn't contain valid components", cType.String()) + } + + compEnabled := getEnabledComponents(config, cType) + + if compEnabled == nil { + return nil, fmt.Errorf("no enabled %ss available as part of the configuration", cType) + } + + ports := []corev1.ServicePort{} + for key, val := range components { + // This check will pass only the enabled components, + // then only the related ports will be opened. + if !compEnabled[key] { + continue + } + exporter, ok := val.(map[interface{}]interface{}) + if !ok { + logger.V(2).Info("component doesn't seem to be a map of properties", cType.String(), key) + exporter = map[interface{}]interface{}{} + } + + cmptName := key.(string) + var cmptParser parser.ComponentPortParser + var err error + switch cType { + case ComponentTypeExporter: + cmptParser, err = exporterParser.For(logger, cmptName, exporter) + case ComponentTypeReceiver: + cmptParser, err = receiverParser.For(logger, cmptName, exporter) + } + + if err != nil { + logger.V(2).Info("no parser found for '%s'", cmptName) + continue + } + + exprtPorts, err := cmptParser.Ports() + if err != nil { + logger.Error(err, "parser for '%s' has returned an error: %w", cmptName, err) + continue + } + + if len(exprtPorts) > 0 { + ports = append(ports, exprtPorts...) + } + } + + sort.Slice(ports, func(i, j int) bool { + return ports[i].Name < ports[j].Name + }) + + return ports, nil +} + +func ConfigToPorts(logger logr.Logger, config map[interface{}]interface{}) ([]corev1.ServicePort, error) { + ports, err := ConfigToComponentPorts(logger, ComponentTypeReceiver, config) + if err != nil { + logger.Error(err, "there was a problem while getting the ports from the receivers") + return nil, err + } + + exporterPorts, err := ConfigToComponentPorts(logger, ComponentTypeExporter, config) + if err != nil { + logger.Error(err, "there was a problem while getting the ports from the exporters") + return nil, err + } + + ports = append(ports, exporterPorts...) + + sort.Slice(ports, func(i, j int) bool { + return ports[i].Name < ports[j].Name + }) + + return ports, nil +} + +// ConfigToMetricsPort gets the port number for the metrics endpoint from the collector config if it has been set. +func ConfigToMetricsPort(logger logr.Logger, config map[interface{}]interface{}) (int32, error) { + // we don't need to unmarshal the whole config, just follow the keys down to + // the metrics address. + type metricsCfg struct { + Address string + } + type telemetryCfg struct { + Metrics metricsCfg + } + type serviceCfg struct { + Telemetry telemetryCfg + } + type cfg struct { + Service serviceCfg + } + var cOut cfg + err := mapstructure.Decode(config, &cOut) + if err != nil { + return 0, err + } + + _, port, netErr := net.SplitHostPort(cOut.Service.Telemetry.Metrics.Address) + if netErr != nil && strings.Contains(netErr.Error(), "missing port in address") { + return 8888, nil + } else if netErr != nil { + return 0, netErr + } + i64, err := strconv.ParseInt(port, 10, 32) + if err != nil { + return 0, err + } + + return int32(i64), nil +} diff --git a/pkg/collector/adapters/config_to_ports_test.go b/internal/manifests/collector/adapters/config_to_ports_test.go similarity index 63% rename from pkg/collector/adapters/config_to_ports_test.go rename to internal/manifests/collector/adapters/config_to_ports_test.go index fbc6989552..7c872606f6 100644 --- a/pkg/collector/adapters/config_to_ports_test.go +++ b/internal/manifests/collector/adapters/config_to_ports_test.go @@ -25,8 +25,9 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" logf "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/receiver" ) var logger = logf.Log.WithName("unit-tests") @@ -67,13 +68,13 @@ service: pipelines: metrics: receivers: [examplereceiver, examplereceiver/settings] - exporters: [logging] + exporters: [debug] metrics/1: receivers: [jaeger, jaeger/custom] - exporters: [logging] + exporters: [debug] metrics/2: receivers: [otlp, otlp/2, zipkin] - exporters: [logging] + exporters: [debug] ` func TestExtractPortsFromConfig(t *testing.T) { @@ -83,9 +84,9 @@ func TestExtractPortsFromConfig(t *testing.T) { require.NotEmpty(t, config) // test - ports, err := adapters.ConfigToReceiverPorts(logger, config) + ports, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeReceiver, config) assert.NoError(t, err) - assert.Len(t, ports, 11) + assert.Len(t, ports, 10) // verify httpAppProtocol := "http" @@ -96,15 +97,14 @@ func TestExtractPortsFromConfig(t *testing.T) { expectedPorts := []corev1.ServicePort{ {Name: "examplereceiver", Port: 12345}, - {Name: "examplereceiver-settings", Port: 12346}, - {Name: "jaeger-custom-thrift-http", AppProtocol: &httpAppProtocol, Protocol: "TCP", Port: 15268, TargetPort: targetPortZero}, + {Name: "port-12346", Port: 12346}, + {Name: "port-15268", AppProtocol: &httpAppProtocol, Protocol: "TCP", Port: 15268, TargetPort: targetPortZero}, {Name: "jaeger-grpc", AppProtocol: &grpcAppProtocol, Protocol: "TCP", Port: 14250}, - {Name: "jaeger-thrift-binary", Protocol: "UDP", Port: 6833}, - {Name: "jaeger-thrift-compact", Protocol: "UDP", Port: 6831}, + {Name: "port-6833", Protocol: "UDP", Port: 6833}, + {Name: "port-6831", Protocol: "UDP", Port: 6831}, {Name: "otlp-2-grpc", AppProtocol: &grpcAppProtocol, Protocol: "TCP", Port: 55555}, {Name: "otlp-grpc", AppProtocol: &grpcAppProtocol, Port: 4317, TargetPort: targetPort4317}, {Name: "otlp-http", AppProtocol: &httpAppProtocol, Port: 4318, TargetPort: targetPort4318}, - {Name: "otlp-http-legacy", AppProtocol: &httpAppProtocol, Port: 55681, TargetPort: targetPort4318}, {Name: "zipkin", AppProtocol: &httpAppProtocol, Protocol: "TCP", Port: 9411}, } assert.ElementsMatch(t, expectedPorts, ports) @@ -117,12 +117,12 @@ func TestNoPortsParsed(t *testing.T) { configStr string }{ { - expected: adapters.ErrNoReceivers, + expected: errors.New("no receivers available as part of the configuration"), desc: "empty", configStr: "", }, { - expected: adapters.ErrReceiversNotAMap, + expected: errors.New("receivers doesn't contain valid components"), desc: "not a map", configStr: "receivers: some-string", }, @@ -133,7 +133,7 @@ func TestNoPortsParsed(t *testing.T) { require.NoError(t, err) // test - ports, err := adapters.ConfigToReceiverPorts(logger, config) + ports, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeReceiver, config) // verify assert.Nil(t, ports) @@ -162,7 +162,7 @@ func TestInvalidReceivers(t *testing.T) { require.NoError(t, err) // test - ports, err := adapters.ConfigToReceiverPorts(logger, config) + ports, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeReceiver, config) // verify assert.NoError(t, err) @@ -180,7 +180,7 @@ func TestParserFailed(t *testing.T) { return nil, errors.New("mocked error") }, } - parser.Register("mock", func(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ReceiverParser { + receiver.Register("mock", func(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return mockParser }) @@ -198,7 +198,7 @@ func TestParserFailed(t *testing.T) { } // test - ports, err := adapters.ConfigToReceiverPorts(logger, config) + ports, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeReceiver, config) // verify assert.Len(t, ports, 0) @@ -206,6 +206,75 @@ func TestParserFailed(t *testing.T) { assert.True(t, mockParserCalled) } +func TestConfigToMetricsPort(t *testing.T) { + t.Run("custom port specified", func(t *testing.T) { + config := map[interface{}]interface{}{ + "service": map[interface{}]interface{}{ + "telemetry": map[interface{}]interface{}{ + "metrics": map[interface{}]interface{}{ + "address": "0.0.0.0:9090", + }, + }, + }, + } + + port, err := adapters.ConfigToMetricsPort(logger, config) + assert.NoError(t, err) + assert.Equal(t, int32(9090), port) + }) + + for _, tt := range []struct { + desc string + config map[interface{}]interface{} + }{ + { + "bad address", + map[interface{}]interface{}{ + "service": map[interface{}]interface{}{ + "telemetry": map[interface{}]interface{}{ + "metrics": map[interface{}]interface{}{ + "address": "0.0.0.0", + }, + }, + }, + }, + }, + { + "missing address", + map[interface{}]interface{}{ + "service": map[interface{}]interface{}{ + "telemetry": map[interface{}]interface{}{ + "metrics": map[interface{}]interface{}{ + "level": "detailed", + }, + }, + }, + }, + }, + { + "missing metrics", + map[interface{}]interface{}{ + "service": map[interface{}]interface{}{ + "telemetry": map[interface{}]interface{}{}, + }, + }, + }, + { + "missing telemetry", + map[interface{}]interface{}{ + "service": map[interface{}]interface{}{}, + }, + }, + } { + t.Run(tt.desc, func(t *testing.T) { + // these are acceptable failures, we return to the collector's default metric port + port, err := adapters.ConfigToMetricsPort(logger, tt.config) + assert.NoError(t, err) + assert.Equal(t, int32(8888), port) + }) + } +} + type mockParser struct { portsFunc func() ([]corev1.ServicePort, error) } diff --git a/pkg/collector/adapters/config_to_probe.go b/internal/manifests/collector/adapters/config_to_probe.go similarity index 88% rename from pkg/collector/adapters/config_to_probe.go rename to internal/manifests/collector/adapters/config_to_probe.go index 10dfa45cd8..897b7db068 100644 --- a/pkg/collector/adapters/config_to_probe.go +++ b/internal/manifests/collector/adapters/config_to_probe.go @@ -31,10 +31,10 @@ var ( errNoExtensionHealthCheck = errors.New("extensions property in the configuration does not contain the expected health_check extension") - errNoServiceExtensions = errors.New("service property in the configuration doesn't contain extensions") + ErrNoServiceExtensions = errors.New("service property in the configuration doesn't contain extensions") errServiceExtensionsNotSlice = errors.New("service extensions property in the configuration does not contain valid extensions") - errNoServiceExtensionHealthCheck = errors.New("no healthcheck extension available in service extension configuration") + ErrNoServiceExtensionHealthCheck = errors.New("no healthcheck extension available in service extension configuration") ) type probeConfiguration struct { @@ -49,22 +49,22 @@ const ( // ConfigToContainerProbe converts the incoming configuration object into a container probe or returns an error. func ConfigToContainerProbe(config map[interface{}]interface{}) (*corev1.Probe, error) { - serviceProperty, ok := config["service"] - if !ok { + serviceProperty, withService := config["service"] + if !withService { return nil, errNoService } - service, ok := serviceProperty.(map[interface{}]interface{}) - if !ok { + service, withSvcProperty := serviceProperty.(map[interface{}]interface{}) + if !withSvcProperty { return nil, errServiceNotAMap } - serviceExtensionsProperty, ok := service["extensions"] - if !ok { - return nil, errNoServiceExtensions + serviceExtensionsProperty, withExtension := service["extensions"] + if !withExtension { + return nil, ErrNoServiceExtensions } - serviceExtensions, ok := serviceExtensionsProperty.([]interface{}) - if !ok { + serviceExtensions, withExtProperty := serviceExtensionsProperty.([]interface{}) + if !withExtProperty { return nil, errServiceExtensionsNotSlice } healthCheckServiceExtensions := make([]string, 0) @@ -76,7 +76,7 @@ func ConfigToContainerProbe(config map[interface{}]interface{}) (*corev1.Probe, } if len(healthCheckServiceExtensions) == 0 { - return nil, errNoServiceExtensionHealthCheck + return nil, ErrNoServiceExtensionHealthCheck } extensionsProperty, ok := config["extensions"] diff --git a/pkg/collector/adapters/config_to_probe_test.go b/internal/manifests/collector/adapters/config_to_probe_test.go similarity index 98% rename from pkg/collector/adapters/config_to_probe_test.go rename to internal/manifests/collector/adapters/config_to_probe_test.go index 6a2ae33981..89e1f97349 100644 --- a/pkg/collector/adapters/config_to_probe_test.go +++ b/internal/manifests/collector/adapters/config_to_probe_test.go @@ -146,7 +146,7 @@ service: desc: "NoHealthCheckInServiceExtensions", config: `service: extensions: [pprof]`, - expectedErr: errNoServiceExtensionHealthCheck, + expectedErr: ErrNoServiceExtensionHealthCheck, }, { desc: "BadlyFormattedServiceExtensions", config: `service: @@ -159,7 +159,7 @@ service: pipelines: traces: receivers: [otlp]`, - expectedErr: errNoServiceExtensions, + expectedErr: ErrNoServiceExtensions, }, { desc: "BadlyFormattedService", config: `extensions: diff --git a/pkg/collector/adapters/config_validate.go b/internal/manifests/collector/adapters/config_validate.go similarity index 56% rename from pkg/collector/adapters/config_validate.go rename to internal/manifests/collector/adapters/config_validate.go index f34f36f2d3..8283702cf5 100644 --- a/pkg/collector/adapters/config_validate.go +++ b/internal/manifests/collector/adapters/config_validate.go @@ -14,49 +14,48 @@ package adapters -import ( - "github.com/go-logr/logr" -) +import "fmt" // Following Otel Doc: Configuring a receiver does not enable it. The receivers are enabled via pipelines within the service section. -// GetEnabledReceivers returns all enabled receivers as a true flag set. If it can't find any receiver, it will return a nil interface. -func GetEnabledReceivers(_ logr.Logger, config map[interface{}]interface{}) map[interface{}]bool { - cfgReceivers, ok := config["receivers"] +// getEnabledComponents returns all enabled components as a true flag set. If it can't find any receiver, it will return a nil interface. +func getEnabledComponents(config map[interface{}]interface{}, componentType ComponentType) map[interface{}]bool { + componentTypePlural := fmt.Sprintf("%ss", componentType) + cfgComponents, ok := config[componentTypePlural] if !ok { return nil } - receivers, ok := cfgReceivers.(map[interface{}]interface{}) + components, ok := cfgComponents.(map[interface{}]interface{}) if !ok { return nil } - availableReceivers := map[interface{}]bool{} + availableComponents := map[interface{}]bool{} - for recvID := range receivers { + for compID := range components { //Safe Cast - receiverID, ok := recvID.(string) - if !ok { + componentID, withComponent := compID.(string) + if !withComponent { return nil } - //Getting all receivers present in the receivers section and setting them to false. - availableReceivers[receiverID] = false + //Getting all components present in the components (exporters,receivers...) section and setting them to false. + availableComponents[componentID] = false } - cfgService, ok := config["service"].(map[interface{}]interface{}) - if !ok { + cfgService, withService := config["service"].(map[interface{}]interface{}) + if !withService { return nil } - pipeline, ok := cfgService["pipelines"].(map[interface{}]interface{}) - if !ok { + pipeline, withPipeline := cfgService["pipelines"].(map[interface{}]interface{}) + if !withPipeline { return nil } availablePipelines := map[string]bool{} for pipID := range pipeline { //Safe Cast - pipelineID, ok := pipID.(string) - if !ok { + pipelineID, existsPipeline := pipID.(string) + if !existsPipeline { return nil } //Getting all the available pipelines. @@ -66,8 +65,8 @@ func GetEnabledReceivers(_ logr.Logger, config map[interface{}]interface{}) map[ if len(pipeline) > 0 { for pipelineID, pipelineCfg := range pipeline { //Safe Cast - pipelineV, ok := pipelineID.(string) - if !ok { + pipelineV, withPipelineCfg := pipelineID.(string) + if !withPipelineCfg { continue } //Condition will get information if there are multiple configured pipelines. @@ -77,29 +76,29 @@ func GetEnabledReceivers(_ logr.Logger, config map[interface{}]interface{}) map[ return nil } for pipSpecID, pipSpecCfg := range pipelineDesc { - if pipSpecID.(string) == "receivers" { + if pipSpecID.(string) == componentTypePlural { receiversList, ok := pipSpecCfg.([]interface{}) if !ok { continue } // If receiversList is empty means that we haven't any enabled Receiver. if len(receiversList) == 0 { - availableReceivers = nil + availableComponents = nil } else { // All enabled receivers will be set as true - for _, recKey := range receiversList { + for _, comKey := range receiversList { //Safe Cast - receiverKey, ok := recKey.(string) + receiverKey, ok := comKey.(string) if !ok { return nil } - availableReceivers[receiverKey] = true + availableComponents[receiverKey] = true } } //Removing all non-enabled receivers - for recID, recKey := range availableReceivers { - if !(recKey) { - delete(availableReceivers, recID) + for comID, comKey := range availableComponents { + if !(comKey) { + delete(availableComponents, comID) } } } @@ -107,5 +106,5 @@ func GetEnabledReceivers(_ logr.Logger, config map[interface{}]interface{}) map[ } } } - return availableReceivers + return availableComponents } diff --git a/pkg/collector/adapters/config_validate_test.go b/internal/manifests/collector/adapters/config_validate_test.go similarity index 87% rename from pkg/collector/adapters/config_validate_test.go rename to internal/manifests/collector/adapters/config_validate_test.go index 27cd8a7d76..7003235fed 100644 --- a/pkg/collector/adapters/config_validate_test.go +++ b/internal/manifests/collector/adapters/config_validate_test.go @@ -17,13 +17,9 @@ package adapters import ( "testing" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/stretchr/testify/require" ) -var logger = logf.Log.WithName("unit-tests") - func TestConfigValidate(t *testing.T) { // prepare @@ -44,16 +40,16 @@ receivers: processors: exporters: - logging: + debug: service: pipelines: metrics: receivers: [httpd/mtls, jaeger] - exporters: [logging] + exporters: [debug] metrics/1: receivers: [httpd/mtls, jaeger] - exporters: [logging] + exporters: [debug] ` // // prepare config, err := ConfigFromString(configStr) @@ -61,7 +57,7 @@ service: require.NotEmpty(t, config) // test - check := GetEnabledReceivers(logger, config) + check := getEnabledComponents(config, ComponentTypeReceiver) require.NotEmpty(t, check) } @@ -85,7 +81,7 @@ receivers: processors: exporters: - logging: + debug: service: pipelines: @@ -102,6 +98,6 @@ service: require.NotEmpty(t, config) // test - check := GetEnabledReceivers(logger, config) + check := getEnabledComponents(config, ComponentTypeReceiver) require.Empty(t, check) } diff --git a/pkg/collector/annotations.go b/internal/manifests/collector/annotations.go similarity index 91% rename from pkg/collector/annotations.go rename to internal/manifests/collector/annotations.go index 578f235dcb..95d25f71d6 100644 --- a/pkg/collector/annotations.go +++ b/internal/manifests/collector/annotations.go @@ -53,6 +53,13 @@ func PodAnnotations(instance v1alpha1.OpenTelemetryCollector) map[string]string podAnnotations[k] = v } + // propagating annotations from metadata.annotations + for kMeta, vMeta := range Annotations(instance) { + if _, found := podAnnotations[kMeta]; !found { + podAnnotations[kMeta] = vMeta + } + } + // make sure sha256 for configMap is always calculated podAnnotations["opentelemetry-operator-config/sha256"] = getConfigMapSHA(instance.Spec.Config) diff --git a/pkg/collector/annotations_test.go b/internal/manifests/collector/annotations_test.go similarity index 90% rename from pkg/collector/annotations_test.go rename to internal/manifests/collector/annotations_test.go index dfd2246cda..ade8f7fbab 100644 --- a/pkg/collector/annotations_test.go +++ b/internal/manifests/collector/annotations_test.go @@ -44,6 +44,10 @@ func TestDefaultAnnotations(t *testing.T) { assert.Equal(t, "8888", annotations["prometheus.io/port"]) assert.Equal(t, "/metrics", annotations["prometheus.io/path"]) assert.Equal(t, "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", annotations["opentelemetry-operator-config/sha256"]) + //verify propagation from metadata.annotations to spec.template.spec.metadata.annotations + assert.Equal(t, "true", podAnnotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", podAnnotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", podAnnotations["prometheus.io/path"]) assert.Equal(t, "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", podAnnotations["opentelemetry-operator-config/sha256"]) } @@ -94,5 +98,6 @@ func TestAnnotationsPropagateDown(t *testing.T) { // verify assert.Len(t, annotations, 5) assert.Equal(t, "mycomponent", annotations["myapp"]) + assert.Equal(t, "mycomponent", podAnnotations["myapp"]) assert.Equal(t, "pod_annotation_value", podAnnotations["pod_annotation"]) } diff --git a/internal/manifests/collector/collector.go b/internal/manifests/collector/collector.go new file mode 100644 index 0000000000..31a0c3a8eb --- /dev/null +++ b/internal/manifests/collector/collector.go @@ -0,0 +1,78 @@ +// 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 collector + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" +) + +const ( + ComponentOpenTelemetryCollector = "opentelemetry-collector" +) + +// Build creates the manifest for the collector resource. +func Build(params manifests.Params) ([]client.Object, error) { + var resourceManifests []client.Object + var manifestFactories []manifests.K8sManifestFactory + switch params.OtelCol.Spec.Mode { + case v1alpha1.ModeDeployment: + manifestFactories = append(manifestFactories, manifests.FactoryWithoutError(Deployment)) + manifestFactories = append(manifestFactories, manifests.FactoryWithoutError(PodDisruptionBudget)) + case v1alpha1.ModeStatefulSet: + manifestFactories = append(manifestFactories, manifests.FactoryWithoutError(StatefulSet)) + manifestFactories = append(manifestFactories, manifests.FactoryWithoutError(PodDisruptionBudget)) + case v1alpha1.ModeDaemonSet: + manifestFactories = append(manifestFactories, manifests.FactoryWithoutError(DaemonSet)) + case v1alpha1.ModeSidecar: + params.Log.V(5).Info("not building sidecar...") + } + manifestFactories = append(manifestFactories, []manifests.K8sManifestFactory{ + manifests.Factory(ConfigMap), + manifests.FactoryWithoutError(HorizontalPodAutoscaler), + manifests.FactoryWithoutError(ServiceAccount), + manifests.Factory(Service), + manifests.Factory(HeadlessService), + manifests.Factory(MonitoringService), + manifests.Factory(Ingress), + }...) + if params.OtelCol.Spec.Observability.Metrics.EnableMetrics && featuregate.PrometheusOperatorIsAvailable.IsEnabled() { + if params.OtelCol.Spec.Mode == v1alpha1.ModeSidecar { + manifestFactories = append(manifestFactories, manifests.Factory(PodMonitor)) + } else { + manifestFactories = append(manifestFactories, manifests.Factory(ServiceMonitor)) + } + } + for _, factory := range manifestFactories { + res, err := factory(params) + if err != nil { + return nil, err + } else if manifests.ObjectIsNotNil(res) { + resourceManifests = append(resourceManifests, res) + } + } + routes, err := Routes(params) + if err != nil { + return nil, err + } + // NOTE: we cannot just unpack the slice, the type checker doesn't coerce the type correctly. + for _, route := range routes { + resourceManifests = append(resourceManifests, route) + } + return resourceManifests, nil +} diff --git a/internal/manifests/collector/config_replace.go b/internal/manifests/collector/config_replace.go new file mode 100644 index 0000000000..c6cc58ad40 --- /dev/null +++ b/internal/manifests/collector/config_replace.go @@ -0,0 +1,101 @@ +// 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 collector + +import ( + "time" + + promconfig "github.com/prometheus/prometheus/config" + _ "github.com/prometheus/prometheus/discovery/install" // Package install has the side-effect of registering all builtin. + "gopkg.in/yaml.v2" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" + ta "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" +) + +type targetAllocator struct { + Endpoint string `yaml:"endpoint"` + Interval time.Duration `yaml:"interval"` + CollectorID string `yaml:"collector_id"` + // HTTPSDConfig is a preference that can be set for the collector's target allocator, but the operator doesn't + // care about what the value is set to. We just need this for validation when unmarshalling the configmap. + HTTPSDConfig interface{} `yaml:"http_sd_config,omitempty"` +} + +type Config struct { + PromConfig *promconfig.Config `yaml:"config"` + TargetAllocConfig *targetAllocator `yaml:"target_allocator,omitempty"` +} + +func ReplaceConfig(instance v1alpha1.OpenTelemetryCollector) (string, error) { + // Check if TargetAllocator is enabled, if not, return the original config + if !instance.Spec.TargetAllocator.Enabled { + return instance.Spec.Config, nil + } + + config, err := adapters.ConfigFromString(instance.Spec.Config) + if err != nil { + return "", err + } + + promCfgMap, getCfgPromErr := ta.ConfigToPromConfig(instance.Spec.Config) + if getCfgPromErr != nil { + return "", getCfgPromErr + } + + validateCfgPromErr := ta.ValidatePromConfig(promCfgMap, instance.Spec.TargetAllocator.Enabled, featuregate.EnableTargetAllocatorRewrite.IsEnabled()) + if validateCfgPromErr != nil { + return "", validateCfgPromErr + } + + if featuregate.EnableTargetAllocatorRewrite.IsEnabled() { + // To avoid issues caused by Prometheus validation logic, which fails regex validation when it encounters + // $$ in the prom config, we update the YAML file directly without marshaling and unmarshalling. + updPromCfgMap, getCfgPromErr := ta.AddTAConfigToPromConfig(promCfgMap, naming.TAService(instance.Name)) + if getCfgPromErr != nil { + return "", getCfgPromErr + } + + // type coercion checks are handled in the AddTAConfigToPromConfig method above + config["receivers"].(map[interface{}]interface{})["prometheus"] = updPromCfgMap + + out, updCfgMarshalErr := yaml.Marshal(config) + if updCfgMarshalErr != nil { + return "", updCfgMarshalErr + } + + return string(out), nil + } + + // To avoid issues caused by Prometheus validation logic, which fails regex validation when it encounters + // $$ in the prom config, we update the YAML file directly without marshaling and unmarshalling. + updPromCfgMap, err := ta.AddHTTPSDConfigToPromConfig(promCfgMap, naming.TAService(instance.Name)) + if err != nil { + return "", err + } + + // type coercion checks are handled in the ConfigToPromConfig method above + config["receivers"].(map[interface{}]interface{})["prometheus"] = updPromCfgMap + + out, err := yaml.Marshal(config) + if err != nil { + return "", err + } + + return string(out), nil +} diff --git a/internal/manifests/collector/config_replace_test.go b/internal/manifests/collector/config_replace_test.go new file mode 100644 index 0000000000..c7ab767505 --- /dev/null +++ b/internal/manifests/collector/config_replace_test.go @@ -0,0 +1,203 @@ +// 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 collector + +import ( + "os" + "testing" + + "github.com/prometheus/prometheus/discovery/http" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + colfeaturegate "go.opentelemetry.io/collector/featuregate" + "gopkg.in/yaml.v2" + + ta "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" +) + +func TestPrometheusParser(t *testing.T) { + param, err := newParams("test/test-img", "testdata/http_sd_config_test.yaml") + assert.NoError(t, err) + + t.Run("should update config with http_sd_config", func(t *testing.T) { + err := colfeaturegate.GlobalRegistry().Set(featuregate.EnableTargetAllocatorRewrite.ID(), false) + require.NoError(t, err) + t.Cleanup(func() { + _ = colfeaturegate.GlobalRegistry().Set(featuregate.EnableTargetAllocatorRewrite.ID(), true) + }) + actualConfig, err := ReplaceConfig(param.OtelCol) + assert.NoError(t, err) + + // prepare + var cfg Config + promCfgMap, err := ta.ConfigToPromConfig(actualConfig) + assert.NoError(t, err) + + promCfg, err := yaml.Marshal(promCfgMap) + assert.NoError(t, err) + + err = yaml.UnmarshalStrict(promCfg, &cfg) + assert.NoError(t, err) + + // test + expectedMap := map[string]bool{ + "prometheus": false, + "service-x": false, + } + for _, scrapeConfig := range cfg.PromConfig.ScrapeConfigs { + assert.Len(t, scrapeConfig.ServiceDiscoveryConfigs, 1) + assert.Equal(t, scrapeConfig.ServiceDiscoveryConfigs[0].Name(), "http") + assert.Equal(t, scrapeConfig.ServiceDiscoveryConfigs[0].(*http.SDConfig).URL, "http://test-targetallocator:80/jobs/"+scrapeConfig.JobName+"/targets?collector_id=$POD_NAME") + expectedMap[scrapeConfig.JobName] = true + } + for k := range expectedMap { + assert.True(t, expectedMap[k], k) + } + assert.True(t, cfg.TargetAllocConfig == nil) + }) + + t.Run("should update config with targetAllocator block if block not present", func(t *testing.T) { + // Set up the test scenario + param.OtelCol.Spec.TargetAllocator.Enabled = true + actualConfig, err := ReplaceConfig(param.OtelCol) + assert.NoError(t, err) + + // Verify the expected changes in the config + promCfgMap, err := ta.ConfigToPromConfig(actualConfig) + assert.NoError(t, err) + + prometheusConfig := promCfgMap["config"].(map[interface{}]interface{}) + + assert.NotContains(t, prometheusConfig, "scrape_configs") + + expectedTAConfig := map[interface{}]interface{}{ + "endpoint": "http://test-targetallocator:80", + "interval": "30s", + "collector_id": "${POD_NAME}", + } + assert.Equal(t, expectedTAConfig, promCfgMap["target_allocator"]) + assert.NoError(t, err) + }) + + t.Run("should update config with targetAllocator block if block already present", func(t *testing.T) { + // Set up the test scenario + paramTa, err := newParams("test/test-img", "testdata/http_sd_config_ta_test.yaml") + require.NoError(t, err) + paramTa.OtelCol.Spec.TargetAllocator.Enabled = true + + actualConfig, err := ReplaceConfig(paramTa.OtelCol) + assert.NoError(t, err) + + // Verify the expected changes in the config + promCfgMap, err := ta.ConfigToPromConfig(actualConfig) + assert.NoError(t, err) + + prometheusConfig := promCfgMap["config"].(map[interface{}]interface{}) + + assert.NotContains(t, prometheusConfig, "scrape_configs") + + expectedTAConfig := map[interface{}]interface{}{ + "endpoint": "http://test-targetallocator:80", + "interval": "30s", + "collector_id": "${POD_NAME}", + } + assert.Equal(t, expectedTAConfig, promCfgMap["target_allocator"]) + assert.NoError(t, err) + }) + + t.Run("should not update config with http_sd_config", func(t *testing.T) { + param.OtelCol.Spec.TargetAllocator.Enabled = false + actualConfig, err := ReplaceConfig(param.OtelCol) + assert.NoError(t, err) + + // prepare + var cfg Config + promCfgMap, err := ta.ConfigToPromConfig(actualConfig) + assert.NoError(t, err) + + promCfg, err := yaml.Marshal(promCfgMap) + assert.NoError(t, err) + + err = yaml.UnmarshalStrict(promCfg, &cfg) + assert.NoError(t, err) + + // test + expectedMap := map[string]bool{ + "prometheus": false, + "service-x": false, + } + for _, scrapeConfig := range cfg.PromConfig.ScrapeConfigs { + assert.Len(t, scrapeConfig.ServiceDiscoveryConfigs, 2) + assert.Equal(t, scrapeConfig.ServiceDiscoveryConfigs[0].Name(), "file") + assert.Equal(t, scrapeConfig.ServiceDiscoveryConfigs[1].Name(), "static") + expectedMap[scrapeConfig.JobName] = true + } + for k := range expectedMap { + assert.True(t, expectedMap[k], k) + } + assert.True(t, cfg.TargetAllocConfig == nil) + }) + +} + +func TestReplaceConfig(t *testing.T) { + param, err := newParams("test/test-img", "testdata/relabel_config_original.yaml") + assert.NoError(t, err) + + t.Run("should not modify config when TargetAllocator is disabled", func(t *testing.T) { + param.OtelCol.Spec.TargetAllocator.Enabled = false + expectedConfigBytes, err := os.ReadFile("testdata/relabel_config_original.yaml") + assert.NoError(t, err) + expectedConfig := string(expectedConfigBytes) + + actualConfig, err := ReplaceConfig(param.OtelCol) + assert.NoError(t, err) + + assert.Equal(t, expectedConfig, actualConfig) + }) + + t.Run("should rewrite scrape configs with SD config when TargetAllocator is enabled and feature flag is not set", func(t *testing.T) { + err := colfeaturegate.GlobalRegistry().Set(featuregate.EnableTargetAllocatorRewrite.ID(), false) + require.NoError(t, err) + t.Cleanup(func() { + _ = colfeaturegate.GlobalRegistry().Set(featuregate.EnableTargetAllocatorRewrite.ID(), true) + }) + + param.OtelCol.Spec.TargetAllocator.Enabled = true + + expectedConfigBytes, err := os.ReadFile("testdata/relabel_config_expected_with_sd_config.yaml") + assert.NoError(t, err) + expectedConfig := string(expectedConfigBytes) + + actualConfig, err := ReplaceConfig(param.OtelCol) + assert.NoError(t, err) + + assert.Equal(t, expectedConfig, actualConfig) + }) + + t.Run("should remove scrape configs if TargetAllocator is enabled and feature flag is set", func(t *testing.T) { + param.OtelCol.Spec.TargetAllocator.Enabled = true + + expectedConfigBytes, err := os.ReadFile("testdata/config_expected_targetallocator.yaml") + assert.NoError(t, err) + expectedConfig := string(expectedConfigBytes) + + actualConfig, err := ReplaceConfig(param.OtelCol) + assert.NoError(t, err) + + assert.Equal(t, expectedConfig, actualConfig) + }) +} diff --git a/internal/manifests/collector/configmap.go b/internal/manifests/collector/configmap.go new file mode 100644 index 0000000000..58ee4c1312 --- /dev/null +++ b/internal/manifests/collector/configmap.go @@ -0,0 +1,47 @@ +// 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 collector + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func ConfigMap(params manifests.Params) (*corev1.ConfigMap, error) { + name := naming.ConfigMap(params.OtelCol.Name) + labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, []string{}) + + replacedConf, err := ReplaceConfig(params.OtelCol) + if err != nil { + params.Log.V(2).Info("failed to update prometheus config to use sharded targets: ", "err", err) + return nil, err + } + + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OtelCol.Namespace, + Labels: labels, + Annotations: params.OtelCol.Annotations, + }, + Data: map[string]string{ + "collector.yaml": replacedConf, + }, + }, nil +} diff --git a/internal/manifests/collector/configmap_test.go b/internal/manifests/collector/configmap_test.go new file mode 100644 index 0000000000..9d60763eef --- /dev/null +++ b/internal/manifests/collector/configmap_test.go @@ -0,0 +1,219 @@ +// 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 collector + +import ( + "testing" + + colfeaturegate "go.opentelemetry.io/collector/featuregate" + + "github.com/stretchr/testify/assert" + + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" +) + +func TestDesiredConfigMap(t *testing.T) { + expectedLables := map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/instance": "default.test", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "0.47.0", + } + + t.Run("should return expected collector config map", func(t *testing.T) { + expectedLables["app.kubernetes.io/component"] = "opentelemetry-collector" + expectedLables["app.kubernetes.io/name"] = "test-collector" + expectedLables["app.kubernetes.io/version"] = "0.47.0" + + expectedData := map[string]string{ + "collector.yaml": `processors: +receivers: + jaeger: + protocols: + grpc: + prometheus: + config: + scrape_configs: + - job_name: otel-collector + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888', '0.0.0.0:9999' ] + +exporters: + debug: + +service: + pipelines: + metrics: + receivers: [prometheus, jaeger] + processors: [] + exporters: [debug]`, + } + + param := deploymentParams() + actual, err := ConfigMap(param) + + assert.NoError(t, err) + assert.Equal(t, "test-collector", actual.Name) + assert.Equal(t, expectedLables, actual.Labels) + assert.Equal(t, expectedData, actual.Data) + + }) + + t.Run("should return expected collector config map with http_sd_config if rewrite flag disabled", func(t *testing.T) { + err := colfeaturegate.GlobalRegistry().Set(featuregate.EnableTargetAllocatorRewrite.ID(), false) + assert.NoError(t, err) + t.Cleanup(func() { + _ = colfeaturegate.GlobalRegistry().Set(featuregate.EnableTargetAllocatorRewrite.ID(), true) + }) + expectedLables["app.kubernetes.io/component"] = "opentelemetry-collector" + expectedLables["app.kubernetes.io/name"] = "test-collector" + + expectedData := map[string]string{ + "collector.yaml": `exporters: + debug: null +processors: null +receivers: + jaeger: + protocols: + grpc: null + prometheus: + config: + scrape_configs: + - http_sd_configs: + - url: http://test-targetallocator:80/jobs/otel-collector/targets?collector_id=$POD_NAME + job_name: otel-collector + scrape_interval: 10s +service: + pipelines: + metrics: + exporters: + - debug + processors: [] + receivers: + - prometheus + - jaeger +`, + } + + param := deploymentParams() + param.OtelCol.Spec.TargetAllocator.Enabled = true + actual, err := ConfigMap(param) + + assert.NoError(t, err) + assert.Equal(t, "test-collector", actual.GetName()) + assert.Equal(t, expectedLables, actual.GetLabels()) + assert.Equal(t, expectedData, actual.Data) + + }) + + t.Run("should return expected escaped collector config map with http_sd_config if rewrite flag disabled", func(t *testing.T) { + err := colfeaturegate.GlobalRegistry().Set(featuregate.EnableTargetAllocatorRewrite.ID(), false) + assert.NoError(t, err) + t.Cleanup(func() { + _ = colfeaturegate.GlobalRegistry().Set(featuregate.EnableTargetAllocatorRewrite.ID(), true) + }) + + expectedLables["app.kubernetes.io/component"] = "opentelemetry-collector" + expectedLables["app.kubernetes.io/name"] = "test-collector" + expectedLables["app.kubernetes.io/version"] = "latest" + + expectedData := map[string]string{ + "collector.yaml": `exporters: + debug: null +processors: null +receivers: + prometheus: + config: + scrape_configs: + - http_sd_configs: + - url: http://test-targetallocator:80/jobs/serviceMonitor%2Ftest%2Ftest%2F0/targets?collector_id=$POD_NAME + job_name: serviceMonitor/test/test/0 + target_allocator: + collector_id: ${POD_NAME} + endpoint: http://test-targetallocator:80 + http_sd_config: + refresh_interval: 60s + interval: 30s +service: + pipelines: + metrics: + exporters: + - debug + processors: [] + receivers: + - prometheus +`, + } + + param, err := newParams("test/test-img", "testdata/http_sd_config_servicemonitor_test_ta_set.yaml") + assert.NoError(t, err) + param.OtelCol.Spec.TargetAllocator.Enabled = true + actual, err := ConfigMap(param) + + assert.NoError(t, err) + assert.Equal(t, "test-collector", actual.Name) + assert.Equal(t, expectedLables, actual.Labels) + assert.Equal(t, expectedData, actual.Data) + + // Reset the value + expectedLables["app.kubernetes.io/version"] = "0.47.0" + + }) + + t.Run("should return expected escaped collector config map with target_allocator config block", func(t *testing.T) { + expectedLables["app.kubernetes.io/component"] = "opentelemetry-collector" + expectedLables["app.kubernetes.io/name"] = "test-collector" + expectedLables["app.kubernetes.io/version"] = "latest" + + expectedData := map[string]string{ + "collector.yaml": `exporters: + debug: null +processors: null +receivers: + prometheus: + config: {} + target_allocator: + collector_id: ${POD_NAME} + endpoint: http://test-targetallocator:80 + interval: 30s +service: + pipelines: + metrics: + exporters: + - debug + processors: [] + receivers: + - prometheus +`, + } + + param, err := newParams("test/test-img", "testdata/http_sd_config_servicemonitor_test.yaml") + assert.NoError(t, err) + param.OtelCol.Spec.TargetAllocator.Enabled = true + actual, err := ConfigMap(param) + + assert.NoError(t, err) + assert.Equal(t, "test-collector", actual.Name) + assert.Equal(t, expectedLables, actual.Labels) + assert.Equal(t, expectedData, actual.Data) + + // Reset the value + expectedLables["app.kubernetes.io/version"] = "0.47.0" + assert.NoError(t, err) + + }) + +} diff --git a/pkg/collector/container.go b/internal/manifests/collector/container.go similarity index 52% rename from pkg/collector/container.go rename to internal/manifests/collector/container.go index 0428243088..e347d0ce56 100644 --- a/pkg/collector/container.go +++ b/internal/manifests/collector/container.go @@ -15,20 +15,20 @@ package collector import ( + "errors" "fmt" - "net" + "path" "sort" - "strconv" "github.com/go-logr/logr" - "github.com/mitchellh/mapstructure" + "github.com/operator-framework/operator-lib/proxy" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/validation" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) // maxPortLen allows us to truncate a port name according to what is considered valid port syntax: @@ -36,14 +36,18 @@ import ( const maxPortLen = 15 // Container builds a container for the given collector. -func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) corev1.Container { +func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector, addConfig bool) corev1.Container { image := otelcol.Spec.Image if len(image) == 0 { image = cfg.CollectorImage() } // build container ports from service ports - ports := getConfigContainerPorts(logger, otelcol.Spec.Config) + ports, err := getConfigContainerPorts(logger, otelcol.Spec.Config) + if err != nil { + logger.Error(err, "container ports config") + } + for _, p := range otelcol.Spec.Ports { ports[p.Name] = corev1.ContainerPort{ Name: p.Name, @@ -52,35 +56,45 @@ func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelem } } + var volumeMounts []corev1.VolumeMount argsMap := otelcol.Spec.Args if argsMap == nil { argsMap = map[string]string{} } - - if _, exists := argsMap["config"]; exists { - logger.Info("the 'config' flag isn't allowed and is being ignored") - } - - // this effectively overrides any 'config' entry that might exist in the CR - argsMap["config"] = fmt.Sprintf("/conf/%s", cfg.CollectorConfigMapEntry()) - + // defines the output (sorted) array for final output var args []string + // When adding a config via v1alpha1.OpenTelemetryCollectorSpec.Config, we ensure that it is always the + // first item in the args. At the time of writing, although multiple configs are allowed in the + // opentelemetry collector, the operator has yet to implement such functionality. When multiple configs + // are present they should be merged in a deterministic manner using the order given, and because + // v1alpha1.OpenTelemetryCollectorSpec.Config is a required field we assume that it will always be the + // "primary" config and in the future additional configs can be appended to the container args in a simple manner. + if addConfig { + // if key exists then delete key and excluded from the iteration after this block + if _, exists := argsMap["config"]; exists { + logger.Info("the 'config' flag isn't allowed and is being ignored") + delete(argsMap, "config") + } + args = append(args, fmt.Sprintf("--config=/conf/%s", cfg.CollectorConfigMapEntry())) + volumeMounts = append(volumeMounts, + corev1.VolumeMount{ + Name: naming.ConfigMapVolume(), + MountPath: "/conf", + }) + } + + // ensure that the v1alpha1.OpenTelemetryCollectorSpec.Args are ordered when moved to container.Args, + // where iterating over a map does not guarantee, so that reconcile will not be fooled by different + // ordering in args. + var sortedArgs []string for k, v := range argsMap { - args = append(args, fmt.Sprintf("--%s=%s", k, v)) + sortedArgs = append(sortedArgs, fmt.Sprintf("--%s=%s", k, v)) } - - volumeMounts := []corev1.VolumeMount{{ - Name: naming.ConfigMapVolume(), - MountPath: "/conf", - }} + sort.Strings(sortedArgs) + args = append(args, sortedArgs...) if len(otelcol.Spec.VolumeMounts) > 0 { volumeMounts = append(volumeMounts, otelcol.Spec.VolumeMounts...) - } else if otelcol.Spec.Mode == "statefulset" { - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: "default-volume", - MountPath: "/usr/share/default-volume", - }) } var envVars = otelcol.Spec.Env @@ -97,6 +111,15 @@ func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelem }, }) + if len(otelcol.Spec.ConfigMaps) > 0 { + for keyCfgMap := range otelcol.Spec.ConfigMaps { + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: naming.ConfigMapExtra(otelcol.Spec.ConfigMaps[keyCfgMap].Name), + MountPath: path.Join("/var/conf", otelcol.Spec.ConfigMaps[keyCfgMap].MountPath, naming.ConfigMapExtra(otelcol.Spec.ConfigMaps[keyCfgMap].Name)), + }) + } + } + if otelcol.Spec.TargetAllocator.Enabled { // We need to add a SHARD here so the collector is able to keep targets after the hashmod operation which is // added by default by the Prometheus operator's config generator. @@ -110,12 +133,19 @@ func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelem } var livenessProbe *corev1.Probe - if config, err := adapters.ConfigFromString(otelcol.Spec.Config); err == nil { - if probe, err := adapters.ConfigToContainerProbe(config); err == nil { + if configFromString, err := adapters.ConfigFromString(otelcol.Spec.Config); err == nil { + if probe, err := getLivenessProbe(configFromString, otelcol.Spec.LivenessProbe); err == nil { livenessProbe = probe + } else if errors.Is(err, adapters.ErrNoServiceExtensions) { + logger.Info("extensions not configured, skipping liveness probe creation") + } else if errors.Is(err, adapters.ErrNoServiceExtensionHealthCheck) { + logger.Info("healthcheck extension not configured, skipping liveness probe creation") + } else { + logger.Error(err, "cannot create liveness probe.") } } + envVars = append(envVars, proxy.ReadProxyVarsFromEnv()...) return corev1.Container{ Name: naming.Container(), Image: image, @@ -128,20 +158,22 @@ func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelem Resources: otelcol.Spec.Resources, SecurityContext: otelcol.Spec.SecurityContext, LivenessProbe: livenessProbe, + Lifecycle: otelcol.Spec.Lifecycle, } } -func getConfigContainerPorts(logger logr.Logger, cfg string) map[string]corev1.ContainerPort { +func getConfigContainerPorts(logger logr.Logger, cfg string) (map[string]corev1.ContainerPort, error) { ports := map[string]corev1.ContainerPort{} c, err := adapters.ConfigFromString(cfg) if err != nil { logger.Error(err, "couldn't extract the configuration") - return ports + return ports, err } - ps, err := adapters.ConfigToReceiverPorts(logger, c) + ps, err := adapters.ConfigToPorts(logger, c) if err != nil { - logger.Error(err, "couldn't build container ports from configuration") - } else { + return ports, err + } + if len(ps) > 0 { for _, p := range ps { truncName := naming.Truncate(p.Name, maxPortLen) if p.Name != truncName { @@ -163,7 +195,7 @@ func getConfigContainerPorts(logger logr.Logger, cfg string) map[string]corev1.C } } - metricsPort, err := getMetricsPort(c) + metricsPort, err := adapters.ConfigToMetricsPort(logger, c) if err != nil { logger.Info("couldn't determine metrics port from configuration, using 8888 default value", "error", err) metricsPort = 8888 @@ -173,41 +205,8 @@ func getConfigContainerPorts(logger logr.Logger, cfg string) map[string]corev1.C ContainerPort: metricsPort, Protocol: corev1.ProtocolTCP, } - return ports -} - -// getMetricsPort gets the port number for the metrics endpoint from the collector config if it has been set. -func getMetricsPort(c map[interface{}]interface{}) (int32, error) { - // we don't need to unmarshal the whole config, just follow the keys down to - // the metrics address. - type metricsCfg struct { - Address string - } - type telemetryCfg struct { - Metrics metricsCfg - } - type serviceCfg struct { - Telemetry telemetryCfg - } - type cfg struct { - Service serviceCfg - } - var cOut cfg - err := mapstructure.Decode(c, &cOut) - if err != nil { - return 0, err - } - - _, port, err := net.SplitHostPort(cOut.Service.Telemetry.Metrics.Address) - if err != nil { - return 0, err - } - i64, err := strconv.ParseInt(port, 10, 32) - if err != nil { - return 0, err - } - return int32(i64), nil + return ports, nil } func portMapToList(portMap map[string]corev1.ContainerPort) []corev1.ContainerPort { @@ -220,3 +219,29 @@ func portMapToList(portMap map[string]corev1.ContainerPort) []corev1.ContainerPo }) return ports } + +func getLivenessProbe(config map[interface{}]interface{}, probeConfig *v1alpha1.Probe) (*corev1.Probe, error) { + probe, err := adapters.ConfigToContainerProbe(config) + if err != nil { + return nil, err + } + if probeConfig != nil { + if probeConfig.InitialDelaySeconds != nil { + probe.InitialDelaySeconds = *probeConfig.InitialDelaySeconds + } + if probeConfig.PeriodSeconds != nil { + probe.PeriodSeconds = *probeConfig.PeriodSeconds + } + if probeConfig.FailureThreshold != nil { + probe.FailureThreshold = *probeConfig.FailureThreshold + } + if probeConfig.SuccessThreshold != nil { + probe.SuccessThreshold = *probeConfig.SuccessThreshold + } + if probeConfig.TimeoutSeconds != nil { + probe.TimeoutSeconds = *probeConfig.TimeoutSeconds + } + probe.TerminationGracePeriodSeconds = probeConfig.TerminationGracePeriodSeconds + } + return probe, nil +} diff --git a/pkg/collector/container_test.go b/internal/manifests/collector/container_test.go similarity index 50% rename from pkg/collector/container_test.go rename to internal/manifests/collector/container_test.go index a0d7155e59..5857798f2c 100644 --- a/pkg/collector/container_test.go +++ b/internal/manifests/collector/container_test.go @@ -15,16 +15,18 @@ package collector_test import ( + "os" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" logf "sigs.k8s.io/controller-runtime/pkg/log" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" + . "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" ) var logger = logf.Log.WithName("unit-tests") @@ -37,11 +39,35 @@ var metricContainerPort = corev1.ContainerPort{ func TestContainerNewDefault(t *testing.T) { // prepare - otelcol := v1alpha1.OpenTelemetryCollector{} + var defaultConfig = `receivers: + otlp: + protocols: + http: + grpc: + exporters: + debug: + service: + pipelines: + metrics: + receivers: [otlp] + exporters: [debug]` + + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Ports: []corev1.ServicePort{ + { + Name: "metrics", + Port: 8888, + Protocol: corev1.ProtocolTCP, + }, + }, + Config: defaultConfig, + }, + } cfg := config.New(config.WithCollectorImage("default-image")) // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Equal(t, "default-image", c.Image) @@ -58,7 +84,7 @@ func TestContainerWithImageOverridden(t *testing.T) { cfg := config.New(config.WithCollectorImage("default-image")) // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Equal(t, "overridden-image", c.Image) @@ -68,12 +94,13 @@ func TestContainerPorts(t *testing.T) { var goodConfig = `receivers: examplereceiver: endpoint: "0.0.0.0:12345" +exporters: + debug: service: pipelines: metrics: receivers: [examplereceiver] - exporters: [logging] -` + exporters: [debug]` tests := []struct { description string @@ -88,9 +115,15 @@ service: expectedPorts: []corev1.ContainerPort{}, }, { - description: "couldn't build ports from spec config", - specConfig: "", - specPorts: nil, + description: "couldn't build ports from spec config", + specConfig: "", + specPorts: []corev1.ServicePort{ + { + Name: "metrics", + Port: 8888, + Protocol: corev1.ProtocolTCP, + }, + }, expectedPorts: []corev1.ContainerPort{metricContainerPort}, }, { @@ -108,6 +141,11 @@ service: { description: "ports in spec ContainerPorts", specPorts: []corev1.ServicePort{ + { + Name: "metrics", + Port: 8888, + Protocol: corev1.ProtocolTCP, + }, { Name: "testport1", Port: 12345, @@ -177,6 +215,136 @@ service: }, }, }, + { + description: "prometheus exporter", + specConfig: `exporters: + prometheus: + endpoint: "0.0.0.0:9090" + debug: +service: + pipelines: + metrics: + receivers: [otlp] + exporters: [prometheus, debug] +`, + specPorts: []corev1.ServicePort{ + { + Name: "metrics", + Port: 8888, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "prometheus", + Port: 9090, + }, + }, + expectedPorts: []corev1.ContainerPort{ + { + Name: "metrics", + ContainerPort: 8888, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "prometheus", + ContainerPort: 9090, + }, + }, + }, + { + description: "multiple prometheus exporters", + specConfig: `exporters: + prometheus/prod: + endpoint: "0.0.0.0:9090" + prometheus/dev: + endpoint: "0.0.0.0:9091" + debug: +service: + pipelines: + metrics: + exporters: [prometheus/prod, prometheus/dev, debug] +`, + specPorts: []corev1.ServicePort{ + { + Name: "metrics", + Port: 8888, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "prometheus-dev", + Port: 9091, + }, + { + Name: "prometheus-prod", + Port: 9090, + }, + }, + expectedPorts: []corev1.ContainerPort{ + metricContainerPort, + { + Name: "prometheus-dev", + ContainerPort: 9091, + }, + { + Name: "prometheus-prod", + ContainerPort: 9090, + }, + }, + }, + { + description: "prometheus RW exporter", + specConfig: `exporters: + prometheusremotewrite/prometheus: + endpoint: http://prometheus-server.monitoring/api/v1/write`, + specPorts: []corev1.ServicePort{ + { + Name: "metrics", + Port: 8888, + Protocol: corev1.ProtocolTCP, + }, + }, + expectedPorts: []corev1.ContainerPort{metricContainerPort}, + }, + { + description: "multiple prometheus exporters and prometheus RW exporter", + specConfig: `exporters: + prometheus/prod: + endpoint: "0.0.0.0:9090" + prometheus/dev: + endpoint: "0.0.0.0:9091" + prometheusremotewrite/prometheus: + endpoint: http://prometheus-server.monitoring/api/v1/write + debug: +service: + pipelines: + metrics: + exporters: [prometheus/prod, prometheus/dev, prometheusremotewrite/prometheus, debug]`, + specPorts: []corev1.ServicePort{ + { + Name: "metrics", + Port: 8888, + Protocol: corev1.ProtocolTCP, + }, + { + Name: "prometheus-dev", + Port: 9091, + }, + { + Name: "prometheus-prod", + Port: 9090, + }, + }, + expectedPorts: []corev1.ContainerPort{ + metricContainerPort, + { + Name: "prometheus-dev", + ContainerPort: 9091, + }, + { + Name: "prometheus-prod", + ContainerPort: 9090, + }, + }, + }, } for _, testCase := range tests { @@ -188,13 +356,13 @@ service: Ports: testCase.specPorts, }, } + cfg := config.New(config.WithCollectorImage("default-image")) // test - c := Container(cfg, logger, otelcol) - + c := Container(cfg, logger, otelcol, true) // verify - assert.ElementsMatch(t, testCase.expectedPorts, c.Ports) + assert.ElementsMatch(t, testCase.expectedPorts, c.Ports, testCase.description) }) } } @@ -212,7 +380,7 @@ func TestContainerConfigFlagIsIgnored(t *testing.T) { cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Len(t, c.Args, 2) @@ -232,16 +400,42 @@ func TestContainerCustomVolumes(t *testing.T) { cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Len(t, c.VolumeMounts, 2) assert.Equal(t, "custom-volume-mount", c.VolumeMounts[1].Name) } +func TestContainerCustomConfigMapsVolumes(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + ConfigMaps: []v1alpha1.ConfigMapsSpec{{ + Name: "test", + MountPath: "/", + }, { + Name: "test2", + MountPath: "/dir", + }}, + }, + } + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol, true) + + // verify + assert.Len(t, c.VolumeMounts, 3) + assert.Equal(t, "configmap-test", c.VolumeMounts[1].Name) + assert.Equal(t, "/var/conf/configmap-test", c.VolumeMounts[1].MountPath) + assert.Equal(t, "configmap-test2", c.VolumeMounts[2].Name) + assert.Equal(t, "/var/conf/dir/configmap-test2", c.VolumeMounts[2].MountPath) +} + func TestContainerCustomSecurityContext(t *testing.T) { // default config without security context - c1 := Container(config.New(), logger, v1alpha1.OpenTelemetryCollector{Spec: v1alpha1.OpenTelemetryCollectorSpec{}}) + c1 := Container(config.New(), logger, v1alpha1.OpenTelemetryCollector{Spec: v1alpha1.OpenTelemetryCollectorSpec{}}, true) // verify assert.Nil(t, c1.SecurityContext) @@ -258,7 +452,7 @@ func TestContainerCustomSecurityContext(t *testing.T) { RunAsUser: &uid, }, }, - }) + }, true) // verify assert.NotNil(t, c2.SecurityContext) @@ -281,7 +475,7 @@ func TestContainerEnvVarsOverridden(t *testing.T) { cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Len(t, c.Env, 2) @@ -297,13 +491,33 @@ func TestContainerDefaultEnvVars(t *testing.T) { cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Len(t, c.Env, 1) assert.Equal(t, c.Env[0].Name, "POD_NAME") } +func TestContainerProxyEnvVars(t *testing.T) { + err := os.Setenv("NO_PROXY", "localhost") + require.NoError(t, err) + defer os.Unsetenv("NO_PROXY") + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{}, + } + + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol, true) + + // verify + require.Len(t, c.Env, 3) + assert.Equal(t, "POD_NAME", c.Env[0].Name) + assert.Equal(t, corev1.EnvVar{Name: "NO_PROXY", Value: "localhost"}, c.Env[1]) + assert.Equal(t, corev1.EnvVar{Name: "no_proxy", Value: "localhost"}, c.Env[2]) +} + func TestContainerResourceRequirements(t *testing.T) { otelcol := v1alpha1.OpenTelemetryCollector{ Spec: v1alpha1.OpenTelemetryCollectorSpec{ @@ -323,7 +537,7 @@ func TestContainerResourceRequirements(t *testing.T) { cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Equal(t, resource.MustParse("100m"), *c.Resources.Limits.Cpu()) @@ -340,7 +554,7 @@ func TestContainerDefaultResourceRequirements(t *testing.T) { cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Empty(t, c.Resources) @@ -359,13 +573,35 @@ func TestContainerArgs(t *testing.T) { cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Contains(t, c.Args, "--metrics-level=detailed") assert.Contains(t, c.Args, "--log-level=debug") } +func TestContainerOrderedArgs(t *testing.T) { + // prepare a scenario where the debug level and a feature gate has been enabled + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Args: map[string]string{ + "log-level": "debug", + "feature-gates": "+random-feature", + }, + }, + } + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol, true) + + // verify that the first args is (always) the config, and the remaining args are ordered alphabetically + // by the key + assert.Equal(t, "--config=/conf/collector.yaml", c.Args[0]) + assert.Equal(t, "--feature-gates=+random-feature", c.Args[1]) + assert.Equal(t, "--log-level=debug", c.Args[2]) +} + func TestContainerImagePullPolicy(t *testing.T) { // prepare otelcol := v1alpha1.OpenTelemetryCollector{ @@ -376,7 +612,7 @@ func TestContainerImagePullPolicy(t *testing.T) { cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Equal(t, c.ImagePullPolicy, corev1.PullIfNotPresent) @@ -409,7 +645,7 @@ func TestContainerEnvFrom(t *testing.T) { cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Contains(t, c.EnvFrom, envFrom1) @@ -418,21 +654,119 @@ func TestContainerEnvFrom(t *testing.T) { func TestContainerProbe(t *testing.T) { // prepare + initialDelaySeconds := int32(10) + timeoutSeconds := int32(11) + periodSeconds := int32(12) + successThreshold := int32(13) + failureThreshold := int32(14) + terminationGracePeriodSeconds := int64(15) + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Config: `extensions: + health_check: +service: + extensions: [health_check]`, + LivenessProbe: &v1alpha1.Probe{ + InitialDelaySeconds: &initialDelaySeconds, + TimeoutSeconds: &timeoutSeconds, + PeriodSeconds: &periodSeconds, + SuccessThreshold: &successThreshold, + FailureThreshold: &failureThreshold, + TerminationGracePeriodSeconds: &terminationGracePeriodSeconds, + }, + }, + } + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol, true) + + // verify + assert.Equal(t, "/", c.LivenessProbe.HTTPGet.Path) + assert.Equal(t, int32(13133), c.LivenessProbe.HTTPGet.Port.IntVal) + assert.Equal(t, "", c.LivenessProbe.HTTPGet.Host) + + assert.Equal(t, initialDelaySeconds, c.LivenessProbe.InitialDelaySeconds) + assert.Equal(t, timeoutSeconds, c.LivenessProbe.TimeoutSeconds) + assert.Equal(t, periodSeconds, c.LivenessProbe.PeriodSeconds) + assert.Equal(t, successThreshold, c.LivenessProbe.SuccessThreshold) + assert.Equal(t, failureThreshold, c.LivenessProbe.FailureThreshold) + assert.Equal(t, terminationGracePeriodSeconds, *c.LivenessProbe.TerminationGracePeriodSeconds) +} + +func TestContainerProbeEmptyConfig(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ Spec: v1alpha1.OpenTelemetryCollectorSpec{ Config: `extensions: health_check: service: extensions: [health_check]`, + LivenessProbe: &v1alpha1.Probe{}, }, } cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, otelcol, true) // verify assert.Equal(t, "/", c.LivenessProbe.HTTPGet.Path) assert.Equal(t, int32(13133), c.LivenessProbe.HTTPGet.Port.IntVal) assert.Equal(t, "", c.LivenessProbe.HTTPGet.Host) } + +func TestContainerProbeNoConfig(t *testing.T) { + // prepare + + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Config: `extensions: + health_check: +service: + extensions: [health_check]`, + }, + } + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol, true) + + // verify + assert.Equal(t, "/", c.LivenessProbe.HTTPGet.Path) + assert.Equal(t, int32(13133), c.LivenessProbe.HTTPGet.Port.IntVal) + assert.Equal(t, "", c.LivenessProbe.HTTPGet.Host) +} + +func TestContainerLifecycle(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Lifecycle: &corev1.Lifecycle{ + PostStart: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{Command: []string{"sh", "sleep 100"}}, + }, + PreStop: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{Command: []string{"sh", "sleep 300"}}, + }, + }, + }, + } + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol, true) + + expectedLifecycleHooks := corev1.Lifecycle{ + PostStart: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{Command: []string{"sh", "sleep 100"}}, + }, + PreStop: &corev1.LifecycleHandler{ + Exec: &corev1.ExecAction{Command: []string{"sh", "sleep 300"}}, + }, + } + + // verify + assert.Equal(t, expectedLifecycleHooks, *c.Lifecycle) +} diff --git a/internal/manifests/collector/daemonset.go b/internal/manifests/collector/daemonset.go new file mode 100644 index 0000000000..8566ee0828 --- /dev/null +++ b/internal/manifests/collector/daemonset.go @@ -0,0 +1,67 @@ +// 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 collector + +import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// DaemonSet builds the deployment for the given instance. +func DaemonSet(params manifests.Params) *appsv1.DaemonSet { + name := naming.Collector(params.OtelCol.Name) + labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, params.Config.LabelsFilter()) + + annotations := Annotations(params.OtelCol) + podAnnotations := PodAnnotations(params.OtelCol) + return &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Collector(params.OtelCol.Name), + Namespace: params.OtelCol.Namespace, + Labels: labels, + Annotations: annotations, + }, + Spec: appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: podAnnotations, + }, + Spec: corev1.PodSpec{ + ServiceAccountName: ServiceAccountName(params.OtelCol), + InitContainers: params.OtelCol.Spec.InitContainers, + Containers: append(params.OtelCol.Spec.AdditionalContainers, Container(params.Config, params.Log, params.OtelCol, true)), + Volumes: Volumes(params.Config, params.OtelCol), + Tolerations: params.OtelCol.Spec.Tolerations, + NodeSelector: params.OtelCol.Spec.NodeSelector, + HostNetwork: params.OtelCol.Spec.HostNetwork, + DNSPolicy: getDNSPolicy(params.OtelCol), + SecurityContext: params.OtelCol.Spec.PodSecurityContext, + PriorityClassName: params.OtelCol.Spec.PriorityClassName, + Affinity: params.OtelCol.Spec.Affinity, + }, + }, + UpdateStrategy: params.OtelCol.Spec.UpdateStrategy, + }, + } +} diff --git a/internal/manifests/collector/daemonset_test.go b/internal/manifests/collector/daemonset_test.go new file mode 100644 index 0000000000..656591e6d8 --- /dev/null +++ b/internal/manifests/collector/daemonset_test.go @@ -0,0 +1,491 @@ +// 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 collector_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + . "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" +) + +func TestDaemonSetNewDefault(t *testing.T) { + // prepare + params := manifests.Params{ + Config: config.New(), + OtelCol: v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Tolerations: testTolerationValues, + }, + }, + Log: logger, + } + + // test + d := DaemonSet(params) + + // verify + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) + assert.Equal(t, testTolerationValues, d.Spec.Template.Spec.Tolerations) + + assert.Len(t, d.Spec.Template.Spec.Containers, 1) + + // verify sha256 podAnnotation + expectedAnnotations := map[string]string{ + "opentelemetry-operator-config/sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + } + assert.Equal(t, expectedAnnotations, d.Spec.Template.Annotations) + + expectedLabels := map[string]string{ + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "my-namespace.my-instance", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "my-instance-collector", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + } + assert.Equal(t, expectedLabels, d.Spec.Template.Labels) + + expectedSelectorLabels := map[string]string{ + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "my-namespace.my-instance", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/part-of": "opentelemetry", + } + assert.Equal(t, expectedSelectorLabels, d.Spec.Selector.MatchLabels) + + // the pod selector must be contained within pod spec's labels + for k, v := range d.Spec.Selector.MatchLabels { + assert.Equal(t, v, d.Spec.Template.Labels[k]) + } +} + +func TestDaemonsetHostNetwork(t *testing.T) { + params1 := manifests.Params{ + Config: config.New(), + OtelCol: v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{}, + }, + Log: logger, + } + // test + d1 := DaemonSet(params1) + assert.False(t, d1.Spec.Template.Spec.HostNetwork) + assert.Equal(t, d1.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirst) + + // verify custom + params2 := manifests.Params{ + Config: config.New(), + OtelCol: v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + HostNetwork: true, + }, + }, + Log: logger, + } + d2 := DaemonSet(params2) + assert.True(t, d2.Spec.Template.Spec.HostNetwork) + assert.Equal(t, d2.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirstWithHostNet) +} + +func TestDaemonsetPodAnnotations(t *testing.T) { + // prepare + testPodAnnotationValues := map[string]string{"annotation-key": "annotation-value"} + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + PodAnnotations: testPodAnnotationValues, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + // test + ds := DaemonSet(params) + + // Add sha256 podAnnotation + testPodAnnotationValues["opentelemetry-operator-config/sha256"] = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + + expectedAnnotations := map[string]string{ + "annotation-key": "annotation-value", + "opentelemetry-operator-config/sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + } + + // verify + assert.Equal(t, "my-instance-collector", ds.Name) + assert.Len(t, ds.Spec.Template.Annotations, 5) + assert.Equal(t, expectedAnnotations, ds.Spec.Template.Annotations) +} + +func TestDaemonstPodSecurityContext(t *testing.T) { + runAsNonRoot := true + runAsUser := int64(1337) + runasGroup := int64(1338) + + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + PodSecurityContext: &v1.PodSecurityContext{ + RunAsNonRoot: &runAsNonRoot, + RunAsUser: &runAsUser, + RunAsGroup: &runasGroup, + }, + }, + } + + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + d := DaemonSet(params) + + assert.Equal(t, &runAsNonRoot, d.Spec.Template.Spec.SecurityContext.RunAsNonRoot) + assert.Equal(t, &runAsUser, d.Spec.Template.Spec.SecurityContext.RunAsUser) + assert.Equal(t, &runasGroup, d.Spec.Template.Spec.SecurityContext.RunAsGroup) +} + +func TestDaemonsetFilterLabels(t *testing.T) { + excludedLabels := map[string]string{ + "foo": "1", + "app.foo.bar": "1", + } + + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Labels: excludedLabels, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{}, + } + + cfg := config.New(config.WithLabelFilters([]string{"foo*", "app.*.bar"})) + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + d := DaemonSet(params) + + assert.Len(t, d.ObjectMeta.Labels, 6) + for k := range excludedLabels { + assert.NotContains(t, d.ObjectMeta.Labels, k) + } +} + +func TestDaemonSetNodeSelector(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OtelCol: otelcol1, + Log: logger, + } + + d1 := DaemonSet(params1) + + assert.Empty(t, d1.Spec.Template.Spec.NodeSelector) + + // Test nodeSelector + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-nodeselector", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + HostNetwork: true, + NodeSelector: map[string]string{ + "node-key": "node-value", + }, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OtelCol: otelcol2, + Log: logger, + } + + d2 := DaemonSet(params2) + assert.Equal(t, d2.Spec.Template.Spec.NodeSelector, map[string]string{"node-key": "node-value"}) +} + +func TestDaemonSetPriorityClassName(t *testing.T) { + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OtelCol: otelcol1, + Log: logger, + } + + d1 := DaemonSet(params1) + assert.Empty(t, d1.Spec.Template.Spec.PriorityClassName) + + priorityClassName := "test-class" + + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-priortyClassName", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + PriorityClassName: priorityClassName, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OtelCol: otelcol2, + Log: logger, + } + + d2 := DaemonSet(params2) + assert.Equal(t, priorityClassName, d2.Spec.Template.Spec.PriorityClassName) +} + +func TestDaemonSetAffinity(t *testing.T) { + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OtelCol: otelcol1, + Log: logger, + } + + d1 := DaemonSet(params1) + assert.Nil(t, d1.Spec.Template.Spec.Affinity) + + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-priortyClassName", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Affinity: testAffinityValue, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OtelCol: otelcol2, + Log: logger, + } + + d2 := DaemonSet(params2) + assert.NotNil(t, d2.Spec.Template.Spec.Affinity) + assert.Equal(t, *testAffinityValue, *d2.Spec.Template.Spec.Affinity) +} + +func TestDaemonSetInitContainer(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + InitContainers: []v1.Container{ + { + Name: "test", + }, + }, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + // test + d := DaemonSet(params) + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) + assert.Len(t, d.Spec.Template.Spec.InitContainers, 1) +} + +func TestDaemonSetAdditionalContainer(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + AdditionalContainers: []v1.Container{ + { + Name: "test", + }, + }, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + // test + d := DaemonSet(params) + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) + assert.Len(t, d.Spec.Template.Spec.Containers, 2) + assert.Equal(t, v1.Container{Name: "test"}, d.Spec.Template.Spec.Containers[0]) +} + +func TestDaemonSetDefaultUpdateStrategy(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ + Type: "RollingUpdate", + RollingUpdate: &appsv1.RollingUpdateDaemonSet{ + MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + }, + }, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + // test + d := DaemonSet(params) + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, appsv1.DaemonSetUpdateStrategyType("RollingUpdate"), d.Spec.UpdateStrategy.Type) + assert.Equal(t, &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, d.Spec.UpdateStrategy.RollingUpdate.MaxSurge) + assert.Equal(t, &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, d.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable) +} + +func TestDaemonSetOnDeleteUpdateStrategy(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ + Type: "OnDelete", + RollingUpdate: &appsv1.RollingUpdateDaemonSet{ + MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, + }, + }, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + // test + d := DaemonSet(params) + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, appsv1.DaemonSetUpdateStrategyType("OnDelete"), d.Spec.UpdateStrategy.Type) + assert.Equal(t, &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, d.Spec.UpdateStrategy.RollingUpdate.MaxSurge) + assert.Equal(t, &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)}, d.Spec.UpdateStrategy.RollingUpdate.MaxUnavailable) +} diff --git a/internal/manifests/collector/deployment.go b/internal/manifests/collector/deployment.go new file mode 100644 index 0000000000..3a1b0abf21 --- /dev/null +++ b/internal/manifests/collector/deployment.go @@ -0,0 +1,70 @@ +// 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 collector + +import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// Deployment builds the deployment for the given instance. +func Deployment(params manifests.Params) *appsv1.Deployment { + name := naming.Collector(params.OtelCol.Name) + labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, params.Config.LabelsFilter()) + + annotations := Annotations(params.OtelCol) + podAnnotations := PodAnnotations(params.OtelCol) + + return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OtelCol.Namespace, + Labels: labels, + Annotations: annotations, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: params.OtelCol.Spec.Replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: podAnnotations, + }, + Spec: corev1.PodSpec{ + ServiceAccountName: ServiceAccountName(params.OtelCol), + InitContainers: params.OtelCol.Spec.InitContainers, + Containers: append(params.OtelCol.Spec.AdditionalContainers, Container(params.Config, params.Log, params.OtelCol, true)), + Volumes: Volumes(params.Config, params.OtelCol), + DNSPolicy: getDNSPolicy(params.OtelCol), + HostNetwork: params.OtelCol.Spec.HostNetwork, + Tolerations: params.OtelCol.Spec.Tolerations, + NodeSelector: params.OtelCol.Spec.NodeSelector, + SecurityContext: params.OtelCol.Spec.PodSecurityContext, + PriorityClassName: params.OtelCol.Spec.PriorityClassName, + Affinity: params.OtelCol.Spec.Affinity, + TerminationGracePeriodSeconds: params.OtelCol.Spec.TerminationGracePeriodSeconds, + TopologySpreadConstraints: params.OtelCol.Spec.TopologySpreadConstraints, + }, + }, + }, + } +} diff --git a/internal/manifests/collector/deployment_test.go b/internal/manifests/collector/deployment_test.go new file mode 100644 index 0000000000..baa66f42ad --- /dev/null +++ b/internal/manifests/collector/deployment_test.go @@ -0,0 +1,556 @@ +// 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 collector_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + . "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" +) + +var testTolerationValues = []v1.Toleration{ + { + Key: "hii", + Value: "greeting", + Effect: "NoSchedule", + }, +} + +var testAffinityValue = &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ + NodeSelectorTerms: []v1.NodeSelectorTerm{ + { + MatchExpressions: []v1.NodeSelectorRequirement{ + { + Key: "node", + Operator: v1.NodeSelectorOpIn, + Values: []string{"test-node"}, + }, + }, + }, + }, + }, + }, +} + +var testTopologySpreadConstraintValue = []v1.TopologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "kubernetes.io/hostname", + WhenUnsatisfiable: "DoNotSchedule", + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "foo": "bar", + }, + }, + }, +} + +func TestDeploymentNewDefault(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Tolerations: testTolerationValues, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + // test + d := Deployment(params) + + // verify + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) + assert.Equal(t, testTolerationValues, d.Spec.Template.Spec.Tolerations) + + assert.Len(t, d.Spec.Template.Spec.Containers, 1) + + // verify sha256 podAnnotation + expectedAnnotations := map[string]string{ + "opentelemetry-operator-config/sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + } + assert.Equal(t, expectedAnnotations, d.Spec.Template.Annotations) + + expectedLabels := map[string]string{ + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "my-namespace.my-instance", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "my-instance-collector", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + } + assert.Equal(t, expectedLabels, d.Spec.Template.Labels) + + expectedSelectorLabels := map[string]string{ + "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/instance": "my-namespace.my-instance", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/part-of": "opentelemetry", + } + assert.Equal(t, expectedSelectorLabels, d.Spec.Selector.MatchLabels) + + // the pod selector must be contained within pod spec's labels + for k, v := range d.Spec.Selector.MatchLabels { + assert.Equal(t, v, d.Spec.Template.Labels[k]) + } +} + +func TestDeploymentPodAnnotations(t *testing.T) { + // prepare + testPodAnnotationValues := map[string]string{"annotation-key": "annotation-value"} + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + PodAnnotations: testPodAnnotationValues, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + // test + d := Deployment(params) + + // Add sha256 podAnnotation + testPodAnnotationValues["opentelemetry-operator-config/sha256"] = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + + expectedPodAnnotationValues := map[string]string{ + "annotation-key": "annotation-value", + "opentelemetry-operator-config/sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + } + + // verify + assert.Len(t, d.Spec.Template.Annotations, 5) + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, expectedPodAnnotationValues, d.Spec.Template.Annotations) +} + +func TestDeploymenttPodSecurityContext(t *testing.T) { + runAsNonRoot := true + runAsUser := int64(1337) + runasGroup := int64(1338) + + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + PodSecurityContext: &v1.PodSecurityContext{ + RunAsNonRoot: &runAsNonRoot, + RunAsUser: &runAsUser, + RunAsGroup: &runasGroup, + }, + }, + } + + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + d := Deployment(params) + + assert.Equal(t, &runAsNonRoot, d.Spec.Template.Spec.SecurityContext.RunAsNonRoot) + assert.Equal(t, &runAsUser, d.Spec.Template.Spec.SecurityContext.RunAsUser) + assert.Equal(t, &runasGroup, d.Spec.Template.Spec.SecurityContext.RunAsGroup) +} + +func TestDeploymentHostNetwork(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OtelCol: otelcol1, + Log: logger, + } + + d1 := Deployment(params1) + + assert.Equal(t, d1.Spec.Template.Spec.HostNetwork, false) + assert.Equal(t, d1.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirst) + + // Test hostNetwork=true + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-hostnetwork", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + HostNetwork: true, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OtelCol: otelcol2, + Log: logger, + } + + d2 := Deployment(params2) + assert.Equal(t, d2.Spec.Template.Spec.HostNetwork, true) + assert.Equal(t, d2.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirstWithHostNet) +} + +func TestDeploymentFilterLabels(t *testing.T) { + excludedLabels := map[string]string{ + "foo": "1", + "app.foo.bar": "1", + } + + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Labels: excludedLabels, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{}, + } + + cfg := config.New(config.WithLabelFilters([]string{"foo*", "app.*.bar"})) + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + d := Deployment(params) + + assert.Len(t, d.ObjectMeta.Labels, 6) + for k := range excludedLabels { + assert.NotContains(t, d.ObjectMeta.Labels, k) + } +} + +func TestDeploymentNodeSelector(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OtelCol: otelcol1, + Log: logger, + } + + d1 := Deployment(params1) + + assert.Empty(t, d1.Spec.Template.Spec.NodeSelector) + + // Test nodeSelector + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-nodeselector", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + HostNetwork: true, + NodeSelector: map[string]string{ + "node-key": "node-value", + }, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OtelCol: otelcol2, + Log: logger, + } + + d2 := Deployment(params2) + assert.Equal(t, d2.Spec.Template.Spec.NodeSelector, map[string]string{"node-key": "node-value"}) +} + +func TestDeploymentPriorityClassName(t *testing.T) { + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OtelCol: otelcol1, + Log: logger, + } + + d1 := Deployment(params1) + assert.Empty(t, d1.Spec.Template.Spec.PriorityClassName) + + priorityClassName := "test-class" + + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-priortyClassName", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + PriorityClassName: priorityClassName, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OtelCol: otelcol2, + Log: logger, + } + + d2 := Deployment(params2) + assert.Equal(t, priorityClassName, d2.Spec.Template.Spec.PriorityClassName) +} + +func TestDeploymentAffinity(t *testing.T) { + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OtelCol: otelcol1, + Log: logger, + } + + d1 := Deployment(params1) + assert.Nil(t, d1.Spec.Template.Spec.Affinity) + + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-priortyClassName", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Affinity: testAffinityValue, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OtelCol: otelcol2, + Log: logger, + } + + d2 := Deployment(params2) + assert.NotNil(t, d2.Spec.Template.Spec.Affinity) + assert.Equal(t, *testAffinityValue, *d2.Spec.Template.Spec.Affinity) +} + +func TestDeploymentTerminationGracePeriodSeconds(t *testing.T) { + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OtelCol: otelcol1, + Log: logger, + } + + d1 := Deployment(params1) + assert.Nil(t, d1.Spec.Template.Spec.TerminationGracePeriodSeconds) + + gracePeriodSec := int64(60) + + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-terminationGracePeriodSeconds", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TerminationGracePeriodSeconds: &gracePeriodSec, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OtelCol: otelcol2, + Log: logger, + } + + d2 := Deployment(params2) + assert.NotNil(t, d2.Spec.Template.Spec.TerminationGracePeriodSeconds) + assert.Equal(t, gracePeriodSec, *d2.Spec.Template.Spec.TerminationGracePeriodSeconds) +} + +func TestDeploymentSetInitContainer(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + InitContainers: []v1.Container{ + { + Name: "test", + }, + }, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + // test + d := Deployment(params) + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) + assert.Len(t, d.Spec.Template.Spec.InitContainers, 1) +} + +func TestDeploymentTopologySpreadConstraints(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OtelCol: otelcol1, + Log: logger, + } + d1 := Deployment(params1) + assert.Equal(t, "my-instance-collector", d1.Name) + assert.Empty(t, d1.Spec.Template.Spec.TopologySpreadConstraints) + + // Test TopologySpreadConstraints + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-topologyspreadconstraint", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TopologySpreadConstraints: testTopologySpreadConstraintValue, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OtelCol: otelcol2, + Log: logger, + } + d2 := Deployment(params2) + assert.Equal(t, "my-instance-topologyspreadconstraint-collector", d2.Name) + assert.NotNil(t, d2.Spec.Template.Spec.TopologySpreadConstraints) + assert.NotEmpty(t, d2.Spec.Template.Spec.TopologySpreadConstraints) + assert.Equal(t, testTopologySpreadConstraintValue, d2.Spec.Template.Spec.TopologySpreadConstraints) +} + +func TestDeploymentAdditionalContainers(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + AdditionalContainers: []v1.Container{ + { + Name: "test", + }, + }, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OtelCol: otelcol, + Log: logger, + } + + // test + d := Deployment(params) + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) + assert.Len(t, d.Spec.Template.Spec.Containers, 2) + assert.Equal(t, v1.Container{Name: "test"}, d.Spec.Template.Spec.Containers[0]) +} diff --git a/internal/manifests/collector/horizontalpodautoscaler.go b/internal/manifests/collector/horizontalpodautoscaler.go new file mode 100644 index 0000000000..0a4bdefccd --- /dev/null +++ b/internal/manifests/collector/horizontalpodautoscaler.go @@ -0,0 +1,120 @@ +// 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 collector + +import ( + autoscalingv2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func HorizontalPodAutoscaler(params manifests.Params) client.Object { + name := naming.Collector(params.OtelCol.Name) + labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, params.Config.LabelsFilter()) + annotations := Annotations(params.OtelCol) + var result client.Object + + objectMeta := metav1.ObjectMeta{ + Name: naming.HorizontalPodAutoscaler(params.OtelCol.Name), + Namespace: params.OtelCol.Namespace, + Labels: labels, + Annotations: annotations, + } + + // defaulting webhook should always set this, but if unset then return nil. + if params.OtelCol.Spec.Autoscaler == nil { + params.Log.Info("hpa field is unset in Spec, skipping autoscaler creation") + return nil + } + + if params.OtelCol.Spec.Autoscaler.MaxReplicas == nil { + params.OtelCol.Spec.Autoscaler.MaxReplicas = params.OtelCol.Spec.MaxReplicas + } + + if params.OtelCol.Spec.Autoscaler.MinReplicas == nil { + if params.OtelCol.Spec.MinReplicas != nil { + params.OtelCol.Spec.Autoscaler.MinReplicas = params.OtelCol.Spec.MinReplicas + } else { + params.OtelCol.Spec.Autoscaler.MinReplicas = params.OtelCol.Spec.Replicas + } + } + + metrics := []autoscalingv2.MetricSpec{} + + if params.OtelCol.Spec.Autoscaler.TargetMemoryUtilization != nil { + memoryTarget := autoscalingv2.MetricSpec{ + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceMemory, + Target: autoscalingv2.MetricTarget{ + Type: autoscalingv2.UtilizationMetricType, + AverageUtilization: params.OtelCol.Spec.Autoscaler.TargetMemoryUtilization, + }, + }, + } + metrics = append(metrics, memoryTarget) + } + + if params.OtelCol.Spec.Autoscaler.TargetCPUUtilization != nil { + cpuTarget := autoscalingv2.MetricSpec{ + Type: autoscalingv2.ResourceMetricSourceType, + Resource: &autoscalingv2.ResourceMetricSource{ + Name: corev1.ResourceCPU, + Target: autoscalingv2.MetricTarget{ + Type: autoscalingv2.UtilizationMetricType, + AverageUtilization: params.OtelCol.Spec.Autoscaler.TargetCPUUtilization, + }, + }, + } + metrics = append(metrics, cpuTarget) + } + + autoscaler := autoscalingv2.HorizontalPodAutoscaler{ + ObjectMeta: objectMeta, + Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ + ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ + APIVersion: v1alpha1.GroupVersion.String(), + Kind: "OpenTelemetryCollector", + Name: naming.OpenTelemetryCollector(params.OtelCol.Name), + }, + MinReplicas: params.OtelCol.Spec.Autoscaler.MinReplicas, + MaxReplicas: *params.OtelCol.Spec.Autoscaler.MaxReplicas, + Metrics: metrics, + }, + } + if params.OtelCol.Spec.Autoscaler.Behavior != nil { + autoscaler.Spec.Behavior = params.OtelCol.Spec.Autoscaler.Behavior + } + + // convert from v1alpha1.MetricSpec into a autoscalingv2.MetricSpec. + for _, metric := range params.OtelCol.Spec.Autoscaler.Metrics { + if metric.Type == autoscalingv2.PodsMetricSourceType { + v2metric := autoscalingv2.MetricSpec{ + Type: metric.Type, + Pods: metric.Pods, + } + autoscaler.Spec.Metrics = append(autoscaler.Spec.Metrics, v2metric) + } // pod metrics + } + result = &autoscaler + + return result +} diff --git a/internal/manifests/collector/horizontalpodautoscaler_test.go b/internal/manifests/collector/horizontalpodautoscaler_test.go new file mode 100644 index 0000000000..8e92360687 --- /dev/null +++ b/internal/manifests/collector/horizontalpodautoscaler_test.go @@ -0,0 +1,103 @@ +// 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 collector_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + autoscalingv2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + . "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" +) + +func TestHPA(t *testing.T) { + type test struct { + name string + } + v2Test := test{} + tests := []test{v2Test} + + var minReplicas int32 = 3 + var maxReplicas int32 = 5 + var cpuUtilization int32 = 66 + var memoryUtilization int32 = 77 + + otelcols := []v1alpha1.OpenTelemetryCollector{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Autoscaler: &v1alpha1.AutoscalerSpec{ + MinReplicas: &minReplicas, + MaxReplicas: &maxReplicas, + TargetCPUUtilization: &cpuUtilization, + TargetMemoryUtilization: &memoryUtilization, + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + MinReplicas: &minReplicas, + MaxReplicas: &maxReplicas, + Autoscaler: &v1alpha1.AutoscalerSpec{ + TargetCPUUtilization: &cpuUtilization, + TargetMemoryUtilization: &memoryUtilization, + }, + }, + }, + } + + for _, otelcol := range otelcols { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + configuration := config.New() + params := manifests.Params{ + Config: configuration, + OtelCol: otelcol, + Log: logger, + } + raw := HorizontalPodAutoscaler(params) + + hpa := raw.(*autoscalingv2.HorizontalPodAutoscaler) + + // verify + assert.Equal(t, "my-instance-collector", hpa.Name) + assert.Equal(t, "my-instance-collector", hpa.Labels["app.kubernetes.io/name"]) + assert.Equal(t, int32(3), *hpa.Spec.MinReplicas) + assert.Equal(t, int32(5), hpa.Spec.MaxReplicas) + assert.Equal(t, 2, len(hpa.Spec.Metrics)) + + for _, metric := range hpa.Spec.Metrics { + if metric.Resource.Name == corev1.ResourceCPU { + assert.Equal(t, cpuUtilization, *metric.Resource.Target.AverageUtilization) + } else if metric.Resource.Name == corev1.ResourceMemory { + assert.Equal(t, memoryUtilization, *metric.Resource.Target.AverageUtilization) + } + } + }) + } + } + +} diff --git a/internal/manifests/collector/ingress.go b/internal/manifests/collector/ingress.go new file mode 100644 index 0000000000..4bb692ff90 --- /dev/null +++ b/internal/manifests/collector/ingress.go @@ -0,0 +1,170 @@ +// 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 collector + +import ( + "fmt" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func Ingress(params manifests.Params) (*networkingv1.Ingress, error) { + if params.OtelCol.Spec.Ingress.Type != v1alpha1.IngressTypeNginx { + return nil, nil + } + + ports, err := servicePortsFromCfg(params.Log, params.OtelCol) + + // if we have no ports, we don't need a ingress entry + if len(ports) == 0 || err != nil { + params.Log.V(1).Info( + "the instance's configuration didn't yield any ports to open, skipping ingress", + "instance.name", params.OtelCol.Name, + "instance.namespace", params.OtelCol.Namespace, + ) + return nil, err + } + + var rules []networkingv1.IngressRule + switch params.OtelCol.Spec.Ingress.RuleType { + case v1alpha1.IngressRuleTypePath, "": + rules = []networkingv1.IngressRule{createPathIngressRules(params.OtelCol.Name, params.OtelCol.Spec.Ingress.Hostname, ports)} + case v1alpha1.IngressRuleTypeSubdomain: + rules = createSubdomainIngressRules(params.OtelCol.Name, params.OtelCol.Spec.Ingress.Hostname, ports) + } + + return &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Ingress(params.OtelCol.Name), + Namespace: params.OtelCol.Namespace, + Annotations: params.OtelCol.Spec.Ingress.Annotations, + Labels: map[string]string{ + "app.kubernetes.io/name": naming.Ingress(params.OtelCol.Name), + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: networkingv1.IngressSpec{ + TLS: params.OtelCol.Spec.Ingress.TLS, + Rules: rules, + IngressClassName: params.OtelCol.Spec.Ingress.IngressClassName, + }, + }, nil +} + +func createPathIngressRules(otelcol string, hostname string, ports []corev1.ServicePort) networkingv1.IngressRule { + pathType := networkingv1.PathTypePrefix + paths := make([]networkingv1.HTTPIngressPath, len(ports)) + for i, port := range ports { + portName := naming.PortName(port.Name, port.Port) + paths[i] = networkingv1.HTTPIngressPath{ + Path: "/" + port.Name, + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: naming.Service(otelcol), + Port: networkingv1.ServiceBackendPort{ + Name: portName, + }, + }, + }, + } + } + return networkingv1.IngressRule{ + Host: hostname, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: paths, + }, + }, + } +} + +func createSubdomainIngressRules(otelcol string, hostname string, ports []corev1.ServicePort) []networkingv1.IngressRule { + var rules []networkingv1.IngressRule + pathType := networkingv1.PathTypePrefix + for _, port := range ports { + portName := naming.PortName(port.Name, port.Port) + + host := fmt.Sprintf("%s.%s", portName, hostname) + // This should not happen due to validation in the webhook. + if hostname == "" || hostname == "*" { + host = portName + } + rules = append(rules, networkingv1.IngressRule{ + Host: host, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: naming.Service(otelcol), + Port: networkingv1.ServiceBackendPort{ + Name: portName, + }, + }, + }, + }, + }, + }, + }, + }) + } + return rules +} + +func servicePortsFromCfg(logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) ([]corev1.ServicePort, error) { + configFromString, err := adapters.ConfigFromString(otelcol.Spec.Config) + if err != nil { + logger.Error(err, "couldn't extract the configuration from the context") + return nil, err + } + + ports, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeReceiver, configFromString) + if err != nil { + logger.Error(err, "couldn't build the ingress for this instance") + return nil, err + } + + if len(otelcol.Spec.Ports) > 0 { + // we should add all the ports from the CR + // there are two cases where problems might occur: + // 1) when the port number is already being used by a receiver + // 2) same, but for the port name + // + // in the first case, we remove the port we inferred from the list + // in the second case, we rename our inferred port to something like "port-%d" + portNumbers, portNames := extractPortNumbersAndNames(otelcol.Spec.Ports) + var resultingInferredPorts []corev1.ServicePort + for _, inferred := range ports { + if filtered := filterPort(logger, inferred, portNumbers, portNames); filtered != nil { + resultingInferredPorts = append(resultingInferredPorts, *filtered) + } + } + ports = append(otelcol.Spec.Ports, resultingInferredPorts...) + } + return ports, err +} diff --git a/internal/manifests/collector/ingress_test.go b/internal/manifests/collector/ingress_test.go new file mode 100644 index 0000000000..40a3fa4ddc --- /dev/null +++ b/internal/manifests/collector/ingress_test.go @@ -0,0 +1,289 @@ +// 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 collector + +import ( + _ "embed" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" +) + +const testFileIngress = "testdata/ingress_testdata.yaml" + +func TestDesiredIngresses(t *testing.T) { + t.Run("should return nil invalid ingress type", func(t *testing.T) { + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Ingress: v1alpha1.Ingress{ + Type: v1alpha1.IngressType("unknown"), + }, + }, + }, + } + + actual, err := Ingress(params) + assert.Nil(t, actual) + assert.NoError(t, err) + }) + + t.Run("should return nil unable to parse config", func(t *testing.T) { + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Config: "!!!", + Ingress: v1alpha1.Ingress{ + Type: v1alpha1.IngressTypeNginx, + }, + }, + }, + } + + actual, err := Ingress(params) + fmt.Printf("error1: %+v", err) + assert.Nil(t, actual) + assert.ErrorContains(t, err, "couldn't parse the opentelemetry-collector configuration") + }) + + t.Run("should return nil unable to parse receiver ports", func(t *testing.T) { + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Config: "---", + Ingress: v1alpha1.Ingress{ + Type: v1alpha1.IngressTypeNginx, + }, + }, + }, + } + + actual, err := Ingress(params) + fmt.Printf("error2: %+v", err) + assert.Nil(t, actual) + assert.ErrorContains(t, err, "no receivers available as part of the configuration") + }) + + t.Run("path per port", func(t *testing.T) { + var ( + ns = "test" + hostname = "example.com" + ingressClassName = "nginx" + ) + + params, err := newParams("something:tag", testFileIngress) + if err != nil { + t.Fatal(err) + } + + params.OtelCol.Namespace = ns + params.OtelCol.Spec.Ingress = v1alpha1.Ingress{ + Type: v1alpha1.IngressTypeNginx, + Hostname: hostname, + Annotations: map[string]string{"some.key": "some.value"}, + IngressClassName: &ingressClassName, + } + + got, err := Ingress(params) + assert.NoError(t, err) + + pathType := networkingv1.PathTypePrefix + + assert.NotEqual(t, &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Ingress(params.OtelCol.Name), + Namespace: ns, + Annotations: params.OtelCol.Spec.Ingress.Annotations, + Labels: map[string]string{ + "app.kubernetes.io/name": naming.Ingress(params.OtelCol.Name), + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: networkingv1.IngressSpec{ + IngressClassName: &ingressClassName, + Rules: []networkingv1.IngressRule{ + { + Host: hostname, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/another-port", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-collector", + Port: networkingv1.ServiceBackendPort{ + Name: "another-port", + }, + }, + }, + }, + { + Path: "/otlp-grpc", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-collector", + Port: networkingv1.ServiceBackendPort{ + Name: "otlp-grpc", + }, + }, + }, + }, + { + Path: "/otlp-test-grpc", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-collector", + Port: networkingv1.ServiceBackendPort{ + Name: "otlp-test-grpc", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, got) + }) + t.Run("subdomain per port", func(t *testing.T) { + var ( + ns = "test" + hostname = "example.com" + ingressClassName = "nginx" + ) + + params, err := newParams("something:tag", testFileIngress) + if err != nil { + t.Fatal(err) + } + + params.OtelCol.Namespace = ns + params.OtelCol.Spec.Ingress = v1alpha1.Ingress{ + Type: v1alpha1.IngressTypeNginx, + RuleType: v1alpha1.IngressRuleTypeSubdomain, + Hostname: hostname, + Annotations: map[string]string{"some.key": "some.value"}, + IngressClassName: &ingressClassName, + } + + got, err := Ingress(params) + assert.NoError(t, err) + + pathType := networkingv1.PathTypePrefix + + assert.NotEqual(t, &networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Ingress(params.OtelCol.Name), + Namespace: ns, + Annotations: params.OtelCol.Spec.Ingress.Annotations, + Labels: map[string]string{ + "app.kubernetes.io/name": naming.Ingress(params.OtelCol.Name), + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: networkingv1.IngressSpec{ + IngressClassName: &ingressClassName, + Rules: []networkingv1.IngressRule{ + { + Host: "another-port." + hostname, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-collector", + Port: networkingv1.ServiceBackendPort{ + Name: "another-port", + }, + }, + }, + }, + }, + }, + }, + }, + { + Host: "otlp-grpc." + hostname, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-collector", + Port: networkingv1.ServiceBackendPort{ + Name: "otlp-grpc", + }, + }, + }, + }, + }, + }, + }, + }, + { + Host: "otlp-test-grpc." + hostname, + IngressRuleValue: networkingv1.IngressRuleValue{ + HTTP: &networkingv1.HTTPIngressRuleValue{ + Paths: []networkingv1.HTTPIngressPath{ + { + Path: "/", + PathType: &pathType, + Backend: networkingv1.IngressBackend{ + Service: &networkingv1.IngressServiceBackend{ + Name: "test-collector", + Port: networkingv1.ServiceBackendPort{ + Name: "otlp-test-grpc", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, got) + }) +} diff --git a/internal/manifests/collector/parser/exporter/exporter.go b/internal/manifests/collector/parser/exporter/exporter.go new file mode 100644 index 0000000000..93c66b8599 --- /dev/null +++ b/internal/manifests/collector/parser/exporter/exporter.go @@ -0,0 +1,128 @@ +// 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 parser is for parsing the OpenTelemetry Collector configuration. +package exporter + +import ( + "errors" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// registry holds a record of all known exporter parsers. +var registry = make(map[string]parser.Builder) + +// BuilderFor returns a parser builder for the given exporter name. +func BuilderFor(name string) parser.Builder { + return registry[exporterType(name)] +} + +// For returns a new parser for the given exporter name + config. +func For(logger logr.Logger, name string, config map[interface{}]interface{}) (parser.ComponentPortParser, error) { + builder := BuilderFor(name) + if builder == nil { + return nil, fmt.Errorf("no builders for %s", name) + } + return builder(logger, name, config), nil +} + +// Register adds a new parser builder to the list of known builders. +func Register(name string, builder parser.Builder) { + registry[name] = builder +} + +// IsRegistered checks whether a parser is registered with the given name. +func IsRegistered(name string) bool { + _, ok := registry[name] + return ok +} + +var ( + endpointKey = "endpoint" +) + +func singlePortFromConfigEndpoint(logger logr.Logger, name string, config map[interface{}]interface{}) *corev1.ServicePort { + endpoint := getAddressFromConfig(logger, name, endpointKey, config) + + switch e := endpoint.(type) { + case nil: + break + case string: + port, err := portFromEndpoint(e) + if err != nil { + logger.WithValues(endpointKey, e).Error(err, "couldn't parse the endpoint's port") + return nil + } + + return &corev1.ServicePort{ + Name: naming.PortName(name, port), + Port: port, + } + default: + logger.WithValues(endpointKey, endpoint).Error(fmt.Errorf("unrecognized type %T", endpoint), "exporter's endpoint isn't a string") + } + + return nil +} + +func getAddressFromConfig(logger logr.Logger, name, key string, config map[interface{}]interface{}) interface{} { + endpoint, ok := config[key] + if !ok { + logger.V(2).Info("%s exporter doesn't have an %s", name, key) + return nil + } + return endpoint +} + +func portFromEndpoint(endpoint string) (int32, error) { + var err error + var port int64 + + r := regexp.MustCompile(":[0-9]+") + + if r.MatchString(endpoint) { + port, err = strconv.ParseInt(strings.Replace(r.FindString(endpoint), ":", "", -1), 10, 32) + + if err != nil { + return 0, err + } + } + + if port == 0 { + return 0, errors.New("port should not be empty") + } + + return int32(port), err +} + +func exporterType(name string) string { + // exporters have a name like: + // - myexporter/custom + // - myexporter + // we extract the "myexporter" part and see if we have a parser for the exporter + if strings.Contains(name, "/") { + return name[:strings.Index(name, "/")] + } + + return name +} diff --git a/internal/manifests/collector/parser/exporter/exporter_prometheus.go b/internal/manifests/collector/parser/exporter/exporter_prometheus.go new file mode 100644 index 0000000000..61d58588da --- /dev/null +++ b/internal/manifests/collector/parser/exporter/exporter_prometheus.go @@ -0,0 +1,77 @@ +// 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 exporter + +import ( + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +var _ parser.ComponentPortParser = &PrometheusExporterParser{} + +const ( + parserNamePrometheus = "__prometheus" + defaultPrometheusPort = 8888 +) + +// PrometheusExporterParser parses the configuration for OTLP receivers. +type PrometheusExporterParser struct { + config map[interface{}]interface{} + logger logr.Logger + name string +} + +// NewPrometheusExporterParser builds a new parser for OTLP receivers. +func NewPrometheusExporterParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { + return &PrometheusExporterParser{ + logger: logger, + name: name, + config: config, + } +} + +// Ports returns all the service ports for all protocols in this parser. +func (o *PrometheusExporterParser) Ports() ([]corev1.ServicePort, error) { + ports := []corev1.ServicePort{} + if o.config == nil { + ports = append(ports, + corev1.ServicePort{ + Name: naming.PortName(o.name, defaultPrometheusPort), + Port: defaultPrometheusPort, + TargetPort: intstr.FromInt(int(defaultPrometheusPort)), + Protocol: corev1.ProtocolTCP, + }, + ) + } else { + ports = append( + ports, *singlePortFromConfigEndpoint(o.logger, o.name, o.config), + ) + } + + return ports, nil +} + +// ParserName returns the name of this parser. +func (o *PrometheusExporterParser) ParserName() string { + return parserNamePrometheus +} + +func init() { + Register("prometheus", NewPrometheusExporterParser) +} diff --git a/internal/manifests/collector/parser/parser.go b/internal/manifests/collector/parser/parser.go new file mode 100644 index 0000000000..62de283b15 --- /dev/null +++ b/internal/manifests/collector/parser/parser.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import ( + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" +) + +type ComponentPortParser interface { + // Ports returns the service ports parsed based on the exporter's configuration + Ports() ([]corev1.ServicePort, error) + + // ParserName returns the name of this parser + ParserName() string +} + +// Builder specifies the signature required for parser builders. +type Builder func(logr.Logger, string, map[interface{}]interface{}) ComponentPortParser diff --git a/pkg/collector/parser/receiver.go b/internal/manifests/collector/parser/receiver/receiver.go similarity index 65% rename from pkg/collector/parser/receiver.go rename to internal/manifests/collector/parser/receiver/receiver.go index ef950c89b7..97147658f4 100644 --- a/pkg/collector/parser/receiver.go +++ b/internal/manifests/collector/parser/receiver/receiver.go @@ -13,7 +13,7 @@ // limitations under the License. // Package parser is for parsing the OpenTelemetry Collector configuration. -package parser +package receiver import ( "errors" @@ -25,30 +25,16 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" -) -var ( - // DNS_LABEL constraints: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names - dnsLabelValidation = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$") + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) -// ReceiverParser is an interface that should be implemented by all receiver parsers. -type ReceiverParser interface { - // Ports returns the service ports parsed based on the receiver's configuration - Ports() ([]corev1.ServicePort, error) - - // ParserName returns the name of this parser - ParserName() string -} - -// Builder specifies the signature required for parser builders. -type Builder func(logr.Logger, string, map[interface{}]interface{}) ReceiverParser - -// registry holds a record of all known parsers. -var registry = make(map[string]Builder) +// registry holds a record of all known receiver parsers. +var registry = make(map[string]parser.Builder) // BuilderFor returns a parser builder for the given receiver name. -func BuilderFor(name string) Builder { +func BuilderFor(name string) parser.Builder { builder := registry[receiverType(name)] if builder == nil { builder = NewGenericReceiverParser @@ -58,13 +44,13 @@ func BuilderFor(name string) Builder { } // For returns a new parser for the given receiver name + config. -func For(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func For(logger logr.Logger, name string, config map[interface{}]interface{}) (parser.ComponentPortParser, error) { builder := BuilderFor(name) - return builder(logger, name, config) + return builder(logger, name, config), nil } // Register adds a new parser builder to the list of known builders. -func Register(name string, builder Builder) { +func Register(name string, builder parser.Builder) { registry[name] = builder } @@ -77,8 +63,45 @@ func IsRegistered(name string) bool { var ( endpointKey = "endpoint" listenAddressKey = "listen_address" + scraperReceivers = map[string]struct{}{ + "prometheus": {}, + "kubeletstats": {}, + "sshcheck": {}, + "cloudfoundry": {}, + "vcenter": {}, + "oracledb": {}, + "snmp": {}, + "googlecloudpubsub": {}, + "chrony": {}, + "jmx": {}, + "podman_stats": {}, + "pulsar": {}, + "docker_stats": {}, + "aerospike": {}, + "zookeeper": {}, + "prometheus_simple": {}, + "saphana": {}, + "riak": {}, + "redis": {}, + "rabbitmq": {}, + "purefb": {}, + "postgresql": {}, + "nsxt": {}, + "nginx": {}, + "mysql": {}, + "memcached": {}, + "httpcheck": {}, + "haproxy": {}, + "flinkmetrics": {}, + "couchdb": {}, + } ) +func isScraperReceiver(name string) bool { + _, exists := scraperReceivers[name] + return exists +} + func singlePortFromConfigEndpoint(logger logr.Logger, name string, config map[interface{}]interface{}) *v1.ServicePort { var endpoint interface{} switch { @@ -101,34 +124,32 @@ func singlePortFromConfigEndpoint(logger logr.Logger, name string, config map[in case name == "tcplog" || name == "udplog": endpoint = getAddressFromConfig(logger, name, listenAddressKey, config) - // ignore kubeletstats receiver as it holds the field key endpoint, and it + // ignore the receiver as it holds the field key endpoint, and it // is a scraper, we only expose endpoint through k8s service objects for // receivers that aren't scrapers. - case name == "kubeletstats": - return nil - - // ignore prometheus receiver as it has no listening endpoint - case name == "prometheus": + case isScraperReceiver(name): return nil default: endpoint = getAddressFromConfig(logger, name, endpointKey, config) } - switch endpoint := endpoint.(type) { + switch e := endpoint.(type) { + case nil: + break case string: - port, err := portFromEndpoint(endpoint) + port, err := portFromEndpoint(e) if err != nil { - logger.WithValues(endpointKey, endpoint).Info("couldn't parse the endpoint's port") + logger.WithValues(endpointKey, e).Error(err, "couldn't parse the endpoint's port") return nil } return &corev1.ServicePort{ - Name: portName(name, port), + Name: naming.PortName(name, port), Port: port, } default: - logger.Info("receiver's endpoint isn't a string") + logger.WithValues(endpointKey, endpoint).Error(fmt.Errorf("unrecognized type %T", endpoint), "receiver's endpoint isn't a string") } return nil @@ -143,22 +164,6 @@ func getAddressFromConfig(logger logr.Logger, name, key string, config map[inter return endpoint } -func portName(receiverName string, port int32) string { - if len(receiverName) > 63 { - return fmt.Sprintf("port-%d", port) - } - - candidate := strings.ReplaceAll(receiverName, "/", "-") - candidate = strings.ReplaceAll(candidate, "_", "-") - - if !dnsLabelValidation.MatchString(candidate) { - return fmt.Sprintf("port-%d", port) - } - - // matches the pattern and has less than 63 chars -- the candidate name is good to go! - return candidate -} - func portFromEndpoint(endpoint string) (int32, error) { var err error var port int64 @@ -174,7 +179,7 @@ func portFromEndpoint(endpoint string) (int32, error) { } if port == 0 { - return 0, errors.New("Port should not be empty") + return 0, errors.New("port should not be empty") } return int32(port), err diff --git a/pkg/collector/parser/receiver_aws-xray.go b/internal/manifests/collector/parser/receiver/receiver_aws-xray.go similarity index 83% rename from pkg/collector/parser/receiver_aws-xray.go rename to internal/manifests/collector/parser/receiver/receiver_aws-xray.go index 6d8f92c7bb..44618b3aa5 100644 --- a/pkg/collector/parser/receiver_aws-xray.go +++ b/internal/manifests/collector/parser/receiver/receiver_aws-xray.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameAWSXRAY = "__awsxray" // NewAWSXrayReceiverParser builds a new parser for AWS xray receivers, from the contrib repository. -func NewAWSXrayReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewAWSXrayReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_aws-xray_test.go b/internal/manifests/collector/parser/receiver/receiver_aws-xray_test.go similarity index 97% rename from pkg/collector/parser/receiver_aws-xray_test.go rename to internal/manifests/collector/parser/receiver/receiver_aws-xray_test.go index bbd752f080..5a99f9e893 100644 --- a/pkg/collector/parser/receiver_aws-xray_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_aws-xray_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the AWS XRAY parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_carbon.go b/internal/manifests/collector/parser/receiver/receiver_carbon.go similarity index 83% rename from pkg/collector/parser/receiver_carbon.go rename to internal/manifests/collector/parser/receiver/receiver_carbon.go index 26f073ebe8..b1b0aaa4ce 100644 --- a/pkg/collector/parser/receiver_carbon.go +++ b/internal/manifests/collector/parser/receiver/receiver_carbon.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameCarbon = "__carbon" // NewCarbonReceiverParser builds a new parser for Carbon receivers, from the contrib repository. -func NewCarbonReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewCarbonReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_carbon_test.go b/internal/manifests/collector/parser/receiver/receiver_carbon_test.go similarity index 97% rename from pkg/collector/parser/receiver_carbon_test.go rename to internal/manifests/collector/parser/receiver/receiver_carbon_test.go index 22a5cb9273..370732182d 100644 --- a/pkg/collector/parser/receiver_carbon_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_carbon_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the Carbon parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_collectd.go b/internal/manifests/collector/parser/receiver/receiver_collectd.go similarity index 83% rename from pkg/collector/parser/receiver_collectd.go rename to internal/manifests/collector/parser/receiver/receiver_collectd.go index 9426399a45..aa42ab0397 100644 --- a/pkg/collector/parser/receiver_collectd.go +++ b/internal/manifests/collector/parser/receiver/receiver_collectd.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameCollectd = "__collectd" // NewCollectdReceiverParser builds a new parser for Collectd receivers, from the contrib repository. -func NewCollectdReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewCollectdReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_collectd_test.go b/internal/manifests/collector/parser/receiver/receiver_collectd_test.go similarity index 97% rename from pkg/collector/parser/receiver_collectd_test.go rename to internal/manifests/collector/parser/receiver/receiver_collectd_test.go index fe63c75cf5..5e0e6d0121 100644 --- a/pkg/collector/parser/receiver_collectd_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_collectd_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the Collectd parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_fluent-forward.go b/internal/manifests/collector/parser/receiver/receiver_fluent-forward.go similarity index 83% rename from pkg/collector/parser/receiver_fluent-forward.go rename to internal/manifests/collector/parser/receiver/receiver_fluent-forward.go index 94170fa051..88881923fa 100644 --- a/pkg/collector/parser/receiver_fluent-forward.go +++ b/internal/manifests/collector/parser/receiver/receiver_fluent-forward.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameFluentForward = "__fluentforward" // NewFluentForwardReceiverParser builds a new parser for FluentForward receivers, from the contrib repository. -func NewFluentForwardReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewFluentForwardReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_fluent-forward_test.go b/internal/manifests/collector/parser/receiver/receiver_fluent-forward_test.go similarity index 97% rename from pkg/collector/parser/receiver_fluent-forward_test.go rename to internal/manifests/collector/parser/receiver/receiver_fluent-forward_test.go index ee8959260f..af15006983 100644 --- a/pkg/collector/parser/receiver_fluent-forward_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_fluent-forward_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the FluentForward parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_generic.go b/internal/manifests/collector/parser/receiver/receiver_generic.go similarity index 86% rename from pkg/collector/parser/receiver_generic.go rename to internal/manifests/collector/parser/receiver/receiver_generic.go index 21b4e2ce53..864b5bb111 100644 --- a/pkg/collector/parser/receiver_generic.go +++ b/internal/manifests/collector/parser/receiver/receiver_generic.go @@ -12,16 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) const parserNameGeneric = "__generic" -var _ ReceiverParser = &GenericReceiver{} +var _ parser.ComponentPortParser = &GenericReceiver{} // GenericReceiver is a special parser for generic receivers. It doesn't self-register and should be created/used directly. type GenericReceiver struct { @@ -38,7 +41,7 @@ type GenericReceiver struct { // so that it can expose the required port based on the receiver's config. Receiver scrapers are ignored. // NewGenericReceiverParser builds a new parser for generic receivers. -func NewGenericReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewGenericReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, @@ -59,7 +62,7 @@ func (g *GenericReceiver) Ports() ([]corev1.ServicePort, error) { if g.defaultPort > 0 { return []corev1.ServicePort{{ Port: g.defaultPort, - Name: portName(g.name, g.defaultPort), + Name: naming.PortName(g.name, g.defaultPort), Protocol: g.defaultProtocol, AppProtocol: g.defaultAppProtocol, }}, nil diff --git a/pkg/collector/parser/receiver_generic_test.go b/internal/manifests/collector/parser/receiver/receiver_generic_test.go similarity index 64% rename from pkg/collector/parser/receiver_generic_test.go rename to internal/manifests/collector/parser/receiver/receiver_generic_test.go index 15fd903591..1adfcf12df 100644 --- a/pkg/collector/parser/receiver_generic_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_generic_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser_test +package receiver_test import ( "testing" @@ -21,7 +21,8 @@ import ( "github.com/stretchr/testify/assert" logf "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/receiver" ) var logger = logf.Log.WithName("unit-tests") @@ -29,7 +30,7 @@ var logger = logf.Log.WithName("unit-tests") func TestParseEndpoint(t *testing.T) { // prepare // there's no parser registered to handle "myreceiver", so, it falls back to the generic parser - builder := parser.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ + builder := receiver.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ "endpoint": "0.0.0.0:1234", }) @@ -45,7 +46,7 @@ func TestParseEndpoint(t *testing.T) { func TestFailedToParseEndpoint(t *testing.T) { // prepare // there's no parser registered to handle "myreceiver", so, it falls back to the generic parser - builder := parser.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ + builder := receiver.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ "endpoint": "0.0.0.0", }) @@ -59,27 +60,27 @@ func TestFailedToParseEndpoint(t *testing.T) { func TestDownstreamParsers(t *testing.T) { for _, tt := range []struct { - builder func(logr.Logger, string, map[interface{}]interface{}) parser.ReceiverParser + builder func(logr.Logger, string, map[interface{}]interface{}) parser.ComponentPortParser desc string receiverName string parserName string defaultPort int }{ - {parser.NewZipkinReceiverParser, "zipkin", "zipkin", "__zipkin", 9411}, - {parser.NewOpenCensusReceiverParser, "opencensus", "opencensus", "__opencensus", 55678}, + {receiver.NewZipkinReceiverParser, "zipkin", "zipkin", "__zipkin", 9411}, + {receiver.NewOpenCensusReceiverParser, "opencensus", "opencensus", "__opencensus", 55678}, // contrib receivers - {parser.NewCarbonReceiverParser, "carbon", "carbon", "__carbon", 2003}, - {parser.NewCollectdReceiverParser, "collectd", "collectd", "__collectd", 8081}, - {parser.NewSAPMReceiverParser, "sapm", "sapm", "__sapm", 7276}, - {parser.NewSignalFxReceiverParser, "signalfx", "signalfx", "__signalfx", 9943}, - {parser.NewWavefrontReceiverParser, "wavefront", "wavefront", "__wavefront", 2003}, - {parser.NewZipkinScribeReceiverParser, "zipkin-scribe", "zipkin-scribe", "__zipkinscribe", 9410}, - {parser.NewFluentForwardReceiverParser, "fluentforward", "fluentforward", "__fluentforward", 8006}, - {parser.NewStatsdReceiverParser, "statsd", "statsd", "__statsd", 8125}, - {parser.NewInfluxdbReceiverParser, "influxdb", "influxdb", "__influxdb", 8086}, - {parser.NewSplunkHecReceiverParser, "splunk-hec", "splunk-hec", "__splunk_hec", 8088}, - {parser.NewAWSXrayReceiverParser, "awsxray", "awsxray", "__awsxray", 2000}, + {receiver.NewCarbonReceiverParser, "carbon", "carbon", "__carbon", 2003}, + {receiver.NewCollectdReceiverParser, "collectd", "collectd", "__collectd", 8081}, + {receiver.NewSAPMReceiverParser, "sapm", "sapm", "__sapm", 7276}, + {receiver.NewSignalFxReceiverParser, "signalfx", "signalfx", "__signalfx", 9943}, + {receiver.NewWavefrontReceiverParser, "wavefront", "wavefront", "__wavefront", 2003}, + {receiver.NewZipkinScribeReceiverParser, "zipkin-scribe", "zipkin-scribe", "__zipkinscribe", 9410}, + {receiver.NewFluentForwardReceiverParser, "fluentforward", "fluentforward", "__fluentforward", 8006}, + {receiver.NewStatsdReceiverParser, "statsd", "statsd", "__statsd", 8125}, + {receiver.NewInfluxdbReceiverParser, "influxdb", "influxdb", "__influxdb", 8086}, + {receiver.NewSplunkHecReceiverParser, "splunk-hec", "splunk-hec", "__splunk_hec", 8088}, + {receiver.NewAWSXrayReceiverParser, "awsxray", "awsxray", "__awsxray", 2000}, } { t.Run(tt.receiverName, func(t *testing.T) { t.Run("builds successfully", func(t *testing.T) { diff --git a/pkg/collector/parser/receiver_influxdb.go b/internal/manifests/collector/parser/receiver/receiver_influxdb.go similarity index 83% rename from pkg/collector/parser/receiver_influxdb.go rename to internal/manifests/collector/parser/receiver/receiver_influxdb.go index 55e393f2ff..0930a29f73 100644 --- a/pkg/collector/parser/receiver_influxdb.go +++ b/internal/manifests/collector/parser/receiver/receiver_influxdb.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameInfluxdb = "__influxdb" // NewInfluxdbReceiverParser builds a new parser for Influxdb receivers, from the contrib repository. -func NewInfluxdbReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewInfluxdbReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_influxdb_test.go b/internal/manifests/collector/parser/receiver/receiver_influxdb_test.go similarity index 97% rename from pkg/collector/parser/receiver_influxdb_test.go rename to internal/manifests/collector/parser/receiver/receiver_influxdb_test.go index 8597204488..94f596ee06 100644 --- a/pkg/collector/parser/receiver_influxdb_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_influxdb_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the Influxdb parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_jaeger.go b/internal/manifests/collector/parser/receiver/receiver_jaeger.go similarity index 91% rename from pkg/collector/parser/receiver_jaeger.go rename to internal/manifests/collector/parser/receiver/receiver_jaeger.go index 428938c313..dcc99d8bd9 100644 --- a/pkg/collector/parser/receiver_jaeger.go +++ b/internal/manifests/collector/parser/receiver/receiver_jaeger.go @@ -12,16 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver import ( "fmt" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) -var _ ReceiverParser = &JaegerReceiverParser{} +var _ parser.ComponentPortParser = &JaegerReceiverParser{} const ( parserNameJaeger = "__jaeger" @@ -40,7 +43,7 @@ type JaegerReceiverParser struct { } // NewJaegerReceiverParser builds a new parser for Jaeger receivers. -func NewJaegerReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewJaegerReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { if protocols, ok := config["protocols"].(map[interface{}]interface{}); ok { return &JaegerReceiverParser{ logger: logger, @@ -104,7 +107,7 @@ func (j *JaegerReceiverParser) Ports() ([]corev1.ServicePort, error) { // if not, we use the default port if protocolPort == nil { protocolPort = &corev1.ServicePort{ - Name: portName(nameWithProtocol, protocol.defaultPort), + Name: naming.PortName(nameWithProtocol, protocol.defaultPort), Port: protocol.defaultPort, } } diff --git a/pkg/collector/parser/receiver_jaeger_test.go b/internal/manifests/collector/parser/receiver/receiver_jaeger_test.go similarity index 87% rename from pkg/collector/parser/receiver_jaeger_test.go rename to internal/manifests/collector/parser/receiver/receiver_jaeger_test.go index 827c988c4a..dadf3e0cb5 100644 --- a/pkg/collector/parser/receiver_jaeger_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_jaeger_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver import ( "testing" @@ -28,7 +28,8 @@ func TestJaegerSelfRegisters(t *testing.T) { func TestJaegerIsFoundByName(t *testing.T) { // test - p := For(logger, "jaeger", map[interface{}]interface{}{}) + p, err := For(logger, "jaeger", map[interface{}]interface{}{}) + assert.NoError(t, err) // verify assert.Equal(t, "__jaeger", p.ParserName()) @@ -88,10 +89,10 @@ func TestJaegerExposeDefaultPorts(t *testing.T) { portNumber int32 seen bool }{ - "jaeger-grpc": {portNumber: 14250, transportProtocol: corev1.ProtocolTCP}, - "jaeger-thrift-http": {portNumber: 14268, transportProtocol: corev1.ProtocolTCP}, - "jaeger-thrift-compact": {portNumber: 6831, transportProtocol: corev1.ProtocolUDP}, - "jaeger-thrift-binary": {portNumber: 6832, transportProtocol: corev1.ProtocolUDP}, + "jaeger-grpc": {portNumber: 14250, transportProtocol: corev1.ProtocolTCP}, + "port-14268": {portNumber: 14268, transportProtocol: corev1.ProtocolTCP}, + "port-6831": {portNumber: 6831, transportProtocol: corev1.ProtocolUDP}, + "port-6832": {portNumber: 6832, transportProtocol: corev1.ProtocolUDP}, } // test diff --git a/pkg/collector/parser/receiver_oc.go b/internal/manifests/collector/parser/receiver/receiver_oc.go similarity index 82% rename from pkg/collector/parser/receiver_oc.go rename to internal/manifests/collector/parser/receiver/receiver_oc.go index 5deb0fe5fc..3463a8d398 100644 --- a/pkg/collector/parser/receiver_oc.go +++ b/internal/manifests/collector/parser/receiver/receiver_oc.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameOpenCensus = "__opencensus" // NewOpenCensusReceiverParser builds a new parser for OpenCensus receivers. -func NewOpenCensusReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewOpenCensusReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_oc_test.go b/internal/manifests/collector/parser/receiver/receiver_oc_test.go similarity index 97% rename from pkg/collector/parser/receiver_oc_test.go rename to internal/manifests/collector/parser/receiver/receiver_oc_test.go index b14b4f9b10..74b6062427 100644 --- a/pkg/collector/parser/receiver_oc_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_oc_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the OpenCensus parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_otlp.go b/internal/manifests/collector/parser/receiver/receiver_otlp.go similarity index 83% rename from pkg/collector/parser/receiver_otlp.go rename to internal/manifests/collector/parser/receiver/receiver_otlp.go index 2fa208fe94..68972fd950 100644 --- a/pkg/collector/parser/receiver_otlp.go +++ b/internal/manifests/collector/parser/receiver/receiver_otlp.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver import ( "fmt" @@ -20,16 +20,18 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) -var _ ReceiverParser = &OTLPReceiverParser{} +var _ parser.ComponentPortParser = &OTLPReceiverParser{} const ( parserNameOTLP = "__otlp" - defaultOTLPGRPCPort int32 = 4317 - defaultOTLPHTTPLegacyPort int32 = 55681 - defaultOTLPHTTPPort int32 = 4318 + defaultOTLPGRPCPort int32 = 4317 + defaultOTLPHTTPPort int32 = 4318 ) var ( @@ -45,7 +47,7 @@ type OTLPReceiverParser struct { } // NewOTLPReceiverParser builds a new parser for OTLP receivers. -func NewOTLPReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewOTLPReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { if protocols, ok := config["protocols"].(map[interface{}]interface{}); ok { return &OTLPReceiverParser{ logger: logger, @@ -72,7 +74,7 @@ func (o *OTLPReceiverParser) Ports() ([]corev1.ServicePort, error) { name: grpc, defaultPorts: []corev1.ServicePort{ { - Name: portName(fmt.Sprintf("%s-grpc", o.name), defaultOTLPGRPCPort), + Name: naming.PortName(fmt.Sprintf("%s-grpc", o.name), defaultOTLPGRPCPort), Port: defaultOTLPGRPCPort, TargetPort: intstr.FromInt(int(defaultOTLPGRPCPort)), AppProtocol: &grpc, @@ -83,17 +85,11 @@ func (o *OTLPReceiverParser) Ports() ([]corev1.ServicePort, error) { name: http, defaultPorts: []corev1.ServicePort{ { - Name: portName(fmt.Sprintf("%s-http", o.name), defaultOTLPHTTPPort), + Name: naming.PortName(fmt.Sprintf("%s-http", o.name), defaultOTLPHTTPPort), Port: defaultOTLPHTTPPort, TargetPort: intstr.FromInt(int(defaultOTLPHTTPPort)), AppProtocol: &http, }, - { - Name: portName(fmt.Sprintf("%s-http-legacy", o.name), defaultOTLPHTTPLegacyPort), - Port: defaultOTLPHTTPLegacyPort, - TargetPort: intstr.FromInt(int(defaultOTLPHTTPPort)), // we target the official port, not the legacy - AppProtocol: &http, - }, }, }, } { diff --git a/pkg/collector/parser/receiver_otlp_test.go b/internal/manifests/collector/parser/receiver/receiver_otlp_test.go similarity index 92% rename from pkg/collector/parser/receiver_otlp_test.go rename to internal/manifests/collector/parser/receiver/receiver_otlp_test.go index 6abca3b5d1..7165153dde 100644 --- a/pkg/collector/parser/receiver_otlp_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_otlp_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver import ( "testing" @@ -27,7 +27,8 @@ func TestOTLPSelfRegisters(t *testing.T) { func TestOTLPIsFoundByName(t *testing.T) { // test - p := For(logger, "otlp", map[interface{}]interface{}{}) + p, err := For(logger, "otlp", map[interface{}]interface{}{}) + assert.NoError(t, err) // verify assert.Equal(t, "__otlp", p.ParserName()) @@ -85,9 +86,8 @@ func TestOTLPExposeDefaultPorts(t *testing.T) { portNumber int32 seen bool }{ - "otlp-grpc": {portNumber: 4317}, - "otlp-http": {portNumber: 4318}, - "otlp-http-legacy": {portNumber: 55681}, + "otlp-grpc": {portNumber: 4317}, + "otlp-http": {portNumber: 4318}, } // test diff --git a/pkg/collector/parser/receiver_sapm.go b/internal/manifests/collector/parser/receiver/receiver_sapm.go similarity index 82% rename from pkg/collector/parser/receiver_sapm.go rename to internal/manifests/collector/parser/receiver/receiver_sapm.go index a9b8b574c5..924b88bff5 100644 --- a/pkg/collector/parser/receiver_sapm.go +++ b/internal/manifests/collector/parser/receiver/receiver_sapm.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameSAPM = "__sapm" // NewSAPMReceiverParser builds a new parser for SAPM receivers, from the contrib repository. -func NewSAPMReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewSAPMReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_sapm_test.go b/internal/manifests/collector/parser/receiver/receiver_sapm_test.go similarity index 97% rename from pkg/collector/parser/receiver_sapm_test.go rename to internal/manifests/collector/parser/receiver/receiver_sapm_test.go index 6a0c3de594..0940086266 100644 --- a/pkg/collector/parser/receiver_sapm_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_sapm_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the SAPM parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_signalfx.go b/internal/manifests/collector/parser/receiver/receiver_signalfx.go similarity index 83% rename from pkg/collector/parser/receiver_signalfx.go rename to internal/manifests/collector/parser/receiver/receiver_signalfx.go index f4140c4701..549e802453 100644 --- a/pkg/collector/parser/receiver_signalfx.go +++ b/internal/manifests/collector/parser/receiver/receiver_signalfx.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameSignalFx = "__signalfx" // NewSignalFxReceiverParser builds a new parser for SignalFx receivers, from the contrib repository. -func NewSignalFxReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewSignalFxReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_signalfx_test.go b/internal/manifests/collector/parser/receiver/receiver_signalfx_test.go similarity index 97% rename from pkg/collector/parser/receiver_signalfx_test.go rename to internal/manifests/collector/parser/receiver/receiver_signalfx_test.go index cd196ee5d0..eaee10189f 100644 --- a/pkg/collector/parser/receiver_signalfx_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_signalfx_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the SignalFx parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/parser/receiver/receiver_skywalking.go b/internal/manifests/collector/parser/receiver/receiver_skywalking.go new file mode 100644 index 0000000000..9b72847ee9 --- /dev/null +++ b/internal/manifests/collector/parser/receiver/receiver_skywalking.go @@ -0,0 +1,131 @@ +// 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 receiver + +import ( + "fmt" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +var _ parser.ComponentPortParser = &SkywalkingReceiverParser{} + +const ( + parserNameSkywalking = "__skywalking" + + defaultSkywalkingGRPCPort int32 = 11800 + defaultSkywalkingHTTPPort int32 = 12800 +) + +// SkywalkingReceiverParser parses the configuration for Skywalking receivers. +type SkywalkingReceiverParser struct { + config map[interface{}]interface{} + logger logr.Logger + name string +} + +// NewSkywalkingReceiverParser builds a new parser for Skywalking receivers. +func NewSkywalkingReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { + if protocols, ok := config["protocols"].(map[interface{}]interface{}); ok { + return &SkywalkingReceiverParser{ + logger: logger, + name: name, + config: protocols, + } + } + + return &SkywalkingReceiverParser{ + name: name, + config: map[interface{}]interface{}{}, + } +} + +// Ports returns all the service ports for all protocols in this parser. +func (o *SkywalkingReceiverParser) Ports() ([]corev1.ServicePort, error) { + ports := []corev1.ServicePort{} + + for _, protocol := range []struct { + name string + defaultPorts []corev1.ServicePort + }{ + { + name: grpc, + defaultPorts: []corev1.ServicePort{ + { + Name: naming.PortName(fmt.Sprintf("%s-grpc", o.name), defaultSkywalkingGRPCPort), + Port: defaultSkywalkingGRPCPort, + TargetPort: intstr.FromInt(int(defaultSkywalkingGRPCPort)), + AppProtocol: &grpc, + }, + }, + }, + { + name: http, + defaultPorts: []corev1.ServicePort{ + { + Name: naming.PortName(fmt.Sprintf("%s-http", o.name), defaultSkywalkingHTTPPort), + Port: defaultSkywalkingHTTPPort, + TargetPort: intstr.FromInt(int(defaultSkywalkingHTTPPort)), + AppProtocol: &http, + }, + }, + }, + } { + // do we have the protocol specified at all? + if receiverProtocol, ok := o.config[protocol.name]; ok { + // we have the specified protocol, we definitely need a service port + nameWithProtocol := fmt.Sprintf("%s-%s", o.name, protocol.name) + var protocolPort *corev1.ServicePort + + // do we have a configuration block for the protocol? + settings, ok := receiverProtocol.(map[interface{}]interface{}) + if ok { + protocolPort = singlePortFromConfigEndpoint(o.logger, nameWithProtocol, settings) + } + + // have we parsed a port based on the configuration block? + // if not, we use the default port + if protocolPort == nil { + ports = append(ports, protocol.defaultPorts...) + } else { + // infer protocol and appProtocol from protocol.name + if protocol.name == grpc { + protocolPort.Protocol = corev1.ProtocolTCP + protocolPort.AppProtocol = &grpc + } else if protocol.name == http { + protocolPort.Protocol = corev1.ProtocolTCP + protocolPort.AppProtocol = &http + } + ports = append(ports, *protocolPort) + } + } + } + + return ports, nil +} + +// ParserName returns the name of this parser. +func (o *SkywalkingReceiverParser) ParserName() string { + return parserNameSkywalking +} + +func init() { + Register("skywalking", NewSkywalkingReceiverParser) +} diff --git a/internal/manifests/collector/parser/receiver/receiver_skywalking_test.go b/internal/manifests/collector/parser/receiver/receiver_skywalking_test.go new file mode 100644 index 0000000000..ab00c852c2 --- /dev/null +++ b/internal/manifests/collector/parser/receiver/receiver_skywalking_test.go @@ -0,0 +1,109 @@ +// 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 receiver + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSkywalkingSelfRegisters(t *testing.T) { + // verify + assert.True(t, IsRegistered("skywalking")) +} + +func TestSkywalkingIsFoundByName(t *testing.T) { + // test + p, err := For(logger, "skywalking", map[interface{}]interface{}{}) + assert.NoError(t, err) + + // verify + assert.Equal(t, "__skywalking", p.ParserName()) +} + +func TestSkywalkingPortsOverridden(t *testing.T) { + // prepare + builder := NewSkywalkingReceiverParser(logger, "skywalking", map[interface{}]interface{}{ + "protocols": map[interface{}]interface{}{ + "grpc": map[interface{}]interface{}{ + "endpoint": "0.0.0.0:1234", + }, + "http": map[interface{}]interface{}{ + "endpoint": "0.0.0.0:1235", + }, + }, + }) + + expectedResults := map[string]struct { + portNumber int32 + seen bool + }{ + "skywalking-grpc": {portNumber: 1234}, + "skywalking-http": {portNumber: 1235}, + } + + // test + ports, err := builder.Ports() + + // verify + assert.NoError(t, err) + assert.Len(t, ports, len(expectedResults)) + + for _, port := range ports { + r := expectedResults[port.Name] + r.seen = true + expectedResults[port.Name] = r + assert.EqualValues(t, r.portNumber, port.Port) + } + for k, v := range expectedResults { + assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) + } +} + +func TestSkywalkingExposeDefaultPorts(t *testing.T) { + // prepare + builder := NewSkywalkingReceiverParser(logger, "skywalking", map[interface{}]interface{}{ + "protocols": map[interface{}]interface{}{ + "grpc": map[interface{}]interface{}{}, + "http": map[interface{}]interface{}{}, + }, + }) + + expectedResults := map[string]struct { + portNumber int32 + seen bool + }{ + "skywalking-grpc": {portNumber: 11800}, + "skywalking-http": {portNumber: 12800}, + } + + // test + ports, err := builder.Ports() + + // verify + assert.NoError(t, err) + assert.Len(t, ports, len(expectedResults)) + + for _, port := range ports { + r := expectedResults[port.Name] + r.seen = true + expectedResults[port.Name] = r + assert.EqualValues(t, r.portNumber, port.Port) + } + for k, v := range expectedResults { + assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) + } +} diff --git a/pkg/collector/parser/receiver_splunk-hec.go b/internal/manifests/collector/parser/receiver/receiver_splunk-hec.go similarity index 83% rename from pkg/collector/parser/receiver_splunk-hec.go rename to internal/manifests/collector/parser/receiver/receiver_splunk-hec.go index 09f75a123e..676415334b 100644 --- a/pkg/collector/parser/receiver_splunk-hec.go +++ b/internal/manifests/collector/parser/receiver/receiver_splunk-hec.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameSplunkHec = "__splunk_hec" // NewSplunkHecReceiverParser builds a new parser for Splunk Hec receivers, from the contrib repository. -func NewSplunkHecReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewSplunkHecReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_splunk-hec_test.go b/internal/manifests/collector/parser/receiver/receiver_splunk-hec_test.go similarity index 97% rename from pkg/collector/parser/receiver_splunk-hec_test.go rename to internal/manifests/collector/parser/receiver/receiver_splunk-hec_test.go index 3ffc6c0ee0..e6f99f7852 100644 --- a/pkg/collector/parser/receiver_splunk-hec_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_splunk-hec_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the Splunk Hec parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_statsd.go b/internal/manifests/collector/parser/receiver/receiver_statsd.go similarity index 68% rename from pkg/collector/parser/receiver_statsd.go rename to internal/manifests/collector/parser/receiver/receiver_statsd.go index c8603c26d7..0f41520e22 100644 --- a/pkg/collector/parser/receiver_statsd.go +++ b/internal/manifests/collector/parser/receiver/receiver_statsd.go @@ -12,20 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameStatsd = "__statsd" // NewStatsdReceiverParser builds a new parser for Statsd receivers, from the contrib repository. -func NewStatsdReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewStatsdReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ - logger: logger, - name: name, - config: config, - defaultPort: 8125, - parserName: parserNameStatsd, + logger: logger, + name: name, + config: config, + defaultPort: 8125, + defaultProtocol: corev1.ProtocolUDP, + parserName: parserNameStatsd, } } diff --git a/pkg/collector/parser/receiver_statsd_test.go b/internal/manifests/collector/parser/receiver/receiver_statsd_test.go similarity index 97% rename from pkg/collector/parser/receiver_statsd_test.go rename to internal/manifests/collector/parser/receiver/receiver_statsd_test.go index 0ab1519352..338971ba44 100644 --- a/pkg/collector/parser/receiver_statsd_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_statsd_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the Statsd parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_test.go b/internal/manifests/collector/parser/receiver/receiver_test.go similarity index 82% rename from pkg/collector/parser/receiver_test.go rename to internal/manifests/collector/parser/receiver/receiver_test.go index 2566f7a7be..44cb04519d 100644 --- a/pkg/collector/parser/receiver_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver import ( "testing" @@ -21,6 +21,9 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) var logger = logf.Log.WithName("unit-tests") @@ -38,7 +41,7 @@ func TestReceiverPortNames(t *testing.T) { {"name starting with invalid char", "-my-receiver", "port-123", 123}, } { t.Run(tt.desc, func(t *testing.T) { - assert.Equal(t, tt.expected, portName(tt.candidate, int32(tt.port))) + assert.Equal(t, tt.expected, naming.PortName(tt.candidate, int32(tt.port))) }) } } @@ -116,7 +119,8 @@ func TestIgnorekubeletstatsEndpoint(t *testing.T) { func TestReceiverFallbackWhenNotRegistered(t *testing.T) { // test - p := For(logger, "myreceiver", map[interface{}]interface{}{}) + p, err := For(logger, "myreceiver", map[interface{}]interface{}{}) + assert.NoError(t, err) // test assert.Equal(t, "__generic", p.ParserName()) @@ -125,13 +129,13 @@ func TestReceiverFallbackWhenNotRegistered(t *testing.T) { func TestReceiverShouldFindRegisteredParser(t *testing.T) { // prepare builderCalled := false - Register("mock", func(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { + Register("mock", func(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { builderCalled = true return &mockParser{} }) // test - For(logger, "mock", map[interface{}]interface{}{}) + _, _ = For(logger, "mock", map[interface{}]interface{}{}) // verify assert.True(t, builderCalled) @@ -147,3 +151,14 @@ func (m *mockParser) Ports() ([]corev1.ServicePort, error) { func (m *mockParser) ParserName() string { return "__mock" } + +func TestSkipPortsForScrapers(t *testing.T) { + for receiver := range scraperReceivers { + builder := NewGenericReceiverParser(logger, receiver, map[interface{}]interface{}{ + "endpoint": "0.0.0.0:42069", + }) + ports, err := builder.Ports() + assert.NoError(t, err) + assert.Len(t, ports, 0) + } +} diff --git a/pkg/collector/parser/receiver_wavefront.go b/internal/manifests/collector/parser/receiver/receiver_wavefront.go similarity index 83% rename from pkg/collector/parser/receiver_wavefront.go rename to internal/manifests/collector/parser/receiver/receiver_wavefront.go index 00c3f17e10..f2eafb8556 100644 --- a/pkg/collector/parser/receiver_wavefront.go +++ b/internal/manifests/collector/parser/receiver/receiver_wavefront.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameWavefront = "__wavefront" // NewWavefrontReceiverParser builds a new parser for Wavefront receivers, from the contrib repository. -func NewWavefrontReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewWavefrontReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_wavefront_test.go b/internal/manifests/collector/parser/receiver/receiver_wavefront_test.go similarity index 97% rename from pkg/collector/parser/receiver_wavefront_test.go rename to internal/manifests/collector/parser/receiver/receiver_wavefront_test.go index 6bea4d91e7..0dd31b9470 100644 --- a/pkg/collector/parser/receiver_wavefront_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_wavefront_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the Wavefront parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_zipkin-scribe.go b/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe.go similarity index 82% rename from pkg/collector/parser/receiver_zipkin-scribe.go rename to internal/manifests/collector/parser/receiver/receiver_zipkin-scribe.go index 1c6fa3631c..8f8d9fe210 100644 --- a/pkg/collector/parser/receiver_zipkin-scribe.go +++ b/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe.go @@ -12,14 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver -import "github.com/go-logr/logr" +import ( + "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" +) const parserNameZipkinScribe = "__zipkinscribe" // NewZipkinScribeReceiverParser builds a new parser for ZipkinScribe receivers. -func NewZipkinScribeReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewZipkinScribeReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { return &GenericReceiver{ logger: logger, name: name, diff --git a/pkg/collector/parser/receiver_zipkin-scribe_test.go b/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe_test.go similarity index 97% rename from pkg/collector/parser/receiver_zipkin-scribe_test.go rename to internal/manifests/collector/parser/receiver/receiver_zipkin-scribe_test.go index dc057612b5..6475eb02fc 100644 --- a/pkg/collector/parser/receiver_zipkin-scribe_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_zipkin-scribe_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the ZipkinScribe parser are currently part of the test TestDownstreamParsers diff --git a/pkg/collector/parser/receiver_zipkin.go b/internal/manifests/collector/parser/receiver/receiver_zipkin.go similarity index 87% rename from pkg/collector/parser/receiver_zipkin.go rename to internal/manifests/collector/parser/receiver/receiver_zipkin.go index d96d951e09..debbf8e9a4 100644 --- a/pkg/collector/parser/receiver_zipkin.go +++ b/internal/manifests/collector/parser/receiver/receiver_zipkin.go @@ -12,17 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser" ) const parserNameZipkin = "__zipkin" // NewZipkinReceiverParser builds a new parser for Zipkin receivers. -func NewZipkinReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { +func NewZipkinReceiverParser(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ComponentPortParser { http := "http" return &GenericReceiver{ logger: logger, diff --git a/pkg/collector/parser/receiver_zipkin_test.go b/internal/manifests/collector/parser/receiver/receiver_zipkin_test.go similarity index 97% rename from pkg/collector/parser/receiver_zipkin_test.go rename to internal/manifests/collector/parser/receiver/receiver_zipkin_test.go index 87bfecc082..8f1af43c5e 100644 --- a/pkg/collector/parser/receiver_zipkin_test.go +++ b/internal/manifests/collector/parser/receiver/receiver_zipkin_test.go @@ -12,6 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parser +package receiver // all tests for the Zipkin parser are currently part of the test TestDownstreamParsers diff --git a/internal/manifests/collector/poddisruptionbudget.go b/internal/manifests/collector/poddisruptionbudget.go new file mode 100644 index 0000000000..bbd3ce50a0 --- /dev/null +++ b/internal/manifests/collector/poddisruptionbudget.go @@ -0,0 +1,55 @@ +// 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 collector + +import ( + policyV1 "k8s.io/api/policy/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func PodDisruptionBudget(params manifests.Params) client.Object { + // defaulting webhook should always set this, but if unset then return nil. + if params.OtelCol.Spec.PodDisruptionBudget == nil { + params.Log.Info("pdb field is unset in Spec, skipping podDisruptionBudget creation") + return nil + } + + name := naming.Collector(params.OtelCol.Name) + labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, params.Config.LabelsFilter()) + annotations := Annotations(params.OtelCol) + + objectMeta := metav1.ObjectMeta{ + Name: naming.PodDisruptionBudget(params.OtelCol.Name), + Namespace: params.OtelCol.Namespace, + Labels: labels, + Annotations: annotations, + } + + return &policyV1.PodDisruptionBudget{ + ObjectMeta: objectMeta, + Spec: policyV1.PodDisruptionBudgetSpec{ + MinAvailable: params.OtelCol.Spec.PodDisruptionBudget.MinAvailable, + MaxUnavailable: params.OtelCol.Spec.PodDisruptionBudget.MaxUnavailable, + Selector: &metav1.LabelSelector{ + MatchLabels: objectMeta.Labels, + }, + }, + } +} diff --git a/internal/manifests/collector/poddisruptionbudget_test.go b/internal/manifests/collector/poddisruptionbudget_test.go new file mode 100644 index 0000000000..adedc9eb54 --- /dev/null +++ b/internal/manifests/collector/poddisruptionbudget_test.go @@ -0,0 +1,99 @@ +// 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 collector_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + policyV1 "k8s.io/api/policy/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + . "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" +) + +func TestPDB(t *testing.T) { + type test struct { + name string + MinAvailable *intstr.IntOrString + MaxUnavailable *intstr.IntOrString + } + tests := []test{ + { + name: "MinAvailable-int", + MinAvailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: 1, + }, + }, + { + name: "MinAvailable-string", + MinAvailable: &intstr.IntOrString{ + Type: intstr.String, + StrVal: "10%", + }, + }, + { + name: "MaxUnavailable-int", + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.Int, + IntVal: 1, + }, + }, + { + name: "MaxUnavailable-string", + MaxUnavailable: &intstr.IntOrString{ + Type: intstr.String, + StrVal: "10%", + }, + }, + } + + otelcols := []v1alpha1.OpenTelemetryCollector{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + }, + } + + for _, otelcol := range otelcols { + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + otelcol.Spec.PodDisruptionBudget = &v1alpha1.PodDisruptionBudgetSpec{ + MinAvailable: test.MinAvailable, + MaxUnavailable: test.MaxUnavailable, + } + configuration := config.New() + raw := PodDisruptionBudget(manifests.Params{ + Log: logger, + Config: configuration, + OtelCol: otelcol, + }) + + pdb := raw.(*policyV1.PodDisruptionBudget) + // verify + assert.Equal(t, "my-instance-collector", pdb.Name) + assert.Equal(t, "my-instance-collector", pdb.Labels["app.kubernetes.io/name"]) + assert.Equal(t, test.MinAvailable, pdb.Spec.MinAvailable) + assert.Equal(t, test.MaxUnavailable, pdb.Spec.MaxUnavailable) + }) + } + } +} diff --git a/internal/manifests/collector/podmonitor.go b/internal/manifests/collector/podmonitor.go new file mode 100644 index 0000000000..428d2673a0 --- /dev/null +++ b/internal/manifests/collector/podmonitor.go @@ -0,0 +1,101 @@ +// 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 collector + +import ( + "fmt" + "strings" + + "github.com/go-logr/logr" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// ServiceMonitor returns the service 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 + } + var pm monitoringv1.PodMonitor + + if params.OtelCol.Spec.Mode != v1alpha1.ModeSidecar { + return nil, nil + } + + pm = monitoringv1.PodMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: params.OtelCol.Namespace, + Name: naming.PodMonitor(params.OtelCol.Name), + Labels: map[string]string{ + "app.kubernetes.io/name": naming.PodMonitor(params.OtelCol.Name), + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: monitoringv1.PodMonitorSpec{ + JobLabel: "app.kubernetes.io/instance", + PodTargetLabels: []string{"app.kubernetes.io/name", "app.kubernetes.io/instance", "app.kubernetes.io/managed-by"}, + NamespaceSelector: monitoringv1.NamespaceSelector{ + MatchNames: []string{params.OtelCol.Namespace}, + }, + Selector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + }, + }, + PodMetricsEndpoints: append( + []monitoringv1.PodMetricsEndpoint{ + { + Port: "monitoring", + }, + }, metricsEndpointsFromConfig(params.Log, params.OtelCol)...), + }, + } + + return &pm, nil +} + +func metricsEndpointsFromConfig(logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) []monitoringv1.PodMetricsEndpoint { + config, err := adapters.ConfigFromString(otelcol.Spec.Config) + if err != nil { + logger.V(2).Error(err, "Error while parsing the configuration") + return []monitoringv1.PodMetricsEndpoint{} + } + exporterPorts, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeExporter, config) + if err != nil { + logger.Error(err, "couldn't build endpoints to podMonitors from configuration") + return []monitoringv1.PodMetricsEndpoint{} + } + metricsEndpoints := []monitoringv1.PodMetricsEndpoint{} + for _, port := range exporterPorts { + if strings.Contains(port.Name, "prometheus") { + e := monitoringv1.PodMetricsEndpoint{ + Port: port.Name, + } + metricsEndpoints = append(metricsEndpoints, e) + } + } + return metricsEndpoints +} diff --git a/internal/manifests/collector/podmonitor_test.go b/internal/manifests/collector/podmonitor_test.go new file mode 100644 index 0000000000..9066b50cbc --- /dev/null +++ b/internal/manifests/collector/podmonitor_test.go @@ -0,0 +1,59 @@ +// 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 collector + +import ( + "fmt" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + + "github.com/stretchr/testify/assert" + + "testing" +) + +func sidecarParams() manifests.Params { + return paramsWithMode(v1alpha1.ModeSidecar) +} + +func TestDesiredPodMonitors(t *testing.T) { + params := sidecarParams() + + actual, err := PodMonitor(params) + assert.NoError(t, err) + assert.Nil(t, actual) + + params.OtelCol.Spec.Observability.Metrics.EnableMetrics = true + actual, err = PodMonitor(params) + assert.NoError(t, err) + 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.PodMetricsEndpoints[0].Port) + + params, err = newParams("", "testdata/prometheus-exporter.yaml") + assert.NoError(t, err) + params.OtelCol.Spec.Mode = v1alpha1.ModeSidecar + params.OtelCol.Spec.Observability.Metrics.EnableMetrics = true + actual, err = PodMonitor(params) + assert.NoError(t, err) + 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.PodMetricsEndpoints[0].Port) + assert.Equal(t, "prometheus-dev", actual.Spec.PodMetricsEndpoints[1].Port) + assert.Equal(t, "prometheus-prod", actual.Spec.PodMetricsEndpoints[2].Port) +} diff --git a/internal/manifests/collector/route.go b/internal/manifests/collector/route.go new file mode 100644 index 0000000000..43901e59bc --- /dev/null +++ b/internal/manifests/collector/route.go @@ -0,0 +1,101 @@ +// 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 collector + +import ( + "fmt" + + routev1 "github.com/openshift/api/route/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func Routes(params manifests.Params) ([]*routev1.Route, error) { + if params.OtelCol.Spec.Ingress.Type != v1alpha1.IngressTypeRoute || params.Config.OpenShiftRoutesAvailability() != openshift.RoutesAvailable { + return nil, nil + } + + if params.OtelCol.Spec.Mode == v1alpha1.ModeSidecar { + params.Log.V(3).Info("ingress settings are not supported in sidecar mode") + return nil, nil + } + + var tlsCfg *routev1.TLSConfig + switch params.OtelCol.Spec.Ingress.Route.Termination { + case v1alpha1.TLSRouteTerminationTypeInsecure: + // NOTE: insecure, no tls cfg. + case v1alpha1.TLSRouteTerminationTypeEdge: + tlsCfg = &routev1.TLSConfig{Termination: routev1.TLSTerminationEdge} + case v1alpha1.TLSRouteTerminationTypePassthrough: + tlsCfg = &routev1.TLSConfig{Termination: routev1.TLSTerminationPassthrough} + case v1alpha1.TLSRouteTerminationTypeReencrypt: + tlsCfg = &routev1.TLSConfig{Termination: routev1.TLSTerminationReencrypt} + default: // NOTE: if unsupported, end here. + return nil, nil + } + + ports, err := servicePortsFromCfg(params.Log, params.OtelCol) + + // if we have no ports, we don't need a ingress entry + if len(ports) == 0 || err != nil { + params.Log.V(1).Info( + "the instance's configuration didn't yield any ports to open, skipping ingress", + "instance.name", params.OtelCol.Name, + "instance.namespace", params.OtelCol.Namespace, + ) + return nil, err + } + + routes := make([]*routev1.Route, len(ports)) + for i, p := range ports { + portName := naming.PortName(p.Name, p.Port) + host := "" + if params.OtelCol.Spec.Ingress.Hostname != "" { + host = fmt.Sprintf("%s.%s", portName, params.OtelCol.Spec.Ingress.Hostname) + } + + routes[i] = &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Route(params.OtelCol.Name, p.Name), + Namespace: params.OtelCol.Namespace, + Annotations: params.OtelCol.Spec.Ingress.Annotations, + Labels: map[string]string{ + "app.kubernetes.io/name": naming.Route(params.OtelCol.Name, p.Name), + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-collector", + }, + }, + Spec: routev1.RouteSpec{ + Host: host, + To: routev1.RouteTargetReference{ + Kind: "Service", + Name: naming.Service(params.OtelCol.Name), + }, + Port: &routev1.RoutePort{ + TargetPort: intstr.FromString(portName), + }, + WildcardPolicy: routev1.WildcardPolicyNone, + TLS: tlsCfg, + }, + } + } + return routes, nil +} diff --git a/internal/manifests/collector/route_test.go b/internal/manifests/collector/route_test.go new file mode 100644 index 0000000000..12104a125c --- /dev/null +++ b/internal/manifests/collector/route_test.go @@ -0,0 +1,212 @@ +// 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 collector + +import ( + _ "embed" + "fmt" + "testing" + + routev1 "github.com/openshift/api/route/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func TestDesiredRoutes(t *testing.T) { + t.Run("should return nil invalid ingress type", func(t *testing.T) { + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Ingress: v1alpha1.Ingress{ + Type: v1alpha1.IngressType("unknown"), + }, + }, + }, + } + + actual, err := Routes(params) + assert.NoError(t, err) + assert.Nil(t, actual) + }) + + t.Run("should return nil unable to parse config", func(t *testing.T) { + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Config: "!!!", + Ingress: v1alpha1.Ingress{ + Type: v1alpha1.IngressTypeRoute, + Route: v1alpha1.OpenShiftRoute{ + Termination: v1alpha1.TLSRouteTerminationTypeInsecure, + }, + }, + }, + }, + } + + actual, err := Routes(params) + assert.Nil(t, actual) + assert.ErrorContains(t, err, "couldn't parse the opentelemetry-collector configuration") + }) + + t.Run("should return nil unable to parse receiver ports", func(t *testing.T) { + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Config: "---", + Ingress: v1alpha1.Ingress{ + Type: v1alpha1.IngressTypeRoute, + Route: v1alpha1.OpenShiftRoute{ + Termination: v1alpha1.TLSRouteTerminationTypeInsecure, + }, + }, + }, + }, + } + + actual, err := Routes(params) + assert.Nil(t, actual) + assert.ErrorContains(t, err, "no receivers available as part of the configuration") + }) + + t.Run("should return nil unable to do something else", func(t *testing.T) { + var ( + ns = "test" + hostname = "example.com" + ) + + params, err := newParams("something:tag", testFileIngress) + if err != nil { + t.Fatal(err) + } + + params.OtelCol.Namespace = ns + params.OtelCol.Spec.Ingress = v1alpha1.Ingress{ + Type: v1alpha1.IngressTypeRoute, + Hostname: hostname, + Annotations: map[string]string{"some.key": "some.value"}, + Route: v1alpha1.OpenShiftRoute{ + Termination: v1alpha1.TLSRouteTerminationTypeInsecure, + }, + } + + routes, err := Routes(params) + assert.NoError(t, err) + got := routes[0] + + assert.NotEqual(t, &routev1.Route{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Route(params.OtelCol.Name, ""), + Namespace: ns, + Annotations: params.OtelCol.Spec.Ingress.Annotations, + Labels: map[string]string{ + "app.kubernetes.io/name": naming.Route(params.OtelCol.Name, ""), + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/component": "opentelemetry-collector", + }, + }, + Spec: routev1.RouteSpec{ + Host: hostname, + Path: "/abc", + To: routev1.RouteTargetReference{ + Kind: "service", + Name: "test-collector", + }, + Port: &routev1.RoutePort{ + TargetPort: intstr.FromString("another-port"), + }, + WildcardPolicy: routev1.WildcardPolicyNone, + TLS: &routev1.TLSConfig{ + Termination: routev1.TLSTerminationPassthrough, + InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyAllow, + }, + }, + }, got) + }) + t.Run("hostname is set", func(t *testing.T) { + params, err := newParams("something:tag", testFileIngress) + if err != nil { + t.Fatal(err) + } + + params.OtelCol.Namespace = "test" + params.OtelCol.Spec.Ingress = v1alpha1.Ingress{ + Hostname: "example.com", + Type: v1alpha1.IngressTypeRoute, + Route: v1alpha1.OpenShiftRoute{ + Termination: v1alpha1.TLSRouteTerminationTypeInsecure, + }, + } + + routes, err := Routes(params) + assert.NoError(t, err) + require.Equal(t, 3, len(routes)) + assert.Equal(t, "web.example.com", routes[0].Spec.Host) + assert.Equal(t, "otlp-grpc.example.com", routes[1].Spec.Host) + assert.Equal(t, "otlp-test-grpc.example.com", routes[2].Spec.Host) + }) + t.Run("hostname is not set", func(t *testing.T) { + params, err := newParams("something:tag", testFileIngress) + if err != nil { + t.Fatal(err) + } + + params.OtelCol.Namespace = "test" + params.OtelCol.Spec.Ingress = v1alpha1.Ingress{ + Type: v1alpha1.IngressTypeRoute, + Route: v1alpha1.OpenShiftRoute{ + Termination: v1alpha1.TLSRouteTerminationTypeInsecure, + }, + } + + routes, err := Routes(params) + assert.NoError(t, err) + require.Equal(t, 3, len(routes)) + assert.Equal(t, "", routes[0].Spec.Host) + assert.Equal(t, "", routes[1].Spec.Host) + assert.Equal(t, "", routes[2].Spec.Host) + }) +} + +func TestRoutes(t *testing.T) { + t.Run("wrong mode", func(t *testing.T) { + params := deploymentParams() + routes, err := Routes(params) + assert.NoError(t, err) + assert.Nil(t, routes) + }) + + t.Run("supported mode and service exists", func(t *testing.T) { + params := deploymentParams() + routes, err := Routes(params) + assert.NoError(t, err) + assert.Nil(t, routes) + }) + +} diff --git a/internal/manifests/collector/service.go b/internal/manifests/collector/service.go new file mode 100644 index 0000000000..5b23888fbe --- /dev/null +++ b/internal/manifests/collector/service.go @@ -0,0 +1,200 @@ +// 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 collector + +import ( + "fmt" + "strings" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// headless label is to differentiate the headless service from the clusterIP service. +const ( + headlessLabel = "operator.opentelemetry.io/collector-headless-service" + headlessExists = "Exists" +) + +func HeadlessService(params manifests.Params) (*corev1.Service, error) { + h, err := Service(params) + if h == nil || err != nil { + return h, err + } + + h.Name = naming.HeadlessService(params.OtelCol.Name) + h.Labels[headlessLabel] = headlessExists + + // copy to avoid modifying params.OtelCol.Annotations + annotations := map[string]string{ + "service.beta.openshift.io/serving-cert-secret-name": fmt.Sprintf("%s-tls", h.Name), + } + for k, v := range h.Annotations { + annotations[k] = v + } + h.Annotations = annotations + + h.Spec.ClusterIP = "None" + return h, nil +} + +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{}) + + c, err := adapters.ConfigFromString(params.OtelCol.Spec.Config) + if err != nil { + params.Log.Error(err, "couldn't extract the configuration") + return nil, err + } + + metricsPort, err := adapters.ConfigToMetricsPort(params.Log, c) + if err != nil { + return nil, err + } + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OtelCol.Namespace, + Labels: labels, + Annotations: params.OtelCol.Annotations, + }, + Spec: corev1.ServiceSpec{ + Selector: manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector), + ClusterIP: "", + Ports: []corev1.ServicePort{{ + Name: "monitoring", + Port: metricsPort, + }}, + }, + }, nil +} + +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{}) + + configFromString, err := adapters.ConfigFromString(params.OtelCol.Spec.Config) + if err != nil { + params.Log.Error(err, "couldn't extract the configuration from the context") + return nil, err + } + + ports, err := adapters.ConfigToPorts(params.Log, configFromString) + if err != nil { + return nil, err + } + + // set appProtocol to h2c for grpc ports on OpenShift. + // OpenShift uses HA proxy that uses appProtocol for its configuration. + for i := range ports { + h2c := "h2c" + if params.OtelCol.Spec.Ingress.Type == v1alpha1.IngressTypeRoute && ports[i].AppProtocol != nil && strings.EqualFold(*ports[i].AppProtocol, "grpc") { + ports[i].AppProtocol = &h2c + } + } + + if len(params.OtelCol.Spec.Ports) > 0 { + // we should add all the ports from the CR + // there are two cases where problems might occur: + // 1) when the port number is already being used by a receiver + // 2) same, but for the port name + // + // in the first case, we remove the port we inferred from the list + // in the second case, we rename our inferred port to something like "port-%d" + portNumbers, portNames := extractPortNumbersAndNames(params.OtelCol.Spec.Ports) + var resultingInferredPorts []corev1.ServicePort + for _, inferred := range ports { + if filtered := filterPort(params.Log, inferred, portNumbers, portNames); filtered != nil { + resultingInferredPorts = append(resultingInferredPorts, *filtered) + } + } + + ports = append(params.OtelCol.Spec.Ports, resultingInferredPorts...) + } + + // if we have no ports, we don't need a service + if len(ports) == 0 { + + params.Log.V(1).Info("the instance's configuration didn't yield any ports to open, skipping service", "instance.name", params.OtelCol.Name, "instance.namespace", params.OtelCol.Namespace) + return nil, err + } + + trafficPolicy := corev1.ServiceInternalTrafficPolicyCluster + if params.OtelCol.Spec.Mode == v1alpha1.ModeDaemonSet { + trafficPolicy = corev1.ServiceInternalTrafficPolicyLocal + } + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.Service(params.OtelCol.Name), + Namespace: params.OtelCol.Namespace, + Labels: labels, + Annotations: params.OtelCol.Annotations, + }, + Spec: corev1.ServiceSpec{ + InternalTrafficPolicy: &trafficPolicy, + Selector: manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector), + ClusterIP: "", + Ports: ports, + }, + }, nil +} + +func filterPort(logger logr.Logger, candidate corev1.ServicePort, portNumbers map[int32]bool, portNames map[string]bool) *corev1.ServicePort { + if portNumbers[candidate.Port] { + return nil + } + + // do we have the port name there already? + if portNames[candidate.Name] { + // there's already a port with the same name! do we have a 'port-%d' already? + fallbackName := fmt.Sprintf("port-%d", candidate.Port) + if portNames[fallbackName] { + // that wasn't expected, better skip this port + logger.V(2).Info("a port name specified in the CR clashes with an inferred port name, and the fallback port name clashes with another port name! Skipping this port.", + "inferred-port-name", candidate.Name, + "fallback-port-name", fallbackName, + ) + return nil + } + + candidate.Name = fallbackName + return &candidate + } + + // this port is unique, return as is + return &candidate +} + +func extractPortNumbersAndNames(ports []corev1.ServicePort) (map[int32]bool, map[string]bool) { + numbers := map[int32]bool{} + names := map[string]bool{} + + for _, port := range ports { + numbers[port.Port] = true + names[port.Name] = true + } + + return numbers, names +} diff --git a/pkg/collector/reconcile/service_test.go b/internal/manifests/collector/service_test.go similarity index 53% rename from pkg/collector/reconcile/service_test.go rename to internal/manifests/collector/service_test.go index c98553b208..37bd668e83 100644 --- a/pkg/collector/reconcile/service_test.go +++ b/internal/manifests/collector/service_test.go @@ -12,21 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -package reconcile +package collector import ( - "context" "testing" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" ) func TestExtractPortNumbersAndNames(t *testing.T) { @@ -97,11 +96,10 @@ func TestFilterPort(t *testing.T) { func TestDesiredService(t *testing.T) { t.Run("should return nil service for unknown receiver and protocol", func(t *testing.T) { - params := Params{ + params := manifests.Params{ Config: config.Config{}, - Client: k8sClient, Log: logger, - Instance: v1alpha1.OpenTelemetryCollector{ + OtelCol: v1alpha1.OpenTelemetryCollector{ Spec: v1alpha1.OpenTelemetryCollectorSpec{Config: `receivers: test: protocols: @@ -109,11 +107,12 @@ func TestDesiredService(t *testing.T) { }, } - actual := desiredService(context.Background(), params) + actual, err := Service(params) + assert.ErrorContains(t, err, "no enabled receivers available as part of the configuration") assert.Nil(t, actual) }) - t.Run("should return service with port mentioned in Instance.Spec.Ports and inferred ports", func(t *testing.T) { + t.Run("should return service with port mentioned in OtelCol.Spec.Ports and inferred ports", func(t *testing.T) { grpc := "grpc" jaegerPorts := v1.ServicePort{ @@ -122,128 +121,136 @@ func TestDesiredService(t *testing.T) { Port: 14250, AppProtocol: &grpc, } - ports := append(params().Instance.Spec.Ports, jaegerPorts) + params := deploymentParams() + ports := append(params.OtelCol.Spec.Ports, jaegerPorts) expected := service("test-collector", ports) - actual := desiredService(context.Background(), params()) + actual, err := Service(params) + assert.NoError(t, err) assert.Equal(t, expected, *actual) }) -} + t.Run("on OpenShift gRPC appProtocol should be h2c", func(t *testing.T) { + h2c := "h2c" + jaegerPort := v1.ServicePort{ + Name: "jaeger-grpc", + Protocol: "TCP", + Port: 14250, + AppProtocol: &h2c, + } -func TestExpectedServices(t *testing.T) { - t.Run("should create the service", func(t *testing.T) { - err := expectedServices(context.Background(), params(), []v1.Service{service("test-collector", params().Instance.Spec.Ports)}) - assert.NoError(t, err) + params := deploymentParams() - exists, err := populateObjectIfExists(t, &v1.Service{}, types.NamespacedName{Namespace: "default", Name: "test-collector"}) + params.OtelCol.Spec.Ingress.Type = v1alpha1.IngressTypeRoute + actual, err := Service(params) + ports := append(params.OtelCol.Spec.Ports, jaegerPort) + expected := service("test-collector", ports) assert.NoError(t, err) - assert.True(t, exists) + assert.Equal(t, expected, *actual) }) - t.Run("should update service", func(t *testing.T) { - serviceInstance := service("test-collector", params().Instance.Spec.Ports) - createObjectIfNotExists(t, "test-collector", &serviceInstance) - - extraPorts := v1.ServicePort{ - Name: "port-web", - Protocol: "TCP", - Port: 8080, - TargetPort: intstr.FromInt(8080), - } - - ports := append(params().Instance.Spec.Ports, extraPorts) - err := expectedServices(context.Background(), params(), []v1.Service{service("test-collector", ports)}) - assert.NoError(t, err) - actual := v1.Service{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) + t.Run("should return service with local internal traffic policy", func(t *testing.T) { - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, instanceUID, actual.OwnerReferences[0].UID) - assert.Contains(t, actual.Spec.Ports, extraPorts) - }) - t.Run("should update service on version change", func(t *testing.T) { - serviceInstance := service("test-collector", params().Instance.Spec.Ports) - createObjectIfNotExists(t, "test-collector", &serviceInstance) + grpc := "grpc" + jaegerPorts := v1.ServicePort{ + Name: "jaeger-grpc", + Protocol: "TCP", + Port: 14250, + AppProtocol: &grpc, + } + p := paramsWithMode(v1alpha1.ModeDaemonSet) + ports := append(p.OtelCol.Spec.Ports, jaegerPorts) + expected := serviceWithInternalTrafficPolicy("test-collector", ports, v1.ServiceInternalTrafficPolicyLocal) - newService := service("test-collector", params().Instance.Spec.Ports) - newService.Spec.Selector["app.kubernetes.io/version"] = "Newest" - err := expectedServices(context.Background(), params(), []v1.Service{newService}) + actual, err := Service(p) assert.NoError(t, err) - actual := v1.Service{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, instanceUID, actual.OwnerReferences[0].UID) - assert.Equal(t, "Newest", actual.Spec.Selector["app.kubernetes.io/version"]) + assert.Equal(t, expected, *actual) }) -} - -func TestDeleteServices(t *testing.T) { - t.Run("should delete excess services", func(t *testing.T) { - ports := []v1.ServicePort{{ - Port: 80, - Name: "web", - }} - deleteService := service("delete-service-collector", ports) - createObjectIfNotExists(t, "delete-service-collector", &deleteService) - exists, err := populateObjectIfExists(t, &v1.Service{}, types.NamespacedName{Namespace: "default", Name: "delete-service-collector"}) - assert.NoError(t, err) - assert.True(t, exists) - - desired := desiredService(context.Background(), params()) - err = deleteServices(context.Background(), params(), []v1.Service{*desired}) - assert.NoError(t, err) + t.Run("should return nil unable to parse config", func(t *testing.T) { + params := manifests.Params{ + Config: config.Config{}, + Log: logger, + OtelCol: v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{Config: `!!!`}, + }, + } - exists, err = populateObjectIfExists(t, &v1.Service{}, types.NamespacedName{Namespace: "default", Name: "delete-service-collector"}) - assert.NoError(t, err) - assert.False(t, exists) + actual, err := Service(params) + assert.ErrorContains(t, err, "couldn't parse the opentelemetry-collector configuration") + assert.Nil(t, actual) }) } func TestHeadlessService(t *testing.T) { t.Run("should return headless service", func(t *testing.T) { - actual := headless(context.Background(), params()) - assert.Equal(t, actual.Annotations["service.beta.openshift.io/serving-cert-secret-name"], "test-collector-headless-tls") + param := deploymentParams() + actual, err := HeadlessService(param) + assert.NoError(t, err) + assert.Equal(t, actual.GetAnnotations()["service.beta.openshift.io/serving-cert-secret-name"], "test-collector-headless-tls") assert.Equal(t, actual.Spec.ClusterIP, "None") }) } func TestMonitoringService(t *testing.T) { - t.Run("returned service should expose monitoring port", func(t *testing.T) { + t.Run("returned service should expose monitoring port in the default port", func(t *testing.T) { expected := []v1.ServicePort{{ Name: "monitoring", Port: 8888, }} - actual := monitoringService(context.Background(), params()) + param := deploymentParams() + + actual, err := MonitoringService(param) + assert.NoError(t, err) + assert.Equal(t, expected, actual.Spec.Ports) + }) + + t.Run("returned the service in a custom port", func(t *testing.T) { + expected := []v1.ServicePort{{ + Name: "monitoring", + Port: 9090, + }} + params := deploymentParams() + params.OtelCol.Spec.Config = `service: + telemetry: + metrics: + level: detailed + address: 0.0.0.0:9090` + + actual, err := MonitoringService(params) + assert.NoError(t, err) + assert.NotNil(t, actual) + assert.Equal(t, expected, actual.Spec.Ports) }) } func service(name string, ports []v1.ServicePort) v1.Service { - labels := collector.Labels(params().Instance, []string{}) - labels["app.kubernetes.io/name"] = name + return serviceWithInternalTrafficPolicy(name, ports, v1.ServiceInternalTrafficPolicyCluster) +} + +func serviceWithInternalTrafficPolicy(name string, ports []v1.ServicePort, internalTrafficPolicy v1.ServiceInternalTrafficPolicyType) v1.Service { + params := deploymentParams() + labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, []string{}) return v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: "default", Labels: labels, - Annotations: params().Instance.Annotations, + Annotations: params.OtelCol.Annotations, }, Spec: v1.ServiceSpec{ - Selector: collector.SelectorLabels(params().Instance), - ClusterIP: "", - Ports: ports, + InternalTrafficPolicy: &internalTrafficPolicy, + Selector: manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector), + ClusterIP: "", + Ports: ports, }, } } diff --git a/pkg/collector/serviceaccount.go b/internal/manifests/collector/serviceaccount.go similarity index 62% rename from pkg/collector/serviceaccount.go rename to internal/manifests/collector/serviceaccount.go index 4804b83b3a..a3648a593a 100644 --- a/pkg/collector/serviceaccount.go +++ b/internal/manifests/collector/serviceaccount.go @@ -19,29 +19,34 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) // ServiceAccountName returns the name of the existing or self-provisioned service account to use for the given instance. func ServiceAccountName(instance v1alpha1.OpenTelemetryCollector) string { if len(instance.Spec.ServiceAccount) == 0 { - return naming.ServiceAccount(instance) + return naming.ServiceAccount(instance.Name) } return instance.Spec.ServiceAccount } // ServiceAccount returns the service account for the given instance. -func ServiceAccount(otelcol v1alpha1.OpenTelemetryCollector) corev1.ServiceAccount { - labels := Labels(otelcol, []string{}) - labels["app.kubernetes.io/name"] = naming.ServiceAccount(otelcol) +func ServiceAccount(params manifests.Params) *corev1.ServiceAccount { + if params.OtelCol.Spec.ServiceAccount != "" { + return nil + } + name := naming.ServiceAccount(params.OtelCol.Name) + labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, []string{}) - return corev1.ServiceAccount{ + return &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ - Name: naming.ServiceAccount(otelcol), - Namespace: otelcol.Namespace, + Name: name, + Namespace: params.OtelCol.Namespace, Labels: labels, - Annotations: otelcol.Annotations, + Annotations: params.OtelCol.Annotations, }, } } diff --git a/pkg/collector/serviceaccount_test.go b/internal/manifests/collector/serviceaccount_test.go similarity index 94% rename from pkg/collector/serviceaccount_test.go rename to internal/manifests/collector/serviceaccount_test.go index 33b82f1728..6d9abafe2f 100644 --- a/pkg/collector/serviceaccount_test.go +++ b/internal/manifests/collector/serviceaccount_test.go @@ -21,7 +21,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" + . "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" ) func TestServiceAccountNewDefault(t *testing.T) { diff --git a/internal/manifests/collector/servicemonitor.go b/internal/manifests/collector/servicemonitor.go new file mode 100644 index 0000000000..db5dcd34d0 --- /dev/null +++ b/internal/manifests/collector/servicemonitor.go @@ -0,0 +1,100 @@ +// 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 collector + +import ( + "fmt" + "strings" + + "github.com/go-logr/logr" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// ServiceMonitor returns the service monitor for the given instance. +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 + } + var sm monitoringv1.ServiceMonitor + + if params.OtelCol.Spec.Mode == v1alpha1.ModeSidecar { + return nil, nil + } + sm = monitoringv1.ServiceMonitor{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: params.OtelCol.Namespace, + Name: naming.ServiceMonitor(params.OtelCol.Name), + Labels: map[string]string{ + "app.kubernetes.io/name": naming.ServiceMonitor(params.OtelCol.Name), + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + }, + Spec: monitoringv1.ServiceMonitorSpec{ + Endpoints: append([]monitoringv1.Endpoint{ + { + Port: "monitoring", + }, + }, endpointsFromConfig(params.Log, params.OtelCol)...), + NamespaceSelector: monitoringv1.NamespaceSelector{ + MatchNames: []string{params.OtelCol.Namespace}, + }, + Selector: metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.OtelCol.Namespace, params.OtelCol.Name), + }, + }, + }, + } + + return &sm, nil +} + +func endpointsFromConfig(logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) []monitoringv1.Endpoint { + c, err := adapters.ConfigFromString(otelcol.Spec.Config) + if err != nil { + logger.V(2).Error(err, "Error while parsing the configuration") + return []monitoringv1.Endpoint{} + } + + exporterPorts, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeExporter, c) + if err != nil { + logger.Error(err, "couldn't build service monitors from configuration") + return []monitoringv1.Endpoint{} + } + + endpoints := []monitoringv1.Endpoint{} + + for _, port := range exporterPorts { + if strings.Contains(port.Name, "prometheus") { + e := monitoringv1.Endpoint{ + Port: port.Name, + } + endpoints = append(endpoints, e) + } + } + return endpoints +} diff --git a/internal/manifests/collector/servicemonitor_test.go b/internal/manifests/collector/servicemonitor_test.go new file mode 100644 index 0000000000..c3fdf0c960 --- /dev/null +++ b/internal/manifests/collector/servicemonitor_test.go @@ -0,0 +1,50 @@ +// 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 collector + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDesiredServiceMonitors(t *testing.T) { + params := deploymentParams() + + actual, err := ServiceMonitor(params) + assert.NoError(t, err) + assert.Nil(t, actual) + + params.OtelCol.Spec.Observability.Metrics.EnableMetrics = true + actual, err = ServiceMonitor(params) + assert.NoError(t, err) + 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) + + params, err = newParams("", "testdata/prometheus-exporter.yaml") + assert.NoError(t, err) + params.OtelCol.Spec.Observability.Metrics.EnableMetrics = true + actual, err = ServiceMonitor(params) + assert.NoError(t, err) + 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) +} diff --git a/internal/manifests/collector/statefulset.go b/internal/manifests/collector/statefulset.go new file mode 100644 index 0000000000..a08304807d --- /dev/null +++ b/internal/manifests/collector/statefulset.go @@ -0,0 +1,72 @@ +// 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 collector + +import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// StatefulSet builds the statefulset for the given instance. +func StatefulSet(params manifests.Params) *appsv1.StatefulSet { + name := naming.Collector(params.OtelCol.Name) + labels := manifestutils.Labels(params.OtelCol.ObjectMeta, name, params.OtelCol.Spec.Image, ComponentOpenTelemetryCollector, params.Config.LabelsFilter()) + + annotations := Annotations(params.OtelCol) + podAnnotations := PodAnnotations(params.OtelCol) + + return &appsv1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OtelCol.Namespace, + Labels: labels, + Annotations: annotations, + }, + Spec: appsv1.StatefulSetSpec{ + ServiceName: naming.Service(params.OtelCol.Name), + Selector: &metav1.LabelSelector{ + MatchLabels: manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, ComponentOpenTelemetryCollector), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: podAnnotations, + }, + Spec: corev1.PodSpec{ + ServiceAccountName: ServiceAccountName(params.OtelCol), + InitContainers: params.OtelCol.Spec.InitContainers, + Containers: append(params.OtelCol.Spec.AdditionalContainers, Container(params.Config, params.Log, params.OtelCol, true)), + Volumes: Volumes(params.Config, params.OtelCol), + DNSPolicy: getDNSPolicy(params.OtelCol), + HostNetwork: params.OtelCol.Spec.HostNetwork, + Tolerations: params.OtelCol.Spec.Tolerations, + NodeSelector: params.OtelCol.Spec.NodeSelector, + SecurityContext: params.OtelCol.Spec.PodSecurityContext, + PriorityClassName: params.OtelCol.Spec.PriorityClassName, + Affinity: params.OtelCol.Spec.Affinity, + TopologySpreadConstraints: params.OtelCol.Spec.TopologySpreadConstraints, + }, + }, + Replicas: params.OtelCol.Spec.Replicas, + PodManagementPolicy: "Parallel", + VolumeClaimTemplates: VolumeClaimTemplates(params.OtelCol), + }, + } +} diff --git a/pkg/collector/statefulset_test.go b/internal/manifests/collector/statefulset_test.go similarity index 57% rename from pkg/collector/statefulset_test.go rename to internal/manifests/collector/statefulset_test.go index 66c5926e6e..332399ed7c 100644 --- a/pkg/collector/statefulset_test.go +++ b/internal/manifests/collector/statefulset_test.go @@ -26,7 +26,8 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + . "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" ) func TestStatefulSetNewDefault(t *testing.T) { @@ -43,8 +44,14 @@ func TestStatefulSetNewDefault(t *testing.T) { } cfg := config.New() + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + // test - ss := StatefulSet(cfg, logger, otelcol) + ss := StatefulSet(params) // verify assert.Equal(t, "my-instance-collector", ss.Name) @@ -59,6 +66,9 @@ func TestStatefulSetNewDefault(t *testing.T) { // verify sha256 podAnnotation expectedAnnotations := map[string]string{ "opentelemetry-operator-config/sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", } assert.Equal(t, expectedAnnotations, ss.Spec.Template.Annotations) @@ -106,8 +116,14 @@ func TestStatefulSetReplicas(t *testing.T) { } cfg := config.New() + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + // test - ss := StatefulSet(cfg, logger, otelcol) + ss := StatefulSet(params) // assert correct number of replicas assert.Equal(t, int32(3), *ss.Spec.Replicas) @@ -136,8 +152,14 @@ func TestStatefulSetVolumeClaimTemplates(t *testing.T) { } cfg := config.New() + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + // test - ss := StatefulSet(cfg, logger, otelcol) + ss := StatefulSet(params) // assert correct pvc name assert.Equal(t, "added-volume", ss.Spec.VolumeClaimTemplates[0].Name) @@ -162,15 +184,28 @@ func TestStatefulSetPodAnnotations(t *testing.T) { } cfg := config.New() + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + // test - ss := StatefulSet(cfg, logger, otelcol) + ss := StatefulSet(params) // Add sha256 podAnnotation testPodAnnotationValues["opentelemetry-operator-config/sha256"] = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + expectedAnnotations := map[string]string{ + "annotation-key": "annotation-value", + "opentelemetry-operator-config/sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "prometheus.io/path": "/metrics", + "prometheus.io/port": "8888", + "prometheus.io/scrape": "true", + } // verify assert.Equal(t, "my-instance-collector", ss.Name) - assert.Equal(t, testPodAnnotationValues, ss.Spec.Template.Annotations) + assert.Equal(t, expectedAnnotations, ss.Spec.Template.Annotations) } func TestStatefulSetPodSecurityContext(t *testing.T) { @@ -193,7 +228,13 @@ func TestStatefulSetPodSecurityContext(t *testing.T) { cfg := config.New() - d := StatefulSet(cfg, logger, otelcol) + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + + d := StatefulSet(params) assert.Equal(t, &runAsNonRoot, d.Spec.Template.Spec.SecurityContext.RunAsNonRoot) assert.Equal(t, &runAsUser, d.Spec.Template.Spec.SecurityContext.RunAsUser) @@ -202,7 +243,7 @@ func TestStatefulSetPodSecurityContext(t *testing.T) { func TestStatefulSetHostNetwork(t *testing.T) { // Test default - otelcol_1 := v1alpha1.OpenTelemetryCollector{ + otelcol1 := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ Name: "my-instance", }, @@ -210,13 +251,19 @@ func TestStatefulSetHostNetwork(t *testing.T) { cfg := config.New() - d1 := StatefulSet(cfg, logger, otelcol_1) + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + + d1 := StatefulSet(params1) assert.Equal(t, d1.Spec.Template.Spec.HostNetwork, false) assert.Equal(t, d1.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirst) // Test hostNetwork=true - otelcol_2 := v1alpha1.OpenTelemetryCollector{ + otelcol2 := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ Name: "my-instance-hostnetwork", }, @@ -227,7 +274,13 @@ func TestStatefulSetHostNetwork(t *testing.T) { cfg = config.New() - d2 := StatefulSet(cfg, logger, otelcol_2) + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + + d2 := StatefulSet(params2) assert.Equal(t, d2.Spec.Template.Spec.HostNetwork, true) assert.Equal(t, d2.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirstWithHostNet) } @@ -248,7 +301,13 @@ func TestStatefulSetFilterLabels(t *testing.T) { cfg := config.New(config.WithLabelFilters([]string{"foo*", "app.*.bar"})) - d := StatefulSet(cfg, logger, otelcol) + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + + d := StatefulSet(params) assert.Len(t, d.ObjectMeta.Labels, 6) for k := range excludedLabels { @@ -258,7 +317,7 @@ func TestStatefulSetFilterLabels(t *testing.T) { func TestStatefulSetNodeSelector(t *testing.T) { // Test default - otelcol_1 := v1alpha1.OpenTelemetryCollector{ + otelcol1 := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ Name: "my-instance", }, @@ -266,12 +325,18 @@ func TestStatefulSetNodeSelector(t *testing.T) { cfg := config.New() - d1 := StatefulSet(cfg, logger, otelcol_1) + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + + d1 := StatefulSet(params1) assert.Empty(t, d1.Spec.Template.Spec.NodeSelector) // Test nodeSelector - otelcol_2 := v1alpha1.OpenTelemetryCollector{ + otelcol2 := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ Name: "my-instance-nodeselector", }, @@ -285,12 +350,18 @@ func TestStatefulSetNodeSelector(t *testing.T) { cfg = config.New() - d2 := StatefulSet(cfg, logger, otelcol_2) + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + + d2 := StatefulSet(params2) assert.Equal(t, d2.Spec.Template.Spec.NodeSelector, map[string]string{"node-key": "node-value"}) } func TestStatefulSetPriorityClassName(t *testing.T) { - otelcol_1 := v1alpha1.OpenTelemetryCollector{ + otelcol1 := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ Name: "my-instance", }, @@ -298,12 +369,18 @@ func TestStatefulSetPriorityClassName(t *testing.T) { cfg := config.New() - sts1 := StatefulSet(cfg, logger, otelcol_1) + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + + sts1 := StatefulSet(params1) assert.Empty(t, sts1.Spec.Template.Spec.PriorityClassName) priorityClassName := "test-class" - otelcol_2 := v1alpha1.OpenTelemetryCollector{ + otelcol2 := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ Name: "my-instance-priortyClassName", }, @@ -314,6 +391,163 @@ func TestStatefulSetPriorityClassName(t *testing.T) { cfg = config.New() - sts2 := StatefulSet(cfg, logger, otelcol_2) + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + + sts2 := StatefulSet(params2) assert.Equal(t, priorityClassName, sts2.Spec.Template.Spec.PriorityClassName) } + +func TestStatefulSetAffinity(t *testing.T) { + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + + sts1 := Deployment(params1) + assert.Nil(t, sts1.Spec.Template.Spec.Affinity) + + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-priortyClassName", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Affinity: testAffinityValue, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + + sts2 := StatefulSet(params2) + assert.NotNil(t, sts2.Spec.Template.Spec.Affinity) + assert.Equal(t, *testAffinityValue, *sts2.Spec.Template.Spec.Affinity) +} + +func TestStatefulSetInitContainer(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + InitContainers: []v1.Container{ + { + Name: "test", + }, + }, + }, + } + cfg := config.New() + + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + + // test + s := StatefulSet(params) + assert.Equal(t, "my-instance-collector", s.Name) + assert.Equal(t, "my-instance-collector", s.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", s.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", s.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", s.Annotations["prometheus.io/path"]) + assert.Len(t, s.Spec.Template.Spec.InitContainers, 1) +} + +func TestStatefulSetTopologySpreadConstraints(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + s1 := StatefulSet(params1) + assert.Equal(t, "my-instance-collector", s1.Name) + assert.Empty(t, s1.Spec.Template.Spec.TopologySpreadConstraints) + + // Test TopologySpreadConstraints + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-topologyspreadconstraint", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TopologySpreadConstraints: testTopologySpreadConstraintValue, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + + s2 := StatefulSet(params2) + assert.Equal(t, "my-instance-topologyspreadconstraint-collector", s2.Name) + assert.NotNil(t, s2.Spec.Template.Spec.TopologySpreadConstraints) + assert.NotEmpty(t, s2.Spec.Template.Spec.TopologySpreadConstraints) + assert.Equal(t, testTopologySpreadConstraintValue, s2.Spec.Template.Spec.TopologySpreadConstraints) +} + +func TestStatefulSetAdditionalContainers(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + AdditionalContainers: []v1.Container{ + { + Name: "test", + }, + }, + }, + } + cfg := config.New() + + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + + // test + s := StatefulSet(params) + assert.Equal(t, "my-instance-collector", s.Name) + assert.Equal(t, "my-instance-collector", s.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", s.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", s.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", s.Annotations["prometheus.io/path"]) + assert.Len(t, s.Spec.Template.Spec.Containers, 2) + assert.Equal(t, v1.Container{Name: "test"}, s.Spec.Template.Spec.Containers[0]) +} diff --git a/internal/manifests/collector/suite_test.go b/internal/manifests/collector/suite_test.go new file mode 100644 index 0000000000..fd5e275856 --- /dev/null +++ b/internal/manifests/collector/suite_test.go @@ -0,0 +1,140 @@ +// 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 collector + +import ( + "fmt" + "os" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/uuid" + "k8s.io/client-go/tools/record" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +var ( + logger = logf.Log.WithName("unit-tests") + instanceUID = uuid.NewUUID() +) + +const ( + defaultCollectorImage = "default-collector" + defaultTaAllocationImage = "default-ta-allocator" +) + +func deploymentParams() manifests.Params { + return paramsWithMode(v1alpha1.ModeDeployment) +} + +func paramsWithMode(mode v1alpha1.Mode) manifests.Params { + replicas := int32(2) + configYAML, err := os.ReadFile("testdata/test.yaml") + if err != nil { + fmt.Printf("Error getting yaml file: %v", err) + } + return manifests.Params{ + Config: config.New(config.WithCollectorImage(defaultCollectorImage), config.WithTargetAllocatorImage(defaultTaAllocationImage)), + OtelCol: v1alpha1.OpenTelemetryCollector{ + TypeMeta: metav1.TypeMeta{ + Kind: "opentelemetry.io", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + UID: instanceUID, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.47.0", + Ports: []v1.ServicePort{{ + Name: "web", + Port: 80, + TargetPort: intstr.IntOrString{ + Type: intstr.Int, + IntVal: 80, + }, + NodePort: 0, + }}, + Replicas: &replicas, + Config: string(configYAML), + Mode: mode, + }, + }, + Log: logger, + Recorder: record.NewFakeRecorder(10), + } +} + +func newParams(taContainerImage string, file string) (manifests.Params, error) { + replicas := int32(1) + var configYAML []byte + var err error + + if file == "" { + configYAML, err = os.ReadFile("testdata/test.yaml") + } else { + configYAML, err = os.ReadFile(file) + } + if err != nil { + return manifests.Params{}, fmt.Errorf("Error getting yaml file: %w", err) + } + + cfg := config.New( + config.WithCollectorImage(defaultCollectorImage), + config.WithTargetAllocatorImage(defaultTaAllocationImage), + config.WithOpenShiftRoutesAvailability(openshift.RoutesAvailable), + ) + + return manifests.Params{ + Config: cfg, + OtelCol: v1alpha1.OpenTelemetryCollector{ + TypeMeta: metav1.TypeMeta{ + Kind: "opentelemetry.io", + APIVersion: "v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + UID: instanceUID, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeStatefulSet, + Ports: []v1.ServicePort{{ + Name: "web", + Port: 80, + TargetPort: intstr.IntOrString{ + Type: intstr.Int, + IntVal: 80, + }, + NodePort: 0, + }}, + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + Image: taContainerImage, + }, + Replicas: &replicas, + Config: string(configYAML), + }, + }, + Log: logger, + }, nil +} diff --git a/internal/manifests/collector/testdata/config_expected_targetallocator.yaml b/internal/manifests/collector/testdata/config_expected_targetallocator.yaml new file mode 100644 index 0000000000..8708b63155 --- /dev/null +++ b/internal/manifests/collector/testdata/config_expected_targetallocator.yaml @@ -0,0 +1,22 @@ +debug: null +exporters: null +processors: null +receivers: + prometheus: + config: + global: + evaluation_interval: 1m + scrape_interval: 1m + scrape_timeout: 10s + target_allocator: + collector_id: ${POD_NAME} + endpoint: http://test-targetallocator:80 + interval: 30s +service: + pipelines: + metrics: + exporters: + - debug + processors: [] + receivers: + - prometheus diff --git a/pkg/collector/testdata/http_sd_config_servicemonitor_test.yaml b/internal/manifests/collector/testdata/http_sd_config_servicemonitor_test.yaml similarity index 91% rename from pkg/collector/testdata/http_sd_config_servicemonitor_test.yaml rename to internal/manifests/collector/testdata/http_sd_config_servicemonitor_test.yaml index 840385d3e7..0cbcb1f0a6 100644 --- a/pkg/collector/testdata/http_sd_config_servicemonitor_test.yaml +++ b/internal/manifests/collector/testdata/http_sd_config_servicemonitor_test.yaml @@ -15,11 +15,11 @@ receivers: - file2.json exporters: - logging: + debug: service: pipelines: metrics: receivers: [prometheus] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/internal/manifests/collector/testdata/http_sd_config_servicemonitor_test_ta_set.yaml b/internal/manifests/collector/testdata/http_sd_config_servicemonitor_test_ta_set.yaml new file mode 100644 index 0000000000..1774e5248a --- /dev/null +++ b/internal/manifests/collector/testdata/http_sd_config_servicemonitor_test_ta_set.yaml @@ -0,0 +1,31 @@ +processors: +receivers: + prometheus: + config: + scrape_configs: + - job_name: serviceMonitor/test/test/0 + + static_configs: + - targets: ["prom.domain:1001", "prom.domain:1002", "prom.domain:1003"] + labels: + my: label + + file_sd_configs: + - files: + - file2.json + target_allocator: + endpoint: http://test-targetallocator:80 + interval: 30s + collector_id: ${POD_NAME} + http_sd_config: + refresh_interval: 60s + +exporters: + debug: + +service: + pipelines: + metrics: + receivers: [prometheus] + processors: [] + exporters: [debug] diff --git a/internal/manifests/collector/testdata/http_sd_config_ta_test.yaml b/internal/manifests/collector/testdata/http_sd_config_ta_test.yaml new file mode 100644 index 0000000000..fb4c8e92b1 --- /dev/null +++ b/internal/manifests/collector/testdata/http_sd_config_ta_test.yaml @@ -0,0 +1,25 @@ +processors: +receivers: + prometheus: + config: + scrape_configs: + - job_name: prometheus + + static_configs: + - targets: ["prom.domain:9001", "prom.domain:9002", "prom.domain:9003"] + labels: + my: label + target_allocator: + collector_id: ${POD_NAME} + endpoint: http://test-sd-targetallocator:80 + interval: 60s + +exporters: + debug: + +service: + pipelines: + metrics: + receivers: [prometheus] + processors: [] + exporters: [debug] diff --git a/pkg/collector/testdata/http_sd_config_test.yaml b/internal/manifests/collector/testdata/http_sd_config_test.yaml similarity index 94% rename from pkg/collector/testdata/http_sd_config_test.yaml rename to internal/manifests/collector/testdata/http_sd_config_test.yaml index 0b82c74e59..361afa093c 100644 --- a/pkg/collector/testdata/http_sd_config_test.yaml +++ b/internal/manifests/collector/testdata/http_sd_config_test.yaml @@ -26,11 +26,11 @@ receivers: - file2.json exporters: - logging: + debug: service: pipelines: metrics: receivers: [prometheus] processors: [] - exporters: [logging] \ No newline at end of file + exporters: [debug] \ No newline at end of file diff --git a/pkg/collector/testdata/ingress_testdata.yaml b/internal/manifests/collector/testdata/ingress_testdata.yaml similarity index 85% rename from pkg/collector/testdata/ingress_testdata.yaml rename to internal/manifests/collector/testdata/ingress_testdata.yaml index 54f9342a76..715d16520a 100644 --- a/pkg/collector/testdata/ingress_testdata.yaml +++ b/internal/manifests/collector/testdata/ingress_testdata.yaml @@ -8,6 +8,9 @@ receivers: protocols: grpc: endpoint: 0.0.0.0:98765 +exporters: + logging: + loglevel: debug service: pipelines: diff --git a/internal/manifests/collector/testdata/prometheus-exporter.yaml b/internal/manifests/collector/testdata/prometheus-exporter.yaml new file mode 100644 index 0000000000..546d900f74 --- /dev/null +++ b/internal/manifests/collector/testdata/prometheus-exporter.yaml @@ -0,0 +1,19 @@ +receivers: + otlp: + protocols: + grpc: + http: + +exporters: + prometheus/prod: + endpoint: 0.0.0.0:8884 + + prometheus/dev: + endpoint: 0.0.0.0:8885 + +service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus/dev, prometheus/prod] diff --git a/internal/manifests/collector/testdata/relabel_config_expected_with_sd_config.yaml b/internal/manifests/collector/testdata/relabel_config_expected_with_sd_config.yaml new file mode 100644 index 0000000000..507feb8028 --- /dev/null +++ b/internal/manifests/collector/testdata/relabel_config_expected_with_sd_config.yaml @@ -0,0 +1,57 @@ +debug: null +exporters: null +processors: null +receivers: + prometheus: + config: + global: + evaluation_interval: 1m + scrape_interval: 1m + scrape_timeout: 10s + scrape_configs: + - honor_labels: true + http_sd_configs: + - url: http://test-targetallocator:80/jobs/service-x/targets?collector_id=$POD_NAME + job_name: service-x + metric_relabel_configs: + - action: keep + regex: (.*) + separator: ; + source_labels: + - label1 + - action: labelmap + regex: (.*) + separator: ; + source_labels: + - label4 + metrics_path: /metrics + relabel_configs: + - action: keep + regex: (.*) + source_labels: + - label1 + - action: replace + regex: (.*) + replacement: $$1_$$2 + separator: ; + source_labels: + - label2 + target_label: label3 + - action: labelmap + regex: (.*) + separator: ; + source_labels: + - label4 + - action: labeldrop + regex: foo_.* + scheme: http + scrape_interval: 1m + scrape_timeout: 10s +service: + pipelines: + metrics: + exporters: + - debug + processors: [] + receivers: + - prometheus diff --git a/internal/manifests/collector/testdata/relabel_config_original.yaml b/internal/manifests/collector/testdata/relabel_config_original.yaml new file mode 100644 index 0000000000..673b4ba4af --- /dev/null +++ b/internal/manifests/collector/testdata/relabel_config_original.yaml @@ -0,0 +1,51 @@ +exporters: +debug: +processors: +receivers: + prometheus: + config: + global: + evaluation_interval: 1m + scrape_interval: 1m + scrape_timeout: 10s + scrape_configs: + - job_name: service-x + metrics_path: /metrics + scheme: http + scrape_interval: 1m + scrape_timeout: 10s + honor_labels: true + relabel_configs: + - source_labels: [label1] + action: keep + regex: (.*) + - target_label: label3 + source_labels: [label2] + action: replace + regex: (.*) + replacement: "$$1_$$2" + separator: ";" + - source_labels: [label4] + action: labelmap + regex: (.*) + separator: ";" + - regex: foo_.* + action: labeldrop + metric_relabel_configs: + - source_labels: [label1] + action: keep + regex: (.*) + separator: ";" + - regex: (.*) + action: labelmap + separator: ";" + source_labels: [label4] + +service: + pipelines: + metrics: + exporters: + - debug + receivers: + - prometheus + processors: [] diff --git a/internal/manifests/collector/testdata/route_crd.go b/internal/manifests/collector/testdata/route_crd.go new file mode 100644 index 0000000000..c32a7f95bd --- /dev/null +++ b/internal/manifests/collector/testdata/route_crd.go @@ -0,0 +1,74 @@ +// 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 testdata + +import ( + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// OpenShiftRouteCRD as go structure. +var OpenShiftRouteCRD = &apiextensionsv1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "routes.route.openshift.io", + }, + Spec: apiextensionsv1.CustomResourceDefinitionSpec{ + Group: "route.openshift.io", + Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ + { + Name: "v1", + Served: true, + Storage: true, + Schema: &apiextensionsv1.CustomResourceValidation{ + OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ + Type: "object", + XPreserveUnknownFields: func(v bool) *bool { return &v }(true), + }, + }, + AdditionalPrinterColumns: []apiextensionsv1.CustomResourceColumnDefinition{ + { + Name: "Host", + Type: "string", + JSONPath: ".status.ingress[0].host", + }, + { + Name: "Admitted", + Type: "string", + JSONPath: `.status.ingress[0].conditions[?(@.type=="Admitted")].status`, + }, + { + Name: "Service", + Type: "string", + JSONPath: ".spec.to.name", + }, + { + Name: "TLS", + Type: "string", + JSONPath: ".spec.tls.type", + }, + }, + Subresources: &apiextensionsv1.CustomResourceSubresources{ + Status: &apiextensionsv1.CustomResourceSubresourceStatus{}, + }, + }, + }, + Scope: apiextensionsv1.NamespaceScoped, + Names: apiextensionsv1.CustomResourceDefinitionNames{ + Plural: "routes", + Singular: "route", + Kind: "Route", + }, + }, +} diff --git a/internal/manifests/collector/testdata/sm_crd.go b/internal/manifests/collector/testdata/sm_crd.go new file mode 100644 index 0000000000..cb7322dbf6 --- /dev/null +++ b/internal/manifests/collector/testdata/sm_crd.go @@ -0,0 +1,38 @@ +package testdata + +import ( + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ServiceMonitorCRD as go structure. +var ServiceMonitorCRD = &apiextensionsv1.CustomResourceDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: "servicemonitors.monitoring.coreos.com", + }, + Spec: apiextensionsv1.CustomResourceDefinitionSpec{ + Group: "monitoring.coreos.com", + Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ + { + Name: "v1", + Served: true, + Storage: true, + Schema: &apiextensionsv1.CustomResourceValidation{ + OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ + Type: "object", + XPreserveUnknownFields: func(v bool) *bool { return &v }(true), + }, + }, + Subresources: &apiextensionsv1.CustomResourceSubresources{ + Status: &apiextensionsv1.CustomResourceSubresourceStatus{}, + }, + }, + }, + Scope: apiextensionsv1.NamespaceScoped, + Names: apiextensionsv1.CustomResourceDefinitionNames{ + Plural: "servicemonitors", + Singular: "servicemonitor", + Kind: "ServiceMonitor", + }, + }, +} diff --git a/pkg/collector/testdata/test.yaml b/internal/manifests/collector/testdata/test.yaml similarity index 90% rename from pkg/collector/testdata/test.yaml rename to internal/manifests/collector/testdata/test.yaml index c920cee82d..4e95f681d4 100644 --- a/pkg/collector/testdata/test.yaml +++ b/internal/manifests/collector/testdata/test.yaml @@ -12,11 +12,11 @@ receivers: - targets: [ '0.0.0.0:8888', '0.0.0.0:9999' ] exporters: - logging: + debug: service: pipelines: metrics: receivers: [prometheus, jaeger] processors: [] - exporters: [logging] \ No newline at end of file + exporters: [debug] \ No newline at end of file diff --git a/pkg/collector/utils.go b/internal/manifests/collector/utils.go similarity index 93% rename from pkg/collector/utils.go rename to internal/manifests/collector/utils.go index 813910bc45..36d6833455 100644 --- a/pkg/collector/utils.go +++ b/internal/manifests/collector/utils.go @@ -20,7 +20,7 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" ) -func getDnsPolicy(otelcol v1alpha1.OpenTelemetryCollector) corev1.DNSPolicy { +func getDNSPolicy(otelcol v1alpha1.OpenTelemetryCollector) corev1.DNSPolicy { dnsPolicy := corev1.DNSClusterFirst if otelcol.Spec.HostNetwork { dnsPolicy = corev1.DNSClusterFirstWithHostNet diff --git a/pkg/collector/volume.go b/internal/manifests/collector/volume.go similarity index 73% rename from pkg/collector/volume.go rename to internal/manifests/collector/volume.go index b6fb467fa2..6b014eba80 100644 --- a/pkg/collector/volume.go +++ b/internal/manifests/collector/volume.go @@ -20,7 +20,7 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) // Volumes builds the volumes for the given instance, including the config map volume. @@ -29,7 +29,7 @@ func Volumes(cfg config.Config, otelcol v1alpha1.OpenTelemetryCollector) []corev Name: naming.ConfigMapVolume(), VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: naming.ConfigMap(otelcol)}, + LocalObjectReference: corev1.LocalObjectReference{Name: naming.ConfigMap(otelcol.Name)}, Items: []corev1.KeyToPath{{ Key: cfg.CollectorConfigMapEntry(), Path: cfg.CollectorConfigMapEntry(), @@ -42,5 +42,20 @@ func Volumes(cfg config.Config, otelcol v1alpha1.OpenTelemetryCollector) []corev volumes = append(volumes, otelcol.Spec.Volumes...) } + if len(otelcol.Spec.ConfigMaps) > 0 { + for keyCfgMap := range otelcol.Spec.ConfigMaps { + volumes = append(volumes, corev1.Volume{ + Name: naming.ConfigMapExtra(otelcol.Spec.ConfigMaps[keyCfgMap].Name), + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: otelcol.Spec.ConfigMaps[keyCfgMap].Name, + }, + }, + }, + }) + } + } + return volumes } diff --git a/pkg/collector/volume_test.go b/internal/manifests/collector/volume_test.go similarity index 66% rename from pkg/collector/volume_test.go rename to internal/manifests/collector/volume_test.go index 44ae84395f..6232b39c4b 100644 --- a/pkg/collector/volume_test.go +++ b/internal/manifests/collector/volume_test.go @@ -22,8 +22,8 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + . "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) func TestVolumeNewDefault(t *testing.T) { @@ -61,3 +61,29 @@ func TestVolumeAllowsMoreToBeAdded(t *testing.T) { // check that it's the otc-internal volume, with the config map assert.Equal(t, "my-volume", volumes[1].Name) } + +func TestVolumeWithMoreConfigMaps(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + ConfigMaps: []v1alpha1.ConfigMapsSpec{{ + Name: "configmap-test", + MountPath: "/", + }, { + Name: "configmap-test2", + MountPath: "/dir", + }}, + }, + } + cfg := config.New() + + // test + volumes := Volumes(cfg, otelcol) + + // verify + assert.Len(t, volumes, 3) + + // check if the volume with the configmap prefix is mounted after defining the config map. + assert.Equal(t, "configmap-configmap-test", volumes[1].Name) + assert.Equal(t, "configmap-configmap-test2", volumes[2].Name) +} diff --git a/internal/manifests/collector/volumeclaim.go b/internal/manifests/collector/volumeclaim.go new file mode 100644 index 0000000000..c641a38317 --- /dev/null +++ b/internal/manifests/collector/volumeclaim.go @@ -0,0 +1,33 @@ +// 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 collector handles the OpenTelemetry Collector. +package collector + +import ( + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +// VolumeClaimTemplates builds the volumeClaimTemplates for the given instance, +// including the config map volume mount. +func VolumeClaimTemplates(otelcol v1alpha1.OpenTelemetryCollector) []corev1.PersistentVolumeClaim { + if otelcol.Spec.Mode != "statefulset" { + return []corev1.PersistentVolumeClaim{} + } + + // Add all user specified claims. + return otelcol.Spec.VolumeClaimTemplates +} diff --git a/pkg/collector/volumeclaim_test.go b/internal/manifests/collector/volumeclaim_test.go similarity index 71% rename from pkg/collector/volumeclaim_test.go rename to internal/manifests/collector/volumeclaim_test.go index 360ba906cc..e7989ca243 100644 --- a/pkg/collector/volumeclaim_test.go +++ b/internal/manifests/collector/volumeclaim_test.go @@ -23,35 +23,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" + . "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" ) -func TestVolumeClaimNewDefault(t *testing.T) { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: "statefulset", - }, - } - cfg := config.New() - - // test - volumeClaims := VolumeClaimTemplates(cfg, otelcol) - - // verify - assert.Len(t, volumeClaims, 1) - - // check that it's the initial-volume - assert.Equal(t, "default-volume", volumeClaims[0].Name) - - // check the access mode is correct - assert.Equal(t, corev1.PersistentVolumeAccessMode("ReadWriteOnce"), volumeClaims[0].Spec.AccessModes[0]) - - //check the storage is correct - assert.Equal(t, resource.MustParse("50Mi"), volumeClaims[0].Spec.Resources.Requests["storage"]) -} - func TestVolumeClaimAllowsUserToAdd(t *testing.T) { // prepare otelcol := v1alpha1.OpenTelemetryCollector{ @@ -70,10 +44,9 @@ func TestVolumeClaimAllowsUserToAdd(t *testing.T) { }}, }, } - cfg := config.New() // test - volumeClaims := VolumeClaimTemplates(cfg, otelcol) + volumeClaims := VolumeClaimTemplates(otelcol) // verify that volume claim replaces assert.Len(t, volumeClaims, 1) @@ -84,7 +57,7 @@ func TestVolumeClaimAllowsUserToAdd(t *testing.T) { // check the access mode is correct assert.Equal(t, corev1.PersistentVolumeAccessMode("ReadWriteOnce"), volumeClaims[0].Spec.AccessModes[0]) - //check the storage is correct + // check the storage is correct assert.Equal(t, resource.MustParse("1Gi"), volumeClaims[0].Spec.Resources.Requests["storage"]) } @@ -106,10 +79,9 @@ func TestVolumeClaimChecksForStatefulset(t *testing.T) { }}, }, } - cfg := config.New() // test - volumeClaims := VolumeClaimTemplates(cfg, otelcol) + volumeClaims := VolumeClaimTemplates(otelcol) // verify that volume claim replaces assert.Len(t, volumeClaims, 0) diff --git a/pkg/collector/labels.go b/internal/manifests/manifestutils/labels.go similarity index 61% rename from pkg/collector/labels.go rename to internal/manifests/manifestutils/labels.go index 311ba6f7e9..cfcf3d2430 100644 --- a/pkg/collector/labels.go +++ b/internal/manifests/manifestutils/labels.go @@ -12,14 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package collector +package manifestutils import ( "regexp" "strings" - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) func isFilteredLabel(label string, filterLabels []string) bool { @@ -27,12 +28,12 @@ func isFilteredLabel(label string, filterLabels []string) bool { match, _ := regexp.MatchString(pattern, label) return match } - return false } -// Labels return the common labels to all objects that are part of a managed OpenTelemetryCollector. -func Labels(instance v1alpha1.OpenTelemetryCollector, filterLabels []string) map[string]string { +// Labels return the common labels to all objects that are part of a managed CR. +func Labels(instance metav1.ObjectMeta, name string, image string, component string, filterLabels []string) map[string]string { + var versionLabel string // new map every time, so that we don't touch the instance's label base := map[string]string{} if nil != instance.Labels { @@ -43,28 +44,40 @@ func Labels(instance v1alpha1.OpenTelemetryCollector, filterLabels []string) map } } - for k, v := range SelectorLabels(instance) { + for k, v := range SelectorLabels(instance, component) { base[k] = v } - version := strings.Split(instance.Spec.Image, ":") - if len(version) > 1 { - base["app.kubernetes.io/version"] = version[len(version)-1] - } else { + version := strings.Split(image, ":") + for _, v := range version { + if strings.HasSuffix(v, "@sha256") { + versionLabel = strings.TrimSuffix(v, "@sha256") + } + } + switch lenVersion := len(version); lenVersion { + case 3: + base["app.kubernetes.io/version"] = versionLabel + case 2: + base["app.kubernetes.io/version"] = naming.Truncate("%s", 63, version[len(version)-1]) + default: base["app.kubernetes.io/version"] = "latest" } + // Don't override the app name if it already exists + if _, ok := base["app.kubernetes.io/name"]; !ok { + base["app.kubernetes.io/name"] = name + } return base } -// SelectorLabels return the common labels to all objects that are part of a managed OpenTelemetryCollector to use as selector. +// SelectorLabels return the common labels to all objects that are part of a managed CR to use as selector. // Selector labels are immutable for Deployment, StatefulSet and DaemonSet, therefore, no labels in selector should be // expected to be modified for the lifetime of the object. -func SelectorLabels(instance v1alpha1.OpenTelemetryCollector) map[string]string { +func SelectorLabels(instance metav1.ObjectMeta, component string) map[string]string { return map[string]string{ "app.kubernetes.io/managed-by": "opentelemetry-operator", "app.kubernetes.io/instance": naming.Truncate("%s.%s", 63, instance.Namespace, instance.Name), "app.kubernetes.io/part-of": "opentelemetry", - "app.kubernetes.io/component": "opentelemetry-collector", + "app.kubernetes.io/component": component, } } diff --git a/pkg/collector/labels_test.go b/internal/manifests/manifestutils/labels_test.go similarity index 51% rename from pkg/collector/labels_test.go rename to internal/manifests/manifestutils/labels_test.go index 2116b9f192..4a0bd794de 100644 --- a/pkg/collector/labels_test.go +++ b/internal/manifests/manifestutils/labels_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package collector_test +package manifestutils import ( "testing" @@ -21,15 +21,19 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" +) + +const ( + collectorName = "my-instance" + collectorNamespace = "my-ns" ) func TestLabelsCommonSet(t *testing.T) { // prepare otelcol := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-ns", + Name: collectorName, + Namespace: collectorNamespace, }, Spec: v1alpha1.OpenTelemetryCollectorSpec{ Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.47.0", @@ -37,20 +41,58 @@ func TestLabelsCommonSet(t *testing.T) { } // test - labels := Labels(otelcol, []string{}) + labels := Labels(otelcol.ObjectMeta, collectorName, otelcol.Spec.Image, "opentelemetry-collector", []string{}) assert.Equal(t, "opentelemetry-operator", labels["app.kubernetes.io/managed-by"]) assert.Equal(t, "my-ns.my-instance", labels["app.kubernetes.io/instance"]) assert.Equal(t, "0.47.0", labels["app.kubernetes.io/version"]) assert.Equal(t, "opentelemetry", labels["app.kubernetes.io/part-of"]) assert.Equal(t, "opentelemetry-collector", labels["app.kubernetes.io/component"]) } +func TestLabelsSha256Set(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: collectorName, + Namespace: collectorNamespace, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator@sha256:c6671841470b83007e0553cdadbc9d05f6cfe17b3ebe9733728dc4a579a5b532", + }, + } + + // test + labels := Labels(otelcol.ObjectMeta, collectorName, otelcol.Spec.Image, "opentelemetry-collector", []string{}) + assert.Equal(t, "opentelemetry-operator", labels["app.kubernetes.io/managed-by"]) + assert.Equal(t, "my-ns.my-instance", labels["app.kubernetes.io/instance"]) + assert.Equal(t, "c6671841470b83007e0553cdadbc9d05f6cfe17b3ebe9733728dc4a579a5b53", labels["app.kubernetes.io/version"]) + assert.Equal(t, "opentelemetry", labels["app.kubernetes.io/part-of"]) + assert.Equal(t, "opentelemetry-collector", labels["app.kubernetes.io/component"]) + // prepare + otelcolTag := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: collectorName, + Namespace: collectorNamespace, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.81.0@sha256:c6671841470b83007e0553cdadbc9d05f6cfe17b3ebe9733728dc4a579a5b532", + }, + } + + // test + labelsTag := Labels(otelcolTag.ObjectMeta, collectorName, otelcolTag.Spec.Image, "opentelemetry-collector", []string{}) + assert.Equal(t, "opentelemetry-operator", labelsTag["app.kubernetes.io/managed-by"]) + assert.Equal(t, "my-ns.my-instance", labelsTag["app.kubernetes.io/instance"]) + assert.Equal(t, "0.81.0", labelsTag["app.kubernetes.io/version"]) + assert.Equal(t, "opentelemetry", labelsTag["app.kubernetes.io/part-of"]) + assert.Equal(t, "opentelemetry-collector", labelsTag["app.kubernetes.io/component"]) +} func TestLabelsTagUnset(t *testing.T) { // prepare otelcol := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-ns", + Name: collectorName, + Namespace: collectorNamespace, }, Spec: v1alpha1.OpenTelemetryCollectorSpec{ Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator", @@ -58,7 +100,7 @@ func TestLabelsTagUnset(t *testing.T) { } // test - labels := Labels(otelcol, []string{}) + labels := Labels(otelcol.ObjectMeta, collectorName, otelcol.Spec.Image, "opentelemetry-collector", []string{}) assert.Equal(t, "opentelemetry-operator", labels["app.kubernetes.io/managed-by"]) assert.Equal(t, "my-ns.my-instance", labels["app.kubernetes.io/instance"]) assert.Equal(t, "latest", labels["app.kubernetes.io/version"]) @@ -70,16 +112,23 @@ func TestLabelsPropagateDown(t *testing.T) { // prepare otelcol := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"myapp": "mycomponent"}, + Labels: map[string]string{ + "myapp": "mycomponent", + "app.kubernetes.io/name": "test", + }, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator", }, } // test - labels := Labels(otelcol, []string{}) + labels := Labels(otelcol.ObjectMeta, collectorName, otelcol.Spec.Image, "opentelemetry-collector", []string{}) // verify - assert.Len(t, labels, 6) + assert.Len(t, labels, 7) assert.Equal(t, "mycomponent", labels["myapp"]) + assert.Equal(t, "test", labels["app.kubernetes.io/name"]) } func TestLabelsFilter(t *testing.T) { @@ -87,13 +136,16 @@ func TestLabelsFilter(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"test.bar.io": "foo", "test.foo.io": "bar"}, }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator", + }, } // This requires the filter to be in regex match form and not the other simpler wildcard one. - labels := Labels(otelcol, []string{".*.bar.io"}) + labels := Labels(otelcol.ObjectMeta, collectorName, otelcol.Spec.Image, "opentelemetry-collector", []string{".*.bar.io"}) // verify - assert.Len(t, labels, 6) + assert.Len(t, labels, 7) assert.NotContains(t, labels, "test.bar.io") assert.Equal(t, "bar", labels["test.foo.io"]) } @@ -111,7 +163,7 @@ func TestSelectorLabels(t *testing.T) { } // test - result := SelectorLabels(otelcol) + result := SelectorLabels(otelcol.ObjectMeta, "opentelemetry-collector") // verify assert.Equal(t, expected, result) diff --git a/internal/manifests/mutate.go b/internal/manifests/mutate.go new file mode 100644 index 0000000000..09f48daf52 --- /dev/null +++ b/internal/manifests/mutate.go @@ -0,0 +1,366 @@ +// 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 manifests + +import ( + "errors" + "fmt" + "reflect" + + "dario.cat/mergo" + routev1 "github.com/openshift/api/route/v1" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + appsv1 "k8s.io/api/apps/v1" + autoscalingv2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" + networkingv1 "k8s.io/api/networking/v1" + policyV1 "k8s.io/api/policy/v1" + rbacv1 "k8s.io/api/rbac/v1" + apiequality "k8s.io/apimachinery/pkg/api/equality" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +var ( + ImmutableChangeErr = errors.New("immutable field change attempted") +) + +// MutateFuncFor returns a mutate function based on the +// existing resource's concrete type. It supports currently +// only the following types or else panics: +// - ConfigMap +// - Service +// - ServiceAccount +// - ClusterRole +// - ClusterRoleBinding +// - Role +// - RoleBinding +// - Deployment +// - DaemonSet +// - StatefulSet +// - ServiceMonitor +// - Ingress +// - HorizontalPodAutoscaler +// - Route +// - Secret +// In order for the operator to reconcile other types, they must be added here. +// The function returned takes no arguments but instead uses the existing and desired inputs here. Existing is expected +// to be set by the controller-runtime package through a client get call. +func MutateFuncFor(existing, desired client.Object) controllerutil.MutateFn { + return func() error { + // Get the existing annotations and override any conflicts with the desired annotations + // This will preserve any annotations on the existing set. + existingAnnotations := existing.GetAnnotations() + if err := mergeWithOverride(&existingAnnotations, desired.GetAnnotations()); err != nil { + return err + } + existing.SetAnnotations(existingAnnotations) + + // Get the existing labels and override any conflicts with the desired labels + // This will preserve any labels on the existing set. + existingLabels := existing.GetLabels() + if err := mergeWithOverride(&existingLabels, desired.GetLabels()); err != nil { + return err + } + existing.SetLabels(existingLabels) + + if ownerRefs := desired.GetOwnerReferences(); len(ownerRefs) > 0 { + existing.SetOwnerReferences(ownerRefs) + } + + switch existing.(type) { + case *corev1.ConfigMap: + cm := existing.(*corev1.ConfigMap) + wantCm := desired.(*corev1.ConfigMap) + mutateConfigMap(cm, wantCm) + + case *corev1.Service: + svc := existing.(*corev1.Service) + wantSvc := desired.(*corev1.Service) + return mutateService(svc, wantSvc) + + case *corev1.ServiceAccount: + sa := existing.(*corev1.ServiceAccount) + wantSa := desired.(*corev1.ServiceAccount) + mutateServiceAccount(sa, wantSa) + + case *rbacv1.ClusterRole: + cr := existing.(*rbacv1.ClusterRole) + wantCr := desired.(*rbacv1.ClusterRole) + mutateClusterRole(cr, wantCr) + + case *rbacv1.ClusterRoleBinding: + crb := existing.(*rbacv1.ClusterRoleBinding) + wantCrb := desired.(*rbacv1.ClusterRoleBinding) + mutateClusterRoleBinding(crb, wantCrb) + + case *rbacv1.Role: + r := existing.(*rbacv1.Role) + wantR := desired.(*rbacv1.Role) + mutateRole(r, wantR) + + case *rbacv1.RoleBinding: + rb := existing.(*rbacv1.RoleBinding) + wantRb := desired.(*rbacv1.RoleBinding) + mutateRoleBinding(rb, wantRb) + + case *appsv1.Deployment: + dpl := existing.(*appsv1.Deployment) + wantDpl := desired.(*appsv1.Deployment) + return mutateDeployment(dpl, wantDpl) + + case *appsv1.DaemonSet: + dpl := existing.(*appsv1.DaemonSet) + wantDpl := desired.(*appsv1.DaemonSet) + return mutateDaemonset(dpl, wantDpl) + + case *appsv1.StatefulSet: + sts := existing.(*appsv1.StatefulSet) + wantSts := desired.(*appsv1.StatefulSet) + return mutateStatefulSet(sts, wantSts) + + case *monitoringv1.ServiceMonitor: + svcMonitor := existing.(*monitoringv1.ServiceMonitor) + wantSvcMonitor := desired.(*monitoringv1.ServiceMonitor) + mutateServiceMonitor(svcMonitor, wantSvcMonitor) + + case *monitoringv1.PodMonitor: + podMonitor := existing.(*monitoringv1.PodMonitor) + wantPodMonitor := desired.(*monitoringv1.PodMonitor) + mutatePodMonitor(podMonitor, wantPodMonitor) + + case *networkingv1.Ingress: + ing := existing.(*networkingv1.Ingress) + wantIng := desired.(*networkingv1.Ingress) + mutateIngress(ing, wantIng) + + case *autoscalingv2.HorizontalPodAutoscaler: + existingHPA := existing.(*autoscalingv2.HorizontalPodAutoscaler) + desiredHPA := desired.(*autoscalingv2.HorizontalPodAutoscaler) + mutateAutoscalingHPA(existingHPA, desiredHPA) + + case *policyV1.PodDisruptionBudget: + existingPDB := existing.(*policyV1.PodDisruptionBudget) + desiredPDB := desired.(*policyV1.PodDisruptionBudget) + mutatePolicyV1PDB(existingPDB, desiredPDB) + + case *routev1.Route: + rt := existing.(*routev1.Route) + wantRt := desired.(*routev1.Route) + mutateRoute(rt, wantRt) + + case *corev1.Secret: + pr := existing.(*corev1.Secret) + wantPr := desired.(*corev1.Secret) + mutateSecret(pr, wantPr) + + default: + t := reflect.TypeOf(existing).String() + return fmt.Errorf("missing mutate implementation for resource type: %s", t) + } + return nil + } +} + +func mergeWithOverride(dst, src interface{}) error { + return mergo.Merge(dst, src, mergo.WithOverride) +} + +func mutateSecret(existing, desired *corev1.Secret) { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Data = desired.Data +} + +func mutateConfigMap(existing, desired *corev1.ConfigMap) { + existing.BinaryData = desired.BinaryData + existing.Data = desired.Data +} + +func mutateServiceAccount(existing, desired *corev1.ServiceAccount) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels +} + +func mutateClusterRole(existing, desired *rbacv1.ClusterRole) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels + existing.Rules = desired.Rules +} + +func mutateClusterRoleBinding(existing, desired *rbacv1.ClusterRoleBinding) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels + existing.Subjects = desired.Subjects +} + +func mutateRole(existing, desired *rbacv1.Role) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels + existing.Rules = desired.Rules +} + +func mutateRoleBinding(existing, desired *rbacv1.RoleBinding) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels + existing.Subjects = desired.Subjects +} + +func mutateAutoscalingHPA(existing, desired *autoscalingv2.HorizontalPodAutoscaler) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels + existing.Spec = desired.Spec +} + +func mutatePolicyV1PDB(existing, desired *policyV1.PodDisruptionBudget) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels + existing.Spec = desired.Spec +} + +func mutateIngress(existing, desired *networkingv1.Ingress) { + existing.Labels = desired.Labels + existing.Annotations = desired.Annotations + existing.Spec.DefaultBackend = desired.Spec.DefaultBackend + existing.Spec.Rules = desired.Spec.Rules + existing.Spec.TLS = desired.Spec.TLS +} + +func mutateRoute(existing, desired *routev1.Route) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels + existing.Spec = desired.Spec +} + +func mutateServiceMonitor(existing, desired *monitoringv1.ServiceMonitor) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels + existing.Spec = desired.Spec +} + +func mutatePodMonitor(existing, desired *monitoringv1.PodMonitor) { + existing.Annotations = desired.Annotations + existing.Labels = desired.Labels + existing.Spec = desired.Spec +} + +func mutateService(existing, desired *corev1.Service) error { + existing.Spec.Ports = desired.Spec.Ports + if err := mergeWithOverride(&existing.Spec.Selector, desired.Spec.Selector); err != nil { + return err + } + return nil +} + +func mutateDaemonset(existing, desired *appsv1.DaemonSet) error { + if !existing.CreationTimestamp.IsZero() && !apiequality.Semantic.DeepEqual(desired.Spec.Selector, existing.Spec.Selector) { + return ImmutableChangeErr + } + // Daemonset selector is immutable so we set this value only if + // a new object is going to be created + if existing.CreationTimestamp.IsZero() { + existing.Spec.Selector = desired.Spec.Selector + } + + if err := mergeWithOverride(&existing.Spec, desired.Spec); err != nil { + return err + } + return nil +} + +func mutateDeployment(existing, desired *appsv1.Deployment) error { + if !existing.CreationTimestamp.IsZero() && !apiequality.Semantic.DeepEqual(desired.Spec.Selector, existing.Spec.Selector) { + return ImmutableChangeErr + } + // Deployment selector is immutable so we set this value only if + // a new object is going to be created + if existing.CreationTimestamp.IsZero() { + existing.Spec.Selector = desired.Spec.Selector + } + existing.Spec.Replicas = desired.Spec.Replicas + if err := mergeWithOverride(&existing.Spec.Template, desired.Spec.Template); err != nil { + return err + } + if err := mergeWithOverride(&existing.Spec.Strategy, desired.Spec.Strategy); err != nil { + return err + } + return nil +} + +func mutateStatefulSet(existing, desired *appsv1.StatefulSet) error { + if hasChange, field := hasImmutableFieldChange(existing, desired); hasChange { + return fmt.Errorf("%s is being changed, %w", field, ImmutableChangeErr) + } + // StatefulSet selector is immutable so we set this value only if + // a new object is going to be created + if existing.CreationTimestamp.IsZero() { + existing.Spec.Selector = desired.Spec.Selector + } + existing.Spec.PodManagementPolicy = desired.Spec.PodManagementPolicy + existing.Spec.Replicas = desired.Spec.Replicas + + for i := range existing.Spec.VolumeClaimTemplates { + existing.Spec.VolumeClaimTemplates[i].TypeMeta = desired.Spec.VolumeClaimTemplates[i].TypeMeta + existing.Spec.VolumeClaimTemplates[i].ObjectMeta = desired.Spec.VolumeClaimTemplates[i].ObjectMeta + existing.Spec.VolumeClaimTemplates[i].Spec = desired.Spec.VolumeClaimTemplates[i].Spec + } + if err := mergeWithOverride(&existing.Spec.Template, desired.Spec.Template); err != nil { + return err + } + return nil +} + +func hasImmutableFieldChange(existing, desired *appsv1.StatefulSet) (bool, string) { + if existing.CreationTimestamp.IsZero() { + return false, "" + } + if !apiequality.Semantic.DeepEqual(desired.Spec.Selector, existing.Spec.Selector) { + return true, fmt.Sprintf("Spec.Selector: desired: %s existing: %s", desired.Spec.Selector, existing.Spec.Selector) + } + + if hasVolumeClaimsTemplatesChanged(existing, desired) { + return true, "Spec.VolumeClaimTemplates" + } + + return false, "" +} + +// hasVolumeClaimsTemplatesChanged if volume claims template change has been detected. +// We need to do this manually due to some fields being automatically filled by the API server +// and these needs to be excluded from the comparison to prevent false positives. +func hasVolumeClaimsTemplatesChanged(existing, desired *appsv1.StatefulSet) bool { + if len(desired.Spec.VolumeClaimTemplates) != len(existing.Spec.VolumeClaimTemplates) { + return true + } + + for i := range desired.Spec.VolumeClaimTemplates { + // VolumeMode is automatically set by the API server, so if it is not set in the CR, assume it's the same as the existing one. + if desired.Spec.VolumeClaimTemplates[i].Spec.VolumeMode == nil || *desired.Spec.VolumeClaimTemplates[i].Spec.VolumeMode == "" { + desired.Spec.VolumeClaimTemplates[i].Spec.VolumeMode = existing.Spec.VolumeClaimTemplates[i].Spec.VolumeMode + } + + if desired.Spec.VolumeClaimTemplates[i].Name != existing.Spec.VolumeClaimTemplates[i].Name { + return true + } + if !apiequality.Semantic.DeepEqual(desired.Spec.VolumeClaimTemplates[i].Annotations, existing.Spec.VolumeClaimTemplates[i].Annotations) { + return true + } + if !apiequality.Semantic.DeepEqual(desired.Spec.VolumeClaimTemplates[i].Spec, existing.Spec.VolumeClaimTemplates[i].Spec) { + return true + } + } + + return false +} diff --git a/internal/manifests/opampbridge/configmap.go b/internal/manifests/opampbridge/configmap.go new file mode 100644 index 0000000000..9f16ef5a6d --- /dev/null +++ b/internal/manifests/opampbridge/configmap.go @@ -0,0 +1,75 @@ +// 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 opampbridge + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "strings" + + "gopkg.in/yaml.v2" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func ConfigMap(params manifests.Params) (*corev1.ConfigMap, error) { + name := naming.OpAMPBridgeConfigMap(params.OpAMPBridge.Name) + version := strings.Split(params.OpAMPBridge.Spec.Image, ":") + labels := manifestutils.Labels(params.OpAMPBridge.ObjectMeta, name, params.OpAMPBridge.Spec.Image, ComponentOpAMPBridge, []string{}) + + if len(version) > 1 { + labels["app.kubernetes.io/version"] = version[len(version)-1] + } else { + labels["app.kubernetes.io/version"] = "latest" + } + + config := make(map[interface{}]interface{}) + + if len(params.OpAMPBridge.Spec.Endpoint) > 0 { + config["endpoint"] = params.OpAMPBridge.Spec.Endpoint + } + + if len(params.OpAMPBridge.Spec.Headers) > 0 { + config["headers"] = params.OpAMPBridge.Spec.Headers + } + + if params.OpAMPBridge.Spec.Capabilities != nil { + config["capabilities"] = params.OpAMPBridge.Spec.Capabilities + } + + if params.OpAMPBridge.Spec.ComponentsAllowed != nil { + config["componentsAllowed"] = params.OpAMPBridge.Spec.ComponentsAllowed + } + + configYAML, err := yaml.Marshal(config) + if err != nil { + return &corev1.ConfigMap{}, err + } + + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OpAMPBridge.Namespace, + Labels: labels, + Annotations: params.OpAMPBridge.Annotations, + }, + Data: map[string]string{ + "remoteconfiguration.yaml": string(configYAML), + }, + }, nil +} diff --git a/internal/manifests/opampbridge/configmap_test.go b/internal/manifests/opampbridge/configmap_test.go new file mode 100644 index 0000000000..221bd275dc --- /dev/null +++ b/internal/manifests/opampbridge/configmap_test.go @@ -0,0 +1,107 @@ +// 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 opampbridge + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "testing" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + + "github.com/stretchr/testify/assert" +) + +func TestDesiredConfigMap(t *testing.T) { + expectedLables := map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/instance": "my-namespace.my-instance", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "0.69.0", + } + + t.Run("should return expected opamp-bridge config map", func(t *testing.T) { + expectedLables["app.kubernetes.io/component"] = "opentelemetry-opamp-bridge" + expectedLables["app.kubernetes.io/name"] = "my-instance-opamp-bridge" + + expectedData := map[string]string{ + "remoteconfiguration.yaml": `capabilities: + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRemoteConfig: true + AcceptsRestartCommand: true + ReportsEffectiveConfig: true + ReportsHealth: true + ReportsOwnLogs: true + ReportsOwnMetrics: true + ReportsOwnTraces: true + ReportsRemoteConfig: true + ReportsStatus: true +componentsAllowed: + exporters: + - logging + processors: + - memory_limiter + receivers: + - otlp +endpoint: ws://opamp-server:4320/v1/opamp +headers: + authorization: access-12345-token +`} + + opampBridge := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + Image: "ghcr.io/open-telemetry/opentelemetry-operator/operator-opamp-bridge:0.69.0", + Endpoint: "ws://opamp-server:4320/v1/opamp", + Headers: map[string]string{"authorization": "access-12345-token"}, + Capabilities: map[v1alpha1.OpAMPBridgeCapability]bool{ + v1alpha1.OpAMPBridgeCapabilityReportsStatus: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsRemoteConfig: true, + v1alpha1.OpAMPBridgeCapabilityReportsEffectiveConfig: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnTraces: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnMetrics: true, + v1alpha1.OpAMPBridgeCapabilityReportsOwnLogs: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsOpAMPConnectionSettings: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsOtherConnectionSettings: true, + v1alpha1.OpAMPBridgeCapabilityAcceptsRestartCommand: true, + v1alpha1.OpAMPBridgeCapabilityReportsHealth: true, + v1alpha1.OpAMPBridgeCapabilityReportsRemoteConfig: true, + }, + ComponentsAllowed: map[string][]string{"receivers": {"otlp"}, "processors": {"memory_limiter"}, "exporters": {"logging"}}, + }, + } + + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge, + Log: logger, + } + + actual, err := ConfigMap(params) + assert.NoError(t, err) + + assert.Equal(t, "my-instance-opamp-bridge", actual.Name) + assert.Equal(t, expectedLables, actual.Labels) + assert.Equal(t, expectedData, actual.Data) + }) +} diff --git a/internal/manifests/opampbridge/container.go b/internal/manifests/opampbridge/container.go new file mode 100644 index 0000000000..131eb040d5 --- /dev/null +++ b/internal/manifests/opampbridge/container.go @@ -0,0 +1,77 @@ +// 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 opampbridge + +import ( + "github.com/go-logr/logr" + "github.com/operator-framework/operator-lib/proxy" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// Container builds a container for the given OpAMPBridge. +func Container(cfg config.Config, logger logr.Logger, opampBridge v1alpha1.OpAMPBridge) corev1.Container { + image := opampBridge.Spec.Image + if len(image) == 0 { + image = cfg.OperatorOpAMPBridgeImage() + } + + volumeMounts := []corev1.VolumeMount{{ + Name: naming.OpAMPBridgeConfigMapVolume(), + MountPath: "/conf", + }} + + if len(opampBridge.Spec.VolumeMounts) > 0 { + volumeMounts = append(volumeMounts, opampBridge.Spec.VolumeMounts...) + } + + var envVars = opampBridge.Spec.Env + if opampBridge.Spec.Env == nil { + envVars = []corev1.EnvVar{} + } + + idx := -1 + for i := range envVars { + if envVars[i].Name == "OTELCOL_NAMESPACE" { + idx = i + } + } + if idx == -1 { + envVars = append(envVars, corev1.EnvVar{ + Name: "OTELCOL_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }) + } + + envVars = append(envVars, proxy.ReadProxyVarsFromEnv()...) + + return corev1.Container{ + Name: naming.OpAMPBridgeContainer(), + Image: image, + ImagePullPolicy: opampBridge.Spec.ImagePullPolicy, + Env: envVars, + VolumeMounts: volumeMounts, + EnvFrom: opampBridge.Spec.EnvFrom, + Resources: opampBridge.Spec.Resources, + SecurityContext: opampBridge.Spec.SecurityContext, + } +} diff --git a/pkg/targetallocator/container_test.go b/internal/manifests/opampbridge/container_test.go similarity index 61% rename from pkg/targetallocator/container_test.go rename to internal/manifests/opampbridge/container_test.go index 504e6526cd..03f87d9cfb 100644 --- a/pkg/targetallocator/container_test.go +++ b/internal/manifests/opampbridge/container_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package targetallocator +package opampbridge import ( "testing" @@ -22,18 +22,18 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) var logger = logf.Log.WithName("unit-tests") func TestContainerNewDefault(t *testing.T) { // prepare - otelcol := v1alpha1.OpenTelemetryCollector{} - cfg := config.New(config.WithTargetAllocatorImage("default-image")) + opampBridge := v1alpha1.OpAMPBridge{} + cfg := config.New(config.WithOperatorOpAMPBridgeImage("default-image")) // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, opampBridge) // verify assert.Equal(t, "default-image", c.Image) @@ -41,15 +41,13 @@ func TestContainerNewDefault(t *testing.T) { func TestContainerWithImageOverridden(t *testing.T) { // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ - Enabled: true, - Image: "overridden-image", - }, + otelcol := v1alpha1.OpAMPBridge{ + Spec: v1alpha1.OpAMPBridgeSpec{ + Image: "overridden-image", }, } - cfg := config.New(config.WithTargetAllocatorImage("default-image")) + + cfg := config.New(config.WithOperatorOpAMPBridgeImage("default-image")) // test c := Container(cfg, logger, otelcol) @@ -60,20 +58,17 @@ func TestContainerWithImageOverridden(t *testing.T) { func TestContainerVolumes(t *testing.T) { // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ - Enabled: true, - Image: "default-image", - }, + opampBridge := v1alpha1.OpAMPBridge{ + Spec: v1alpha1.OpAMPBridgeSpec{ + Image: "default-image", }, } cfg := config.New() // test - c := Container(cfg, logger, otelcol) + c := Container(cfg, logger, opampBridge) // verify assert.Len(t, c.VolumeMounts, 1) - assert.Equal(t, naming.TAConfigMapVolume(), c.VolumeMounts[0].Name) + assert.Equal(t, naming.OpAMPBridgeConfigMapVolume(), c.VolumeMounts[0].Name) } diff --git a/internal/manifests/opampbridge/deployment.go b/internal/manifests/opampbridge/deployment.go new file mode 100644 index 0000000000..81c1e908a9 --- /dev/null +++ b/internal/manifests/opampbridge/deployment.go @@ -0,0 +1,65 @@ +// 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 opampbridge + +import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// Deployment builds the deployment for the given instance. +func Deployment(params manifests.Params) *appsv1.Deployment { + name := naming.OpAMPBridge(params.OpAMPBridge.Name) + labels := manifestutils.Labels(params.OpAMPBridge.ObjectMeta, name, params.OpAMPBridge.Spec.Image, ComponentOpAMPBridge, params.Config.LabelsFilter()) + + return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OpAMPBridge.Namespace, + Labels: labels, + Annotations: params.OpAMPBridge.Annotations, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: params.OpAMPBridge.Spec.Replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: manifestutils.SelectorLabels(params.OpAMPBridge.ObjectMeta, ComponentOpAMPBridge), + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: params.OpAMPBridge.Spec.PodAnnotations, + }, + Spec: corev1.PodSpec{ + ServiceAccountName: ServiceAccountName(params.OpAMPBridge), + Containers: []corev1.Container{Container(params.Config, params.Log, params.OpAMPBridge)}, + Volumes: Volumes(params.Config, params.OpAMPBridge), + DNSPolicy: getDNSPolicy(params.OpAMPBridge), + HostNetwork: params.OpAMPBridge.Spec.HostNetwork, + Tolerations: params.OpAMPBridge.Spec.Tolerations, + NodeSelector: params.OpAMPBridge.Spec.NodeSelector, + SecurityContext: params.OpAMPBridge.Spec.PodSecurityContext, + PriorityClassName: params.OpAMPBridge.Spec.PriorityClassName, + Affinity: params.OpAMPBridge.Spec.Affinity, + TopologySpreadConstraints: params.OpAMPBridge.Spec.TopologySpreadConstraints, + }, + }, + }, + } +} diff --git a/internal/manifests/opampbridge/deployment_test.go b/internal/manifests/opampbridge/deployment_test.go new file mode 100644 index 0000000000..4ec3f07de6 --- /dev/null +++ b/internal/manifests/opampbridge/deployment_test.go @@ -0,0 +1,425 @@ +// 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 opampbridge + +import ( + "testing" + + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +var testTolerationValues = []v1.Toleration{ + { + Key: "hii", + Value: "greeting", + Effect: "NoSchedule", + }, +} + +var testAffinityValue = &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ + NodeSelectorTerms: []v1.NodeSelectorTerm{ + { + MatchExpressions: []v1.NodeSelectorRequirement{ + { + Key: "node", + Operator: v1.NodeSelectorOpIn, + Values: []string{"test-node"}, + }, + }, + }, + }, + }, + }, +} + +var testTopologySpreadConstraintValue = []v1.TopologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "kubernetes.io/hostname", + WhenUnsatisfiable: "DoNotSchedule", + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "foo": "bar", + }, + }, + }, +} + +func TestDeploymentNewDefault(t *testing.T) { + // prepare + opampBridge := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + Tolerations: testTolerationValues, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge, + Log: logger, + } + + // test + d := Deployment(params) + + // verify + assert.Equal(t, "my-instance-opamp-bridge", d.Name) + assert.Equal(t, "my-instance-opamp-bridge", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, testTolerationValues, d.Spec.Template.Spec.Tolerations) + + assert.Len(t, d.Spec.Template.Spec.Containers, 1) + + expectedLabels := map[string]string{ + "app.kubernetes.io/component": "opentelemetry-opamp-bridge", + "app.kubernetes.io/instance": "my-namespace.my-instance", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/name": "my-instance-opamp-bridge", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "latest", + } + assert.Equal(t, expectedLabels, d.Spec.Template.Labels) + + expectedSelectorLabels := map[string]string{ + "app.kubernetes.io/component": "opentelemetry-opamp-bridge", + "app.kubernetes.io/instance": "my-namespace.my-instance", + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/part-of": "opentelemetry", + } + assert.Equal(t, expectedSelectorLabels, d.Spec.Selector.MatchLabels) + + // the pod selector must be contained within pod spec's labels + for k, v := range d.Spec.Selector.MatchLabels { + assert.Equal(t, v, d.Spec.Template.Labels[k]) + } +} + +func TestDeploymentPodAnnotations(t *testing.T) { + // prepare + testPodAnnotationValues := map[string]string{"annotation-key": "annotation-value"} + opampBridge := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + PodAnnotations: testPodAnnotationValues, + }, + } + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge, + Log: logger, + } + + // test + d := Deployment(params) + + // verify + assert.Len(t, d.Spec.Template.Annotations, 1) + assert.Equal(t, "my-instance-opamp-bridge", d.Name) + assert.Equal(t, testPodAnnotationValues, d.Spec.Template.Annotations) +} + +func TestDeploymentPodSecurityContext(t *testing.T) { + runAsNonRoot := true + runAsUser := int64(1337) + runasGroup := int64(1338) + + opampBridge := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + PodSecurityContext: &v1.PodSecurityContext{ + RunAsNonRoot: &runAsNonRoot, + RunAsUser: &runAsUser, + RunAsGroup: &runasGroup, + }, + }, + } + + cfg := config.New() + + params := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge, + Log: logger, + } + + d := Deployment(params) + + assert.Equal(t, &runAsNonRoot, d.Spec.Template.Spec.SecurityContext.RunAsNonRoot) + assert.Equal(t, &runAsUser, d.Spec.Template.Spec.SecurityContext.RunAsUser) + assert.Equal(t, &runasGroup, d.Spec.Template.Spec.SecurityContext.RunAsGroup) +} + +func TestDeploymentHostNetwork(t *testing.T) { + // Test default + opampBridge1 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge1, + Log: logger, + } + + d1 := Deployment(params1) + + assert.Equal(t, d1.Spec.Template.Spec.HostNetwork, false) + assert.Equal(t, d1.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirst) + + // Test hostNetwork=true + opampBridge2 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-hostnetwork", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + HostNetwork: true, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge2, + Log: logger, + } + + d2 := Deployment(params2) + assert.Equal(t, d2.Spec.Template.Spec.HostNetwork, true) + assert.Equal(t, d2.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirstWithHostNet) +} + +func TestDeploymentFilterLabels(t *testing.T) { + excludedLabels := map[string]string{ + "foo": "1", + "app.foo.bar": "1", + } + + opampBridge := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Labels: excludedLabels, + }, + Spec: v1alpha1.OpAMPBridgeSpec{}, + } + + cfg := config.New(config.WithLabelFilters([]string{"foo*", "app.*.bar"})) + + params := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge, + Log: logger, + } + + d := Deployment(params) + + assert.Len(t, d.ObjectMeta.Labels, 6) + for k := range excludedLabels { + assert.NotContains(t, d.ObjectMeta.Labels, k) + } +} + +func TestDeploymentNodeSelector(t *testing.T) { + // Test default + opampBridge1 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge1, + Log: logger, + } + + d1 := Deployment(params1) + + assert.Empty(t, d1.Spec.Template.Spec.NodeSelector) + + // Test nodeSelector + opampBridge2 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-nodeselector", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + HostNetwork: true, + NodeSelector: map[string]string{ + "node-key": "node-value", + }, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge2, + Log: logger, + } + + d2 := Deployment(params2) + assert.Equal(t, d2.Spec.Template.Spec.NodeSelector, map[string]string{"node-key": "node-value"}) +} + +func TestDeploymentPriorityClassName(t *testing.T) { + opampBridge1 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge1, + Log: logger, + } + + d1 := Deployment(params1) + assert.Empty(t, d1.Spec.Template.Spec.PriorityClassName) + + priorityClassName := "test-class" + + opampBridge2 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-priortyClassName", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + PriorityClassName: priorityClassName, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge2, + Log: logger, + } + + d2 := Deployment(params2) + assert.Equal(t, priorityClassName, d2.Spec.Template.Spec.PriorityClassName) +} + +func TestDeploymentAffinity(t *testing.T) { + opampBridge1 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge1, + Log: logger, + } + + d1 := Deployment(params1) + assert.Nil(t, d1.Spec.Template.Spec.Affinity) + + opampBridge2 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-priortyClassName", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + Affinity: testAffinityValue, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge2, + Log: logger, + } + + d2 := Deployment(params2) + assert.NotNil(t, d2.Spec.Template.Spec.Affinity) + assert.Equal(t, *testAffinityValue, *d2.Spec.Template.Spec.Affinity) +} + +func TestDeploymentTopologySpreadConstraints(t *testing.T) { + // Test default + opampBridge1 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge1, + Log: logger, + } + + d1 := Deployment(params1) + assert.Equal(t, "my-instance-opamp-bridge", d1.Name) + assert.Empty(t, d1.Spec.Template.Spec.TopologySpreadConstraints) + + // Test TopologySpreadConstraints + opampBridge2 := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-topologyspreadconstraint", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + TopologySpreadConstraints: testTopologySpreadConstraintValue, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + Config: cfg, + OpAMPBridge: opampBridge2, + Log: logger, + } + + d2 := Deployment(params2) + assert.Equal(t, "my-instance-topologyspreadconstraint-opamp-bridge", d2.Name) + assert.NotNil(t, d2.Spec.Template.Spec.TopologySpreadConstraints) + assert.NotEmpty(t, d2.Spec.Template.Spec.TopologySpreadConstraints) + assert.Equal(t, testTopologySpreadConstraintValue, d2.Spec.Template.Spec.TopologySpreadConstraints) +} diff --git a/internal/manifests/opampbridge/opampbridge.go b/internal/manifests/opampbridge/opampbridge.go new file mode 100644 index 0000000000..5e8573b118 --- /dev/null +++ b/internal/manifests/opampbridge/opampbridge.go @@ -0,0 +1,45 @@ +// 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 opampbridge + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +const ( + ComponentOpAMPBridge = "opentelemetry-opamp-bridge" +) + +// Build creates the manifest for the OpAMPBridge resource. +func Build(params manifests.Params) ([]client.Object, error) { + var resourceManifests []client.Object + resourceFactories := []manifests.K8sManifestFactory{ + manifests.FactoryWithoutError(Deployment), + manifests.Factory(ConfigMap), + manifests.FactoryWithoutError(ServiceAccount), + manifests.FactoryWithoutError(Service), + } + for _, factory := range resourceFactories { + res, err := factory(params) + if err != nil { + return nil, err + } else if manifests.ObjectIsNotNil(res) { + resourceManifests = append(resourceManifests, res) + } + } + return resourceManifests, nil +} diff --git a/internal/manifests/opampbridge/service.go b/internal/manifests/opampbridge/service.go new file mode 100644 index 0000000000..0c2a7dd9a0 --- /dev/null +++ b/internal/manifests/opampbridge/service.go @@ -0,0 +1,51 @@ +// 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 opampbridge + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func Service(params manifests.Params) *corev1.Service { + name := naming.OpAMPBridgeService(params.OpAMPBridge.Name) + labels := manifestutils.Labels(params.OpAMPBridge.ObjectMeta, name, params.OpAMPBridge.Spec.Image, ComponentOpAMPBridge, []string{}) + selector := manifestutils.SelectorLabels(params.OpAMPBridge.ObjectMeta, ComponentOpAMPBridge) + + ports := []corev1.ServicePort{{ + Name: "opamp-bridge", + Port: 80, + TargetPort: intstr.FromInt(8080), + }} + + ports = append(params.OpAMPBridge.Spec.Ports, ports...) + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OpAMPBridge.Namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Selector: selector, + Ports: ports, + }, + } +} diff --git a/internal/manifests/opampbridge/serviceaccount.go b/internal/manifests/opampbridge/serviceaccount.go new file mode 100644 index 0000000000..f5b0236923 --- /dev/null +++ b/internal/manifests/opampbridge/serviceaccount.go @@ -0,0 +1,48 @@ +// 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 opampbridge + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// ServiceAccountName returns the name of the existing or self-provisioned service account to use for the given instance. +func ServiceAccountName(instance v1alpha1.OpAMPBridge) string { + if len(instance.Spec.ServiceAccount) == 0 { + return naming.OpAMPBridgeServiceAccount(instance.Name) + } + return instance.Spec.ServiceAccount +} + +// ServiceAccount returns the service account for the given instance. +func ServiceAccount(params manifests.Params) *corev1.ServiceAccount { + name := naming.OpAMPBridgeServiceAccount(params.OpAMPBridge.Name) + labels := manifestutils.Labels(params.OpAMPBridge.ObjectMeta, name, params.OpAMPBridge.Spec.Image, ComponentOpAMPBridge, []string{}) + + return &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OpAMPBridge.Namespace, + Labels: labels, + Annotations: params.OpAMPBridge.Annotations, + }, + } +} diff --git a/internal/manifests/opampbridge/serviceaccount_test.go b/internal/manifests/opampbridge/serviceaccount_test.go new file mode 100644 index 0000000000..e3f6c7e059 --- /dev/null +++ b/internal/manifests/opampbridge/serviceaccount_test.go @@ -0,0 +1,57 @@ +// 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 opampbridge + +import ( + "testing" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +func TestServiceAccountNewDefault(t *testing.T) { + // prepare + opampBridge := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + // test + sa := ServiceAccountName(opampBridge) + + // verify + assert.Equal(t, "my-instance-opamp-bridge", sa) +} + +func TestServiceAccountOverride(t *testing.T) { + // prepare + opampBridge := v1alpha1.OpAMPBridge{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpAMPBridgeSpec{ + ServiceAccount: "my-special-sa", + }, + } + + // test + sa := ServiceAccountName(opampBridge) + + // verify + assert.Equal(t, "my-special-sa", sa) +} diff --git a/internal/manifests/opampbridge/utils.go b/internal/manifests/opampbridge/utils.go new file mode 100644 index 0000000000..2400e0101a --- /dev/null +++ b/internal/manifests/opampbridge/utils.go @@ -0,0 +1,29 @@ +// 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 opampbridge + +import ( + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +func getDNSPolicy(opampBridge v1alpha1.OpAMPBridge) corev1.DNSPolicy { + dnsPolicy := corev1.DNSClusterFirst + if opampBridge.Spec.HostNetwork { + dnsPolicy = corev1.DNSClusterFirstWithHostNet + } + return dnsPolicy +} diff --git a/internal/manifests/opampbridge/volume.go b/internal/manifests/opampbridge/volume.go new file mode 100644 index 0000000000..eac9d50811 --- /dev/null +++ b/internal/manifests/opampbridge/volume.go @@ -0,0 +1,43 @@ +// 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 opampbridge + +import ( + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// Volumes builds the volumes for the given instance, including the config map volume. +func Volumes(cfg config.Config, opampBridge v1alpha1.OpAMPBridge) []corev1.Volume { + volumes := []corev1.Volume{{ + Name: naming.OpAMPBridgeConfigMapVolume(), + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: naming.OpAMPBridgeConfigMap(opampBridge.Name)}, + Items: []corev1.KeyToPath{ + { + Key: cfg.OperatorOpAMPBridgeConfigMapEntry(), + Path: cfg.OperatorOpAMPBridgeConfigMapEntry(), + }, + }, + }, + }, + }} + + return volumes +} diff --git a/pkg/collector/reconcile/opentelemetry_test.go b/internal/manifests/opampbridge/volume_test.go similarity index 53% rename from pkg/collector/reconcile/opentelemetry_test.go rename to internal/manifests/opampbridge/volume_test.go index 59f5d7ebac..17fefb3d58 100644 --- a/pkg/collector/reconcile/opentelemetry_test.go +++ b/internal/manifests/opampbridge/volume_test.go @@ -12,31 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. -package reconcile +package opampbridge import ( - "context" "testing" "github.com/stretchr/testify/assert" - "k8s.io/apimachinery/pkg/types" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) -func TestSelf(t *testing.T) { - t.Run("should add version to the status", func(t *testing.T) { - instance := params().Instance - createObjectIfNotExists(t, "test", &instance) - err := Self(context.Background(), params()) - assert.NoError(t, err) +func TestVolumeNewDefault(t *testing.T) { + // prepare + opampBridge := v1alpha1.OpAMPBridge{} + cfg := config.New() - actual := v1alpha1.OpenTelemetryCollector{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test"}) - assert.NoError(t, err) - assert.True(t, exists) + // test + volumes := Volumes(cfg, opampBridge) - assert.Equal(t, actual.Status.Version, "0.0.0") + // verify + assert.Len(t, volumes, 1) - }) + // check if the number of elements in the volume source items list is 1 + assert.Len(t, volumes[0].VolumeSource.ConfigMap.Items, 1) + + // check that it's the opamp-bridge-internal volume, with the config map + assert.Equal(t, naming.OpAMPBridgeConfigMapVolume(), volumes[0].Name) } diff --git a/pkg/collector/reconcile/params.go b/internal/manifests/params.go similarity index 80% rename from pkg/collector/reconcile/params.go rename to internal/manifests/params.go index e704dc37d3..a7b377e4f2 100644 --- a/pkg/collector/reconcile/params.go +++ b/internal/manifests/params.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package reconcile +package manifests import ( "github.com/go-logr/logr" @@ -26,10 +26,11 @@ import ( // Params holds the reconciliation-specific parameters. type Params struct { - Client client.Client - Recorder record.EventRecorder - Scheme *runtime.Scheme - Log logr.Logger - Instance v1alpha1.OpenTelemetryCollector - Config config.Config + Client client.Client + Recorder record.EventRecorder + Scheme *runtime.Scheme + Log logr.Logger + OtelCol v1alpha1.OpenTelemetryCollector + OpAMPBridge v1alpha1.OpAMPBridge + Config config.Config } diff --git a/internal/manifests/targetallocator/adapters/config_to_prom_config.go b/internal/manifests/targetallocator/adapters/config_to_prom_config.go new file mode 100644 index 0000000000..139901daad --- /dev/null +++ b/internal/manifests/targetallocator/adapters/config_to_prom_config.go @@ -0,0 +1,331 @@ +// 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 adapters + +import ( + "errors" + "fmt" + "net/url" + "regexp" + "strings" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" +) + +func errorNoComponent(component string) error { + return fmt.Errorf("no %s available as part of the configuration", component) +} + +func errorNotAMapAtIndex(component string, index int) error { + return fmt.Errorf("index %d: %s property in the configuration doesn't contain a valid map: %s", index, component, component) +} + +func errorNotAMap(component string) error { + return fmt.Errorf("%s property in the configuration doesn't contain valid %s", component, component) +} + +func errorNotAList(component string) error { + return fmt.Errorf("%s must be a list in the config", component) +} + +func errorNotAListAtIndex(component string, index int) error { + return fmt.Errorf("index %d: %s property in the configuration doesn't contain a valid index: %s", index, component, component) +} + +func errorNotAStringAtIndex(component string, index int) error { + return fmt.Errorf("index %d: %s property in the configuration doesn't contain a valid string: %s", index, component, component) +} + +// getScrapeConfigsFromPromConfig extracts the scrapeConfig array from prometheus receiver config. +func getScrapeConfigsFromPromConfig(promReceiverConfig map[interface{}]interface{}) ([]interface{}, error) { + prometheusConfigProperty, ok := promReceiverConfig["config"] + if !ok { + return nil, errorNoComponent("prometheusConfig") + } + + prometheusConfig, ok := prometheusConfigProperty.(map[interface{}]interface{}) + if !ok { + return nil, errorNotAMap("prometheusConfig") + } + + scrapeConfigsProperty, ok := prometheusConfig["scrape_configs"] + if !ok { + return nil, errorNoComponent("scrape_configs") + } + + scrapeConfigs, ok := scrapeConfigsProperty.([]interface{}) + if !ok { + return nil, errorNotAList("scrape_configs") + } + + return scrapeConfigs, nil +} + +// ConfigToPromConfig converts the incoming configuration object into the Prometheus receiver config. +func ConfigToPromConfig(cfg string) (map[interface{}]interface{}, error) { + config, err := adapters.ConfigFromString(cfg) + if err != nil { + return nil, err + } + + receiversProperty, ok := config["receivers"] + if !ok { + return nil, errorNoComponent("receivers") + } + + receivers, ok := receiversProperty.(map[interface{}]interface{}) + if !ok { + return nil, errorNotAMap("receivers") + } + + prometheusProperty, ok := receivers["prometheus"] + if !ok { + return nil, errorNoComponent("prometheus") + } + + prometheus, ok := prometheusProperty.(map[interface{}]interface{}) + if !ok { + return nil, errorNotAMap("prometheus") + } + + return prometheus, nil +} + +// UnescapeDollarSignsInPromConfig replaces "$$" with "$" in the "replacement" fields of +// both "relabel_configs" and "metric_relabel_configs" in a Prometheus configuration file. +func UnescapeDollarSignsInPromConfig(cfg string) (map[interface{}]interface{}, error) { + prometheus, err := ConfigToPromConfig(cfg) + if err != nil { + return nil, err + } + + scrapeConfigs, err := getScrapeConfigsFromPromConfig(prometheus) + if err != nil { + return nil, err + } + + for i, config := range scrapeConfigs { + scrapeConfig, ok := config.(map[interface{}]interface{}) + if !ok { + return nil, errorNotAMapAtIndex("scrape_config", i) + } + + relabelConfigsProperty, ok := scrapeConfig["relabel_configs"] + if !ok { + continue + } + + relabelConfigs, ok := relabelConfigsProperty.([]interface{}) + if !ok { + return nil, errorNotAListAtIndex("relabel_configs", i) + } + + for i, rc := range relabelConfigs { + relabelConfig, rcErr := rc.(map[interface{}]interface{}) + if !rcErr { + return nil, errorNotAMapAtIndex("relabel_config", i) + } + + replacementProperty, rcErr := relabelConfig["replacement"] + if !rcErr { + continue + } + + replacement, rcErr := replacementProperty.(string) + if !rcErr { + return nil, errorNotAStringAtIndex("replacement", i) + } + + relabelConfig["replacement"] = strings.ReplaceAll(replacement, "$$", "$") + } + + metricRelabelConfigsProperty, ok := scrapeConfig["metric_relabel_configs"] + if !ok { + continue + } + + metricRelabelConfigs, ok := metricRelabelConfigsProperty.([]interface{}) + if !ok { + return nil, errorNotAListAtIndex("metric_relabel_configs", i) + } + + for i, rc := range metricRelabelConfigs { + relabelConfig, ok := rc.(map[interface{}]interface{}) + if !ok { + return nil, errorNotAMapAtIndex("metric_relabel_config", i) + } + + replacementProperty, ok := relabelConfig["replacement"] + if !ok { + continue + } + + replacement, ok := replacementProperty.(string) + if !ok { + return nil, errorNotAStringAtIndex("replacement", i) + } + + relabelConfig["replacement"] = strings.ReplaceAll(replacement, "$$", "$") + } + } + + return prometheus, nil +} + +// AddHTTPSDConfigToPromConfig adds HTTP SD (Service Discovery) configuration to the Prometheus configuration. +// This function removes any existing service discovery configurations (e.g., `sd_configs`, `dns_sd_configs`, `file_sd_configs`, etc.) +// from the `scrape_configs` section and adds a single `http_sd_configs` configuration. +// The `http_sd_configs` points to the TA (Target Allocator) endpoint that provides the list of targets for the given job. +func AddHTTPSDConfigToPromConfig(prometheus map[interface{}]interface{}, taServiceName string) (map[interface{}]interface{}, error) { + prometheusConfigProperty, ok := prometheus["config"] + if !ok { + return nil, errorNoComponent("prometheusConfig") + } + + prometheusConfig, ok := prometheusConfigProperty.(map[interface{}]interface{}) + if !ok { + return nil, errorNotAMap("prometheusConfig") + } + + scrapeConfigsProperty, ok := prometheusConfig["scrape_configs"] + if !ok { + return nil, errorNoComponent("scrape_configs") + } + + scrapeConfigs, ok := scrapeConfigsProperty.([]interface{}) + if !ok { + return nil, errorNotAList("scrape_configs") + } + + sdRegex := regexp.MustCompile(`^.*(sd|static)_configs$`) + + for i, config := range scrapeConfigs { + scrapeConfig, ok := config.(map[interface{}]interface{}) + if !ok { + return nil, errorNotAMapAtIndex("scrape_config", i) + } + + // Check for other types of service discovery configs (e.g. dns_sd_configs, file_sd_configs, etc.) + for key := range scrapeConfig { + keyStr, keyErr := key.(string) + if !keyErr { + continue + } + if sdRegex.MatchString(keyStr) { + delete(scrapeConfig, key) + } + } + + jobNameProperty, ok := scrapeConfig["job_name"] + if !ok { + return nil, errorNotAStringAtIndex("job_name", i) + } + + jobName, ok := jobNameProperty.(string) + if !ok { + return nil, errorNotAStringAtIndex("job_name is not a string", i) + } + + escapedJob := url.QueryEscape(jobName) + scrapeConfig["http_sd_configs"] = []interface{}{ + map[string]interface{}{ + "url": fmt.Sprintf("http://%s:80/jobs/%s/targets?collector_id=$POD_NAME", taServiceName, escapedJob), + }, + } + } + + return prometheus, nil +} + +// AddTAConfigToPromConfig adds or updates the target_allocator configuration in the Prometheus configuration. +// If the `EnableTargetAllocatorRewrite` feature flag for the target allocator is enabled, this function +// removes the existing scrape_configs from the collector's Prometheus configuration as it's not required. +func AddTAConfigToPromConfig(prometheus map[interface{}]interface{}, taServiceName string) (map[interface{}]interface{}, error) { + prometheusConfigProperty, ok := prometheus["config"] + if !ok { + return nil, errorNoComponent("prometheusConfig") + } + + prometheusCfg, ok := prometheusConfigProperty.(map[interface{}]interface{}) + if !ok { + return nil, errorNotAMap("prometheusConfig") + } + + // Create the TargetAllocConfig dynamically if it doesn't exist + if prometheus["target_allocator"] == nil { + prometheus["target_allocator"] = make(map[interface{}]interface{}) + } + + targetAllocatorCfg, ok := prometheus["target_allocator"].(map[interface{}]interface{}) + if !ok { + return nil, errorNotAMap("target_allocator") + } + + targetAllocatorCfg["endpoint"] = fmt.Sprintf("http://%s:80", taServiceName) + targetAllocatorCfg["interval"] = "30s" + targetAllocatorCfg["collector_id"] = "${POD_NAME}" + + // Remove the scrape_configs key from the map + delete(prometheusCfg, "scrape_configs") + + return prometheus, nil +} + +// ValidatePromConfig checks if the prometheus receiver config is valid given other collector-level settings. +func ValidatePromConfig(config map[interface{}]interface{}, targetAllocatorEnabled bool, targetAllocatorRewriteEnabled bool) error { + _, promConfigExists := config["config"] + + if targetAllocatorEnabled { + if targetAllocatorRewriteEnabled { // if rewrite is enabled, we will add a target_allocator section during rewrite + return nil + } + _, targetAllocatorExists := config["target_allocator"] + + // otherwise, either the target_allocator or config section needs to be here + if !(promConfigExists || targetAllocatorExists) { + return errors.New("either target allocator or prometheus config needs to be present") + } + + return nil + } + // if target allocator isn't enabled, we need a config section + if !promConfigExists { + return errorNoComponent("prometheusConfig") + } + + return nil +} + +// ValidateTargetAllocatorConfig checks if the Target Allocator config is valid +// In order for Target Allocator to do anything useful, at least one of the following has to be true: +// - at least one scrape config has to be defined in Prometheus receiver configuration +// - PrometheusCR has to be enabled in target allocator settings +func ValidateTargetAllocatorConfig(targetAllocatorPrometheusCR bool, promReceiverConfig map[interface{}]interface{}) error { + + if targetAllocatorPrometheusCR { + return nil + } + // if PrometheusCR isn't enabled, we need at least one scrape config + scrapeConfigs, err := getScrapeConfigsFromPromConfig(promReceiverConfig) + if err != nil { + return err + } + + if len(scrapeConfigs) == 0 { + return fmt.Errorf("either at least one scrape config needs to be defined or PrometheusCR needs to be enabled") + } + + return nil +} diff --git a/internal/manifests/targetallocator/adapters/config_to_prom_config_test.go b/internal/manifests/targetallocator/adapters/config_to_prom_config_test.go new file mode 100644 index 0000000000..cef21dca16 --- /dev/null +++ b/internal/manifests/targetallocator/adapters/config_to_prom_config_test.go @@ -0,0 +1,450 @@ +// 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 adapters_test + +import ( + "errors" + "fmt" + "net/url" + "reflect" + "testing" + + ta "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters" + + "github.com/stretchr/testify/assert" +) + +func TestExtractPromConfigFromConfig(t *testing.T) { + configStr := `receivers: + examplereceiver: + endpoint: "0.0.0.0:12345" + examplereceiver/settings: + endpoint: "0.0.0.0:12346" + prometheus: + config: + scrape_config: + job_name: otel-collector + scrape_interval: 10s + jaeger/custom: + protocols: + thrift_http: + endpoint: 0.0.0.0:15268 +` + expectedData := map[interface{}]interface{}{ + "config": map[interface{}]interface{}{ + "scrape_config": map[interface{}]interface{}{ + "job_name": "otel-collector", + "scrape_interval": "10s", + }, + }, + } + + // test + promConfig, err := ta.ConfigToPromConfig(configStr) + assert.NoError(t, err) + + // verify + assert.Equal(t, expectedData, promConfig) +} + +func TestExtractPromConfigWithTAConfigFromConfig(t *testing.T) { + configStr := `receivers: + examplereceiver: + endpoint: "0.0.0.0:12345" + examplereceiver/settings: + endpoint: "0.0.0.0:12346" + prometheus: + config: + scrape_config: + job_name: otel-collector + scrape_interval: 10s + target_allocator: + endpoint: "test:80" + jaeger/custom: + protocols: + thrift_http: + endpoint: 0.0.0.0:15268 +` + expectedData := map[interface{}]interface{}{ + "config": map[interface{}]interface{}{ + "scrape_config": map[interface{}]interface{}{ + "job_name": "otel-collector", + "scrape_interval": "10s", + }, + }, + "target_allocator": map[interface{}]interface{}{ + "endpoint": "test:80", + }, + } + + // test + promConfig, err := ta.ConfigToPromConfig(configStr) + assert.NoError(t, err) + + // verify + assert.Equal(t, expectedData, promConfig) +} + +func TestExtractPromConfigFromNullConfig(t *testing.T) { + configStr := `receivers: + examplereceiver: + endpoint: "0.0.0.0:12345" + examplereceiver/settings: + endpoint: "0.0.0.0:12346" + jaeger/custom: + protocols: + thrift_http: + endpoint: 0.0.0.0:15268 +` + + // test + promConfig, err := ta.ConfigToPromConfig(configStr) + assert.Equal(t, err, fmt.Errorf("no prometheus available as part of the configuration")) + + // verify + assert.True(t, reflect.ValueOf(promConfig).IsNil()) +} + +func TestUnescapeDollarSignsInPromConfig(t *testing.T) { + actual := ` +receivers: + prometheus: + config: + scrape_configs: + - job_name: 'example' + relabel_configs: + - source_labels: ['__meta_service_id'] + target_label: 'job' + replacement: 'my_service_$$1' + - source_labels: ['__meta_service_name'] + target_label: 'instance' + replacement: '$1' + metric_relabel_configs: + - source_labels: ['job'] + target_label: 'job' + replacement: '$$1_$2' +` + expected := ` +receivers: + prometheus: + config: + scrape_configs: + - job_name: 'example' + relabel_configs: + - source_labels: ['__meta_service_id'] + target_label: 'job' + replacement: 'my_service_$1' + - source_labels: ['__meta_service_name'] + target_label: 'instance' + replacement: '$1' + metric_relabel_configs: + - source_labels: ['job'] + target_label: 'job' + replacement: '$1_$2' +` + + config, err := ta.UnescapeDollarSignsInPromConfig(actual) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + expectedConfig, err := ta.UnescapeDollarSignsInPromConfig(expected) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + if !reflect.DeepEqual(config, expectedConfig) { + t.Errorf("unexpected config: got %v, want %v", config, expectedConfig) + } +} + +func TestAddHTTPSDConfigToPromConfig(t *testing.T) { + t.Run("ValidConfiguration, add http_sd_config", func(t *testing.T) { + cfg := map[interface{}]interface{}{ + "config": map[interface{}]interface{}{ + "scrape_configs": []interface{}{ + map[interface{}]interface{}{ + "job_name": "test_job", + "static_configs": []interface{}{ + map[interface{}]interface{}{ + "targets": []interface{}{ + "localhost:9090", + }, + }, + }, + }, + }, + }, + } + taServiceName := "test-service" + expectedCfg := map[interface{}]interface{}{ + "config": map[interface{}]interface{}{ + "scrape_configs": []interface{}{ + map[interface{}]interface{}{ + "job_name": "test_job", + "http_sd_configs": []interface{}{ + map[string]interface{}{ + "url": fmt.Sprintf("http://%s:80/jobs/%s/targets?collector_id=$POD_NAME", taServiceName, url.QueryEscape("test_job")), + }, + }, + }, + }, + }, + } + + actualCfg, err := ta.AddHTTPSDConfigToPromConfig(cfg, taServiceName) + assert.NoError(t, err) + assert.Equal(t, expectedCfg, actualCfg) + }) + + t.Run("invalid config property, returns error", func(t *testing.T) { + cfg := map[interface{}]interface{}{ + "config": map[interface{}]interface{}{ + "job_name": "test_job", + "static_configs": []interface{}{ + map[interface{}]interface{}{ + "targets": []interface{}{ + "localhost:9090", + }, + }, + }, + }, + } + + taServiceName := "test-service" + + _, err := ta.AddHTTPSDConfigToPromConfig(cfg, taServiceName) + assert.Error(t, err) + assert.EqualError(t, err, "no scrape_configs available as part of the configuration") + }) +} + +func TestAddTAConfigToPromConfig(t *testing.T) { + t.Run("should return expected prom config map with TA config", func(t *testing.T) { + cfg := map[interface{}]interface{}{ + "config": map[interface{}]interface{}{ + "scrape_configs": []interface{}{ + map[interface{}]interface{}{ + "job_name": "test_job", + "static_configs": []interface{}{ + map[interface{}]interface{}{ + "targets": []interface{}{ + "localhost:9090", + }, + }, + }, + }, + }, + }, + } + + taServiceName := "test-targetallocator" + + expectedResult := map[interface{}]interface{}{ + "config": map[interface{}]interface{}{}, + "target_allocator": map[interface{}]interface{}{ + "endpoint": "http://test-targetallocator:80", + "interval": "30s", + "collector_id": "${POD_NAME}", + }, + } + + result, err := ta.AddTAConfigToPromConfig(cfg, taServiceName) + + assert.NoError(t, err) + assert.Equal(t, expectedResult, result) + }) + + t.Run("missing or invalid prometheusConfig property, returns error", func(t *testing.T) { + testCases := []struct { + name string + cfg map[interface{}]interface{} + errText string + }{ + { + name: "missing config property", + cfg: map[interface{}]interface{}{}, + errText: "no prometheusConfig available as part of the configuration", + }, + { + name: "invalid config property", + cfg: map[interface{}]interface{}{ + "config": "invalid", + }, + errText: "prometheusConfig property in the configuration doesn't contain valid prometheusConfig", + }, + } + + taServiceName := "test-targetallocator" + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := ta.AddTAConfigToPromConfig(tc.cfg, taServiceName) + + assert.Error(t, err) + assert.EqualError(t, err, tc.errText) + }) + } + }) +} + +func TestValidatePromConfig(t *testing.T) { + testCases := []struct { + description string + config map[interface{}]interface{} + targetAllocatorEnabled bool + targetAllocatorRewriteEnabled bool + expectedError error + }{ + { + description: "target_allocator and rewrite enabled", + config: map[interface{}]interface{}{}, + targetAllocatorEnabled: true, + targetAllocatorRewriteEnabled: true, + expectedError: nil, + }, + { + description: "target_allocator enabled, target_allocator section present", + config: map[interface{}]interface{}{ + "target_allocator": map[interface{}]interface{}{}, + }, + targetAllocatorEnabled: true, + targetAllocatorRewriteEnabled: false, + expectedError: nil, + }, + { + description: "target_allocator enabled, config section present", + config: map[interface{}]interface{}{ + "config": map[interface{}]interface{}{}, + }, + targetAllocatorEnabled: true, + targetAllocatorRewriteEnabled: false, + expectedError: nil, + }, + { + description: "target_allocator enabled, neither section present", + config: map[interface{}]interface{}{}, + targetAllocatorEnabled: true, + targetAllocatorRewriteEnabled: false, + expectedError: errors.New("either target allocator or prometheus config needs to be present"), + }, + { + description: "target_allocator disabled, config section present", + config: map[interface{}]interface{}{ + "config": map[interface{}]interface{}{}, + }, + targetAllocatorEnabled: false, + targetAllocatorRewriteEnabled: false, + expectedError: nil, + }, + { + description: "target_allocator disabled, config section not present", + config: map[interface{}]interface{}{}, + targetAllocatorEnabled: false, + targetAllocatorRewriteEnabled: false, + expectedError: fmt.Errorf("no %s available as part of the configuration", "prometheusConfig"), + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.description, func(t *testing.T) { + err := ta.ValidatePromConfig(testCase.config, testCase.targetAllocatorEnabled, testCase.targetAllocatorRewriteEnabled) + assert.Equal(t, testCase.expectedError, err) + }) + } +} + +func TestValidateTargetAllocatorConfig(t *testing.T) { + testCases := []struct { + description string + config map[interface{}]interface{} + targetAllocatorPrometheusCR bool + expectedError error + }{ + { + description: "scrape configs present and PrometheusCR enabled", + config: map[interface{}]interface{}{ + "config": map[interface{}]interface{}{ + "scrape_configs": []interface{}{ + map[interface{}]interface{}{ + "job_name": "test_job", + "static_configs": []interface{}{ + map[interface{}]interface{}{ + "targets": []interface{}{ + "localhost:9090", + }, + }, + }, + }, + }, + }, + }, + targetAllocatorPrometheusCR: true, + expectedError: nil, + }, + { + description: "scrape configs present and PrometheusCR disabled", + config: map[interface{}]interface{}{ + "config": map[interface{}]interface{}{ + "scrape_configs": []interface{}{ + map[interface{}]interface{}{ + "job_name": "test_job", + "static_configs": []interface{}{ + map[interface{}]interface{}{ + "targets": []interface{}{ + "localhost:9090", + }, + }, + }, + }, + }, + }, + }, + targetAllocatorPrometheusCR: false, + expectedError: nil, + }, + { + description: "receiver config empty and PrometheusCR enabled", + config: map[interface{}]interface{}{}, + targetAllocatorPrometheusCR: true, + expectedError: nil, + }, + { + description: "receiver config empty and PrometheusCR disabled", + config: map[interface{}]interface{}{}, + targetAllocatorPrometheusCR: false, + expectedError: fmt.Errorf("no %s available as part of the configuration", "prometheusConfig"), + }, + { + description: "scrape configs empty and PrometheusCR disabled", + config: map[interface{}]interface{}{ + "config": map[interface{}]interface{}{ + "scrape_configs": []interface{}{}, + }, + }, + targetAllocatorPrometheusCR: false, + expectedError: fmt.Errorf("either at least one scrape config needs to be defined or PrometheusCR needs to be enabled"), + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.description, func(t *testing.T) { + err := ta.ValidateTargetAllocatorConfig(testCase.targetAllocatorPrometheusCR, testCase.config) + assert.Equal(t, testCase.expectedError, err) + }) + } +} diff --git a/internal/manifests/targetallocator/annotations.go b/internal/manifests/targetallocator/annotations.go new file mode 100644 index 0000000000..72e648e5bd --- /dev/null +++ b/internal/manifests/targetallocator/annotations.go @@ -0,0 +1,54 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + "crypto/sha256" + "fmt" + + v1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +const configMapHashAnnotationKey = "opentelemetry-targetallocator-config/hash" + +// Annotations returns the annotations for the TargetAllocator Pod. +func Annotations(instance v1alpha1.OpenTelemetryCollector, configMap *v1.ConfigMap) map[string]string { + // Make a copy of PodAnnotations to be safe + annotations := make(map[string]string, len(instance.Spec.PodAnnotations)) + for key, value := range instance.Spec.PodAnnotations { + annotations[key] = value + } + + if configMap != nil { + cmHash := getConfigMapSHA(configMap) + if cmHash != "" { + annotations[configMapHashAnnotationKey] = getConfigMapSHA(configMap) + } + } + + return annotations +} + +// getConfigMapSHA returns the hash of the content of the TA ConfigMap. +func getConfigMapSHA(configMap *v1.ConfigMap) string { + configString, ok := configMap.Data[targetAllocatorFilename] + if !ok { + return "" + } + h := sha256.Sum256([]byte(configString)) + return fmt.Sprintf("%x", h) +} diff --git a/internal/manifests/targetallocator/annotations_test.go b/internal/manifests/targetallocator/annotations_test.go new file mode 100644 index 0000000000..f50a9d77fb --- /dev/null +++ b/internal/manifests/targetallocator/annotations_test.go @@ -0,0 +1,63 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + "crypto/sha256" + "fmt" + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +func TestPodAnnotations(t *testing.T) { + instance := collectorInstance() + instance.Spec.PodAnnotations = map[string]string{ + "key": "value", + } + annotations := Annotations(instance, nil) + assert.Subset(t, annotations, instance.Spec.PodAnnotations) +} + +func TestConfigMapHash(t *testing.T) { + cfg := config.New() + instance := collectorInstance() + params := manifests.Params{ + OtelCol: instance, + Config: cfg, + Log: logr.Discard(), + } + expectedConfigMap, err := ConfigMap(params) + require.NoError(t, err) + expectedConfig := expectedConfigMap.Data[targetAllocatorFilename] + require.NotEmpty(t, expectedConfig) + expectedHash := sha256.Sum256([]byte(expectedConfig)) + annotations := Annotations(instance, expectedConfigMap) + require.Contains(t, annotations, configMapHashAnnotationKey) + cmHash := annotations[configMapHashAnnotationKey] + assert.Equal(t, fmt.Sprintf("%x", expectedHash), cmHash) +} + +func TestInvalidConfigNoHash(t *testing.T) { + instance := collectorInstance() + instance.Spec.Config = "" + annotations := Annotations(instance, nil) + require.NotContains(t, annotations, configMapHashAnnotationKey) +} diff --git a/internal/manifests/targetallocator/configmap.go b/internal/manifests/targetallocator/configmap.go new file mode 100644 index 0000000000..5e46209d45 --- /dev/null +++ b/internal/manifests/targetallocator/configmap.go @@ -0,0 +1,103 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + "strings" + + "gopkg.in/yaml.v2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +const ( + targetAllocatorFilename = "targetallocator.yaml" +) + +func ConfigMap(params manifests.Params) (*corev1.ConfigMap, error) { + name := naming.TAConfigMap(params.OtelCol.Name) + version := strings.Split(params.OtelCol.Spec.Image, ":") + labels := Labels(params.OtelCol, name) + if len(version) > 1 { + labels["app.kubernetes.io/version"] = version[len(version)-1] + } else { + labels["app.kubernetes.io/version"] = "latest" + } + + // Collector supports environment variable substitution, but the TA does not. + // TA ConfigMap should have a single "$", as it does not support env var substitution + prometheusReceiverConfig, err := adapters.UnescapeDollarSignsInPromConfig(params.OtelCol.Spec.Config) + if err != nil { + return &corev1.ConfigMap{}, err + } + + taConfig := make(map[interface{}]interface{}) + prometheusCRConfig := make(map[interface{}]interface{}) + taConfig["label_selector"] = manifestutils.SelectorLabels(params.OtelCol.ObjectMeta, collector.ComponentOpenTelemetryCollector) + // We only take the "config" from the returned object, if it's present + if prometheusConfig, ok := prometheusReceiverConfig["config"]; ok { + taConfig["config"] = prometheusConfig + } + + if len(params.OtelCol.Spec.TargetAllocator.AllocationStrategy) > 0 { + taConfig["allocation_strategy"] = params.OtelCol.Spec.TargetAllocator.AllocationStrategy + } else { + taConfig["allocation_strategy"] = v1alpha1.OpenTelemetryTargetAllocatorAllocationStrategyLeastWeighted + } + + if len(params.OtelCol.Spec.TargetAllocator.FilterStrategy) > 0 { + taConfig["filter_strategy"] = params.OtelCol.Spec.TargetAllocator.FilterStrategy + } + + if params.OtelCol.Spec.TargetAllocator.PrometheusCR.ScrapeInterval.Size() > 0 { + prometheusCRConfig["scrape_interval"] = params.OtelCol.Spec.TargetAllocator.PrometheusCR.ScrapeInterval.Duration + } + + if params.OtelCol.Spec.TargetAllocator.PrometheusCR.ServiceMonitorSelector != nil { + taConfig["service_monitor_selector"] = ¶ms.OtelCol.Spec.TargetAllocator.PrometheusCR.ServiceMonitorSelector + } + + if params.OtelCol.Spec.TargetAllocator.PrometheusCR.PodMonitorSelector != nil { + taConfig["pod_monitor_selector"] = ¶ms.OtelCol.Spec.TargetAllocator.PrometheusCR.PodMonitorSelector + } + + if len(prometheusCRConfig) > 0 { + taConfig["prometheus_cr"] = prometheusCRConfig + } + + taConfigYAML, err := yaml.Marshal(taConfig) + if err != nil { + return &corev1.ConfigMap{}, err + } + + return &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OtelCol.Namespace, + Labels: labels, + Annotations: params.OtelCol.Annotations, + }, + Data: map[string]string{ + targetAllocatorFilename: string(taConfigYAML), + }, + }, nil +} diff --git a/internal/manifests/targetallocator/configmap_test.go b/internal/manifests/targetallocator/configmap_test.go new file mode 100644 index 0000000000..8baa202400 --- /dev/null +++ b/internal/manifests/targetallocator/configmap_test.go @@ -0,0 +1,160 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + "testing" + "time" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +func TestDesiredConfigMap(t *testing.T) { + expectedLables := map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + "app.kubernetes.io/instance": "default.my-instance", + "app.kubernetes.io/part-of": "opentelemetry", + "app.kubernetes.io/version": "0.47.0", + } + + t.Run("should return expected target allocator config map", func(t *testing.T) { + expectedLables["app.kubernetes.io/component"] = "opentelemetry-targetallocator" + expectedLables["app.kubernetes.io/name"] = "my-instance-targetallocator" + + expectedData := map[string]string{ + "targetallocator.yaml": `allocation_strategy: least-weighted +config: + scrape_configs: + - job_name: otel-collector + scrape_interval: 10s + static_configs: + - targets: + - 0.0.0.0:8888 + - 0.0.0.0:9999 +label_selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: default.my-instance + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry +`, + } + instance := collectorInstance() + cfg := config.New() + params := manifests.Params{ + OtelCol: instance, + Config: cfg, + Log: logr.Discard(), + } + actual, err := ConfigMap(params) + assert.NoError(t, err) + + assert.Equal(t, "my-instance-targetallocator", actual.Name) + assert.Equal(t, expectedLables, actual.Labels) + assert.Equal(t, expectedData, actual.Data) + + }) + t.Run("should return expected target allocator config map with label selectors", func(t *testing.T) { + expectedLables["app.kubernetes.io/component"] = "opentelemetry-targetallocator" + expectedLables["app.kubernetes.io/name"] = "my-instance-targetallocator" + + expectedData := map[string]string{ + "targetallocator.yaml": `allocation_strategy: least-weighted +config: + scrape_configs: + - job_name: otel-collector + scrape_interval: 10s + static_configs: + - targets: + - 0.0.0.0:8888 + - 0.0.0.0:9999 +label_selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: default.my-instance + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry +pod_monitor_selector: + release: my-instance +service_monitor_selector: + release: my-instance +`, + } + instance := collectorInstance() + instance.Spec.TargetAllocator.PrometheusCR.PodMonitorSelector = map[string]string{ + "release": "my-instance", + } + instance.Spec.TargetAllocator.PrometheusCR.ServiceMonitorSelector = map[string]string{ + "release": "my-instance", + } + cfg := config.New() + params := manifests.Params{ + OtelCol: instance, + Config: cfg, + Log: logr.Discard(), + } + actual, err := ConfigMap(params) + assert.NoError(t, err) + + assert.Equal(t, "my-instance-targetallocator", actual.Name) + assert.Equal(t, expectedLables, actual.Labels) + assert.Equal(t, expectedData, actual.Data) + + }) + t.Run("should return expected target allocator config map with scrape interval set", func(t *testing.T) { + expectedLables["app.kubernetes.io/component"] = "opentelemetry-targetallocator" + expectedLables["app.kubernetes.io/name"] = "my-instance-targetallocator" + + expectedData := map[string]string{ + "targetallocator.yaml": `allocation_strategy: least-weighted +config: + scrape_configs: + - job_name: otel-collector + scrape_interval: 10s + static_configs: + - targets: + - 0.0.0.0:8888 + - 0.0.0.0:9999 +label_selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: default.my-instance + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry +prometheus_cr: + scrape_interval: 30s +`, + } + + collector := collectorInstance() + collector.Spec.TargetAllocator.PrometheusCR.ScrapeInterval = &metav1.Duration{Duration: time.Second * 30} + cfg := config.New() + params := manifests.Params{ + OtelCol: collector, + Config: cfg, + Log: logr.Discard(), + } + actual, err := ConfigMap(params) + assert.NoError(t, err) + + assert.Equal(t, "my-instance-targetallocator", actual.Name) + assert.Equal(t, expectedLables, actual.Labels) + assert.Equal(t, expectedData, actual.Data) + + }) + +} diff --git a/internal/manifests/targetallocator/container.go b/internal/manifests/targetallocator/container.go new file mode 100644 index 0000000000..979a2d8d58 --- /dev/null +++ b/internal/manifests/targetallocator/container.go @@ -0,0 +1,102 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + "github.com/go-logr/logr" + "github.com/operator-framework/operator-lib/proxy" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// Container builds a container for the given TargetAllocator. +func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) corev1.Container { + image := otelcol.Spec.TargetAllocator.Image + if len(image) == 0 { + image = cfg.TargetAllocatorImage() + } + + ports := make([]corev1.ContainerPort, 0) + ports = append(ports, corev1.ContainerPort{ + Name: "http", + ContainerPort: 8080, + Protocol: corev1.ProtocolTCP, + }) + + volumeMounts := []corev1.VolumeMount{{ + Name: naming.TAConfigMapVolume(), + MountPath: "/conf", + }} + + var envVars = otelcol.Spec.TargetAllocator.Env + if otelcol.Spec.TargetAllocator.Env == nil { + envVars = []corev1.EnvVar{} + } + + idx := -1 + for i := range envVars { + if envVars[i].Name == "OTELCOL_NAMESPACE" { + idx = i + } + } + if idx == -1 { + envVars = append(envVars, corev1.EnvVar{ + Name: "OTELCOL_NAMESPACE", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.namespace", + }, + }, + }) + } + + var args []string + if otelcol.Spec.TargetAllocator.PrometheusCR.Enabled { + args = append(args, "--enable-prometheus-cr-watcher") + } + readinessProbe := &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/readyz", + Port: intstr.FromInt(8080), + }, + }, + } + livenessProbe := &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/livez", + Port: intstr.FromInt(8080), + }, + }, + } + + envVars = append(envVars, proxy.ReadProxyVarsFromEnv()...) + return corev1.Container{ + Name: naming.TAContainer(), + Image: image, + Ports: ports, + Env: envVars, + VolumeMounts: volumeMounts, + Resources: otelcol.Spec.TargetAllocator.Resources, + Args: args, + LivenessProbe: livenessProbe, + ReadinessProbe: readinessProbe, + } +} diff --git a/internal/manifests/targetallocator/container_test.go b/internal/manifests/targetallocator/container_test.go new file mode 100644 index 0000000000..91a2b93a17 --- /dev/null +++ b/internal/manifests/targetallocator/container_test.go @@ -0,0 +1,370 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/util/intstr" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +var logger = logf.Log.WithName("unit-tests") + +func TestContainerNewDefault(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{} + cfg := config.New(config.WithTargetAllocatorImage("default-image")) + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Equal(t, "default-image", c.Image) +} + +func TestContainerWithImageOverridden(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + Image: "overridden-image", + }, + }, + } + cfg := config.New(config.WithTargetAllocatorImage("default-image")) + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Equal(t, "overridden-image", c.Image) +} + +func TestContainerPorts(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + Image: "default-image", + }, + }, + } + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Len(t, c.Ports, 1) + assert.Equal(t, "http", c.Ports[0].Name) + assert.Equal(t, int32(8080), c.Ports[0].ContainerPort) +} + +func TestContainerVolumes(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + Image: "default-image", + }, + }, + } + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Len(t, c.VolumeMounts, 1) + assert.Equal(t, naming.TAConfigMapVolume(), c.VolumeMounts[0].Name) +} + +func TestContainerResourceRequirements(t *testing.T) { + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Resources: corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("128M"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("200m"), + corev1.ResourceMemory: resource.MustParse("256M"), + }, + }, + }, + }, + } + + cfg := config.New() + resourceTest := corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("100m"), + corev1.ResourceMemory: resource.MustParse("128M"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("200m"), + corev1.ResourceMemory: resource.MustParse("256M"), + }, + } + // test + c := Container(cfg, logger, otelcol) + resourcesValues := c.Resources + + // verify + assert.Equal(t, resourceTest, resourcesValues) +} + +func TestContainerHasEnvVars(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + Env: []corev1.EnvVar{ + { + Name: "TEST_ENV", + Value: "test", + }, + }, + }, + }, + } + cfg := config.New(config.WithTargetAllocatorImage("default-image")) + + expected := corev1.Container{ + Name: "ta-container", + Image: "default-image", + Env: []corev1.EnvVar{ + { + Name: "TEST_ENV", + Value: "test", + }, + { + Name: "OTELCOL_NAMESPACE", + Value: "", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + APIVersion: "", + FieldPath: "metadata.namespace", + }, + ResourceFieldRef: nil, + ConfigMapKeyRef: nil, + SecretKeyRef: nil, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "ta-internal", + ReadOnly: false, + MountPath: "/conf", + SubPath: "", + MountPropagation: nil, + SubPathExpr: "", + }, + }, + Ports: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: 8080, + Protocol: corev1.ProtocolTCP, + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/readyz", + Port: intstr.FromInt(8080), + }, + }, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/livez", + Port: intstr.FromInt(8080), + }, + }, + }, + } + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Equal(t, expected, c) +} + +func TestContainerHasProxyEnvVars(t *testing.T) { + err := os.Setenv("NO_PROXY", "localhost") + require.NoError(t, err) + defer os.Unsetenv("NO_PROXY") + + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + Env: []corev1.EnvVar{ + { + Name: "TEST_ENV", + Value: "test", + }, + }, + }, + }, + } + cfg := config.New(config.WithTargetAllocatorImage("default-image")) + + // test + c := Container(cfg, logger, otelcol) + + // verify + require.Len(t, c.Env, 4) + assert.Equal(t, corev1.EnvVar{Name: "NO_PROXY", Value: "localhost"}, c.Env[2]) + assert.Equal(t, corev1.EnvVar{Name: "no_proxy", Value: "localhost"}, c.Env[3]) +} + +func TestContainerDoesNotOverrideEnvVars(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + Env: []corev1.EnvVar{ + { + Name: "OTELCOL_NAMESPACE", + Value: "test", + }, + }, + }, + }, + } + cfg := config.New(config.WithTargetAllocatorImage("default-image")) + + expected := corev1.Container{ + Name: "ta-container", + Image: "default-image", + Env: []corev1.EnvVar{ + { + Name: "OTELCOL_NAMESPACE", + Value: "test", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "ta-internal", + ReadOnly: false, + MountPath: "/conf", + SubPath: "", + MountPropagation: nil, + SubPathExpr: "", + }, + }, + Ports: []corev1.ContainerPort{ + { + Name: "http", + ContainerPort: 8080, + Protocol: corev1.ProtocolTCP, + }, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/readyz", + Port: intstr.FromInt(8080), + }, + }, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/livez", + Port: intstr.FromInt(8080), + }, + }, + }, + } + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Equal(t, expected, c) +} +func TestReadinessProbe(t *testing.T) { + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + }, + }, + } + cfg := config.New() + expected := &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/readyz", + Port: intstr.FromInt(8080), + }, + }, + } + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Equal(t, expected, c.ReadinessProbe) +} +func TestLivenessProbe(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Enabled: true, + }, + }, + } + cfg := config.New() + expected := &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/livez", + Port: intstr.FromInt(8080), + }, + }, + } + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Equal(t, expected, c.LivenessProbe) +} diff --git a/internal/manifests/targetallocator/deployment.go b/internal/manifests/targetallocator/deployment.go new file mode 100644 index 0000000000..e4e99b70e6 --- /dev/null +++ b/internal/manifests/targetallocator/deployment.go @@ -0,0 +1,67 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +// Deployment builds the deployment for the given instance. +func Deployment(params manifests.Params) (*appsv1.Deployment, error) { + name := naming.TargetAllocator(params.OtelCol.Name) + labels := Labels(params.OtelCol, name) + + configMap, err := ConfigMap(params) + if err != nil { + params.Log.Info("failed to construct target allocator config map for annotations") + configMap = nil + } + annotations := Annotations(params.OtelCol, configMap) + + return &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: params.OtelCol.Namespace, + Labels: labels, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: params.OtelCol.Spec.TargetAllocator.Replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: labels, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + Annotations: annotations, + }, + Spec: corev1.PodSpec{ + ServiceAccountName: ServiceAccountName(params.OtelCol), + Containers: []corev1.Container{Container(params.Config, params.Log, params.OtelCol)}, + Volumes: Volumes(params.Config, params.OtelCol), + NodeSelector: params.OtelCol.Spec.TargetAllocator.NodeSelector, + Tolerations: params.OtelCol.Spec.TargetAllocator.Tolerations, + TopologySpreadConstraints: params.OtelCol.Spec.TargetAllocator.TopologySpreadConstraints, + Affinity: params.OtelCol.Spec.TargetAllocator.Affinity, + SecurityContext: params.OtelCol.Spec.TargetAllocator.SecurityContext, + }, + }, + }, + }, nil +} diff --git a/internal/manifests/targetallocator/deployment_test.go b/internal/manifests/targetallocator/deployment_test.go new file mode 100644 index 0000000000..0d7d921f0d --- /dev/null +++ b/internal/manifests/targetallocator/deployment_test.go @@ -0,0 +1,372 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +var testTolerationValues = []v1.Toleration{ + { + Key: "hii", + Value: "greeting", + Effect: "NoSchedule", + }, +} + +var testTopologySpreadConstraintValue = []v1.TopologySpreadConstraint{ + { + MaxSkew: 1, + TopologyKey: "kubernetes.io/hostname", + WhenUnsatisfiable: "DoNotSchedule", + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "foo": "bar", + }, + }, + }, +} + +var testAffinityValue = &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ + NodeSelectorTerms: []v1.NodeSelectorTerm{ + { + MatchExpressions: []v1.NodeSelectorRequirement{ + { + Key: "node", + Operator: v1.NodeSelectorOpIn, + Values: []string{"test-node"}, + }, + }, + }, + }, + }, + }, +} + +var runAsUser int64 = 1000 +var runAsGroup int64 = 1000 + +var testSecurityContextValue = &v1.PodSecurityContext{ + RunAsUser: &runAsUser, + RunAsGroup: &runAsGroup, +} + +func TestDeploymentSecurityContext(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + d1, err := Deployment(params1) + if err != nil { + t.Fatal(err) + } + assert.Empty(t, d1.Spec.Template.Spec.SecurityContext) + + // Test SecurityContext + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-securitycontext", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + SecurityContext: testSecurityContextValue, + }, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + + d2, err := Deployment(params2) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, *testSecurityContextValue, *d2.Spec.Template.Spec.SecurityContext) +} + +func TestDeploymentNewDefault(t *testing.T) { + // prepare + otelcol := collectorInstance() + cfg := config.New() + + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + + // test + d, err := Deployment(params) + + assert.NoError(t, err) + + // verify + assert.Equal(t, "my-instance-targetallocator", d.GetName()) + assert.Equal(t, "my-instance-targetallocator", d.GetLabels()["app.kubernetes.io/name"]) + + assert.Len(t, d.Spec.Template.Spec.Containers, 1) + + // should only have the ConfigMap hash annotation + assert.Contains(t, d.Spec.Template.Annotations, configMapHashAnnotationKey) + assert.Len(t, d.Spec.Template.Annotations, 1) + + // the pod selector should match the pod spec's labels + assert.Equal(t, d.Spec.Template.Labels, d.Spec.Selector.MatchLabels) +} + +func TestDeploymentPodAnnotations(t *testing.T) { + // prepare + testPodAnnotationValues := map[string]string{"annotation-key": "annotation-value"} + otelcol := collectorInstance() + otelcol.Spec.PodAnnotations = testPodAnnotationValues + cfg := config.New() + + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + + // test + ds, err := Deployment(params) + assert.NoError(t, err) + // verify + assert.Equal(t, "my-instance-targetallocator", ds.Name) + assert.Subset(t, ds.Spec.Template.Annotations, testPodAnnotationValues) +} + +func collectorInstance() v1alpha1.OpenTelemetryCollector { + configYAML, err := os.ReadFile("testdata/test.yaml") + if err != nil { + fmt.Printf("Error getting yaml file: %v", err) + } + return v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "default", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.47.0", + Config: string(configYAML), + }, + } +} + +func TestDeploymentNodeSelector(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + d1, err := Deployment(params1) + assert.NoError(t, err) + assert.Empty(t, d1.Spec.Template.Spec.NodeSelector) + + // Test nodeSelector + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-nodeselector", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + NodeSelector: map[string]string{ + "node-key": "node-value", + }, + }, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + + d2, err := Deployment(params2) + assert.NoError(t, err) + assert.Equal(t, map[string]string{"node-key": "node-value"}, d2.Spec.Template.Spec.NodeSelector) +} +func TestDeploymentAffinity(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + d1, err := Deployment(params1) + assert.NoError(t, err) + assert.Empty(t, d1.Spec.Template.Spec.Affinity) + + // Test affinity + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-affinity", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Affinity: testAffinityValue, + }, + }, + } + + cfg = config.New() + + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + + d2, err := Deployment(params2) + assert.NoError(t, err) + assert.Equal(t, *testAffinityValue, *d2.Spec.Template.Spec.Affinity) +} + +func TestDeploymentTolerations(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + d1, err := Deployment(params1) + assert.NoError(t, err) + assert.Equal(t, "my-instance-targetallocator", d1.Name) + assert.Empty(t, d1.Spec.Template.Spec.Tolerations) + + // Test Tolerations + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-toleration", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + Tolerations: testTolerationValues, + }, + }, + } + + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + d2, err := Deployment(params2) + assert.NoError(t, err) + assert.Equal(t, "my-instance-toleration-targetallocator", d2.Name) + assert.NotNil(t, d2.Spec.Template.Spec.Tolerations) + assert.NotEmpty(t, d2.Spec.Template.Spec.Tolerations) + assert.Equal(t, testTolerationValues, d2.Spec.Template.Spec.Tolerations) +} + +func TestDeploymentTopologySpreadConstraints(t *testing.T) { + // Test default + otelcol1 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + cfg := config.New() + + params1 := manifests.Params{ + OtelCol: otelcol1, + Config: cfg, + Log: logger, + } + d1, err := Deployment(params1) + assert.NoError(t, err) + assert.Equal(t, "my-instance-targetallocator", d1.Name) + assert.Empty(t, d1.Spec.Template.Spec.TopologySpreadConstraints) + + // Test TopologySpreadConstraints + otelcol2 := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-topologyspreadconstraint", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ + TopologySpreadConstraints: testTopologySpreadConstraintValue, + }, + }, + } + + cfg = config.New() + params2 := manifests.Params{ + OtelCol: otelcol2, + Config: cfg, + Log: logger, + } + + d2, err := Deployment(params2) + assert.NoError(t, err) + assert.Equal(t, "my-instance-topologyspreadconstraint-targetallocator", d2.Name) + assert.NotNil(t, d2.Spec.Template.Spec.TopologySpreadConstraints) + assert.NotEmpty(t, d2.Spec.Template.Spec.TopologySpreadConstraints) + assert.Equal(t, testTopologySpreadConstraintValue, d2.Spec.Template.Spec.TopologySpreadConstraints) +} diff --git a/pkg/targetallocator/labels.go b/internal/manifests/targetallocator/labels.go similarity index 83% rename from pkg/targetallocator/labels.go rename to internal/manifests/targetallocator/labels.go index 5cd4f5373a..3c9ae4dae4 100644 --- a/pkg/targetallocator/labels.go +++ b/internal/manifests/targetallocator/labels.go @@ -16,11 +16,11 @@ package targetallocator import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) // Labels return the common labels to all TargetAllocator objects that are part of a managed OpenTelemetryCollector. -func Labels(instance v1alpha1.OpenTelemetryCollector) map[string]string { +func Labels(instance v1alpha1.OpenTelemetryCollector, name string) map[string]string { // new map every time, so that we don't touch the instance's label base := map[string]string{} if nil != instance.Labels { @@ -34,5 +34,9 @@ func Labels(instance v1alpha1.OpenTelemetryCollector) map[string]string { base["app.kubernetes.io/part-of"] = "opentelemetry" base["app.kubernetes.io/component"] = "opentelemetry-targetallocator" + if _, ok := base["app.kubernetes.io/name"]; !ok { + base["app.kubernetes.io/name"] = name + } + return base } diff --git a/pkg/targetallocator/labels_test.go b/internal/manifests/targetallocator/labels_test.go similarity index 77% rename from pkg/targetallocator/labels_test.go rename to internal/manifests/targetallocator/labels_test.go index abb56d35cd..996128beac 100644 --- a/pkg/targetallocator/labels_test.go +++ b/internal/manifests/targetallocator/labels_test.go @@ -23,35 +23,45 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" ) +const ( + name = "my-instance" + namespace = "my-ns" +) + func TestLabelsCommonSet(t *testing.T) { // prepare otelcol := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-ns", + Name: name, + Namespace: namespace, }, } // test - labels := Labels(otelcol) + labels := Labels(otelcol, name) assert.Equal(t, "opentelemetry-operator", labels["app.kubernetes.io/managed-by"]) assert.Equal(t, "my-ns.my-instance", labels["app.kubernetes.io/instance"]) assert.Equal(t, "opentelemetry", labels["app.kubernetes.io/part-of"]) assert.Equal(t, "opentelemetry-targetallocator", labels["app.kubernetes.io/component"]) + assert.Equal(t, name, labels["app.kubernetes.io/name"]) } func TestLabelsPropagateDown(t *testing.T) { // prepare otelcol := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"myapp": "mycomponent"}, + Labels: map[string]string{ + "myapp": "mycomponent", + "app.kubernetes.io/name": "test", + }, }, } // test - labels := Labels(otelcol) + labels := Labels(otelcol, name) // verify - assert.Len(t, labels, 5) + assert.Len(t, labels, 6) assert.Equal(t, "mycomponent", labels["myapp"]) + assert.Equal(t, "test", labels["app.kubernetes.io/name"]) } diff --git a/internal/manifests/targetallocator/service.go b/internal/manifests/targetallocator/service.go new file mode 100644 index 0000000000..bb3079dda9 --- /dev/null +++ b/internal/manifests/targetallocator/service.go @@ -0,0 +1,47 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" +) + +func Service(params manifests.Params) *corev1.Service { + name := naming.TAService(params.OtelCol.Name) + labels := Labels(params.OtelCol, name) + + selector := Labels(params.OtelCol, name) + + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.TAService(params.OtelCol.Name), + Namespace: params.OtelCol.Namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Selector: selector, + Ports: []corev1.ServicePort{{ + Name: "targetallocation", + Port: 80, + TargetPort: intstr.FromString("http"), + }}, + }, + } +} diff --git a/internal/manifests/targetallocator/service_test.go b/internal/manifests/targetallocator/service_test.go new file mode 100644 index 0000000000..ad0676147b --- /dev/null +++ b/internal/manifests/targetallocator/service_test.go @@ -0,0 +1,45 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + "testing" + + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" + + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +func TestServicePorts(t *testing.T) { + otelcol := collectorInstance() + cfg := config.New() + + params := manifests.Params{ + OtelCol: otelcol, + Config: cfg, + Log: logger, + } + + ports := []v1.ServicePort{{Name: "targetallocation", Port: 80, TargetPort: intstr.FromString("http")}} + + s := Service(params) + + assert.Equal(t, ports[0].Name, s.Spec.Ports[0].Name) + assert.Equal(t, ports[0].Port, s.Spec.Ports[0].Port) + assert.Equal(t, ports[0].TargetPort, s.Spec.Ports[0].TargetPort) +} diff --git a/pkg/targetallocator/serviceaccount.go b/internal/manifests/targetallocator/serviceaccount.go similarity index 71% rename from pkg/targetallocator/serviceaccount.go rename to internal/manifests/targetallocator/serviceaccount.go index 78627f3bf9..c9cf732995 100644 --- a/pkg/targetallocator/serviceaccount.go +++ b/internal/manifests/targetallocator/serviceaccount.go @@ -18,30 +18,32 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" ) // ServiceAccountName returns the name of the existing or self-provisioned service account to use for the given instance. func ServiceAccountName(instance v1alpha1.OpenTelemetryCollector) string { if len(instance.Spec.TargetAllocator.ServiceAccount) == 0 { - return naming.ServiceAccount(instance) + return naming.ServiceAccount(instance.Name) } return instance.Spec.TargetAllocator.ServiceAccount } // ServiceAccount returns the service account for the given instance. -func ServiceAccount(otelcol v1alpha1.OpenTelemetryCollector) corev1.ServiceAccount { - labels := Labels(otelcol) - labels["app.kubernetes.io/name"] = naming.TargetAllocatorServiceAccount(otelcol) +func ServiceAccount(params manifests.Params) *corev1.ServiceAccount { + name := naming.TargetAllocatorServiceAccount(params.OtelCol.Name) + labels := Labels(params.OtelCol, name) - return corev1.ServiceAccount{ + return &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ - Name: naming.TargetAllocatorServiceAccount(otelcol), - Namespace: otelcol.Namespace, + Name: name, + Namespace: params.OtelCol.Namespace, Labels: labels, - Annotations: otelcol.Annotations, + Annotations: params.OtelCol.Annotations, }, } } diff --git a/pkg/targetallocator/serviceaccount_test.go b/internal/manifests/targetallocator/serviceaccount_test.go similarity index 100% rename from pkg/targetallocator/serviceaccount_test.go rename to internal/manifests/targetallocator/serviceaccount_test.go diff --git a/internal/manifests/targetallocator/targetallocator.go b/internal/manifests/targetallocator/targetallocator.go new file mode 100644 index 0000000000..aa2535b654 --- /dev/null +++ b/internal/manifests/targetallocator/targetallocator.go @@ -0,0 +1,44 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package targetallocator + +import ( + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +// Build creates the manifest for the TargetAllocator resource. +func Build(params manifests.Params) ([]client.Object, error) { + var resourceManifests []client.Object + if !params.OtelCol.Spec.TargetAllocator.Enabled { + return resourceManifests, nil + } + resourceFactories := []manifests.K8sManifestFactory{ + manifests.Factory(ConfigMap), + manifests.Factory(Deployment), + manifests.FactoryWithoutError(ServiceAccount), + manifests.FactoryWithoutError(Service), + } + for _, factory := range resourceFactories { + res, err := factory(params) + if err != nil { + return nil, err + } else if manifests.ObjectIsNotNil(res) { + resourceManifests = append(resourceManifests, res) + } + } + return resourceManifests, nil +} diff --git a/pkg/collector/reconcile/test.yaml b/internal/manifests/targetallocator/testdata/test.yaml similarity index 73% rename from pkg/collector/reconcile/test.yaml rename to internal/manifests/targetallocator/testdata/test.yaml index e88b110245..f03253f78e 100644 --- a/pkg/collector/reconcile/test.yaml +++ b/internal/manifests/targetallocator/testdata/test.yaml @@ -6,17 +6,17 @@ receivers: prometheus: config: scrape_configs: - job_name: otel-collector + - job_name: otel-collector scrape_interval: 10s static_configs: - targets: [ '0.0.0.0:8888', '0.0.0.0:9999' ] exporters: - logging: + debug: service: pipelines: metrics: - receivers: [prometheus] + receivers: [prometheus, jaeger] processors: [] - exporters: [logging] \ No newline at end of file + exporters: [debug] diff --git a/pkg/targetallocator/volume.go b/internal/manifests/targetallocator/volume.go similarity index 93% rename from pkg/targetallocator/volume.go rename to internal/manifests/targetallocator/volume.go index 7e1b6d1e06..8200b00d38 100644 --- a/pkg/targetallocator/volume.go +++ b/internal/manifests/targetallocator/volume.go @@ -19,7 +19,7 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) // Volumes builds the volumes for the given instance, including the config map volume. @@ -28,7 +28,7 @@ func Volumes(cfg config.Config, otelcol v1alpha1.OpenTelemetryCollector) []corev Name: naming.TAConfigMapVolume(), VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{Name: naming.TAConfigMap(otelcol)}, + LocalObjectReference: corev1.LocalObjectReference{Name: naming.TAConfigMap(otelcol.Name)}, Items: []corev1.KeyToPath{ { Key: cfg.TargetAllocatorConfigMapEntry(), diff --git a/pkg/targetallocator/volume_test.go b/internal/manifests/targetallocator/volume_test.go similarity index 89% rename from pkg/targetallocator/volume_test.go rename to internal/manifests/targetallocator/volume_test.go index aeaef7c92c..0b50006e4f 100644 --- a/pkg/targetallocator/volume_test.go +++ b/internal/manifests/targetallocator/volume_test.go @@ -21,7 +21,7 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) func TestVolumeNewDefault(t *testing.T) { @@ -35,7 +35,7 @@ func TestVolumeNewDefault(t *testing.T) { // verify assert.Len(t, volumes, 1) - //check if the number of elements in the volume source items list is 1 + // check if the number of elements in the volume source items list is 1 assert.Len(t, volumes[0].VolumeSource.ConfigMap.Items, 1) // check that it's the ta-internal volume, with the config map diff --git a/pkg/naming/dns.go b/internal/naming/dns.go similarity index 100% rename from pkg/naming/dns.go rename to internal/naming/dns.go diff --git a/pkg/naming/dns_test.go b/internal/naming/dns_test.go similarity index 100% rename from pkg/naming/dns_test.go rename to internal/naming/dns_test.go diff --git a/internal/naming/main.go b/internal/naming/main.go new file mode 100644 index 0000000000..a19301a01f --- /dev/null +++ b/internal/naming/main.go @@ -0,0 +1,161 @@ +// 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 naming is for determining the names for components (containers, services, ...). +package naming + +// ConfigMap builds the name for the config map used in the OpenTelemetryCollector containers. +func ConfigMap(otelcol string) string { + return DNSName(Truncate("%s-collector", 63, otelcol)) +} + +// TAConfigMap returns the name for the config map used in the TargetAllocator. +func TAConfigMap(otelcol string) string { + return DNSName(Truncate("%s-targetallocator", 63, otelcol)) +} + +// OpAMPBridgeConfigMap builds the name for the config map used in the OpAMPBridge containers. +func OpAMPBridgeConfigMap(opampBridge string) string { + return DNSName(Truncate("%s-opamp-bridge", 63, opampBridge)) +} + +// ConfigMapVolume returns the name to use for the config map's volume in the pod. +func ConfigMapVolume() string { + return "otc-internal" +} + +// ConfigMapExtra returns the prefix to use for the extras mounted configmaps in the pod. +func ConfigMapExtra(extraConfigMapName string) string { + return DNSName(Truncate("configmap-%s", 63, extraConfigMapName)) +} + +// TAConfigMapVolume returns the name to use for the config map's volume in the TargetAllocator pod. +func TAConfigMapVolume() string { + return "ta-internal" +} + +// OpAMPBridgeConfigMapVolume returns the name to use for the config map's volume in the OpAMPBridge pod. +func OpAMPBridgeConfigMapVolume() string { + return "opamp-bridge-internal" +} + +// Container returns the name to use for the container in the pod. +func Container() string { + return "otc-container" +} + +// TAContainer returns the name to use for the container in the TargetAllocator pod. +func TAContainer() string { + return "ta-container" +} + +// OpAMPBridgeContainer returns the name to use for the container in the OpAMPBridge pod. +func OpAMPBridgeContainer() string { + return "opamp-bridge-container" +} + +// Collector builds the collector (deployment/daemonset) name based on the instance. +func Collector(otelcol string) string { + return DNSName(Truncate("%s-collector", 63, otelcol)) +} + +// HorizontalPodAutoscaler builds the autoscaler name based on the instance. +func HorizontalPodAutoscaler(otelcol string) string { + return DNSName(Truncate("%s-collector", 63, otelcol)) +} + +// HorizontalPodAutoscaler builds the autoscaler name based on the instance. +func PodDisruptionBudget(otelcol string) string { + return DNSName(Truncate("%s-collector", 63, otelcol)) +} + +// OpenTelemetryCollector builds the collector (deployment/daemonset) name based on the instance. +func OpenTelemetryCollector(otelcol string) string { + return DNSName(Truncate("%s", 63, otelcol)) +} + +// OpenTelemetryCollectorName builds the collector (deployment/daemonset) name based on the instance. +func OpenTelemetryCollectorName(otelcolName string) string { + return DNSName(Truncate("%s", 63, otelcolName)) +} + +// TargetAllocator returns the TargetAllocator deployment resource name. +func TargetAllocator(otelcol string) string { + return DNSName(Truncate("%s-targetallocator", 63, otelcol)) +} + +// OpAMPBridge returns the OpAMPBridge deployment resource name. +func OpAMPBridge(opampBridge string) string { + return DNSName(Truncate("%s-opamp-bridge", 63, opampBridge)) +} + +// HeadlessService builds the name for the headless service based on the instance. +func HeadlessService(otelcol string) string { + return DNSName(Truncate("%s-headless", 63, Service(otelcol))) +} + +// MonitoringService builds the name for the monitoring service based on the instance. +func MonitoringService(otelcol string) string { + return DNSName(Truncate("%s-monitoring", 63, Service(otelcol))) +} + +// Service builds the service name based on the instance. +func Service(otelcol string) string { + return DNSName(Truncate("%s-collector", 63, otelcol)) +} + +// Ingress builds the ingress name based on the instance. +func Ingress(otelcol string) string { + return DNSName(Truncate("%s-ingress", 63, otelcol)) +} + +// Route builds the route name based on the instance. +func Route(otelcol string, prefix string) string { + return DNSName(Truncate("%s-%s-route", 63, prefix, otelcol)) +} + +// TAService returns the name to use for the TargetAllocator service. +func TAService(otelcol string) string { + return DNSName(Truncate("%s-targetallocator", 63, otelcol)) +} + +// OpAMPBridgeService returns the name to use for the OpAMPBridge service. +func OpAMPBridgeService(opampBridge string) string { + return DNSName(Truncate("%s-opamp-bridge", 63, opampBridge)) +} + +// ServiceAccount builds the service account name based on the instance. +func ServiceAccount(otelcol string) string { + return DNSName(Truncate("%s-collector", 63, otelcol)) +} + +// ServiceMonitor builds the service Monitor name based on the instance. +func ServiceMonitor(otelcol string) string { + return DNSName(Truncate("%s-collector", 63, otelcol)) +} + +// PodMonitor builds the pod Monitor name based on the instance. +func PodMonitor(otelcol string) string { + return DNSName(Truncate("%s-collector", 63, otelcol)) +} + +// TargetAllocatorServiceAccount returns the TargetAllocator service account resource name. +func TargetAllocatorServiceAccount(otelcol string) string { + return DNSName(Truncate("%s-targetallocator", 63, otelcol)) +} + +// OpAMPBridgeServiceAccount builds the service account name based on the instance. +func OpAMPBridgeServiceAccount(opampBridge string) string { + return DNSName(Truncate("%s-opamp-bridge", 63, opampBridge)) +} diff --git a/internal/naming/port.go b/internal/naming/port.go new file mode 100644 index 0000000000..f4ba37acab --- /dev/null +++ b/internal/naming/port.go @@ -0,0 +1,44 @@ +// 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 naming + +import ( + "fmt" + "regexp" + "strings" +) + +var ( + // DNS_LABEL constraints: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names + dnsLabelValidation = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$") +) + +// PortName defines the port name used in services, ingresses and routes. +// The port name in pod and ingress spec has to be maximum 15 characters long. +func PortName(receiverName string, port int32) string { + if len(receiverName) > 15 { + return fmt.Sprintf("port-%d", port) + } + + candidate := strings.ReplaceAll(receiverName, "/", "-") + candidate = strings.ReplaceAll(candidate, "_", "-") + + if !dnsLabelValidation.MatchString(candidate) { + return fmt.Sprintf("port-%d", port) + } + + // matches the pattern and has less than 15 chars -- the candidate name is good to go! + return candidate +} diff --git a/internal/naming/port_test.go b/internal/naming/port_test.go new file mode 100644 index 0000000000..172956b4cd --- /dev/null +++ b/internal/naming/port_test.go @@ -0,0 +1,68 @@ +// 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 naming + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test(t *testing.T) { + tests := []struct { + testName string + receiverName string + port int32 + expected string + }{ + { + testName: "too_long", + receiverName: "otlphttpotlphttpotlphttpotlphttpotlphttpotlphttpotlphttpotlphttpotlphttpotlphttpotlphttpotlphttp", + port: 4318, + expected: "port-4318", + }, + { + testName: "with underscore", + receiverName: "otlp_http", + port: 4318, + expected: "otlp-http", + }, + { + testName: "with slash", + receiverName: "otlp/http", + port: 4318, + expected: "otlp-http", + }, + { + testName: "not DNS", + receiverName: "otlp&&**http", + port: 4318, + expected: "port-4318", + }, + { + testName: "ok", + receiverName: "otlphttp", + port: 4318, + expected: "otlphttp", + }, + } + + for _, test := range tests { + t.Run(test.testName, func(t *testing.T) { + name := PortName(test.receiverName, test.port) + assert.Equal(t, test.expected, name) + }) + } +} diff --git a/pkg/naming/triming.go b/internal/naming/triming.go similarity index 100% rename from pkg/naming/triming.go rename to internal/naming/triming.go diff --git a/pkg/naming/triming_test.go b/internal/naming/triming_test.go similarity index 100% rename from pkg/naming/triming_test.go rename to internal/naming/triming_test.go diff --git a/pkg/collector/reconcile/opentelemetry.go b/internal/status/collector/collector.go similarity index 58% rename from pkg/collector/reconcile/opentelemetry.go rename to internal/status/collector/collector.go index daaef753cd..cdebae02d0 100644 --- a/pkg/collector/reconcile/opentelemetry.go +++ b/internal/status/collector/collector.go @@ -12,63 +12,40 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package reconcile contains reconciliation logic for OpenTelemetry Collector components. -package reconcile +package collector import ( "context" "fmt" + "strconv" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" "github.com/open-telemetry/opentelemetry-operator/internal/version" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" ) -// Self updates this instance's self data. This should be the last item in the reconciliation, as it causes changes -// making params.Instance obsolete. Default values should be set in the Defaulter webhook, this should only be used -// for the Status, which can't be set by the defaulter. -func Self(ctx context.Context, params Params) error { - changed := params.Instance - - // this field is only changed for new instances: on existing instances this - // field is reconciled when the operator is first started, i.e. during - // the upgrade mechanism - if params.Instance.Status.Version == "" { +func UpdateCollectorStatus(ctx context.Context, cli client.Client, changed *v1alpha1.OpenTelemetryCollector) error { + if changed.Status.Version == "" { // a version is not set, otherwise let the upgrade mechanism take care of it! changed.Status.Version = version.OpenTelemetryCollector() } - - if err := updateScaleSubResourceStatus(ctx, params.Client, &changed); err != nil { - return fmt.Errorf("failed to update the scale subresource status for the OpenTelemetry CR: %w", err) - } - - statusPatch := client.MergeFrom(¶ms.Instance) - if err := params.Client.Status().Patch(ctx, &changed, statusPatch); err != nil { - return fmt.Errorf("failed to apply status changes to the OpenTelemetry CR: %w", err) - } - - return nil -} - -func updateScaleSubResourceStatus(ctx context.Context, cli client.Client, changed *v1alpha1.OpenTelemetryCollector) error { mode := changed.Spec.Mode if mode != v1alpha1.ModeDeployment && mode != v1alpha1.ModeStatefulSet { changed.Status.Scale.Replicas = 0 changed.Status.Scale.Selector = "" - return nil } - name := naming.Collector(*changed) + name := naming.Collector(changed.Name) // Set the scale selector - labels := collector.Labels(*changed, []string{}) - labels["app.kubernetes.io/name"] = name + labels := manifestutils.Labels(changed.ObjectMeta, name, changed.Spec.Image, collector.ComponentOpenTelemetryCollector, []string{}) selector, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{MatchLabels: labels}) if err != nil { return fmt.Errorf("failed to get selector for labelSelector: %w", err) @@ -78,10 +55,14 @@ func updateScaleSubResourceStatus(ctx context.Context, cli client.Client, change // Set the scale replicas objKey := client.ObjectKey{ Namespace: changed.GetNamespace(), - Name: naming.Collector(*changed), + Name: naming.Collector(changed.Name), } var replicas int32 + var readyReplicas int32 + var statusReplicas string + var statusImage string + switch mode { // nolint:exhaustive case v1alpha1.ModeDeployment: obj := &appsv1.Deployment{} @@ -89,6 +70,9 @@ func updateScaleSubResourceStatus(ctx context.Context, cli client.Client, change return fmt.Errorf("failed to get deployment status.replicas: %w", err) } replicas = obj.Status.Replicas + readyReplicas = obj.Status.ReadyReplicas + statusReplicas = strconv.Itoa(int(readyReplicas)) + "/" + strconv.Itoa(int(replicas)) + statusImage = obj.Spec.Template.Spec.Containers[0].Image case v1alpha1.ModeStatefulSet: obj := &appsv1.StatefulSet{} @@ -96,8 +80,20 @@ func updateScaleSubResourceStatus(ctx context.Context, cli client.Client, change return fmt.Errorf("failed to get statefulSet status.replicas: %w", err) } replicas = obj.Status.Replicas + readyReplicas = obj.Status.ReadyReplicas + statusReplicas = strconv.Itoa(int(readyReplicas)) + "/" + strconv.Itoa(int(replicas)) + statusImage = obj.Spec.Template.Spec.Containers[0].Image + + case v1alpha1.ModeDaemonSet: + obj := &appsv1.DaemonSet{} + if err := cli.Get(ctx, objKey, obj); err != nil { + return fmt.Errorf("failed to get daemonSet status.replicas: %w", err) + } + statusImage = obj.Spec.Template.Spec.Containers[0].Image } changed.Status.Scale.Replicas = replicas + changed.Status.Image = statusImage + changed.Status.Scale.StatusReplicas = statusReplicas return nil } diff --git a/internal/status/collector/handle.go b/internal/status/collector/handle.go new file mode 100644 index 0000000000..b20b03d24b --- /dev/null +++ b/internal/status/collector/handle.go @@ -0,0 +1,72 @@ +// 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 collector + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" + "github.com/open-telemetry/opentelemetry-operator/internal/version" + collectorupgrade "github.com/open-telemetry/opentelemetry-operator/pkg/collector/upgrade" +) + +const ( + eventTypeNormal = "Normal" + eventTypeWarning = "Warning" + + reasonError = "Error" + reasonStatusFailure = "StatusFailure" + reasonInfo = "Info" +) + +// HandleReconcileStatus handles updating the status of the CRDs managed by the operator. +// TODO: make the status more useful https://github.com/open-telemetry/opentelemetry-operator/issues/1972 +func HandleReconcileStatus(ctx context.Context, log logr.Logger, params manifests.Params, err error) (ctrl.Result, error) { + log.V(2).Info("updating collector status") + if err != nil { + params.Recorder.Event(¶ms.OtelCol, eventTypeWarning, reasonError, err.Error()) + return ctrl.Result{}, err + } + changed := params.OtelCol.DeepCopy() + + up := &collectorupgrade.VersionUpgrade{ + Log: params.Log, + Version: version.Get(), + Client: params.Client, + Recorder: params.Recorder, + } + upgraded, upgradeErr := up.ManagedInstance(ctx, *changed) + if upgradeErr != nil { + // don't fail to allow setting the status + params.Log.Error(upgradeErr, "failed to upgrade the OpenTelemetry CR") + } + changed = &upgraded + statusErr := UpdateCollectorStatus(ctx, params.Client, changed) + if statusErr != nil { + params.Recorder.Event(changed, eventTypeWarning, reasonStatusFailure, statusErr.Error()) + return ctrl.Result{}, statusErr + } + statusPatch := client.MergeFrom(¶ms.OtelCol) + if err := params.Client.Status().Patch(ctx, changed, statusPatch); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to apply status changes to the OpenTelemetry CR: %w", err) + } + params.Recorder.Event(changed, eventTypeNormal, reasonInfo, "applied status changes") + return ctrl.Result{}, nil +} diff --git a/internal/status/opampbridge/handle.go b/internal/status/opampbridge/handle.go new file mode 100644 index 0000000000..18e5719904 --- /dev/null +++ b/internal/status/opampbridge/handle.go @@ -0,0 +1,58 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package opampbridge + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/internal/manifests" +) + +const ( + eventTypeNormal = "Normal" + eventTypeWarning = "Warning" + + reasonError = "Error" + reasonStatusFailure = "StatusFailure" + reasonInfo = "Info" +) + +// HandleReconcileStatus handles updating the status of the CRDs managed by the operator. +// TODO: make the status more useful https://github.com/open-telemetry/opentelemetry-operator/issues/1972 +func HandleReconcileStatus(ctx context.Context, log logr.Logger, params manifests.Params, err error) (ctrl.Result, error) { + log.V(2).Info("updating opampbridge status") + if err != nil { + params.Recorder.Event(¶ms.OpAMPBridge, eventTypeWarning, reasonError, err.Error()) + return ctrl.Result{}, err + } + changed := params.OpAMPBridge.DeepCopy() + + statusErr := UpdateOpAMPBridgeStatus(ctx, params.Client, changed) + if statusErr != nil { + params.Recorder.Event(changed, eventTypeWarning, reasonStatusFailure, statusErr.Error()) + return ctrl.Result{}, statusErr + } + statusPatch := client.MergeFrom(¶ms.OpAMPBridge) + if err := params.Client.Status().Patch(ctx, changed, statusPatch); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to apply status changes to the OpenTelemetry CR: %w", err) + } + params.Recorder.Event(changed, eventTypeNormal, reasonInfo, "applied status changes") + return ctrl.Result{}, nil +} diff --git a/internal/status/opampbridge/opampbridge.go b/internal/status/opampbridge/opampbridge.go new file mode 100644 index 0000000000..d09bfbf8aa --- /dev/null +++ b/internal/status/opampbridge/opampbridge.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package opampbridge + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/version" +) + +func UpdateOpAMPBridgeStatus(ctx context.Context, cli client.Client, changed *v1alpha1.OpAMPBridge) error { + if changed.Status.Version == "" { + changed.Status.Version = version.OperatorOpAMPBridge() + } + return nil +} diff --git a/internal/version/main.go b/internal/version/main.go index d6e7cf1db6..7e85af2f12 100644 --- a/internal/version/main.go +++ b/internal/version/main.go @@ -21,56 +21,72 @@ import ( ) var ( - version string - buildDate string - otelCol string - targetAllocator string - autoInstrumentationJava string - autoInstrumentationNodeJS string - autoInstrumentationPython string - autoInstrumentationDotNet string + version string + buildDate string + otelCol string + targetAllocator string + operatorOpAMPBridge string + autoInstrumentationJava string + autoInstrumentationNodeJS string + autoInstrumentationPython string + autoInstrumentationDotNet string + autoInstrumentationApacheHttpd string + autoInstrumentationNginx string + autoInstrumentationGo string ) // Version holds this Operator's version as well as the version of some of the components it uses. type Version struct { - Operator string `json:"opentelemetry-operator"` - BuildDate string `json:"build-date"` - OpenTelemetryCollector string `json:"opentelemetry-collector-version"` - Go string `json:"go-version"` - TargetAllocator string `json:"target-allocator-version"` - AutoInstrumentationJava string `json:"auto-instrumentation-java"` - AutoInstrumentationNodeJS string `json:"auto-instrumentation-nodejs"` - AutoInstrumentationPython string `json:"auto-instrumentation-python"` - AutoInstrumentationDotNet string `json:"auto-instrumentation-dotnet"` + Operator string `json:"opentelemetry-operator"` + BuildDate string `json:"build-date"` + OpenTelemetryCollector string `json:"opentelemetry-collector-version"` + Go string `json:"go-version"` + TargetAllocator string `json:"target-allocator-version"` + OperatorOpAMPBridge string `json:"operator-opamp-bridge"` + AutoInstrumentationJava string `json:"auto-instrumentation-java"` + AutoInstrumentationNodeJS string `json:"auto-instrumentation-nodejs"` + AutoInstrumentationPython string `json:"auto-instrumentation-python"` + AutoInstrumentationDotNet string `json:"auto-instrumentation-dotnet"` + AutoInstrumentationGo string `json:"auto-instrumentation-go"` + AutoInstrumentationApacheHttpd string `json:"auto-instrumentation-apache-httpd"` + AutoInstrumentationNginx string `json:"auto-instrumentation-nginx"` } // Get returns the Version object with the relevant information. func Get() Version { return Version{ - Operator: version, - BuildDate: buildDate, - OpenTelemetryCollector: OpenTelemetryCollector(), - Go: runtime.Version(), - TargetAllocator: TargetAllocator(), - AutoInstrumentationJava: AutoInstrumentationJava(), - AutoInstrumentationNodeJS: AutoInstrumentationNodeJS(), - AutoInstrumentationPython: AutoInstrumentationPython(), - AutoInstrumentationDotNet: AutoInstrumentationDotNet(), + Operator: version, + BuildDate: buildDate, + OpenTelemetryCollector: OpenTelemetryCollector(), + Go: runtime.Version(), + TargetAllocator: TargetAllocator(), + OperatorOpAMPBridge: OperatorOpAMPBridge(), + AutoInstrumentationJava: AutoInstrumentationJava(), + AutoInstrumentationNodeJS: AutoInstrumentationNodeJS(), + AutoInstrumentationPython: AutoInstrumentationPython(), + AutoInstrumentationDotNet: AutoInstrumentationDotNet(), + AutoInstrumentationGo: AutoInstrumentationGo(), + AutoInstrumentationApacheHttpd: AutoInstrumentationApacheHttpd(), + AutoInstrumentationNginx: AutoInstrumentationNginx(), } } func (v Version) String() string { return fmt.Sprintf( - "Version(Operator='%v', BuildDate='%v', OpenTelemetryCollector='%v', Go='%v', TargetAllocator='%v', AutoInstrumentationJava='%v', AutoInstrumentationNodeJS='%v', AutoInstrumentationPython='%v', AutoInstrumentationDotNet='%v')", + "Version(Operator='%v', BuildDate='%v', OpenTelemetryCollector='%v', Go='%v', TargetAllocator='%v', OperatorOpAMPBridge='%v', AutoInstrumentationJava='%v', AutoInstrumentationNodeJS='%v', AutoInstrumentationPython='%v', AutoInstrumentationDotNet='%v', AutoInstrumentationGo='%v', AutoInstrumentationApacheHttpd='%v', AutoInstrumentationNginx='%v')", v.Operator, v.BuildDate, v.OpenTelemetryCollector, v.Go, v.TargetAllocator, + v.OperatorOpAMPBridge, v.AutoInstrumentationJava, v.AutoInstrumentationNodeJS, v.AutoInstrumentationPython, v.AutoInstrumentationDotNet, + v.AutoInstrumentationGo, + v.AutoInstrumentationApacheHttpd, + v.AutoInstrumentationNginx, ) } @@ -96,6 +112,17 @@ func TargetAllocator() string { return "0.0.0" } +// OperatorOpAMPBridge returns the default OperatorOpAMPBridge to use when no versions are specified via CLI or configuration. +func OperatorOpAMPBridge() string { + if len(operatorOpAMPBridge) > 0 { + // this should always be set, as it's specified during the build + return operatorOpAMPBridge + } + + // fallback value, useful for tests + return "0.0.0" +} + func AutoInstrumentationJava() string { if len(autoInstrumentationJava) > 0 { return autoInstrumentationJava @@ -123,3 +150,24 @@ func AutoInstrumentationDotNet() string { } return "0.0.0" } + +func AutoInstrumentationApacheHttpd() string { + if len(autoInstrumentationApacheHttpd) > 0 { + return autoInstrumentationApacheHttpd + } + return "0.0.0" +} + +func AutoInstrumentationNginx() string { + if len(autoInstrumentationNginx) > 0 { + return autoInstrumentationNginx + } + return "0.0.0" +} + +func AutoInstrumentationGo() string { + if len(autoInstrumentationGo) > 0 { + return autoInstrumentationGo + } + return "0.0.0" +} diff --git a/internal/webhookhandler/webhookhandler.go b/internal/webhook/podmutation/webhookhandler.go similarity index 86% rename from internal/webhookhandler/webhookhandler.go rename to internal/webhook/podmutation/webhookhandler.go index 4b2614ecca..9d9fa6e743 100644 --- a/internal/webhookhandler/webhookhandler.go +++ b/internal/webhook/podmutation/webhookhandler.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package webhookhandler contains the webhook that injects sidecars into pods. -package webhookhandler +// Package podmutation contains the webhook that injects sidecars into pods. +package podmutation import ( "context" @@ -35,16 +35,15 @@ import ( // +kubebuilder:rbac:groups=opentelemetry.io,resources=instrumentations,verbs=get;list;watch // +kubebuilder:rbac:groups="apps",resources=replicasets,verbs=get;list;watch -var _ WebhookHandler = (*podSidecarInjector)(nil) +var _ WebhookHandler = (*podMutationWebhook)(nil) // WebhookHandler is a webhook handler that analyzes new pods and injects appropriate sidecars into it. type WebhookHandler interface { admission.Handler - admission.DecoderInjector } // the implementation. -type podSidecarInjector struct { +type podMutationWebhook struct { client client.Client decoder *admission.Decoder logger logr.Logger @@ -58,16 +57,17 @@ type PodMutator interface { } // NewWebhookHandler creates a new WebhookHandler. -func NewWebhookHandler(cfg config.Config, logger logr.Logger, cl client.Client, podMutators []PodMutator) WebhookHandler { - return &podSidecarInjector{ +func NewWebhookHandler(cfg config.Config, logger logr.Logger, decoder *admission.Decoder, cl client.Client, podMutators []PodMutator) WebhookHandler { + return &podMutationWebhook{ config: cfg, + decoder: decoder, logger: logger, client: cl, podMutators: podMutators, } } -func (p *podSidecarInjector) Handle(ctx context.Context, req admission.Request) admission.Response { +func (p *podMutationWebhook) Handle(ctx context.Context, req admission.Request) admission.Response { pod := corev1.Pod{} err := p.decoder.Decode(req, &pod) if err != nil { @@ -105,8 +105,3 @@ func (p *podSidecarInjector) Handle(ctx context.Context, req admission.Request) } return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod) } - -func (p *podSidecarInjector) InjectDecoder(d *admission.Decoder) error { - p.decoder = d - return nil -} diff --git a/internal/webhookhandler/webhookhandler_suite_test.go b/internal/webhook/podmutation/webhookhandler_suite_test.go similarity index 71% rename from internal/webhookhandler/webhookhandler_suite_test.go rename to internal/webhook/podmutation/webhookhandler_suite_test.go index 4c0b1c6814..05f6c062be 100644 --- a/internal/webhookhandler/webhookhandler_suite_test.go +++ b/internal/webhook/podmutation/webhookhandler_suite_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package webhookhandler_test +package podmutation_test import ( "context" @@ -28,12 +28,16 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" "k8s.io/client-go/util/retry" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" // +kubebuilder:scaffold:imports ) @@ -43,6 +47,8 @@ var ( testScheme *runtime.Scheme = scheme.Scheme ctx context.Context cancel context.CancelFunc + err error + cfg *rest.Config ) func TestMain(m *testing.M) { @@ -50,18 +56,18 @@ func TestMain(m *testing.M) { defer cancel() testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, WebhookInstallOptions: envtest.WebhookInstallOptions{ - Paths: []string{filepath.Join("..", "..", "config", "webhook")}, + Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")}, }, } - cfg, err := testEnv.Start() + cfg, err = testEnv.Start() if err != nil { fmt.Printf("failed to start testEnv: %v", err) os.Exit(1) } - if err := v1alpha1.AddToScheme(testScheme); err != nil { + if err = v1alpha1.AddToScheme(testScheme); err != nil { fmt.Printf("failed to register scheme: %v", err) os.Exit(1) } @@ -75,20 +81,24 @@ func TestMain(m *testing.M) { // start webhook server using Manager webhookInstallOptions := &testEnv.WebhookInstallOptions - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: testScheme, - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - LeaderElection: false, - MetricsBindAddress: "0", + mgr, mgrErr := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: testScheme, + LeaderElection: false, + WebhookServer: webhook.NewServer(webhook.Options{ + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), + Metrics: metricsserver.Options{ + BindAddress: "0", + }, }) - if err != nil { - fmt.Printf("failed to start webhook server: %v", err) + if mgrErr != nil { + fmt.Printf("failed to start webhook server: %v", mgrErr) os.Exit(1) } - if err := (&v1alpha1.OpenTelemetryCollector{}).SetupWebhookWithManager(mgr); err != nil { + if err = v1alpha1.SetupCollectorWebhook(mgr, config.New()); err != nil { fmt.Printf("failed to SetupWebhookWithManager: %v", err) os.Exit(1) } @@ -119,9 +129,9 @@ func TestMain(m *testing.M) { return true }, func() error { // #nosec G402 - conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) - if err != nil { - return err + conn, tlsErr := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) + if tlsErr != nil { + return tlsErr } _ = conn.Close() return nil diff --git a/internal/webhookhandler/webhookhandler_test.go b/internal/webhook/podmutation/webhookhandler_test.go similarity index 90% rename from internal/webhookhandler/webhookhandler_test.go rename to internal/webhook/podmutation/webhookhandler_test.go index 16b3543f4c..0adbc2b929 100644 --- a/internal/webhookhandler/webhookhandler_test.go +++ b/internal/webhook/podmutation/webhookhandler_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package webhookhandler_test +package podmutation_test import ( "context" @@ -32,8 +32,8 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - . "github.com/open-telemetry/opentelemetry-operator/internal/webhookhandler" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" + . "github.com/open-telemetry/opentelemetry-operator/internal/webhook/podmutation" "github.com/open-telemetry/opentelemetry-operator/pkg/sidecar" ) @@ -123,6 +123,7 @@ func TestShouldInjectSidecar(t *testing.T) { }, }, } { + tt := tt t.Run(tt.name, func(t *testing.T) { err := k8sClient.Create(context.Background(), &tt.ns) require.NoError(t, err) @@ -131,8 +132,8 @@ func TestShouldInjectSidecar(t *testing.T) { }() for i := range tt.otelcols { - err := k8sClient.Create(context.Background(), &tt.otelcols[i]) - require.NoError(t, err) + clientErr := k8sClient.Create(context.Background(), &tt.otelcols[i]) + require.NoError(t, clientErr) } encoded, err := json.Marshal(tt.pod) @@ -150,12 +151,8 @@ func TestShouldInjectSidecar(t *testing.T) { // the webhook handler cfg := config.New() - decoder, err := admission.NewDecoder(scheme.Scheme) - require.NoError(t, err) - - injector := NewWebhookHandler(cfg, logger, k8sClient, []PodMutator{sidecar.NewMutator(logger, cfg, k8sClient)}) - err = injector.InjectDecoder(decoder) - require.NoError(t, err) + decoder := admission.NewDecoder(scheme.Scheme) + injector := NewWebhookHandler(cfg, logger, decoder, k8sClient, []PodMutator{sidecar.NewMutator(logger, cfg, k8sClient)}) // test res := injector.Handle(context.Background(), req) @@ -163,11 +160,10 @@ func TestShouldInjectSidecar(t *testing.T) { // verify assert.True(t, res.Allowed) assert.Nil(t, res.AdmissionResponse.Result) - assert.Len(t, res.Patches, 3) + assert.Len(t, res.Patches, 2) expectedMap := map[string]bool{ "/metadata/labels": false, // add a new label - "/spec/volumes": false, // add a new volume with the configmap "/spec/containers": false, // replace the containers, adding one new container } for _, patch := range res.Patches { @@ -346,6 +342,7 @@ func TestPodShouldNotBeChanged(t *testing.T) { }}, }, } { + tt := tt t.Run(tt.name, func(t *testing.T) { err := k8sClient.Create(context.Background(), &tt.ns) require.NoError(t, err) @@ -354,8 +351,8 @@ func TestPodShouldNotBeChanged(t *testing.T) { }() for i := range tt.otelcols { - err := k8sClient.Create(context.Background(), &tt.otelcols[i]) - require.NoError(t, err) + clientErr := k8sClient.Create(context.Background(), &tt.otelcols[i]) + require.NoError(t, clientErr) } encoded, err := json.Marshal(tt.pod) @@ -373,11 +370,8 @@ func TestPodShouldNotBeChanged(t *testing.T) { // the webhook handler cfg := config.New() - decoder, err := admission.NewDecoder(scheme.Scheme) - require.NoError(t, err) - - injector := NewWebhookHandler(cfg, logger, k8sClient, []PodMutator{sidecar.NewMutator(logger, cfg, k8sClient)}) - err = injector.InjectDecoder(decoder) + decoder := admission.NewDecoder(scheme.Scheme) + injector := NewWebhookHandler(cfg, logger, decoder, k8sClient, []PodMutator{sidecar.NewMutator(logger, cfg, k8sClient)}) require.NoError(t, err) // test @@ -434,12 +428,8 @@ func TestFailOnInvalidRequest(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // prepare cfg := config.New() - decoder, err := admission.NewDecoder(scheme.Scheme) - require.NoError(t, err) - - injector := NewWebhookHandler(cfg, logger, k8sClient, []PodMutator{sidecar.NewMutator(logger, cfg, k8sClient)}) - err = injector.InjectDecoder(decoder) - require.NoError(t, err) + decoder := admission.NewDecoder(scheme.Scheme) + injector := NewWebhookHandler(cfg, logger, decoder, k8sClient, []PodMutator{sidecar.NewMutator(logger, cfg, k8sClient)}) // test res := injector.Handle(context.Background(), tt.req) diff --git a/kind-1.19.yaml b/kind-1.19.yaml deleted file mode 100644 index 8b85a6a66d..0000000000 --- a/kind-1.19.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -nodes: -- role: control-plane - image: kindest/node:v1.19.16@sha256:707469aac7e6805e52c3bde2a8a8050ce2b15decff60db6c5077ba9975d28b98 diff --git a/kind-1.20.yaml b/kind-1.20.yaml deleted file mode 100644 index 44e7f6ae9c..0000000000 --- a/kind-1.20.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -nodes: -- role: control-plane - image: kindest/node:v1.20.15@sha256:d67de8f84143adebe80a07672f370365ec7d23f93dc86866f0e29fa29ce026fe diff --git a/kind-1.21.yaml b/kind-1.21.yaml deleted file mode 100644 index 2894a87c46..0000000000 --- a/kind-1.21.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -nodes: -- role: control-plane - image: kindest/node:v1.21.14@sha256:f9b4d3d1112f24a7254d2ee296f177f628f9b4c1b32f0006567af11b91c1f301 diff --git a/kind-1.22.yaml b/kind-1.22.yaml deleted file mode 100644 index 27fe599db8..0000000000 --- a/kind-1.22.yaml +++ /dev/null @@ -1,5 +0,0 @@ -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -nodes: -- role: control-plane - image: kindest/node:v1.22.13@sha256:4904eda4d6e64b402169797805b8ec01f50133960ad6c19af45173a27eadf959 \ No newline at end of file diff --git a/kind-1.23.yaml b/kind-1.23.yaml index 5728285a18..09a2521d81 100644 --- a/kind-1.23.yaml +++ b/kind-1.23.yaml @@ -1,5 +1,18 @@ kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: -- role: control-plane - image: kindest/node:v1.23.10@sha256:f047448af6a656fae7bc909e2fab360c18c487ef3edc93f06d78cdfd864b2d12 + - role: control-plane + image: kindest/node:v1.23.17@sha256:59c989ff8a517a93127d4a536e7014d28e235fb3529d9fba91b3951d461edfdb + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP diff --git a/kind-1.24.yaml b/kind-1.24.yaml index 56553b608f..0d76591c1a 100644 --- a/kind-1.24.yaml +++ b/kind-1.24.yaml @@ -2,4 +2,17 @@ kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - image: kindest/node:v1.24.4@sha256:adfaebada924a26c2c9308edd53c6e33b3d4e453782c0063dc0028bdebaddf98 + image: kindest/node:v1.24.15@sha256:7db4f8bea3e14b82d12e044e25e34bd53754b7f2b0e9d56df21774e6f66a70ab + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP diff --git a/kind-1.25.yaml b/kind-1.25.yaml index 6340109a9f..340110f545 100644 --- a/kind-1.25.yaml +++ b/kind-1.25.yaml @@ -2,4 +2,17 @@ kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - image: kindest/node:v1.25.0@sha256:428aaa17ec82ccde0131cb2d1ca6547d13cf5fdabcc0bbecf749baa935387cbf + image: kindest/node:v1.25.11@sha256:227fa11ce74ea76a0474eeefb84cb75d8dad1b08638371ecf0e86259b35be0c8 + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP diff --git a/kind-1.26.yaml b/kind-1.26.yaml new file mode 100644 index 0000000000..5a7ae790d4 --- /dev/null +++ b/kind-1.26.yaml @@ -0,0 +1,18 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + image: kindest/node:v1.26.6@sha256:6e2d8b28a5b601defe327b98bd1c2d1930b49e5d8c512e1895099e4504007adb + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP diff --git a/kind-1.27.yaml b/kind-1.27.yaml new file mode 100644 index 0000000000..53b8f092b3 --- /dev/null +++ b/kind-1.27.yaml @@ -0,0 +1,18 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + image: kindest/node:v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72 + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP diff --git a/kind-1.28.yaml b/kind-1.28.yaml new file mode 100644 index 0000000000..711640f952 --- /dev/null +++ b/kind-1.28.yaml @@ -0,0 +1,18 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 +nodes: + - role: control-plane + image: kindest/node:v1.28.0@sha256:b7a4cad12c197af3ba43202d3efe03246b3f0793f162afb40a33c923952d5b31 + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + extraPortMappings: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP diff --git a/kuttl-test-autoscale.yaml b/kuttl-test-autoscale.yaml new file mode 100644 index 0000000000..93e94af6ec --- /dev/null +++ b/kuttl-test-autoscale.yaml @@ -0,0 +1,6 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestSuite +artifactsDir: ./tests/_build/artifacts/ +testDirs: + - ./tests/e2e-autoscale/ +timeout: 150 diff --git a/kuttl-test-instrumentation.yaml b/kuttl-test-instrumentation.yaml new file mode 100644 index 0000000000..545357c420 --- /dev/null +++ b/kuttl-test-instrumentation.yaml @@ -0,0 +1,5 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestSuite +testDirs: + - ./tests/e2e-instrumentation/ +timeout: 300 diff --git a/kuttl-test-multi-instr.yaml b/kuttl-test-multi-instr.yaml new file mode 100644 index 0000000000..5381f3bd92 --- /dev/null +++ b/kuttl-test-multi-instr.yaml @@ -0,0 +1,9 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestSuite +artifactsDir: ./tests/_build/artifacts/ +commands: + - command: kubectl apply -f ./tests/e2e-multi-instrumentation/manager_deployment_feature_gate.yaml + - command: go run hack/check-operator-ready.go +testDirs: + - ./tests/e2e-multi-instrumentation/ +timeout: 150 diff --git a/kuttl-test-opampbridge.yaml b/kuttl-test-opampbridge.yaml new file mode 100644 index 0000000000..f658ce15ac --- /dev/null +++ b/kuttl-test-opampbridge.yaml @@ -0,0 +1,6 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestSuite +artifactsDir: ./tests/_build/artifacts/ +testDirs: + - ./tests/e2e-opampbridge/ +timeout: 300 diff --git a/kuttl-test-openshift.yaml b/kuttl-test-openshift.yaml new file mode 100644 index 0000000000..4185980835 --- /dev/null +++ b/kuttl-test-openshift.yaml @@ -0,0 +1,6 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestSuite +startKIND: false +testDirs: + - ./tests/e2e-openshift/ +timeout: 150 diff --git a/kuttl-test-pdb.yaml b/kuttl-test-pdb.yaml new file mode 100644 index 0000000000..62fc2d8042 --- /dev/null +++ b/kuttl-test-pdb.yaml @@ -0,0 +1,6 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestSuite +artifactsDir: ./tests/_build/artifacts/ +testDirs: + - ./tests/e2e-pdb/ +timeout: 30 diff --git a/kuttl-test-prometheuscr.yaml b/kuttl-test-prometheuscr.yaml new file mode 100644 index 0000000000..266e5ccd89 --- /dev/null +++ b/kuttl-test-prometheuscr.yaml @@ -0,0 +1,20 @@ +# Make sure that the OT operator after upgrading itself, can upgrade the OT collectors without error. +# The test is based on the version v0.49.0, a breaking change was introduced from PR +# https://github.com/open-telemetry/opentelemetry-operator/pull/797, which added a version label "app.kubernetes.io/version", +# The version label would change between OT operator upgrade, and since at the time, the collector pod selector was the same +# as this labels, resulted in selector being modified during reconciliation which caused error due to the selector is immutable. +# Please be aware of that the collector labels are changeable in various ways, so this issue may happen in any operator < v0.52.0 +# which changed the selector to be a static set of labels. +# The fix for this issue including: +# https://github.com/open-telemetry/opentelemetry-operator/issues/840, make the selector be a static set of labels; +# https://github.com/open-telemetry/opentelemetry-operator/issues/1117, delete the old collector to let the operator +# create a new one when the selector changed. +apiVersion: kuttl.dev/v1beta1 +kind: TestSuite +commands: + - command: make undeploy + - command: make enable-prometheus-feature-flag deploy install-prometheus-operator + - command: go run hack/check-operator-ready.go +testDirs: + - ./tests/e2e-prometheuscr/ +timeout: 300 diff --git a/kuttl-test-upgrade.yaml b/kuttl-test-upgrade.yaml index 3af212f433..0c4f7e7d8b 100644 --- a/kuttl-test-upgrade.yaml +++ b/kuttl-test-upgrade.yaml @@ -11,16 +11,9 @@ # create a new one when the selector changed. apiVersion: kuttl.dev/v1beta1 kind: TestSuite -crdDir: ./tests/_build/crds/ -artifactsDir: ./tests/_build/artifacts/ -kindContainers: - - local/opentelemetry-operator:e2e - - ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:v0.49.0 commands: - - command: make cert-manager - - command: kubectl apply -f ./tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.49.0.yaml - - command: kubectl rollout status -w deployment/opentelemetry-operator-controller-manager -n opentelemetry-operator-system - - command: sleep 60s + - command: kubectl apply -f ./tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.86.0.yaml + - command: go run hack/check-operator-ready.go testDirs: - ./tests/e2e-upgrade/ timeout: 300 diff --git a/kuttl-test.yaml b/kuttl-test.yaml index de2c6041f4..401a478213 100644 --- a/kuttl-test.yaml +++ b/kuttl-test.yaml @@ -1,14 +1,7 @@ apiVersion: kuttl.dev/v1beta1 kind: TestSuite -crdDir: ./tests/_build/crds/ artifactsDir: ./tests/_build/artifacts/ -kindContainers: - - local/opentelemetry-operator:e2e -commands: - - command: make cert-manager - - command: kubectl apply -f ./tests/_build/manifests/01-opentelemetry-operator.yaml - - command: kubectl wait --timeout=5m --for=condition=available deployment opentelemetry-operator-controller-manager -n opentelemetry-operator-system - - command: sleep 5s testDirs: - ./tests/e2e/ -timeout: 150 \ No newline at end of file +timeout: 150 +parallel: 4 diff --git a/main.go b/main.go index 70d24c869e..11dd91cb13 100644 --- a/main.go +++ b/main.go @@ -16,6 +16,7 @@ package main import ( "context" + "crypto/tls" "flag" "fmt" "os" @@ -23,27 +24,33 @@ import ( "strings" "time" + routev1 "github.com/openshift/api/route/v1" + monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "github.com/spf13/pflag" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + colfeaturegate "go.opentelemetry.io/collector/featuregate" k8sruntime "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "k8s.io/client-go/tools/record" + k8sapiflag "k8s.io/component-base/cli/flag" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/manager" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" otelv1alpha1 "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/controllers" + "github.com/open-telemetry/opentelemetry-operator/internal/autodetect" "github.com/open-telemetry/opentelemetry-operator/internal/config" "github.com/open-telemetry/opentelemetry-operator/internal/version" - "github.com/open-telemetry/opentelemetry-operator/internal/webhookhandler" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" + "github.com/open-telemetry/opentelemetry-operator/internal/webhook/podmutation" collectorupgrade "github.com/open-telemetry/opentelemetry-operator/pkg/collector/upgrade" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" "github.com/open-telemetry/opentelemetry-operator/pkg/instrumentation" instrumentationupgrade "github.com/open-telemetry/opentelemetry-operator/pkg/instrumentation/upgrade" "github.com/open-telemetry/opentelemetry-operator/pkg/sidecar" @@ -55,49 +62,80 @@ var ( setupLog = ctrl.Log.WithName("setup") ) +type tlsConfig struct { + minVersion string + cipherSuites []string +} + func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(otelv1alpha1.AddToScheme(scheme)) + utilruntime.Must(routev1.AddToScheme(scheme)) + utilruntime.Must(monitoringv1.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } +// stringFlagOrEnv defines a string flag which can be set by an environment variable. +// Precedence: flag > env var > default value. +func stringFlagOrEnv(p *string, name string, envName string, defaultValue string, usage string) { + envValue := os.Getenv(envName) + if envValue != "" { + defaultValue = envValue + } + pflag.StringVar(p, name, defaultValue, usage) +} + func main() { // registers any flags that underlying libraries might use opts := zap.Options{} + flagset := featuregate.Flags(colfeaturegate.GlobalRegistry()) opts.BindFlags(flag.CommandLine) pflag.CommandLine.AddGoFlagSet(flag.CommandLine) + pflag.CommandLine.AddGoFlagSet(flagset) v := version.Get() // add flags related to this operator var ( - metricsAddr string - probeAddr string - enableLeaderElection bool - collectorImage string - targetAllocatorImage string - autoInstrumentationJava string - autoInstrumentationNodeJS string - autoInstrumentationPython string - autoInstrumentationDotNet string - labelsFilter []string - webhookPort int + metricsAddr string + probeAddr string + pprofAddr string + enableLeaderElection bool + collectorImage string + targetAllocatorImage string + operatorOpAMPBridgeImage string + autoInstrumentationJava string + autoInstrumentationNodeJS string + autoInstrumentationPython string + autoInstrumentationDotNet string + autoInstrumentationApacheHttpd string + autoInstrumentationNginx string + autoInstrumentationGo string + labelsFilter []string + webhookPort int + tlsOpt tlsConfig ) pflag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.") pflag.StringVar(&probeAddr, "health-probe-addr", ":8081", "The address the probe endpoint binds to.") + pflag.StringVar(&pprofAddr, "pprof-addr", "", "The address to expose the pprof server. Default is empty string which disables the pprof server.") pflag.BoolVar(&enableLeaderElection, "enable-leader-election", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") - pflag.StringVar(&collectorImage, "collector-image", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:%s", v.OpenTelemetryCollector), "The default OpenTelemetry collector image. This image is used when no image is specified in the CustomResource.") - pflag.StringVar(&targetAllocatorImage, "target-allocator-image", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/target-allocator:%s", v.TargetAllocator), "The default OpenTelemetry target allocator image. This image is used when no image is specified in the CustomResource.") - pflag.StringVar(&autoInstrumentationJava, "auto-instrumentation-java-image", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:%s", v.AutoInstrumentationJava), "The default OpenTelemetry Java instrumentation image. This image is used when no image is specified in the CustomResource.") - pflag.StringVar(&autoInstrumentationNodeJS, "auto-instrumentation-nodejs-image", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:%s", v.AutoInstrumentationNodeJS), "The default OpenTelemetry NodeJS instrumentation image. This image is used when no image is specified in the CustomResource.") - pflag.StringVar(&autoInstrumentationPython, "auto-instrumentation-python-image", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:%s", v.AutoInstrumentationPython), "The default OpenTelemetry Python instrumentation image. This image is used when no image is specified in the CustomResource.") - pflag.StringVar(&autoInstrumentationDotNet, "auto-instrumentation-dotnet-image", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-dotnet:%s", v.AutoInstrumentationDotNet), "The default OpenTelemetry DotNet instrumentation image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&collectorImage, "collector-image", "RELATED_IMAGE_COLLECTOR", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:%s", v.OpenTelemetryCollector), "The default OpenTelemetry collector image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&targetAllocatorImage, "target-allocator-image", "RELATED_IMAGE_TARGET_ALLOCATOR", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/target-allocator:%s", v.TargetAllocator), "The default OpenTelemetry target allocator image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&operatorOpAMPBridgeImage, "operator-opamp-bridge-image", "RELATED_IMAGE_OPERATOR_OPAMP_BRIDGE", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/operator-opamp-bridge:%s", v.OperatorOpAMPBridge), "The default OpenTelemetry Operator OpAMP Bridge image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&autoInstrumentationJava, "auto-instrumentation-java-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_JAVA", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:%s", v.AutoInstrumentationJava), "The default OpenTelemetry Java instrumentation image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&autoInstrumentationNodeJS, "auto-instrumentation-nodejs-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_NODEJS", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:%s", v.AutoInstrumentationNodeJS), "The default OpenTelemetry NodeJS instrumentation image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&autoInstrumentationPython, "auto-instrumentation-python-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_PYTHON", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:%s", v.AutoInstrumentationPython), "The default OpenTelemetry Python instrumentation image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&autoInstrumentationDotNet, "auto-instrumentation-dotnet-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_DOTNET", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-dotnet:%s", v.AutoInstrumentationDotNet), "The default OpenTelemetry DotNet instrumentation image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&autoInstrumentationGo, "auto-instrumentation-go-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_GO", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-go-instrumentation/autoinstrumentation-go:%s", v.AutoInstrumentationGo), "The default OpenTelemetry Go instrumentation image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&autoInstrumentationApacheHttpd, "auto-instrumentation-apache-httpd-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_APACHE_HTTPD", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-apache-httpd:%s", v.AutoInstrumentationApacheHttpd), "The default OpenTelemetry Apache HTTPD instrumentation image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&autoInstrumentationNginx, "auto-instrumentation-nginx-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_NGINX", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-apache-httpd:%s", v.AutoInstrumentationNginx), "The default OpenTelemetry Nginx instrumentation image. This image is used when no image is specified in the CustomResource.") pflag.StringArrayVar(&labelsFilter, "labels", []string{}, "Labels to filter away from propagating onto deploys") pflag.IntVar(&webhookPort, "webhook-port", 9443, "The port the webhook endpoint binds to.") + pflag.StringVar(&tlsOpt.minVersion, "tls-min-version", "VersionTLS12", "Minimum TLS version supported. Value must match version names from https://golang.org/pkg/crypto/tls/#pkg-constants.") + pflag.StringSliceVar(&tlsOpt.cipherSuites, "tls-cipher-suites", nil, "Comma-separated list of cipher suites for the server. Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). If omitted, the default Go cipher suites will be used") pflag.Parse() logger := zap.New(zap.UseFlagOptions(&opts)) @@ -107,10 +145,15 @@ func main() { "opentelemetry-operator", v.Operator, "opentelemetry-collector", collectorImage, "opentelemetry-targetallocator", targetAllocatorImage, + "operator-opamp-bridge", operatorOpAMPBridgeImage, "auto-instrumentation-java", autoInstrumentationJava, "auto-instrumentation-nodejs", autoInstrumentationNodeJS, "auto-instrumentation-python", autoInstrumentationPython, "auto-instrumentation-dotnet", autoInstrumentationDotNet, + "auto-instrumentation-go", autoInstrumentationGo, + "auto-instrumentation-apache-httpd", autoInstrumentationApacheHttpd, + "auto-instrumentation-nginx", autoInstrumentationNginx, + "feature-gates", flagset.Lookup(featuregate.FeatureGatesFlag).Value.String(), "build-date", v.BuildDate, "go-version", v.Go, "go-arch", runtime.GOARCH, @@ -132,13 +175,21 @@ func main() { config.WithVersion(v), config.WithCollectorImage(collectorImage), config.WithTargetAllocatorImage(targetAllocatorImage), + config.WithOperatorOpAMPBridgeImage(operatorOpAMPBridgeImage), config.WithAutoInstrumentationJavaImage(autoInstrumentationJava), config.WithAutoInstrumentationNodeJSImage(autoInstrumentationNodeJS), config.WithAutoInstrumentationPythonImage(autoInstrumentationPython), config.WithAutoInstrumentationDotNetImage(autoInstrumentationDotNet), + config.WithAutoInstrumentationGoImage(autoInstrumentationGo), + config.WithAutoInstrumentationApacheHttpdImage(autoInstrumentationApacheHttpd), + config.WithAutoInstrumentationNginxImage(autoInstrumentationNginx), config.WithAutoDetect(ad), config.WithLabelFilters(labelsFilter), ) + err = cfg.AutoDetect() + if err != nil { + setupLog.Error(err, "failed to autodetect config variables") + } watchNamespace, found := os.LookupEnv("WATCH_NAMESPACE") if found { @@ -151,22 +202,37 @@ func main() { leaseDuration := time.Second * 137 renewDeadline := time.Second * 107 retryPeriod := time.Second * 26 + + optionsTlSOptsFuncs := []func(*tls.Config){ + func(config *tls.Config) { tlsConfigSetting(config, tlsOpt) }, + } + var namespaces map[string]cache.Config + if strings.Contains(watchNamespace, ",") { + namespaces = map[string]cache.Config{} + for _, ns := range strings.Split(watchNamespace, ",") { + namespaces[ns] = cache.Config{} + } + } + mgrOptions := ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: webhookPort, + Scheme: scheme, + Metrics: metricsserver.Options{ + BindAddress: metricsAddr, + }, HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "9f7554c3.opentelemetry.io", - Namespace: watchNamespace, LeaseDuration: &leaseDuration, RenewDeadline: &renewDeadline, RetryPeriod: &retryPeriod, - } - - if strings.Contains(watchNamespace, ",") { - mgrOptions.Namespace = "" - mgrOptions.NewCache = cache.MultiNamespacedCacheBuilder(strings.Split(watchNamespace, ",")) + PprofBindAddress: pprofAddr, + WebhookServer: webhook.NewServer(webhook.Options{ + Port: webhookPort, + TLSOpts: optionsTlSOptsFuncs, + }), + Cache: cache.Options{ + DefaultNamespaces: namespaces, + }, } mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), mgrOptions) @@ -193,32 +259,41 @@ func main() { os.Exit(1) } + if err = controllers.NewOpAMPBridgeReconciler(controllers.OpAMPBridgeReconcilerParams{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("OpAMPBridge"), + Scheme: mgr.GetScheme(), + Config: cfg, + Recorder: mgr.GetEventRecorderFor("opamp-bridge"), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "OpAMPBridge") + os.Exit(1) + } + if os.Getenv("ENABLE_WEBHOOKS") != "false" { - if err = (&otelv1alpha1.OpenTelemetryCollector{}).SetupWebhookWithManager(mgr); err != nil { + if err = otelv1alpha1.SetupCollectorWebhook(mgr, cfg); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "OpenTelemetryCollector") os.Exit(1) } - if err = (&otelv1alpha1.Instrumentation{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - otelv1alpha1.AnnotationDefaultAutoInstrumentationJava: autoInstrumentationJava, - otelv1alpha1.AnnotationDefaultAutoInstrumentationNodeJS: autoInstrumentationNodeJS, - otelv1alpha1.AnnotationDefaultAutoInstrumentationPython: autoInstrumentationPython, - otelv1alpha1.AnnotationDefaultAutoInstrumentationDotNet: autoInstrumentationDotNet, - }, - }, - }).SetupWebhookWithManager(mgr); err != nil { + if err = otelv1alpha1.SetupInstrumentationWebhook(mgr, cfg); err != nil { setupLog.Error(err, "unable to create webhook", "webhook", "Instrumentation") os.Exit(1) } - + decoder := admission.NewDecoder(mgr.GetScheme()) mgr.GetWebhookServer().Register("/mutate-v1-pod", &webhook.Admission{ - Handler: webhookhandler.NewWebhookHandler(cfg, ctrl.Log.WithName("pod-webhook"), mgr.GetClient(), - []webhookhandler.PodMutator{ + Handler: podmutation.NewWebhookHandler(cfg, ctrl.Log.WithName("pod-webhook"), decoder, mgr.GetClient(), + []podmutation.PodMutator{ sidecar.NewMutator(logger, cfg, mgr.GetClient()), - instrumentation.NewMutator(logger, mgr.GetClient()), + instrumentation.NewMutator(logger, mgr.GetClient(), mgr.GetEventRecorderFor("opentelemetry-operator")), }), }) + + if err = otelv1alpha1.SetupOpAMPBridgeWebhook(mgr, cfg); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "OpAMPBridge") + os.Exit(1) + } + } else { + ctrl.Log.Info("Webhooks are disabled, operator is running an unsupported mode", "ENABLE_WEBHOOKS", "false") } // +kubebuilder:scaffold:builder @@ -239,15 +314,8 @@ func main() { } func addDependencies(_ context.Context, mgr ctrl.Manager, cfg config.Config, v version.Version) error { - // run the auto-detect mechanism for the configuration - err := mgr.Add(manager.RunnableFunc(func(_ context.Context) error { - return cfg.StartAutoDetect() - })) - if err != nil { - return fmt.Errorf("failed to start the auto-detect mechanism: %w", err) - } // adds the upgrade mechanism to be executed once the manager is ready - err = mgr.Add(manager.RunnableFunc(func(c context.Context) error { + err := mgr.Add(manager.RunnableFunc(func(c context.Context) error { up := &collectorupgrade.VersionUpgrade{ Log: ctrl.Log.WithName("collector-upgrade"), Version: v, @@ -263,12 +331,16 @@ func addDependencies(_ context.Context, mgr ctrl.Manager, cfg config.Config, v v // adds the upgrade mechanism to be executed once the manager is ready err = mgr.Add(manager.RunnableFunc(func(c context.Context) error { u := &instrumentationupgrade.InstrumentationUpgrade{ - Logger: ctrl.Log.WithName("instrumentation-upgrade"), - DefaultAutoInstJava: cfg.AutoInstrumentationJavaImage(), - DefaultAutoInstNodeJS: cfg.AutoInstrumentationNodeJSImage(), - DefaultAutoInstPython: cfg.AutoInstrumentationPythonImage(), - DefaultAutoInstDotNet: cfg.AutoInstrumentationDotNetImage(), - Client: mgr.GetClient(), + Logger: ctrl.Log.WithName("instrumentation-upgrade"), + DefaultAutoInstJava: cfg.AutoInstrumentationJavaImage(), + DefaultAutoInstNodeJS: cfg.AutoInstrumentationNodeJSImage(), + DefaultAutoInstPython: cfg.AutoInstrumentationPythonImage(), + DefaultAutoInstDotNet: cfg.AutoInstrumentationDotNetImage(), + DefaultAutoInstGo: cfg.AutoInstrumentationDotNetImage(), + DefaultAutoInstApacheHttpd: cfg.AutoInstrumentationApacheHttpdImage(), + DefaultAutoInstNginx: cfg.AutoInstrumentationNginxImage(), + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("opentelemetry-operator"), } return u.ManagedInstances(c) })) @@ -277,3 +349,22 @@ func addDependencies(_ context.Context, mgr ctrl.Manager, cfg config.Config, v v } return nil } + +// This function get the option from command argument (tlsConfig), check the validity through k8sapiflag +// and set the config for webhook server. +// refer to https://pkg.go.dev/k8s.io/component-base/cli/flag +func tlsConfigSetting(cfg *tls.Config, tlsOpt tlsConfig) { + // TLSVersion helper function returns the TLS Version ID for the version name passed. + tlsVersion, err := k8sapiflag.TLSVersion(tlsOpt.minVersion) + if err != nil { + setupLog.Error(err, "TLS version invalid") + } + cfg.MinVersion = tlsVersion + + // TLSCipherSuites helper function returns a list of cipher suite IDs from the cipher suite names passed. + cipherSuiteIDs, err := k8sapiflag.TLSCipherSuites(tlsOpt.cipherSuites) + if err != nil { + setupLog.Error(err, "Failed to convert TLS cipher suite name to ID") + } + cfg.CipherSuites = cipherSuiteIDs +} diff --git a/pkg/autodetect/main.go b/pkg/autodetect/main.go deleted file mode 100644 index dc4c237363..0000000000 --- a/pkg/autodetect/main.go +++ /dev/null @@ -1,128 +0,0 @@ -// 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 autodetect is for auto-detecting traits from the environment (platform, APIs, ...). -package autodetect - -import ( - "errors" - "sort" - - "k8s.io/client-go/discovery" - "k8s.io/client-go/rest" - - "github.com/open-telemetry/opentelemetry-operator/pkg/platform" -) - -var _ AutoDetect = (*autoDetect)(nil) - -// AutoDetect provides an assortment of routines that auto-detect traits based on the runtime. -type AutoDetect interface { - Platform() (platform.Platform, error) - HPAVersion() (AutoscalingVersion, error) -} - -type autoDetect struct { - dcl discovery.DiscoveryInterface -} - -type AutoscalingVersion int - -const ( - AutoscalingVersionV2 AutoscalingVersion = iota - AutoscalingVersionV2Beta2 - AutoscalingVersionUnknown -) - -const DefaultAutoscalingVersion = AutoscalingVersionV2 - -// New creates a new auto-detection worker, using the given client when talking to the current cluster. -func New(restConfig *rest.Config) (AutoDetect, error) { - dcl, err := discovery.NewDiscoveryClientForConfig(restConfig) - if err != nil { - // it's pretty much impossible to get into this problem, as most of the - // code branches from the previous call just won't fail at all, - // but let's handle this error anyway... - return nil, err - } - - return &autoDetect{ - dcl: dcl, - }, nil -} - -// Platform returns the detected platform this operator is running on. Possible values: Kubernetes, OpenShift. -func (a *autoDetect) Platform() (platform.Platform, error) { - apiList, err := a.dcl.ServerGroups() - if err != nil { - return platform.Unknown, err - } - - apiGroups := apiList.Groups - for i := 0; i < len(apiGroups); i++ { - if apiGroups[i].Name == "route.openshift.io" { - return platform.OpenShift, nil - } - } - - return platform.Kubernetes, nil -} - -func (a *autoDetect) HPAVersion() (AutoscalingVersion, error) { - apiList, err := a.dcl.ServerGroups() - if err != nil { - return AutoscalingVersionUnknown, err - } - - for _, apiGroup := range apiList.Groups { - if apiGroup.Name == "autoscaling" { - // Sort this so we can make sure to get v2 before v2beta2 - versions := apiGroup.Versions - sort.Slice(versions, func(i, j int) bool { - return versions[i].Version < versions[j].Version - }) - - for _, version := range versions { - if version.Version == "v2" || version.Version == "v2beta2" { - return ToAutoScalingVersion(version.Version), nil - } - } - return AutoscalingVersionUnknown, errors.New("Failed to find appropriate version of apiGroup autoscaling, only v2 and v2beta2 are supported") - } - } - - return AutoscalingVersionUnknown, errors.New("Failed to find apiGroup autoscaling") -} - -func (v AutoscalingVersion) String() string { - switch v { - case AutoscalingVersionV2: - return "v2" - case AutoscalingVersionV2Beta2: - return "v2beta2" - case AutoscalingVersionUnknown: - return "unknown" - } - return "unknown" -} - -func ToAutoScalingVersion(version string) AutoscalingVersion { - switch version { - case "v2": - return AutoscalingVersionV2 - case "v2beta2": - return AutoscalingVersionV2Beta2 - } - return AutoscalingVersionUnknown -} diff --git a/pkg/collector/adapters/config_to_ports.go b/pkg/collector/adapters/config_to_ports.go deleted file mode 100644 index c897bec832..0000000000 --- a/pkg/collector/adapters/config_to_ports.go +++ /dev/null @@ -1,99 +0,0 @@ -// 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 adapters - -import ( - "errors" - "sort" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/parser" -) - -var ( - // ErrNoReceivers indicates that there are no receivers in the configuration. - ErrNoReceivers = errors.New("no receivers available as part of the configuration") - - // ErrReceiversNotAMap indicates that the receivers property isn't a map of values. - ErrReceiversNotAMap = errors.New("receivers property in the configuration doesn't contain valid receivers") -) - -// ConfigToReceiverPorts converts the incoming configuration object into a set of service ports required by the receivers. -func ConfigToReceiverPorts(logger logr.Logger, config map[interface{}]interface{}) ([]corev1.ServicePort, error) { - // now, we gather which ports we might need to open - // for that, we get all the receivers and check their `endpoint` properties, - // extracting the port from it. The port name has to be a "DNS_LABEL", so, we try to make it follow the pattern: - // ${instance.Name}-${receiver.name}-${receiver.qualifier} - // the receiver-name is typically the node name from the receivers map - // the receiver-qualifier is what comes after the slash in the receiver name, but typically nil - // examples: - // ```yaml - // receivers: - // examplereceiver: - // endpoint: 0.0.0.0:12345 - // examplereceiver/settings: - // endpoint: 0.0.0.0:12346 - // in this case, we have two ports, named: "examplereceiver" and "examplereceiver-settings" - receiversProperty, ok := config["receivers"] - if !ok { - return nil, ErrNoReceivers - } - recEnabled := GetEnabledReceivers(logger, config) - if recEnabled == nil { - return nil, ErrReceiversNotAMap - } - receivers, ok := receiversProperty.(map[interface{}]interface{}) - if !ok { - return nil, ErrReceiversNotAMap - } - - ports := []corev1.ServicePort{} - for key, val := range receivers { - // This check will pass only the enabled receivers, - // then only the related ports will be opened. - if !recEnabled[key] { - continue - } - receiver, ok := val.(map[interface{}]interface{}) - if !ok { - logger.Info("receiver doesn't seem to be a map of properties", "receiver", key) - receiver = map[interface{}]interface{}{} - } - - rcvrName := key.(string) - rcvrParser := parser.For(logger, rcvrName, receiver) - - rcvrPorts, err := rcvrParser.Ports() - if err != nil { - // should we break the process and return an error, or just ignore this faulty parser - // and let the other parsers add their ports to the service? right now, the best - // option seems to be to log the failures and move on, instead of failing them all - logger.Error(err, "parser for '%s' has returned an error: %w", rcvrName, err) - continue - } - - if len(rcvrPorts) > 0 { - ports = append(ports, rcvrPorts...) - } - } - - sort.Slice(ports, func(i, j int) bool { - return ports[i].Name < ports[j].Name - }) - - return ports, nil -} diff --git a/pkg/collector/daemonset.go b/pkg/collector/daemonset.go deleted file mode 100644 index b8245354c3..0000000000 --- a/pkg/collector/daemonset.go +++ /dev/null @@ -1,65 +0,0 @@ -// 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 collector - -import ( - "github.com/go-logr/logr" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" -) - -// DaemonSet builds the deployment for the given instance. -func DaemonSet(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) appsv1.DaemonSet { - labels := Labels(otelcol, cfg.LabelsFilter()) - labels["app.kubernetes.io/name"] = naming.Collector(otelcol) - - annotations := Annotations(otelcol) - podAnnotations := PodAnnotations(otelcol) - return appsv1.DaemonSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: naming.Collector(otelcol), - Namespace: otelcol.Namespace, - Labels: labels, - Annotations: annotations, - }, - Spec: appsv1.DaemonSetSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: SelectorLabels(otelcol), - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - Annotations: podAnnotations, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: ServiceAccountName(otelcol), - Containers: []corev1.Container{Container(cfg, logger, otelcol)}, - Volumes: Volumes(cfg, otelcol), - Tolerations: otelcol.Spec.Tolerations, - NodeSelector: otelcol.Spec.NodeSelector, - HostNetwork: otelcol.Spec.HostNetwork, - DNSPolicy: getDnsPolicy(otelcol), - SecurityContext: otelcol.Spec.PodSecurityContext, - PriorityClassName: otelcol.Spec.PriorityClassName, - }, - }, - }, - } -} diff --git a/pkg/collector/daemonset_test.go b/pkg/collector/daemonset_test.go deleted file mode 100644 index 7be991de4e..0000000000 --- a/pkg/collector/daemonset_test.go +++ /dev/null @@ -1,238 +0,0 @@ -// 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 collector_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" -) - -func TestDaemonSetNewDefault(t *testing.T) { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-namespace", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Tolerations: testTolerationValues, - }, - } - cfg := config.New() - - // test - d := DaemonSet(cfg, logger, otelcol) - - // verify - assert.Equal(t, "my-instance-collector", d.Name) - assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) - assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) - assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) - assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) - assert.Equal(t, testTolerationValues, d.Spec.Template.Spec.Tolerations) - - assert.Len(t, d.Spec.Template.Spec.Containers, 1) - - // verify sha256 podAnnotation - expectedAnnotations := map[string]string{ - "opentelemetry-operator-config/sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - } - assert.Equal(t, expectedAnnotations, d.Spec.Template.Annotations) - - expectedLabels := map[string]string{ - "app.kubernetes.io/component": "opentelemetry-collector", - "app.kubernetes.io/instance": "my-namespace.my-instance", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/name": "my-instance-collector", - "app.kubernetes.io/part-of": "opentelemetry", - "app.kubernetes.io/version": "latest", - } - assert.Equal(t, expectedLabels, d.Spec.Template.Labels) - - expectedSelectorLabels := map[string]string{ - "app.kubernetes.io/component": "opentelemetry-collector", - "app.kubernetes.io/instance": "my-namespace.my-instance", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/part-of": "opentelemetry", - } - assert.Equal(t, expectedSelectorLabels, d.Spec.Selector.MatchLabels) - - // the pod selector must be contained within pod spec's labels - for k, v := range d.Spec.Selector.MatchLabels { - assert.Equal(t, v, d.Spec.Template.Labels[k]) - } -} - -func TestDaemonsetHostNetwork(t *testing.T) { - // test - d1 := DaemonSet(config.New(), logger, v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{}, - }) - assert.False(t, d1.Spec.Template.Spec.HostNetwork) - assert.Equal(t, d1.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirst) - - // verify custom - d2 := DaemonSet(config.New(), logger, v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - HostNetwork: true, - }, - }) - assert.True(t, d2.Spec.Template.Spec.HostNetwork) - assert.Equal(t, d2.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirstWithHostNet) -} - -func TestDaemonsetPodAnnotations(t *testing.T) { - // prepare - testPodAnnotationValues := map[string]string{"annotation-key": "annotation-value"} - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - PodAnnotations: testPodAnnotationValues, - }, - } - cfg := config.New() - - // test - ds := DaemonSet(cfg, logger, otelcol) - - // Add sha256 podAnnotation - testPodAnnotationValues["opentelemetry-operator-config/sha256"] = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - - // verify - assert.Equal(t, "my-instance-collector", ds.Name) - assert.Equal(t, testPodAnnotationValues, ds.Spec.Template.Annotations) -} - -func TestDaemonstPodSecurityContext(t *testing.T) { - runAsNonRoot := true - runAsUser := int64(1337) - runasGroup := int64(1338) - - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - PodSecurityContext: &v1.PodSecurityContext{ - RunAsNonRoot: &runAsNonRoot, - RunAsUser: &runAsUser, - RunAsGroup: &runasGroup, - }, - }, - } - - cfg := config.New() - - d := DaemonSet(cfg, logger, otelcol) - - assert.Equal(t, &runAsNonRoot, d.Spec.Template.Spec.SecurityContext.RunAsNonRoot) - assert.Equal(t, &runAsUser, d.Spec.Template.Spec.SecurityContext.RunAsUser) - assert.Equal(t, &runasGroup, d.Spec.Template.Spec.SecurityContext.RunAsGroup) -} - -func TestDaemonsetFilterLabels(t *testing.T) { - excludedLabels := map[string]string{ - "foo": "1", - "app.foo.bar": "1", - } - - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Labels: excludedLabels, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{}, - } - - cfg := config.New(config.WithLabelFilters([]string{"foo*", "app.*.bar"})) - - d := DaemonSet(cfg, logger, otelcol) - - assert.Len(t, d.ObjectMeta.Labels, 6) - for k := range excludedLabels { - assert.NotContains(t, d.ObjectMeta.Labels, k) - } -} - -func TestDaemonSetNodeSelector(t *testing.T) { - // Test default - otelcol_1 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - - cfg := config.New() - - d1 := DaemonSet(cfg, logger, otelcol_1) - - assert.Empty(t, d1.Spec.Template.Spec.NodeSelector) - - // Test nodeSelector - otelcol_2 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance-nodeselector", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - HostNetwork: true, - NodeSelector: map[string]string{ - "node-key": "node-value", - }, - }, - } - - cfg = config.New() - - d2 := DaemonSet(cfg, logger, otelcol_2) - assert.Equal(t, d2.Spec.Template.Spec.NodeSelector, map[string]string{"node-key": "node-value"}) -} - -func TestDaemonSetPriorityClassName(t *testing.T) { - otelcol_1 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - - cfg := config.New() - - d1 := DaemonSet(cfg, logger, otelcol_1) - assert.Empty(t, d1.Spec.Template.Spec.PriorityClassName) - - priorityClassName := "test-class" - - otelcol_2 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance-priortyClassName", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - PriorityClassName: priorityClassName, - }, - } - - cfg = config.New() - - d2 := DaemonSet(cfg, logger, otelcol_2) - assert.Equal(t, priorityClassName, d2.Spec.Template.Spec.PriorityClassName) -} diff --git a/pkg/collector/deployment.go b/pkg/collector/deployment.go deleted file mode 100644 index f6ce3fd187..0000000000 --- a/pkg/collector/deployment.go +++ /dev/null @@ -1,67 +0,0 @@ -// 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 collector - -import ( - "github.com/go-logr/logr" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" -) - -// Deployment builds the deployment for the given instance. -func Deployment(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) appsv1.Deployment { - labels := Labels(otelcol, cfg.LabelsFilter()) - labels["app.kubernetes.io/name"] = naming.Collector(otelcol) - - annotations := Annotations(otelcol) - podAnnotations := PodAnnotations(otelcol) - - return appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: naming.Collector(otelcol), - Namespace: otelcol.Namespace, - Labels: labels, - Annotations: annotations, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: otelcol.Spec.Replicas, - Selector: &metav1.LabelSelector{ - MatchLabels: SelectorLabels(otelcol), - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - Annotations: podAnnotations, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: ServiceAccountName(otelcol), - Containers: []corev1.Container{Container(cfg, logger, otelcol)}, - Volumes: Volumes(cfg, otelcol), - DNSPolicy: getDnsPolicy(otelcol), - HostNetwork: otelcol.Spec.HostNetwork, - Tolerations: otelcol.Spec.Tolerations, - NodeSelector: otelcol.Spec.NodeSelector, - SecurityContext: otelcol.Spec.PodSecurityContext, - PriorityClassName: otelcol.Spec.PriorityClassName, - }, - }, - }, - } -} diff --git a/pkg/collector/deployment_test.go b/pkg/collector/deployment_test.go deleted file mode 100644 index 23ca4bc93d..0000000000 --- a/pkg/collector/deployment_test.go +++ /dev/null @@ -1,260 +0,0 @@ -// 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 collector_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" -) - -var testTolerationValues = []v1.Toleration{ - { - Key: "hii", - Value: "greeting", - Effect: "NoSchedule", - }, -} - -func TestDeploymentNewDefault(t *testing.T) { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-namespace", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Tolerations: testTolerationValues, - }, - } - cfg := config.New() - - // test - d := Deployment(cfg, logger, otelcol) - - // verify - assert.Equal(t, "my-instance-collector", d.Name) - assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) - assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) - assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) - assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) - assert.Equal(t, testTolerationValues, d.Spec.Template.Spec.Tolerations) - - assert.Len(t, d.Spec.Template.Spec.Containers, 1) - - // verify sha256 podAnnotation - expectedAnnotations := map[string]string{ - "opentelemetry-operator-config/sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", - } - assert.Equal(t, expectedAnnotations, d.Spec.Template.Annotations) - - expectedLabels := map[string]string{ - "app.kubernetes.io/component": "opentelemetry-collector", - "app.kubernetes.io/instance": "my-namespace.my-instance", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/name": "my-instance-collector", - "app.kubernetes.io/part-of": "opentelemetry", - "app.kubernetes.io/version": "latest", - } - assert.Equal(t, expectedLabels, d.Spec.Template.Labels) - - expectedSelectorLabels := map[string]string{ - "app.kubernetes.io/component": "opentelemetry-collector", - "app.kubernetes.io/instance": "my-namespace.my-instance", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/part-of": "opentelemetry", - } - assert.Equal(t, expectedSelectorLabels, d.Spec.Selector.MatchLabels) - - // the pod selector must be contained within pod spec's labels - for k, v := range d.Spec.Selector.MatchLabels { - assert.Equal(t, v, d.Spec.Template.Labels[k]) - } -} - -func TestDeploymentPodAnnotations(t *testing.T) { - // prepare - testPodAnnotationValues := map[string]string{"annotation-key": "annotation-value"} - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - PodAnnotations: testPodAnnotationValues, - }, - } - cfg := config.New() - - // test - d := Deployment(cfg, logger, otelcol) - - // Add sha256 podAnnotation - testPodAnnotationValues["opentelemetry-operator-config/sha256"] = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - - // verify - assert.Equal(t, "my-instance-collector", d.Name) - assert.Equal(t, testPodAnnotationValues, d.Spec.Template.Annotations) -} - -func TestDeploymenttPodSecurityContext(t *testing.T) { - runAsNonRoot := true - runAsUser := int64(1337) - runasGroup := int64(1338) - - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - PodSecurityContext: &v1.PodSecurityContext{ - RunAsNonRoot: &runAsNonRoot, - RunAsUser: &runAsUser, - RunAsGroup: &runasGroup, - }, - }, - } - - cfg := config.New() - - d := Deployment(cfg, logger, otelcol) - - assert.Equal(t, &runAsNonRoot, d.Spec.Template.Spec.SecurityContext.RunAsNonRoot) - assert.Equal(t, &runAsUser, d.Spec.Template.Spec.SecurityContext.RunAsUser) - assert.Equal(t, &runasGroup, d.Spec.Template.Spec.SecurityContext.RunAsGroup) -} - -func TestDeploymentHostNetwork(t *testing.T) { - // Test default - otelcol_1 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - - cfg := config.New() - - d1 := Deployment(cfg, logger, otelcol_1) - - assert.Equal(t, d1.Spec.Template.Spec.HostNetwork, false) - assert.Equal(t, d1.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirst) - - // Test hostNetwork=true - otelcol_2 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance-hostnetwork", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - HostNetwork: true, - }, - } - - cfg = config.New() - - d2 := Deployment(cfg, logger, otelcol_2) - assert.Equal(t, d2.Spec.Template.Spec.HostNetwork, true) - assert.Equal(t, d2.Spec.Template.Spec.DNSPolicy, v1.DNSClusterFirstWithHostNet) -} - -func TestDeploymentFilterLabels(t *testing.T) { - excludedLabels := map[string]string{ - "foo": "1", - "app.foo.bar": "1", - } - - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Labels: excludedLabels, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{}, - } - - cfg := config.New(config.WithLabelFilters([]string{"foo*", "app.*.bar"})) - - d := Deployment(cfg, logger, otelcol) - - assert.Len(t, d.ObjectMeta.Labels, 6) - for k := range excludedLabels { - assert.NotContains(t, d.ObjectMeta.Labels, k) - } -} - -func TestDeploymentNodeSelector(t *testing.T) { - // Test default - otelcol_1 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - - cfg := config.New() - - d1 := Deployment(cfg, logger, otelcol_1) - - assert.Empty(t, d1.Spec.Template.Spec.NodeSelector) - - // Test nodeSelector - otelcol_2 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance-nodeselector", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - HostNetwork: true, - NodeSelector: map[string]string{ - "node-key": "node-value", - }, - }, - } - - cfg = config.New() - - d2 := Deployment(cfg, logger, otelcol_2) - assert.Equal(t, d2.Spec.Template.Spec.NodeSelector, map[string]string{"node-key": "node-value"}) -} - -func TestDeploymentPriorityClassName(t *testing.T) { - otelcol_1 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - - cfg := config.New() - - d1 := Deployment(cfg, logger, otelcol_1) - assert.Empty(t, d1.Spec.Template.Spec.PriorityClassName) - - priorityClassName := "test-class" - - otelcol_2 := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance-priortyClassName", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - PriorityClassName: priorityClassName, - }, - } - - cfg = config.New() - - d2 := Deployment(cfg, logger, otelcol_2) - assert.Equal(t, priorityClassName, d2.Spec.Template.Spec.PriorityClassName) -} diff --git a/pkg/collector/horizontalpodautoscaler.go b/pkg/collector/horizontalpodautoscaler.go deleted file mode 100644 index 8fc133ab1b..0000000000 --- a/pkg/collector/horizontalpodautoscaler.go +++ /dev/null @@ -1,196 +0,0 @@ -// 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 collector - -import ( - "github.com/go-logr/logr" - autoscalingv2 "k8s.io/api/autoscaling/v2" - autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" -) - -func HorizontalPodAutoscaler(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) client.Object { - autoscalingVersion := cfg.AutoscalingVersion() - - labels := Labels(otelcol, cfg.LabelsFilter()) - labels["app.kubernetes.io/name"] = naming.Collector(otelcol) - - annotations := Annotations(otelcol) - - var result client.Object - - objectMeta := metav1.ObjectMeta{ - Name: naming.HorizontalPodAutoscaler(otelcol), - Namespace: otelcol.Namespace, - Labels: labels, - Annotations: annotations, - } - - if autoscalingVersion == autodetect.AutoscalingVersionV2Beta2 { - targetCPUUtilization := autoscalingv2beta2.MetricSpec{ - Type: autoscalingv2beta2.ResourceMetricSourceType, - Resource: &autoscalingv2beta2.ResourceMetricSource{ - Name: corev1.ResourceCPU, - Target: autoscalingv2beta2.MetricTarget{ - Type: autoscalingv2beta2.UtilizationMetricType, - AverageUtilization: otelcol.Spec.Autoscaler.TargetCPUUtilization, - }, - }, - } - metrics := []autoscalingv2beta2.MetricSpec{targetCPUUtilization} - - autoscaler := autoscalingv2beta2.HorizontalPodAutoscaler{ - ObjectMeta: objectMeta, - Spec: autoscalingv2beta2.HorizontalPodAutoscalerSpec{ - ScaleTargetRef: autoscalingv2beta2.CrossVersionObjectReference{ - APIVersion: v1alpha1.GroupVersion.String(), - Kind: "OpenTelemetryCollector", - Name: naming.OpenTelemetryCollector(otelcol), - }, - MinReplicas: otelcol.Spec.Replicas, - MaxReplicas: *otelcol.Spec.MaxReplicas, - Metrics: metrics, - }, - } - - if otelcol.Spec.Autoscaler != nil && otelcol.Spec.Autoscaler.Behavior != nil { - behavior := ConvertToV2beta2Behavior(*otelcol.Spec.Autoscaler.Behavior) - autoscaler.Spec.Behavior = &behavior - } - - result = &autoscaler - } else { - targetCPUUtilization := autoscalingv2.MetricSpec{ - Type: autoscalingv2.ResourceMetricSourceType, - Resource: &autoscalingv2.ResourceMetricSource{ - Name: corev1.ResourceCPU, - Target: autoscalingv2.MetricTarget{ - Type: autoscalingv2.UtilizationMetricType, - AverageUtilization: otelcol.Spec.Autoscaler.TargetCPUUtilization, - }, - }, - } - metrics := []autoscalingv2.MetricSpec{targetCPUUtilization} - - autoscaler := autoscalingv2.HorizontalPodAutoscaler{ - ObjectMeta: objectMeta, - Spec: autoscalingv2.HorizontalPodAutoscalerSpec{ - ScaleTargetRef: autoscalingv2.CrossVersionObjectReference{ - APIVersion: v1alpha1.GroupVersion.String(), - Kind: "OpenTelemetryCollector", - Name: naming.OpenTelemetryCollector(otelcol), - }, - MinReplicas: otelcol.Spec.Replicas, - MaxReplicas: *otelcol.Spec.MaxReplicas, - Metrics: metrics, - }, - } - if otelcol.Spec.Autoscaler != nil && otelcol.Spec.Autoscaler.Behavior != nil { - autoscaler.Spec.Behavior = otelcol.Spec.Autoscaler.Behavior - } - result = &autoscaler - } - - return result -} - -// Create a v2beta2 HorizontalPodAutoscalerBehavior from a v2 instance. -func ConvertToV2beta2Behavior(v2behavior autoscalingv2.HorizontalPodAutoscalerBehavior) autoscalingv2beta2.HorizontalPodAutoscalerBehavior { - behavior := &autoscalingv2beta2.HorizontalPodAutoscalerBehavior{} - - if v2behavior.ScaleUp != nil { - scaleUpRules := &autoscalingv2beta2.HPAScalingRules{} - scaleUpTime := *v2behavior.ScaleUp.StabilizationWindowSeconds - scaleUpRules.StabilizationWindowSeconds = &scaleUpTime - - if v2behavior.ScaleUp.SelectPolicy != nil { - scaleUpSelectPolicy := ConvertToV2Beta2SelectPolicy(*v2behavior.ScaleUp.SelectPolicy) - scaleUpRules.SelectPolicy = &scaleUpSelectPolicy - } - if v2behavior.ScaleUp.Policies != nil { - scaleUpPolicies := []autoscalingv2beta2.HPAScalingPolicy{} - for _, policy := range v2behavior.ScaleUp.Policies { - v2beta2policy := ConvertToV2Beta2HPAScalingPolicy(policy) - scaleUpPolicies = append(scaleUpPolicies, v2beta2policy) - } - scaleUpRules.Policies = scaleUpPolicies - } - - behavior.ScaleUp = scaleUpRules - } - - if v2behavior.ScaleDown != nil { - scaleDownRules := &autoscalingv2beta2.HPAScalingRules{} - scaleDownTime := *v2behavior.ScaleDown.StabilizationWindowSeconds - scaleDownRules.StabilizationWindowSeconds = &scaleDownTime - - if v2behavior.ScaleDown.SelectPolicy != nil { - scaleDownSelectPolicy := ConvertToV2Beta2SelectPolicy(*v2behavior.ScaleDown.SelectPolicy) - scaleDownRules.SelectPolicy = &scaleDownSelectPolicy - } - if v2behavior.ScaleDown.Policies != nil { - ScaleDownPolicies := []autoscalingv2beta2.HPAScalingPolicy{} - for _, policy := range v2behavior.ScaleDown.Policies { - v2beta2policy := ConvertToV2Beta2HPAScalingPolicy(policy) - ScaleDownPolicies = append(ScaleDownPolicies, v2beta2policy) - } - scaleDownRules.Policies = ScaleDownPolicies - } - - behavior.ScaleDown = scaleDownRules - } - - return *behavior -} - -func ConvertToV2Beta2HPAScalingPolicy(v2policy autoscalingv2.HPAScalingPolicy) autoscalingv2beta2.HPAScalingPolicy { - v2beta2Policy := &autoscalingv2beta2.HPAScalingPolicy{ - Value: v2policy.Value, - PeriodSeconds: v2policy.PeriodSeconds, - } - - switch v2policy.Type { - case autoscalingv2.PodsScalingPolicy: - v2beta2Policy.Type = autoscalingv2beta2.PodsScalingPolicy - case autoscalingv2.PercentScalingPolicy: - v2beta2Policy.Type = autoscalingv2beta2.PercentScalingPolicy - } - - return *v2beta2Policy -} - -func ConvertToV2Beta2SelectPolicy(scalingPolicy autoscalingv2.ScalingPolicySelect) autoscalingv2beta2.ScalingPolicySelect { - max := autoscalingv2beta2.MaxPolicySelect - min := autoscalingv2beta2.MinPolicySelect - disabled := autoscalingv2beta2.DisabledPolicySelect - - switch scalingPolicy { - case autoscalingv2.MaxChangePolicySelect: - return max - case autoscalingv2.MinChangePolicySelect: - return min - case autoscalingv2.DisabledPolicySelect: - return disabled - } - - return disabled -} diff --git a/pkg/collector/horizontalpodautoscaler_test.go b/pkg/collector/horizontalpodautoscaler_test.go deleted file mode 100644 index 04b90b3480..0000000000 --- a/pkg/collector/horizontalpodautoscaler_test.go +++ /dev/null @@ -1,167 +0,0 @@ -// 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 collector_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - autoscalingv2 "k8s.io/api/autoscaling/v2" - autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/platform" -) - -func TestHPA(t *testing.T) { - type test struct { - name string - autoscalingVersion autodetect.AutoscalingVersion - } - v2Test := test{autodetect.AutoscalingVersionV2.String(), autodetect.AutoscalingVersionV2} - v2beta2Test := test{autodetect.AutoscalingVersionV2Beta2.String(), autodetect.AutoscalingVersionV2Beta2} - tests := []test{v2Test, v2beta2Test} - - var minReplicas int32 = 3 - var maxReplicas int32 = 5 - var cpuUtilization int32 = 90 - - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Replicas: &minReplicas, - MaxReplicas: &maxReplicas, - Autoscaler: &v1alpha1.AutoscalerSpec{ - TargetCPUUtilization: &cpuUtilization, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - mockAutoDetector := &mockAutoDetect{ - HPAVersionFunc: func() (autodetect.AutoscalingVersion, error) { - return test.autoscalingVersion, nil - }, - } - configuration := config.New(config.WithAutoDetect(mockAutoDetector)) - err := configuration.AutoDetect() - assert.NoError(t, err) - raw := HorizontalPodAutoscaler(configuration, logger, otelcol) - - if configuration.AutoscalingVersion() == autodetect.AutoscalingVersionV2Beta2 { - hpa := raw.(*autoscalingv2beta2.HorizontalPodAutoscaler) - - // verify - assert.Equal(t, "my-instance-collector", hpa.Name) - assert.Equal(t, "my-instance-collector", hpa.Labels["app.kubernetes.io/name"]) - assert.Equal(t, int32(3), *hpa.Spec.MinReplicas) - assert.Equal(t, int32(5), hpa.Spec.MaxReplicas) - assert.Equal(t, 1, len(hpa.Spec.Metrics)) - assert.Equal(t, corev1.ResourceCPU, hpa.Spec.Metrics[0].Resource.Name) - assert.Equal(t, int32(90), *hpa.Spec.Metrics[0].Resource.Target.AverageUtilization) - } else { - hpa := raw.(*autoscalingv2.HorizontalPodAutoscaler) - - // verify - assert.Equal(t, "my-instance-collector", hpa.Name) - assert.Equal(t, "my-instance-collector", hpa.Labels["app.kubernetes.io/name"]) - assert.Equal(t, int32(3), *hpa.Spec.MinReplicas) - assert.Equal(t, int32(5), hpa.Spec.MaxReplicas) - assert.Equal(t, 1, len(hpa.Spec.Metrics)) - assert.Equal(t, corev1.ResourceCPU, hpa.Spec.Metrics[0].Resource.Name) - assert.Equal(t, int32(90), *hpa.Spec.Metrics[0].Resource.Target.AverageUtilization) - } - }) - } -} - -func TestConvertToV2beta2Behavior(t *testing.T) { - ten := int32(10) - thirty := int32(30) - max := autoscalingv2.MaxChangePolicySelect - min := autoscalingv2.MinChangePolicySelect - - v2ScaleUp := &autoscalingv2.HPAScalingRules{ - StabilizationWindowSeconds: &thirty, - SelectPolicy: &max, - } - - v2ScaleDown := &autoscalingv2.HPAScalingRules{ - StabilizationWindowSeconds: &ten, - SelectPolicy: &min, - } - - v2Behavior := &autoscalingv2.HorizontalPodAutoscalerBehavior{ - ScaleUp: v2ScaleUp, - ScaleDown: v2ScaleDown, - } - - v2Beta2Behavior := ConvertToV2beta2Behavior(*v2Behavior) - - assert.Equal(t, thirty, *v2Beta2Behavior.ScaleUp.StabilizationWindowSeconds) - assert.Equal(t, ten, *v2Beta2Behavior.ScaleDown.StabilizationWindowSeconds) - assert.Equal(t, autoscalingv2beta2.MaxPolicySelect, *v2Beta2Behavior.ScaleUp.SelectPolicy) - assert.EqualValues(t, autoscalingv2beta2.MinPolicySelect, *v2Beta2Behavior.ScaleDown.SelectPolicy) -} - -func TestConvertToV2Beta2HPAScalingPolicy(t *testing.T) { - v2Policy := autoscalingv2.HPAScalingPolicy{ - Type: autoscalingv2.PodsScalingPolicy, - Value: 5, - PeriodSeconds: 10, - } - - v2Beta2Policy := ConvertToV2Beta2HPAScalingPolicy(v2Policy) - assert.Equal(t, autoscalingv2beta2.PodsScalingPolicy, v2Beta2Policy.Type) - assert.Equal(t, int32(5), v2Beta2Policy.Value) - assert.Equal(t, int32(10), v2Beta2Policy.PeriodSeconds) - -} - -func TestConvertToV2Beta2SelectPolicy(t *testing.T) { - min := autoscalingv2.MinChangePolicySelect - max := autoscalingv2.MaxChangePolicySelect - disabled := autoscalingv2.DisabledPolicySelect - - assert.Equal(t, autoscalingv2beta2.MinPolicySelect, ConvertToV2Beta2SelectPolicy(min)) - assert.Equal(t, autoscalingv2beta2.MaxPolicySelect, ConvertToV2Beta2SelectPolicy(max)) - assert.Equal(t, autoscalingv2beta2.DisabledPolicySelect, ConvertToV2Beta2SelectPolicy(disabled)) -} - -var _ autodetect.AutoDetect = (*mockAutoDetect)(nil) - -type mockAutoDetect struct { - PlatformFunc func() (platform.Platform, error) - HPAVersionFunc func() (autodetect.AutoscalingVersion, error) -} - -func (m *mockAutoDetect) HPAVersion() (autodetect.AutoscalingVersion, error) { - return m.HPAVersionFunc() -} - -func (m *mockAutoDetect) Platform() (platform.Platform, error) { - if m.PlatformFunc != nil { - return m.PlatformFunc() - } - return platform.Unknown, nil -} diff --git a/pkg/collector/reconcile/config_replace.go b/pkg/collector/reconcile/config_replace.go deleted file mode 100644 index 3d18d2d15f..0000000000 --- a/pkg/collector/reconcile/config_replace.go +++ /dev/null @@ -1,86 +0,0 @@ -// 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 reconcile - -import ( - "fmt" - "net/url" - - "github.com/mitchellh/mapstructure" - promconfig "github.com/prometheus/prometheus/config" - "github.com/prometheus/prometheus/discovery" - "github.com/prometheus/prometheus/discovery/http" - _ "github.com/prometheus/prometheus/discovery/install" - "gopkg.in/yaml.v2" - - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" - ta "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator/adapters" -) - -type Config struct { - PromConfig *promconfig.Config `yaml:"config"` -} - -func ReplaceConfig(params Params) (string, error) { - if !params.Instance.Spec.TargetAllocator.Enabled { - return params.Instance.Spec.Config, nil - } - config, err := adapters.ConfigFromString(params.Instance.Spec.Config) - if err != nil { - return "", err - } - - promCfgMap, err := ta.ConfigToPromConfig(params.Instance.Spec.Config) - if err != nil { - return "", err - } - - // yaml marshaling/unsmarshaling is preferred because of the problems associated with the conversion of map to a struct using mapstructure - promCfg, err := yaml.Marshal(map[string]interface{}{ - "config": promCfgMap, - }) - if err != nil { - return "", err - } - - var cfg Config - if err = yaml.UnmarshalStrict(promCfg, &cfg); err != nil { - return "", fmt.Errorf("error unmarshaling YAML: %w", err) - } - - for i := range cfg.PromConfig.ScrapeConfigs { - escapedJob := url.QueryEscape(cfg.PromConfig.ScrapeConfigs[i].JobName) - cfg.PromConfig.ScrapeConfigs[i].ServiceDiscoveryConfigs = discovery.Configs{ - &http.SDConfig{ - URL: fmt.Sprintf("http://%s:80/jobs/%s/targets?collector_id=$POD_NAME", naming.TAService(params.Instance), escapedJob), - }, - } - } - - updPromCfgMap := make(map[string]interface{}) - if err := mapstructure.Decode(cfg, &updPromCfgMap); err != nil { - return "", err - } - - // type coercion checks are handled in the ConfigToPromConfig method above - config["receivers"].(map[interface{}]interface{})["prometheus"].(map[interface{}]interface{})["config"] = updPromCfgMap["PromConfig"] - - out, err := yaml.Marshal(config) - if err != nil { - return "", err - } - return string(out), nil -} diff --git a/pkg/collector/reconcile/config_replace_test.go b/pkg/collector/reconcile/config_replace_test.go deleted file mode 100644 index 0d867492b8..0000000000 --- a/pkg/collector/reconcile/config_replace_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// 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 reconcile - -import ( - "testing" - - "github.com/prometheus/prometheus/discovery/http" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v2" - - ta "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator/adapters" -) - -func TestPrometheusParser(t *testing.T) { - param, err := newParams("test/test-img", "../testdata/http_sd_config_test.yaml") - assert.NoError(t, err) - - t.Run("should update config with http_sd_config", func(t *testing.T) { - actualConfig, err := ReplaceConfig(param) - assert.NoError(t, err) - - // prepare - var cfg Config - promCfgMap, err := ta.ConfigToPromConfig(actualConfig) - assert.NoError(t, err) - - promCfg, err := yaml.Marshal(map[string]interface{}{ - "config": promCfgMap, - }) - assert.NoError(t, err) - - err = yaml.UnmarshalStrict(promCfg, &cfg) - assert.NoError(t, err) - - // test - expectedMap := map[string]bool{ - "prometheus": false, - "service-x": false, - } - for _, scrapeConfig := range cfg.PromConfig.ScrapeConfigs { - assert.Len(t, scrapeConfig.ServiceDiscoveryConfigs, 1) - assert.Equal(t, scrapeConfig.ServiceDiscoveryConfigs[0].Name(), "http") - assert.Equal(t, scrapeConfig.ServiceDiscoveryConfigs[0].(*http.SDConfig).URL, "http://test-targetallocator:80/jobs/"+scrapeConfig.JobName+"/targets?collector_id=$POD_NAME") - expectedMap[scrapeConfig.JobName] = true - } - for k := range expectedMap { - assert.True(t, expectedMap[k], k) - } - }) - - t.Run("should not update config with http_sd_config", func(t *testing.T) { - param.Instance.Spec.TargetAllocator.Enabled = false - actualConfig, err := ReplaceConfig(param) - assert.NoError(t, err) - - // prepare - var cfg Config - promCfgMap, err := ta.ConfigToPromConfig(actualConfig) - assert.NoError(t, err) - - promCfg, err := yaml.Marshal(map[string]interface{}{ - "config": promCfgMap, - }) - assert.NoError(t, err) - - err = yaml.UnmarshalStrict(promCfg, &cfg) - assert.NoError(t, err) - - // test - expectedMap := map[string]bool{ - "prometheus": false, - "service-x": false, - } - for _, scrapeConfig := range cfg.PromConfig.ScrapeConfigs { - assert.Len(t, scrapeConfig.ServiceDiscoveryConfigs, 2) - assert.Equal(t, scrapeConfig.ServiceDiscoveryConfigs[0].Name(), "file") - assert.Equal(t, scrapeConfig.ServiceDiscoveryConfigs[1].Name(), "static") - expectedMap[scrapeConfig.JobName] = true - } - for k := range expectedMap { - assert.True(t, expectedMap[k], k) - } - }) - -} diff --git a/pkg/collector/reconcile/configmap.go b/pkg/collector/reconcile/configmap.go deleted file mode 100644 index 4c15e4296c..0000000000 --- a/pkg/collector/reconcile/configmap.go +++ /dev/null @@ -1,243 +0,0 @@ -// 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 reconcile - -import ( - "context" - "fmt" - "reflect" - "strings" - - "gopkg.in/yaml.v2" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" - "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator" - ta "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator/adapters" -) - -// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;patch;delete - -// ConfigMaps reconciles the config map(s) required for the instance in the current context. -func ConfigMaps(ctx context.Context, params Params) error { - desired := []corev1.ConfigMap{ - desiredConfigMap(ctx, params), - } - - if params.Instance.Spec.TargetAllocator.Enabled { - cm, err := desiredTAConfigMap(params) - if err != nil { - return fmt.Errorf("failed to parse config: %w", err) - } - desired = append(desired, cm) - } - - // first, handle the create/update parts - if err := expectedConfigMaps(ctx, params, desired, true); err != nil { - return fmt.Errorf("failed to reconcile the expected configmaps: %w", err) - } - - // then, delete the extra objects - if err := deleteConfigMaps(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the configmaps to be deleted: %w", err) - } - - return nil -} - -func desiredConfigMap(_ context.Context, params Params) corev1.ConfigMap { - name := naming.ConfigMap(params.Instance) - version := strings.Split(params.Instance.Spec.Image, ":") - labels := collector.Labels(params.Instance, []string{}) - labels["app.kubernetes.io/name"] = name - if len(version) > 1 { - labels["app.kubernetes.io/version"] = version[len(version)-1] - } else { - labels["app.kubernetes.io/version"] = "latest" - } - config, err := ReplaceConfig(params) - if err != nil { - params.Log.V(2).Info("failed to update prometheus config to use sharded targets: ", err) - } - - return corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: params.Instance.Namespace, - Labels: labels, - Annotations: params.Instance.Annotations, - }, - Data: map[string]string{ - "collector.yaml": config, - }, - } -} - -func desiredTAConfigMap(params Params) (corev1.ConfigMap, error) { - name := naming.TAConfigMap(params.Instance) - version := strings.Split(params.Instance.Spec.Image, ":") - labels := targetallocator.Labels(params.Instance) - labels["app.kubernetes.io/name"] = name - if len(version) > 1 { - labels["app.kubernetes.io/version"] = version[len(version)-1] - } else { - labels["app.kubernetes.io/version"] = "latest" - } - - promConfig, err := ta.ConfigToPromConfig(params.Instance.Spec.Config) - if err != nil { - return corev1.ConfigMap{}, err - } - - taConfig := make(map[interface{}]interface{}) - taConfig["label_selector"] = map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/component": "opentelemetry-collector", - } - taConfig["config"] = promConfig - if len(params.Instance.Spec.TargetAllocator.AllocationStrategy) > 0 { - taConfig["allocation_strategy"] = params.Instance.Spec.TargetAllocator.AllocationStrategy - } else { - taConfig["allocation_strategy"] = "least-weighted" - } - taConfigYAML, err := yaml.Marshal(taConfig) - if err != nil { - return corev1.ConfigMap{}, err - } - - return corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: params.Instance.Namespace, - Labels: labels, - Annotations: params.Instance.Annotations, - }, - Data: map[string]string{ - "targetallocator.yaml": string(taConfigYAML), - }, - }, nil -} - -func expectedConfigMaps(ctx context.Context, params Params, expected []corev1.ConfigMap, retry bool) error { - for _, obj := range expected { - desired := obj - - if err := controllerutil.SetControllerReference(¶ms.Instance, &desired, params.Scheme); err != nil { - return fmt.Errorf("failed to set controller reference: %w", err) - } - - existing := &corev1.ConfigMap{} - nns := types.NamespacedName{Namespace: desired.Namespace, Name: desired.Name} - err := params.Client.Get(ctx, nns, existing) - if err != nil && errors.IsNotFound(err) { - if err := params.Client.Create(ctx, &desired); err != nil { - if errors.IsAlreadyExists(err) && retry { - // let's try again? we probably had multiple updates at one, and now it exists already - if err := expectedConfigMaps(ctx, params, expected, false); err != nil { - // somethin else happened now... - return err - } - - // we succeeded in the retry, exit this attempt - return nil - } - return fmt.Errorf("failed to create: %w", err) - } - params.Log.V(2).Info("created", "configmap.name", desired.Name, "configmap.namespace", desired.Namespace) - continue - } else if err != nil { - return fmt.Errorf("failed to get: %w", err) - } - - // it exists already, merge the two if the end result isn't identical to the existing one - updated := existing.DeepCopy() - if updated.Annotations == nil { - updated.Annotations = map[string]string{} - } - if updated.Labels == nil { - updated.Labels = map[string]string{} - } - - updated.Data = desired.Data - updated.BinaryData = desired.BinaryData - updated.ObjectMeta.OwnerReferences = desired.ObjectMeta.OwnerReferences - - for k, v := range desired.ObjectMeta.Annotations { - updated.ObjectMeta.Annotations[k] = v - } - for k, v := range desired.ObjectMeta.Labels { - updated.ObjectMeta.Labels[k] = v - } - - patch := client.MergeFrom(existing) - - if err := params.Client.Patch(ctx, updated, patch); err != nil { - return fmt.Errorf("failed to apply changes: %w", err) - } - if configMapChanged(&desired, existing) { - params.Recorder.Event(updated, "Normal", "ConfigUpdate ", fmt.Sprintf("OpenTelemetry Config changed - %s/%s", desired.Namespace, desired.Name)) - } - - params.Log.V(2).Info("applied", "configmap.name", desired.Name, "configmap.namespace", desired.Namespace) - } - - return nil -} - -func deleteConfigMaps(ctx context.Context, params Params, expected []corev1.ConfigMap) error { - opts := []client.ListOption{ - client.InNamespace(params.Instance.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - list := &corev1.ConfigMapList{} - if err := params.Client.List(ctx, list, opts...); err != nil { - return fmt.Errorf("failed to list: %w", err) - } - - for i := range list.Items { - existing := list.Items[i] - del := true - for _, keep := range expected { - if keep.Name == existing.Name && keep.Namespace == existing.Namespace { - del = false - break - } - } - - if del { - if err := params.Client.Delete(ctx, &existing); err != nil { - return fmt.Errorf("failed to delete: %w", err) - } - params.Log.V(2).Info("deleted", "configmap.name", existing.Name, "configmap.namespace", existing.Namespace) - } - } - - return nil -} - -func configMapChanged(desired *corev1.ConfigMap, actual *corev1.ConfigMap) bool { - return !reflect.DeepEqual(desired.Data, actual.Data) - -} diff --git a/pkg/collector/reconcile/configmap_test.go b/pkg/collector/reconcile/configmap_test.go deleted file mode 100644 index bdc13d79ca..0000000000 --- a/pkg/collector/reconcile/configmap_test.go +++ /dev/null @@ -1,357 +0,0 @@ -// 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 reconcile - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v2" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/tools/record" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - ta "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator/adapters" -) - -func TestDesiredConfigMap(t *testing.T) { - expectedLables := map[string]string{ - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/part-of": "opentelemetry", - "app.kubernetes.io/version": "0.47.0", - } - - t.Run("should return expected collector config map", func(t *testing.T) { - expectedLables["app.kubernetes.io/component"] = "opentelemetry-collector" - expectedLables["app.kubernetes.io/name"] = "test-collector" - expectedLables["app.kubernetes.io/version"] = "0.47.0" - - expectedData := map[string]string{ - "collector.yaml": `processors: -receivers: - jaeger: - protocols: - grpc: - prometheus: - config: - scrape_configs: - - job_name: otel-collector - scrape_interval: 10s - static_configs: - - targets: [ '0.0.0.0:8888', '0.0.0.0:9999' ] - -exporters: - logging: - -service: - pipelines: - metrics: - receivers: [prometheus, jaeger] - processors: [] - exporters: [logging]`, - } - - actual := desiredConfigMap(context.Background(), params()) - - assert.Equal(t, "test-collector", actual.Name) - assert.Equal(t, expectedLables, actual.Labels) - assert.Equal(t, expectedData, actual.Data) - - }) - - t.Run("should return expected collector config map with http_sd_config", func(t *testing.T) { - expectedLables["app.kubernetes.io/component"] = "opentelemetry-collector" - expectedLables["app.kubernetes.io/name"] = "test-collector" - - expectedData := map[string]string{ - "collector.yaml": `exporters: - logging: null -processors: null -receivers: - jaeger: - protocols: - grpc: null - prometheus: - config: - global: - scrape_interval: 1m - scrape_timeout: 10s - evaluation_interval: 1m - scrape_configs: - - job_name: otel-collector - honor_timestamps: true - scrape_interval: 10s - scrape_timeout: 10s - metrics_path: /metrics - scheme: http - follow_redirects: true - http_sd_configs: - - follow_redirects: false - url: http://test-targetallocator:80/jobs/otel-collector/targets?collector_id=$POD_NAME -service: - pipelines: - metrics: - exporters: - - logging - processors: [] - receivers: - - prometheus - - jaeger -`, - } - - param := params() - param.Instance.Spec.TargetAllocator.Enabled = true - actual := desiredConfigMap(context.Background(), param) - - assert.Equal(t, "test-collector", actual.Name) - assert.Equal(t, expectedLables, actual.Labels) - assert.Equal(t, expectedData, actual.Data) - - }) - - t.Run("should return expected escaped collector config map with http_sd_config", func(t *testing.T) { - expectedLables["app.kubernetes.io/component"] = "opentelemetry-collector" - expectedLables["app.kubernetes.io/name"] = "test-collector" - expectedLables["app.kubernetes.io/version"] = "latest" - - expectedData := map[string]string{ - "collector.yaml": `exporters: - logging: null -processors: null -receivers: - prometheus: - config: - global: - scrape_interval: 1m - scrape_timeout: 10s - evaluation_interval: 1m - scrape_configs: - - job_name: serviceMonitor/test/test/0 - honor_timestamps: true - scrape_interval: 1m - scrape_timeout: 10s - metrics_path: /metrics - scheme: http - follow_redirects: true - http_sd_configs: - - follow_redirects: false - url: http://test-targetallocator:80/jobs/serviceMonitor%2Ftest%2Ftest%2F0/targets?collector_id=$POD_NAME -service: - pipelines: - metrics: - exporters: - - logging - processors: [] - receivers: - - prometheus -`, - } - - param, err := newParams("test/test-img", "../testdata/http_sd_config_servicemonitor_test.yaml") - assert.NoError(t, err) - param.Instance.Spec.TargetAllocator.Enabled = true - actual := desiredConfigMap(context.Background(), param) - - assert.Equal(t, "test-collector", actual.Name) - assert.Equal(t, expectedLables, actual.Labels) - assert.Equal(t, expectedData, actual.Data) - - // Reset the value - expectedLables["app.kubernetes.io/version"] = "0.47.0" - - }) - - t.Run("should return expected target allocator config map", func(t *testing.T) { - expectedLables["app.kubernetes.io/component"] = "opentelemetry-targetallocator" - expectedLables["app.kubernetes.io/name"] = "test-targetallocator" - - expectedData := map[string]string{ - "targetallocator.yaml": `allocation_strategy: least-weighted -config: - scrape_configs: - - job_name: otel-collector - scrape_interval: 10s - static_configs: - - targets: - - 0.0.0.0:8888 - - 0.0.0.0:9999 -label_selector: - app.kubernetes.io/component: opentelemetry-collector - app.kubernetes.io/instance: default.test - app.kubernetes.io/managed-by: opentelemetry-operator -`, - } - - actual, err := desiredTAConfigMap(params()) - assert.NoError(t, err) - - assert.Equal(t, "test-targetallocator", actual.Name) - assert.Equal(t, expectedLables, actual.Labels) - assert.Equal(t, expectedData, actual.Data) - - }) - -} - -func TestExpectedConfigMap(t *testing.T) { - t.Run("should create collector and target allocator config maps", func(t *testing.T) { - configMap, err := desiredTAConfigMap(params()) - assert.NoError(t, err) - err = expectedConfigMaps(context.Background(), params(), []v1.ConfigMap{desiredConfigMap(context.Background(), params()), configMap}, true) - assert.NoError(t, err) - - exists, err := populateObjectIfExists(t, &v1.ConfigMap{}, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - - exists, err = populateObjectIfExists(t, &v1.ConfigMap{}, types.NamespacedName{Namespace: "default", Name: "test-targetallocator"}) - - assert.NoError(t, err) - assert.True(t, exists) - }) - - t.Run("should update collector config map", func(t *testing.T) { - - param := Params{ - Config: config.New(), - Client: k8sClient, - Instance: v1alpha1.OpenTelemetryCollector{ - TypeMeta: metav1.TypeMeta{ - Kind: "opentelemetry.io", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - UID: instanceUID, - }, - }, - Scheme: testScheme, - Log: logger, - Recorder: record.NewFakeRecorder(10), - } - cm := desiredConfigMap(context.Background(), param) - createObjectIfNotExists(t, "test-collector", &cm) - - err := expectedConfigMaps(context.Background(), params(), []v1.ConfigMap{desiredConfigMap(context.Background(), params())}, true) - assert.NoError(t, err) - - actual := v1.ConfigMap{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, instanceUID, actual.OwnerReferences[0].UID) - assert.Equal(t, params().Instance.Spec.Config, actual.Data["collector.yaml"]) - }) - - t.Run("should update target allocator config map", func(t *testing.T) { - - param := Params{ - Client: k8sClient, - Instance: v1alpha1.OpenTelemetryCollector{ - TypeMeta: metav1.TypeMeta{ - Kind: "opentelemetry.io", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - UID: instanceUID, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeStatefulSet, - Ports: []v1.ServicePort{{ - Name: "web", - Port: 80, - TargetPort: intstr.IntOrString{ - Type: intstr.Int, - IntVal: 80, - }, - NodePort: 0, - }}, - TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ - Enabled: true, - }, - Config: "", - }, - }, - Scheme: testScheme, - Log: logger, - } - cm, err := desiredTAConfigMap(param) - assert.EqualError(t, err, "no receivers available as part of the configuration") - createObjectIfNotExists(t, "test-targetallocator", &cm) - - configMap, err := desiredTAConfigMap(params()) - assert.NoError(t, err) - err = expectedConfigMaps(context.Background(), params(), []v1.ConfigMap{configMap}, true) - assert.NoError(t, err) - - actual := v1.ConfigMap{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-targetallocator"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, instanceUID, actual.OwnerReferences[0].UID) - - parmConfig, err := ta.ConfigToPromConfig(params().Instance.Spec.Config) - assert.NoError(t, err) - - taConfig := make(map[interface{}]interface{}) - taConfig["label_selector"] = map[string]string{ - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - "app.kubernetes.io/component": "opentelemetry-collector", - } - taConfig["config"] = parmConfig - taConfig["allocation_strategy"] = "least-weighted" - taConfigYAML, _ := yaml.Marshal(taConfig) - - assert.Equal(t, string(taConfigYAML), actual.Data["targetallocator.yaml"]) - }) - - t.Run("should delete config map", func(t *testing.T) { - - deletecm := v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-delete-collector", - Namespace: "default", - Labels: map[string]string{ - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }, - }, - } - createObjectIfNotExists(t, "test-delete-collector", &deletecm) - - exists, _ := populateObjectIfExists(t, &v1.ConfigMap{}, types.NamespacedName{Namespace: "default", Name: "test-delete-collector"}) - assert.True(t, exists) - - err := deleteConfigMaps(context.Background(), params(), []v1.ConfigMap{desiredConfigMap(context.Background(), params())}) - assert.NoError(t, err) - - exists, _ = populateObjectIfExists(t, &v1.ConfigMap{}, types.NamespacedName{Namespace: "default", Name: "test-delete-collector"}) - assert.False(t, exists) - }) -} diff --git a/pkg/collector/reconcile/daemonset.go b/pkg/collector/reconcile/daemonset.go deleted file mode 100644 index a14649c59c..0000000000 --- a/pkg/collector/reconcile/daemonset.go +++ /dev/null @@ -1,146 +0,0 @@ -// 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 reconcile - -import ( - "context" - "fmt" - - appsv1 "k8s.io/api/apps/v1" - apiequality "k8s.io/apimachinery/pkg/api/equality" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" -) - -// +kubebuilder:rbac:groups="apps",resources=daemonsets,verbs=get;list;watch;create;update;patch;delete - -// DaemonSets reconciles the daemon set(s) required for the instance in the current context. -func DaemonSets(ctx context.Context, params Params) error { - desired := []appsv1.DaemonSet{} - if params.Instance.Spec.Mode == "daemonset" { - desired = append(desired, collector.DaemonSet(params.Config, params.Log, params.Instance)) - } - - // first, handle the create/update parts - if err := expectedDaemonSets(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the expected daemon sets: %w", err) - } - - // then, delete the extra objects - if err := deleteDaemonSets(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the daemon sets to be deleted: %w", err) - } - - return nil -} - -func expectedDaemonSets(ctx context.Context, params Params, expected []appsv1.DaemonSet) error { - for _, obj := range expected { - desired := obj - - if err := controllerutil.SetControllerReference(¶ms.Instance, &desired, params.Scheme); err != nil { - return fmt.Errorf("failed to set controller reference: %w", err) - } - - existing := &appsv1.DaemonSet{} - nns := types.NamespacedName{Namespace: desired.Namespace, Name: desired.Name} - err := params.Client.Get(ctx, nns, existing) - if err != nil && k8serrors.IsNotFound(err) { - if err := params.Client.Create(ctx, &desired); err != nil { - return fmt.Errorf("failed to create: %w", err) - } - params.Log.V(2).Info("created", "daemonset.name", desired.Name, "daemonset.namespace", desired.Namespace) - continue - } else if err != nil { - return fmt.Errorf("failed to get: %w", err) - } - - // Selector is an immutable field, if set, we cannot modify it otherwise we will face reconciliation error. - if !apiequality.Semantic.DeepEqual(desired.Spec.Selector, existing.Spec.Selector) { - params.Log.V(2).Info("Spec.Selector change detected, trying to delete, the new collector daemonset will be created in the next reconcile cycle", "daemonset.name", existing.Name, "daemonset.namespace", existing.Namespace) - - if err := params.Client.Delete(ctx, existing); err != nil { - return fmt.Errorf("failed to request deleting daemonset: %w", err) - } - continue - } - - // it exists already, merge the two if the end result isn't identical to the existing one - updated := existing.DeepCopy() - if updated.Annotations == nil { - updated.Annotations = map[string]string{} - } - if updated.Labels == nil { - updated.Labels = map[string]string{} - } - - updated.Spec = desired.Spec - updated.ObjectMeta.OwnerReferences = desired.ObjectMeta.OwnerReferences - - for k, v := range desired.ObjectMeta.Annotations { - updated.ObjectMeta.Annotations[k] = v - } - for k, v := range desired.ObjectMeta.Labels { - updated.ObjectMeta.Labels[k] = v - } - - patch := client.MergeFrom(existing) - if err := params.Client.Patch(ctx, updated, patch); err != nil { - return fmt.Errorf("failed to apply changes: %w", err) - } - - params.Log.V(2).Info("applied", "daemonset.name", desired.Name, "daemonset.namespace", desired.Namespace) - } - - return nil -} - -func deleteDaemonSets(ctx context.Context, params Params, expected []appsv1.DaemonSet) error { - opts := []client.ListOption{ - client.InNamespace(params.Instance.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - list := &appsv1.DaemonSetList{} - if err := params.Client.List(ctx, list, opts...); err != nil { - return fmt.Errorf("failed to list: %w", err) - } - - for i := range list.Items { - existing := list.Items[i] - del := true - for _, keep := range expected { - if keep.Name == existing.Name && keep.Namespace == existing.Namespace { - del = false - break - } - } - - if del { - if err := params.Client.Delete(ctx, &existing); err != nil { - return fmt.Errorf("failed to delete: %w", err) - } - params.Log.V(2).Info("deleted", "daemonset.name", existing.Name, "daemonset.namespace", existing.Namespace) - } - } - - return nil -} diff --git a/pkg/collector/reconcile/daemonset_test.go b/pkg/collector/reconcile/daemonset_test.go deleted file mode 100644 index 02d8d0c8aa..0000000000 --- a/pkg/collector/reconcile/daemonset_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// 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 reconcile - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" -) - -func TestExpectedDaemonsets(t *testing.T) { - param := params() - expectedDs := collector.DaemonSet(param.Config, logger, param.Instance) - - t.Run("should create Daemonset", func(t *testing.T) { - err := expectedDaemonSets(context.Background(), param, []v1.DaemonSet{expectedDs}) - assert.NoError(t, err) - - exists, err := populateObjectIfExists(t, &v1.DaemonSet{}, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - - }) - t.Run("should update Daemonset", func(t *testing.T) { - createObjectIfNotExists(t, "test-collector", &expectedDs) - err := expectedDaemonSets(context.Background(), param, []v1.DaemonSet{expectedDs}) - assert.NoError(t, err) - - actual := v1.DaemonSet{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, instanceUID, actual.OwnerReferences[0].UID) - }) - - t.Run("should delete daemonset", func(t *testing.T) { - - labels := map[string]string{ - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - } - ds := v1.DaemonSet{} - ds.Name = "dummy" - ds.Namespace = "default" - ds.Labels = labels - ds.Spec = v1.DaemonSetSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "dummy", - Image: "busybox", - }}, - }, - }, - } - - createObjectIfNotExists(t, "dummy", &ds) - - err := deleteDaemonSets(context.Background(), param, []v1.DaemonSet{expectedDs}) - assert.NoError(t, err) - - actual := v1.DaemonSet{} - exists, _ := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "dummy"}) - - assert.False(t, exists) - - }) - - t.Run("should not delete daemonset", func(t *testing.T) { - - labels := map[string]string{ - "app.kubernetes.io/managed-by": "helm-opentelemetry-operator", - } - ds := v1.DaemonSet{} - ds.Name = "dummy" - ds.Namespace = "default" - ds.Labels = labels - ds.Spec = v1.DaemonSetSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "dummy", - Image: "busybox", - }}, - }, - }, - } - - createObjectIfNotExists(t, "dummy", &ds) - - err := deleteDaemonSets(context.Background(), param, []v1.DaemonSet{expectedDs}) - assert.NoError(t, err) - - actual := v1.DaemonSet{} - exists, _ := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "dummy"}) - - assert.True(t, exists) - - }) - - t.Run("change Spec.Selector should recreate daemonset", func(t *testing.T) { - - oldDs := collector.DaemonSet(param.Config, logger, param.Instance) - oldDs.Spec.Selector.MatchLabels["app.kubernetes.io/version"] = "latest" - oldDs.Spec.Template.Labels["app.kubernetes.io/version"] = "latest" - oldDs.Name = "update-ds" - - err := expectedDaemonSets(context.Background(), param, []v1.DaemonSet{oldDs}) - assert.NoError(t, err) - exists, err := populateObjectIfExists(t, &v1.DaemonSet{}, types.NamespacedName{Namespace: "default", Name: oldDs.Name}) - assert.NoError(t, err) - assert.True(t, exists) - - newDs := collector.DaemonSet(param.Config, logger, param.Instance) - newDs.Name = oldDs.Name - err = expectedDaemonSets(context.Background(), param, []v1.DaemonSet{newDs}) - assert.NoError(t, err) - exists, err = populateObjectIfExists(t, &v1.DaemonSet{}, types.NamespacedName{Namespace: "default", Name: oldDs.Name}) - assert.NoError(t, err) - assert.False(t, exists) - - err = expectedDaemonSets(context.Background(), param, []v1.DaemonSet{newDs}) - assert.NoError(t, err) - actual := v1.DaemonSet{} - exists, err = populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: oldDs.Name}) - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, newDs.Spec.Selector.MatchLabels, actual.Spec.Selector.MatchLabels) - }) -} diff --git a/pkg/collector/reconcile/deployment.go b/pkg/collector/reconcile/deployment.go deleted file mode 100644 index 5e4b4bd18d..0000000000 --- a/pkg/collector/reconcile/deployment.go +++ /dev/null @@ -1,166 +0,0 @@ -// 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 reconcile - -import ( - "context" - "fmt" - - appsv1 "k8s.io/api/apps/v1" - apiequality "k8s.io/apimachinery/pkg/api/equality" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator" -) - -// +kubebuilder:rbac:groups="apps",resources=deployments,verbs=get;list;watch;create;update;patch;delete - -// Deployments reconciles the deployment(s) required for the instance in the current context. -func Deployments(ctx context.Context, params Params) error { - desired := []appsv1.Deployment{} - if params.Instance.Spec.Mode == "deployment" { - desired = append(desired, collector.Deployment(params.Config, params.Log, params.Instance)) - } - - if params.Instance.Spec.TargetAllocator.Enabled { - desired = append(desired, targetallocator.Deployment(params.Config, params.Log, params.Instance)) - } - - // first, handle the create/update parts - if err := expectedDeployments(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the expected deployments: %w", err) - } - - // then, delete the extra objects - if err := deleteDeployments(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the deployments to be deleted: %w", err) - } - - return nil -} - -func expectedDeployments(ctx context.Context, params Params, expected []appsv1.Deployment) error { - for _, obj := range expected { - desired := obj - - if err := controllerutil.SetControllerReference(¶ms.Instance, &desired, params.Scheme); err != nil { - return fmt.Errorf("failed to set controller reference: %w", err) - } - - existing := &appsv1.Deployment{} - nns := types.NamespacedName{Namespace: desired.Namespace, Name: desired.Name} - err := params.Client.Get(ctx, nns, existing) - if err != nil && k8serrors.IsNotFound(err) { - if err := params.Client.Create(ctx, &desired); err != nil { - return fmt.Errorf("failed to create: %w", err) - } - params.Log.V(2).Info("created", "deployment.name", desired.Name, "deployment.namespace", desired.Namespace) - continue - } else if err != nil { - return fmt.Errorf("failed to get: %w", err) - } - - // Selector is an immutable field, if set, we cannot modify it otherwise we will face reconciliation error. - if !apiequality.Semantic.DeepEqual(desired.Spec.Selector, existing.Spec.Selector) { - params.Log.V(2).Info("Spec.Selector change detected, trying to delete, the new collector deployment will be created in the next reconcile cycle ", "deployment.name", existing.Name, "deployment.namespace", existing.Namespace) - - if err := params.Client.Delete(ctx, existing); err != nil { - return fmt.Errorf("failed to delete deployment: %w", err) - } - continue - } - - // it exists already, merge the two if the end result isn't identical to the existing one - updated := existing.DeepCopy() - if updated.Annotations == nil { - updated.Annotations = map[string]string{} - } - if updated.Labels == nil { - updated.Labels = map[string]string{} - } - - updated.Spec = desired.Spec - updated.ObjectMeta.OwnerReferences = desired.ObjectMeta.OwnerReferences - - for k, v := range desired.ObjectMeta.Annotations { - updated.ObjectMeta.Annotations[k] = v - } - for k, v := range desired.ObjectMeta.Labels { - updated.ObjectMeta.Labels[k] = v - } - - patch := client.MergeFrom(existing) - - if err := params.Client.Patch(ctx, updated, patch); err != nil { - return fmt.Errorf("failed to apply changes: %w", err) - } - - params.Log.V(2).Info("applied", "deployment.name", desired.Name, "deployment.namespace", desired.Namespace) - } - - return nil -} - -func deleteDeployments(ctx context.Context, params Params, expected []appsv1.Deployment) error { - opts := []client.ListOption{ - client.InNamespace(params.Instance.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - list := &appsv1.DeploymentList{} - if err := params.Client.List(ctx, list, opts...); err != nil { - return fmt.Errorf("failed to list: %w", err) - } - - for i := range list.Items { - existing := list.Items[i] - del := true - for _, keep := range expected { - if keep.Name == existing.Name && keep.Namespace == existing.Namespace { - del = false - break - } - } - - if del { - if err := params.Client.Delete(ctx, &existing); err != nil { - return fmt.Errorf("failed to delete: %w", err) - } - params.Log.V(2).Info("deleted", "deployment.name", existing.Name, "deployment.namespace", existing.Namespace) - } - } - - return nil -} - -// currentReplicasWithHPA calculates deployment replicas if HPA is enabled. -func currentReplicasWithHPA(spec v1alpha1.OpenTelemetryCollectorSpec, curr int32) int32 { - if curr < *spec.Replicas { - return *spec.Replicas - } - - if curr > *spec.MaxReplicas { - return *spec.MaxReplicas - } - - return curr -} diff --git a/pkg/collector/reconcile/deployment_test.go b/pkg/collector/reconcile/deployment_test.go deleted file mode 100644 index ab146b469a..0000000000 --- a/pkg/collector/reconcile/deployment_test.go +++ /dev/null @@ -1,393 +0,0 @@ -// 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 reconcile - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator" -) - -func TestExpectedDeployments(t *testing.T) { - param := params() - expectedDeploy := collector.Deployment(param.Config, logger, param.Instance) - expectedTADeploy := targetallocator.Deployment(param.Config, logger, param.Instance) - - t.Run("should create collector deployment", func(t *testing.T) { - err := expectedDeployments(context.Background(), param, []v1.Deployment{expectedDeploy}) - assert.NoError(t, err) - - exists, err := populateObjectIfExists(t, &v1.Deployment{}, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - - }) - - t.Run("should create target allocator deployment", func(t *testing.T) { - err := expectedDeployments(context.Background(), param, []v1.Deployment{expectedTADeploy}) - assert.NoError(t, err) - - exists, err := populateObjectIfExists(t, &v1.Deployment{}, types.NamespacedName{Namespace: "default", Name: "test-targetallocator"}) - - assert.NoError(t, err) - assert.True(t, exists) - - }) - - t.Run("should not create target allocator deployment when targetallocator is not enabled", func(t *testing.T) { - param := Params{ - Client: k8sClient, - Instance: v1alpha1.OpenTelemetryCollector{ - TypeMeta: metav1.TypeMeta{ - Kind: "opentelemetry.io", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - UID: instanceUID, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeStatefulSet, - Config: ` - receivers: - jaeger: - protocols: - grpc: - processors: - - exporters: - logging: - - service: - pipelines: - traces: - receivers: [jaeger] - processors: [] - exporters: [logging] - - `, - }, - }, - Scheme: testScheme, - Log: logger, - } - expected := []v1.Deployment{} - if param.Instance.Spec.TargetAllocator.Enabled { - expected = append(expected, targetallocator.Deployment(param.Config, param.Log, param.Instance)) - } - - assert.Len(t, expected, 0) - }) - - t.Run("should update target allocator deployment when the prometheusCR is updated", func(t *testing.T) { - ctx := context.Background() - createObjectIfNotExists(t, "test-targetallocator", &expectedTADeploy) - orgUID := expectedTADeploy.OwnerReferences[0].UID - - updatedParam, err := newParams(expectedTADeploy.Spec.Template.Spec.Containers[0].Image, "") - assert.NoError(t, err) - updatedParam.Instance.Spec.TargetAllocator.PrometheusCR.Enabled = true - updatedDeploy := targetallocator.Deployment(updatedParam.Config, logger, updatedParam.Instance) - - err = expectedDeployments(ctx, param, []v1.Deployment{updatedDeploy}) - assert.NoError(t, err) - - actual := v1.Deployment{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-targetallocator"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, orgUID, actual.OwnerReferences[0].UID) - assert.ElementsMatch(t, actual.Spec.Template.Spec.Containers[0].Args, []string{"--enable-prometheus-cr-watcher"}) - assert.Equal(t, int32(1), *actual.Spec.Replicas) - }) - - t.Run("should not update target allocator deployment replicas when collector max replicas is set", func(t *testing.T) { - replicas, maxReplicas := int32(2), int32(10) - oneReplica := int32(1) - param := Params{ - Client: k8sClient, - Instance: v1alpha1.OpenTelemetryCollector{ - TypeMeta: metav1.TypeMeta{ - Kind: "opentelemetry.io", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - UID: instanceUID, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - MaxReplicas: &maxReplicas, - Replicas: &replicas, - Mode: v1alpha1.ModeStatefulSet, - TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ - Enabled: true, - Replicas: &oneReplica, - }, - Config: ` - receivers: - jaeger: - protocols: - grpc: - processors: - - exporters: - logging: - - service: - pipelines: - traces: - receivers: [jaeger] - processors: [] - exporters: [logging] - - `, - }, - }, - Scheme: testScheme, - Log: logger, - } - expected := []v1.Deployment{} - allocator := targetallocator.Deployment(param.Config, param.Log, param.Instance) - expected = append(expected, allocator) - - assert.Len(t, expected, 1) - assert.Equal(t, *allocator.Spec.Replicas, int32(1)) - }) - - t.Run("should update target allocator deployment replicas when changed", func(t *testing.T) { - initialReplicas, nextReplicas := int32(1), int32(2) - param := Params{ - Client: k8sClient, - Instance: v1alpha1.OpenTelemetryCollector{ - TypeMeta: metav1.TypeMeta{ - Kind: "opentelemetry.io", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - UID: instanceUID, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Replicas: &initialReplicas, - Mode: v1alpha1.ModeStatefulSet, - TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ - Enabled: true, - Replicas: &initialReplicas, - }, - Config: ` - receivers: - jaeger: - protocols: - grpc: - processors: - - exporters: - logging: - - service: - pipelines: - traces: - receivers: [jaeger] - processors: [] - exporters: [logging] - - `, - }, - }, - Scheme: testScheme, - Log: logger, - } - expected := []v1.Deployment{} - allocator := targetallocator.Deployment(param.Config, param.Log, param.Instance) - expected = append(expected, allocator) - - assert.Len(t, expected, 1) - assert.Equal(t, *allocator.Spec.Replicas, int32(1)) - param.Instance.Spec.TargetAllocator.Replicas = &nextReplicas - finalAllocator := targetallocator.Deployment(param.Config, param.Log, param.Instance) - assert.Equal(t, *finalAllocator.Spec.Replicas, int32(2)) - }) - - t.Run("should update deployment", func(t *testing.T) { - createObjectIfNotExists(t, "test-collector", &expectedDeploy) - err := expectedDeployments(context.Background(), param, []v1.Deployment{expectedDeploy}) - assert.NoError(t, err) - - actual := v1.Deployment{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, instanceUID, actual.OwnerReferences[0].UID) - assert.Equal(t, int32(2), *actual.Spec.Replicas) - }) - - t.Run("should update target allocator deployment when the container image is updated", func(t *testing.T) { - ctx := context.Background() - createObjectIfNotExists(t, "test-targetallocator", &expectedTADeploy) - orgUID := expectedTADeploy.OwnerReferences[0].UID - - updatedParam, err := newParams("test/test-img", "") - assert.NoError(t, err) - updatedDeploy := targetallocator.Deployment(updatedParam.Config, logger, updatedParam.Instance) - - err = expectedDeployments(ctx, param, []v1.Deployment{updatedDeploy}) - assert.NoError(t, err) - - actual := v1.Deployment{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-targetallocator"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, orgUID, actual.OwnerReferences[0].UID) - assert.NotEqual(t, expectedTADeploy.Spec.Template.Spec.Containers[0].Image, actual.Spec.Template.Spec.Containers[0].Image) - assert.Equal(t, int32(1), *actual.Spec.Replicas) - }) - - t.Run("should delete deployment", func(t *testing.T) { - labels := map[string]string{ - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - } - deploy := v1.Deployment{} - deploy.Name = "dummy" - deploy.Namespace = "default" - deploy.Labels = labels - deploy.Spec = v1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "dummy", - Image: "busybox", - }}, - }, - }, - } - createObjectIfNotExists(t, "dummy", &deploy) - - err := deleteDeployments(context.Background(), param, []v1.Deployment{expectedDeploy}) - assert.NoError(t, err) - - actual := v1.Deployment{} - exists, _ := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "dummy"}) - - assert.False(t, exists) - - }) - - t.Run("should not delete deployment", func(t *testing.T) { - labels := map[string]string{ - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/managed-by": "helm-opentelemetry-operator", - } - deploy := v1.Deployment{} - deploy.Name = "dummy" - deploy.Namespace = "default" - deploy.Spec = v1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "dummy", - Image: "busybox", - }}, - }, - }, - } - createObjectIfNotExists(t, "dummy", &deploy) - - err := deleteDeployments(context.Background(), param, []v1.Deployment{expectedDeploy}) - assert.NoError(t, err) - - actual := v1.Deployment{} - exists, _ := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "dummy"}) - - assert.True(t, exists) - - }) - - t.Run("change Spec.Selector should recreate deployment", func(t *testing.T) { - - oldDeploy := collector.Deployment(param.Config, logger, param.Instance) - oldDeploy.Spec.Selector.MatchLabels["app.kubernetes.io/version"] = "latest" - oldDeploy.Spec.Template.Labels["app.kubernetes.io/version"] = "latest" - oldDeploy.Name = "update-deploy" - - err := expectedDeployments(context.Background(), param, []v1.Deployment{oldDeploy}) - assert.NoError(t, err) - exists, err := populateObjectIfExists(t, &v1.Deployment{}, types.NamespacedName{Namespace: "default", Name: oldDeploy.Name}) - assert.NoError(t, err) - assert.True(t, exists) - - newDeploy := collector.Deployment(param.Config, logger, param.Instance) - newDeploy.Name = oldDeploy.Name - err = expectedDeployments(context.Background(), param, []v1.Deployment{newDeploy}) - assert.NoError(t, err) - exists, err = populateObjectIfExists(t, &v1.Deployment{}, types.NamespacedName{Namespace: "default", Name: oldDeploy.Name}) - assert.NoError(t, err) - assert.False(t, exists) - - err = expectedDeployments(context.Background(), param, []v1.Deployment{newDeploy}) - assert.NoError(t, err) - actual := v1.Deployment{} - exists, err = populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: oldDeploy.Name}) - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, newDeploy.Spec.Selector.MatchLabels, actual.Spec.Selector.MatchLabels) - }) -} - -func TestCurrentReplicasWithHPA(t *testing.T) { - minReplicas := int32(2) - maxReplicas := int32(5) - spec := v1alpha1.OpenTelemetryCollectorSpec{ - Replicas: &minReplicas, - MaxReplicas: &maxReplicas, - } - - res := currentReplicasWithHPA(spec, 10) - assert.Equal(t, int32(5), res) - - res = currentReplicasWithHPA(spec, 1) - assert.Equal(t, int32(2), res) - - res = currentReplicasWithHPA(spec, 3) - assert.Equal(t, int32(3), res) -} diff --git a/pkg/collector/reconcile/horizontalpodautoscaler.go b/pkg/collector/reconcile/horizontalpodautoscaler.go deleted file mode 100644 index 1f8435ff11..0000000000 --- a/pkg/collector/reconcile/horizontalpodautoscaler.go +++ /dev/null @@ -1,197 +0,0 @@ -// 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 reconcile - -import ( - "context" - "fmt" - - autoscalingv2 "k8s.io/api/autoscaling/v2" - autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" -) - -// +kubebuilder:rbac:groups=autoscaling,resources=horizontalpodautoscalers,verbs=get;list;watch;create;update;patch;delete - -// HorizontalPodAutoscaler reconciles HorizontalPodAutoscalers if autoscale is true and replicas is nil. -func HorizontalPodAutoscalers(ctx context.Context, params Params) error { - desired := []client.Object{} - - // check if autoscale mode is on, e.g MaxReplicas is not nil - if params.Instance.Spec.MaxReplicas != nil { - desired = append(desired, collector.HorizontalPodAutoscaler(params.Config, params.Log, params.Instance)) - } - - // first, handle the create/update parts - if err := expectedHorizontalPodAutoscalers(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the expected horizontal pod autoscalers: %w", err) - } - - // then, delete the extra objects - if err := deleteHorizontalPodAutoscalers(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the horizontal pod autoscalers: %w", err) - } - - return nil -} - -func expectedHorizontalPodAutoscalers(ctx context.Context, params Params, expected []client.Object) error { - autoscalingVersion := params.Config.AutoscalingVersion() - var existing client.Object - if autoscalingVersion == autodetect.AutoscalingVersionV2Beta2 { - existing = &autoscalingv2beta2.HorizontalPodAutoscaler{} - } else { - existing = &autoscalingv2.HorizontalPodAutoscaler{} - } - - for _, obj := range expected { - desired, _ := meta.Accessor(obj) - - if err := controllerutil.SetControllerReference(¶ms.Instance, desired, params.Scheme); err != nil { - return fmt.Errorf("failed to set controller reference: %w", err) - } - - nns := types.NamespacedName{Namespace: desired.GetNamespace(), Name: desired.GetName()} - err := params.Client.Get(ctx, nns, existing) - if k8serrors.IsNotFound(err) { - if err := params.Client.Create(ctx, obj); err != nil { - return fmt.Errorf("failed to create: %w", err) - } - params.Log.V(2).Info("created", "hpa.name", desired.GetName(), "hpa.namespace", desired.GetNamespace()) - continue - } else if err != nil { - return fmt.Errorf("failed to get %w", err) - } - - updated := existing.DeepCopyObject().(client.Object) - updated.SetOwnerReferences(desired.GetOwnerReferences()) - setAutoscalerSpec(params, autoscalingVersion, updated) - - annotations := updated.GetAnnotations() - for k, v := range desired.GetAnnotations() { - annotations[k] = v - } - updated.SetAnnotations(annotations) - labels := updated.GetLabels() - for k, v := range desired.GetLabels() { - labels[k] = v - } - updated.SetLabels(labels) - - patch := client.MergeFrom(existing) - - if err := params.Client.Patch(ctx, updated, patch); err != nil { - return fmt.Errorf("failed to apply changes: %w", err) - } - - params.Log.V(2).Info("applied", "hpa.name", desired.GetName(), "hpa.namespace", desired.GetNamespace()) - } - - return nil -} - -func setAutoscalerSpec(params Params, autoscalingVersion autodetect.AutoscalingVersion, updated client.Object) { - one := int32(1) - if params.Instance.Spec.MaxReplicas != nil { - if autoscalingVersion == autodetect.AutoscalingVersionV2Beta2 { - updated.(*autoscalingv2beta2.HorizontalPodAutoscaler).Spec.MaxReplicas = *params.Instance.Spec.MaxReplicas - if params.Instance.Spec.MinReplicas != nil { - updated.(*autoscalingv2beta2.HorizontalPodAutoscaler).Spec.MinReplicas = params.Instance.Spec.MinReplicas - } else { - updated.(*autoscalingv2beta2.HorizontalPodAutoscaler).Spec.MinReplicas = &one - } - updated.(*autoscalingv2beta2.HorizontalPodAutoscaler).Spec.Metrics[0].Resource.Target.AverageUtilization = params.Instance.Spec.Autoscaler.TargetCPUUtilization - } else { - updated.(*autoscalingv2.HorizontalPodAutoscaler).Spec.MaxReplicas = *params.Instance.Spec.MaxReplicas - if params.Instance.Spec.MinReplicas != nil { - updated.(*autoscalingv2.HorizontalPodAutoscaler).Spec.MinReplicas = params.Instance.Spec.MinReplicas - } else { - updated.(*autoscalingv2.HorizontalPodAutoscaler).Spec.MinReplicas = &one - } - updated.(*autoscalingv2.HorizontalPodAutoscaler).Spec.Metrics[0].Resource.Target.AverageUtilization = params.Instance.Spec.Autoscaler.TargetCPUUtilization - } - } -} - -func deleteHorizontalPodAutoscalers(ctx context.Context, params Params, expected []client.Object) error { - autoscalingVersion := params.Config.AutoscalingVersion() - - opts := []client.ListOption{ - client.InNamespace(params.Instance.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - - if autoscalingVersion == autodetect.AutoscalingVersionV2Beta2 { - list := &autoscalingv2beta2.HorizontalPodAutoscalerList{} - if err := params.Client.List(ctx, list, opts...); err != nil { - return fmt.Errorf("failed to list: %w", err) - } - - for i := range list.Items { - existing := list.Items[i] - del := true - for _, k := range expected { - keep := k.(*autoscalingv2beta2.HorizontalPodAutoscaler) - if keep.Name == existing.Name && keep.Namespace == existing.Namespace { - del = false - break - } - } - - if del { - if err := params.Client.Delete(ctx, &existing); err != nil { - return fmt.Errorf("failed to delete: %w", err) - } - params.Log.V(2).Info("deleted", "hpa.name", existing.Name, "hpa.namespace", existing.Namespace) - } - } - } else { - list := &autoscalingv2.HorizontalPodAutoscalerList{} - if err := params.Client.List(ctx, list, opts...); err != nil { - return fmt.Errorf("failed to list: %w", err) - } - - for i := range list.Items { - existing := list.Items[i] - del := true - for _, k := range expected { - keep := k.(*autoscalingv2.HorizontalPodAutoscaler) - if keep.Name == existing.Name && keep.Namespace == existing.Namespace { - del = false - break - } - } - - if del { - if err := params.Client.Delete(ctx, &existing); err != nil { - return fmt.Errorf("failed to delete: %w", err) - } - params.Log.V(2).Info("deleted", "hpa.name", existing.Name, "hpa.namespace", existing.Namespace) - } - } - } - - return nil -} diff --git a/pkg/collector/reconcile/horizontalpodautoscaler_test.go b/pkg/collector/reconcile/horizontalpodautoscaler_test.go deleted file mode 100644 index 3d23f29c70..0000000000 --- a/pkg/collector/reconcile/horizontalpodautoscaler_test.go +++ /dev/null @@ -1,178 +0,0 @@ -// 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 reconcile - -import ( - "context" - "fmt" - "os" - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/apps/v1" - autoscalingv2 "k8s.io/api/autoscaling/v2" - autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/autodetect" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/platform" -) - -func TestExpectedHPA(t *testing.T) { - params := paramsWithHPA(autodetect.AutoscalingVersionV2Beta2) - err := params.Config.AutoDetect() - assert.NoError(t, err) - autoscalingVersion := params.Config.AutoscalingVersion() - - expectedHPA := collector.HorizontalPodAutoscaler(params.Config, logger, params.Instance) - t.Run("should create HPA", func(t *testing.T) { - err = expectedHorizontalPodAutoscalers(context.Background(), params, []client.Object{expectedHPA}) - assert.NoError(t, err) - - exists, err := populateObjectIfExists(t, &autoscalingv2beta2.HorizontalPodAutoscaler{}, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - assert.NoError(t, err) - assert.True(t, exists) - }) - - t.Run("should update HPA", func(t *testing.T) { - minReplicas := int32(1) - maxReplicas := int32(3) - updateParms := paramsWithHPA(autodetect.AutoscalingVersionV2Beta2) - updateParms.Instance.Spec.Replicas = &minReplicas - updateParms.Instance.Spec.MaxReplicas = &maxReplicas - updatedHPA := collector.HorizontalPodAutoscaler(updateParms.Config, logger, updateParms.Instance) - - if autoscalingVersion == autodetect.AutoscalingVersionV2Beta2 { - updatedAutoscaler := *updatedHPA.(*autoscalingv2beta2.HorizontalPodAutoscaler) - createObjectIfNotExists(t, "test-collector", &updatedAutoscaler) - err := expectedHorizontalPodAutoscalers(context.Background(), updateParms, []client.Object{updatedHPA}) - assert.NoError(t, err) - - actual := autoscalingv2beta2.HorizontalPodAutoscaler{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, int32(1), *actual.Spec.MinReplicas) - assert.Equal(t, int32(3), actual.Spec.MaxReplicas) - } else { - updatedAutoscaler := *updatedHPA.(*autoscalingv2.HorizontalPodAutoscaler) - createObjectIfNotExists(t, "test-collector", &updatedAutoscaler) - err := expectedHorizontalPodAutoscalers(context.Background(), updateParms, []client.Object{updatedHPA}) - assert.NoError(t, err) - - actual := autoscalingv2.HorizontalPodAutoscaler{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, int32(1), *actual.Spec.MinReplicas) - assert.Equal(t, int32(3), actual.Spec.MaxReplicas) - } - }) - - t.Run("should delete HPA", func(t *testing.T) { - err = deleteHorizontalPodAutoscalers(context.Background(), params, []client.Object{expectedHPA}) - assert.NoError(t, err) - - actual := v1.Deployment{} - exists, _ := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collecto"}) - assert.False(t, exists) - }) -} - -func paramsWithHPA(autoscalingVersion autodetect.AutoscalingVersion) Params { - configYAML, err := os.ReadFile("../testdata/test.yaml") - if err != nil { - fmt.Printf("Error getting yaml file: %v", err) - } - - minReplicas := int32(3) - maxReplicas := int32(5) - cpuUtilization := int32(90) - - mockAutoDetector := &mockAutoDetect{ - HPAVersionFunc: func() (autodetect.AutoscalingVersion, error) { - return autoscalingVersion, nil - }, - } - configuration := config.New(config.WithAutoDetect(mockAutoDetector), config.WithCollectorImage(defaultCollectorImage), config.WithTargetAllocatorImage(defaultTaAllocationImage)) - err = configuration.AutoDetect() - if err != nil { - logger.Error(err, "configuration.autodetect failed") - } - - return Params{ - Config: configuration, - Client: k8sClient, - Instance: v1alpha1.OpenTelemetryCollector{ - TypeMeta: metav1.TypeMeta{ - Kind: "opentelemetry.io", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - UID: instanceUID, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Ports: []corev1.ServicePort{{ - Name: "web", - Port: 80, - TargetPort: intstr.IntOrString{ - Type: intstr.Int, - IntVal: 80, - }, - NodePort: 0, - }}, - Config: string(configYAML), - Replicas: &minReplicas, - MaxReplicas: &maxReplicas, - Autoscaler: &v1alpha1.AutoscalerSpec{ - TargetCPUUtilization: &cpuUtilization, - }, - }, - }, - Scheme: testScheme, - Log: logger, - Recorder: record.NewFakeRecorder(10), - } -} - -var _ autodetect.AutoDetect = (*mockAutoDetect)(nil) - -type mockAutoDetect struct { - PlatformFunc func() (platform.Platform, error) - HPAVersionFunc func() (autodetect.AutoscalingVersion, error) -} - -func (m *mockAutoDetect) HPAVersion() (autodetect.AutoscalingVersion, error) { - return m.HPAVersionFunc() -} - -func (m *mockAutoDetect) Platform() (platform.Platform, error) { - if m.PlatformFunc != nil { - return m.PlatformFunc() - } - return platform.Unknown, nil -} diff --git a/pkg/collector/reconcile/ingress.go b/pkg/collector/reconcile/ingress.go deleted file mode 100644 index 79576ebf74..0000000000 --- a/pkg/collector/reconcile/ingress.go +++ /dev/null @@ -1,242 +0,0 @@ -// 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 reconcile - -import ( - "context" - "fmt" - - corev1 "k8s.io/api/core/v1" - networkingv1 "k8s.io/api/networking/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" -) - -func desiredIngresses(_ context.Context, params Params) *networkingv1.Ingress { - if params.Instance.Spec.Ingress.Type != v1alpha1.IngressTypeNginx { - return nil - } - - config, err := adapters.ConfigFromString(params.Instance.Spec.Config) - if err != nil { - params.Log.Error(err, "couldn't extract the configuration from the context") - return nil - } - - ports, err := adapters.ConfigToReceiverPorts(params.Log, config) - if err != nil { - params.Log.Error(err, "couldn't build the ingress for this instance") - return nil - } - - if len(params.Instance.Spec.Ports) > 0 { - // we should add all the ports from the CR - // there are two cases where problems might occur: - // 1) when the port number is already being used by a receiver - // 2) same, but for the port name - // - // in the first case, we remove the port we inferred from the list - // in the second case, we rename our inferred port to something like "port-%d" - portNumbers, portNames := extractPortNumbersAndNames(params.Instance.Spec.Ports) - resultingInferredPorts := []corev1.ServicePort{} - for _, inferred := range ports { - if filtered := filterPort(params.Log, inferred, portNumbers, portNames); filtered != nil { - resultingInferredPorts = append(resultingInferredPorts, *filtered) - } - } - - ports = append(params.Instance.Spec.Ports, resultingInferredPorts...) - } - - // if we have no ports, we don't need a ingress entry - if len(ports) == 0 { - params.Log.V(1).Info( - "the instance's configuration didn't yield any ports to open, skipping ingress", - "instance.name", params.Instance.Name, - "instance.namespace", params.Instance.Namespace, - ) - return nil - } - - pathType := networkingv1.PathTypePrefix - paths := make([]networkingv1.HTTPIngressPath, len(ports)) - for i, p := range ports { - paths[i] = networkingv1.HTTPIngressPath{ - Path: "/" + p.Name, - PathType: &pathType, - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: naming.Service(params.Instance), - Port: networkingv1.ServiceBackendPort{ - // Valid names must be non-empty and no more than 15 characters long. - Name: naming.Truncate(p.Name, 15), - }, - }, - }, - } - } - - return &networkingv1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: naming.Ingress(params.Instance), - Namespace: params.Instance.Namespace, - Annotations: params.Instance.Spec.Ingress.Annotations, - Labels: map[string]string{ - "app.kubernetes.io/name": naming.Ingress(params.Instance), - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }, - }, - Spec: networkingv1.IngressSpec{ - TLS: params.Instance.Spec.Ingress.TLS, - Rules: []networkingv1.IngressRule{ - { - Host: params.Instance.Spec.Ingress.Hostname, - IngressRuleValue: networkingv1.IngressRuleValue{ - HTTP: &networkingv1.HTTPIngressRuleValue{ - Paths: paths, - }, - }, - }, - }, - }, - } -} - -// Ingresses reconciles the ingress(s) required for the instance in the current context. -func Ingresses(ctx context.Context, params Params) error { - isSupportedMode := true - if params.Instance.Spec.Mode == v1alpha1.ModeSidecar { - params.Log.V(3).Info("ingress settings are not supported in sidecar mode") - isSupportedMode = false - } - - nns := types.NamespacedName{Namespace: params.Instance.Namespace, Name: params.Instance.Name} - err := params.Client.Get(ctx, nns, &corev1.Service{}) // NOTE: check if service exists. - serviceExists := err != nil - - var desired []networkingv1.Ingress - if isSupportedMode && serviceExists { - if d := desiredIngresses(ctx, params); d != nil { - desired = append(desired, *d) - } - } - - // first, handle the create/update parts - if err := expectedIngresses(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the expected ingresses: %w", err) - } - - // then, delete the extra objects - if err := deleteIngresses(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the ingresses to be deleted: %w", err) - } - - return nil -} - -func expectedIngresses(ctx context.Context, params Params, expected []networkingv1.Ingress) error { - for _, obj := range expected { - desired := obj - - if err := controllerutil.SetControllerReference(¶ms.Instance, &desired, params.Scheme); err != nil { - return fmt.Errorf("failed to set controller reference: %w", err) - } - - existing := &networkingv1.Ingress{} - nns := types.NamespacedName{Namespace: desired.Namespace, Name: desired.Name} - err := params.Client.Get(ctx, nns, existing) - if err != nil && k8serrors.IsNotFound(err) { - if err := params.Client.Create(ctx, &desired); err != nil { - return fmt.Errorf("failed to create: %w", err) - } - params.Log.V(2).Info("created", "ingress.name", desired.Name, "ingress.namespace", desired.Namespace) - return nil - } else if err != nil { - return fmt.Errorf("failed to get: %w", err) - } - - // it exists already, merge the two if the end result isn't identical to the existing one - updated := existing.DeepCopy() - if updated.Annotations == nil { - updated.Annotations = map[string]string{} - } - if updated.Labels == nil { - updated.Labels = map[string]string{} - } - updated.ObjectMeta.OwnerReferences = desired.ObjectMeta.OwnerReferences - updated.Spec.Rules = desired.Spec.Rules - updated.Spec.TLS = desired.Spec.TLS - updated.Spec.DefaultBackend = desired.Spec.DefaultBackend - updated.Spec.IngressClassName = desired.Spec.IngressClassName - - for k, v := range desired.ObjectMeta.Annotations { - updated.ObjectMeta.Annotations[k] = v - } - for k, v := range desired.ObjectMeta.Labels { - updated.ObjectMeta.Labels[k] = v - } - - patch := client.MergeFrom(existing) - - if err := params.Client.Patch(ctx, updated, patch); err != nil { - return fmt.Errorf("failed to apply changes: %w", err) - } - - params.Log.V(2).Info("applied", "ingress.name", desired.Name, "ingress.namespace", desired.Namespace) - } - return nil -} - -func deleteIngresses(ctx context.Context, params Params, expected []networkingv1.Ingress) error { - opts := []client.ListOption{ - client.InNamespace(params.Instance.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - list := &networkingv1.IngressList{} - if err := params.Client.List(ctx, list, opts...); err != nil { - return fmt.Errorf("failed to list: %w", err) - } - - for i := range list.Items { - existing := list.Items[i] - del := true - for _, keep := range expected { - if keep.Name == existing.Name && keep.Namespace == existing.Namespace { - del = false - break - } - } - - if del { - if err := params.Client.Delete(ctx, &existing); err != nil { - return fmt.Errorf("failed to delete: %w", err) - } - params.Log.V(2).Info("deleted", "ingress.name", existing.Name, "ingress.namespace", existing.Namespace) - } - } - - return nil -} diff --git a/pkg/collector/reconcile/ingress_test.go b/pkg/collector/reconcile/ingress_test.go deleted file mode 100644 index 2c3447c7f4..0000000000 --- a/pkg/collector/reconcile/ingress_test.go +++ /dev/null @@ -1,268 +0,0 @@ -// 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 reconcile - -import ( - "context" - _ "embed" - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - networkingv1 "k8s.io/api/networking/v1" - v1 "k8s.io/api/networking/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" -) - -const test_file_ingress = "../testdata/ingress_testdata.yaml" - -func TestDesiredIngresses(t *testing.T) { - t.Run("should return nil invalid ingress type", func(t *testing.T) { - params := Params{ - Config: config.Config{}, - Client: k8sClient, - Log: logger, - Instance: v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Ingress: v1alpha1.Ingress{ - Type: v1alpha1.IngressType("unknown"), - }, - }, - }, - } - - actual := desiredIngresses(context.Background(), params) - assert.Nil(t, actual) - }) - - t.Run("should return nil unable to parse config", func(t *testing.T) { - params := Params{ - Config: config.Config{}, - Client: k8sClient, - Log: logger, - Instance: v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Config: "!!!", - Ingress: v1alpha1.Ingress{ - Type: v1alpha1.IngressTypeNginx, - }, - }, - }, - } - - actual := desiredIngresses(context.Background(), params) - assert.Nil(t, actual) - }) - - t.Run("should return nil unable to parse receiver ports", func(t *testing.T) { - params := Params{ - Config: config.Config{}, - Client: k8sClient, - Log: logger, - Instance: v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Config: "---", - Ingress: v1alpha1.Ingress{ - Type: v1alpha1.IngressTypeNginx, - }, - }, - }, - } - - actual := desiredIngresses(context.Background(), params) - assert.Nil(t, actual) - }) - - t.Run("should return nil unable to do something else", func(t *testing.T) { - var ( - ns = "test" - hostname = "example.com" - ) - - params, err := newParams("something:tag", test_file_ingress) - if err != nil { - t.Fatal(err) - } - - params.Instance.Namespace = ns - params.Instance.Spec.Ingress = v1alpha1.Ingress{ - Type: v1alpha1.IngressTypeNginx, - Hostname: hostname, - Annotations: map[string]string{"some.key": "some.value"}, - } - - got := desiredIngresses(context.Background(), params) - pathType := networkingv1.PathTypePrefix - - assert.NotEqual(t, &networkingv1.Ingress{ - ObjectMeta: metav1.ObjectMeta{ - Name: naming.Ingress(params.Instance), - Namespace: ns, - Annotations: params.Instance.Spec.Ingress.Annotations, - Labels: map[string]string{ - "app.kubernetes.io/name": naming.Ingress(params.Instance), - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }, - }, - Spec: networkingv1.IngressSpec{ - Rules: []networkingv1.IngressRule{ - { - Host: hostname, - IngressRuleValue: networkingv1.IngressRuleValue{ - HTTP: &networkingv1.HTTPIngressRuleValue{ - Paths: []networkingv1.HTTPIngressPath{ - { - Path: "/another-port", - PathType: &pathType, - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: "test-collector", - Port: networkingv1.ServiceBackendPort{ - Name: "another-port", - }, - }, - }, - }, - { - Path: "/otlp-grpc", - PathType: &pathType, - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: "test-collector", - Port: networkingv1.ServiceBackendPort{ - Name: "otlp-grpc", - }, - }, - }, - }, - { - Path: "/otlp-test-grpc", - PathType: &pathType, - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: "test-collector", - Port: networkingv1.ServiceBackendPort{ - Name: "otlp-test-grpc", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, got) - }) - -} - -func TestExpectedIngresses(t *testing.T) { - t.Run("should create and update ingress entry", func(t *testing.T) { - ctx := context.Background() - - params, err := newParams("something:tag", test_file_ingress) - if err != nil { - t.Fatal(err) - } - params.Instance.Spec.Ingress.Type = "ingress" - - err = expectedIngresses(ctx, params, []networkingv1.Ingress{*desiredIngresses(ctx, params)}) - assert.NoError(t, err) - - nns := types.NamespacedName{Namespace: "default", Name: "test-ingress"} - exists, err := populateObjectIfExists(t, &networkingv1.Ingress{}, nns) - assert.NoError(t, err) - assert.True(t, exists) - - // update fields - const expectHostname = "something-else.com" - params.Instance.Spec.Ingress.Annotations = map[string]string{"blub": "blob"} - params.Instance.Spec.Ingress.Hostname = expectHostname - - err = expectedIngresses(ctx, params, []networkingv1.Ingress{*desiredIngresses(ctx, params)}) - assert.NoError(t, err) - - got := &networkingv1.Ingress{} - err = params.Client.Get(ctx, nns, got) - assert.NoError(t, err) - - gotHostname := got.Spec.Rules[0].Host - if gotHostname != expectHostname { - t.Errorf("host name is not up-to-date. expect: %s, got: %s", expectHostname, gotHostname) - } - - if v, ok := got.Annotations["blub"]; !ok || v != "blob" { - t.Error("annotations are not up-to-date. Missing entry or value is invalid.") - } - }) -} - -func TestDeleteIngresses(t *testing.T) { - t.Run("should delete excess ingress", func(t *testing.T) { - // create - ctx := context.Background() - - myParams, err := newParams("something:tag", test_file_ingress) - if err != nil { - t.Fatal(err) - } - myParams.Instance.Spec.Ingress.Type = "ingress" - - err = expectedIngresses(ctx, myParams, []networkingv1.Ingress{*desiredIngresses(ctx, myParams)}) - assert.NoError(t, err) - - nns := types.NamespacedName{Namespace: "default", Name: "test-ingress"} - exists, err := populateObjectIfExists(t, &networkingv1.Ingress{}, nns) - assert.NoError(t, err) - assert.True(t, exists) - - // delete - if err := deleteIngresses(ctx, params(), []networkingv1.Ingress{}); err != nil { - t.Error(err) - } - - // check - exists, err = populateObjectIfExists(t, &v1.Ingress{}, nns) - assert.NoError(t, err) - assert.False(t, exists) - }) -} - -func TestIngresses(t *testing.T) { - t.Run("wrong mode", func(t *testing.T) { - ctx := context.Background() - err := Ingresses(ctx, params()) - assert.Nil(t, err) - }) - - t.Run("supported mode and service exists", func(t *testing.T) { - ctx := context.Background() - myParams := params() - err := expectedServices(context.Background(), myParams, []corev1.Service{service("test-collector", params().Instance.Spec.Ports)}) - assert.NoError(t, err) - - assert.Nil(t, Ingresses(ctx, myParams)) - }) - -} diff --git a/pkg/collector/reconcile/service.go b/pkg/collector/reconcile/service.go deleted file mode 100644 index 86a84f2002..0000000000 --- a/pkg/collector/reconcile/service.go +++ /dev/null @@ -1,315 +0,0 @@ -// 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 reconcile - -import ( - "context" - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" - "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator" -) - -// +kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete - -// Services reconciles the service(s) required for the instance in the current context. -func Services(ctx context.Context, params Params) error { - desired := []corev1.Service{} - if params.Instance.Spec.Mode != v1alpha1.ModeSidecar { - type builder func(context.Context, Params) *corev1.Service - for _, builder := range []builder{desiredService, headless, monitoringService} { - svc := builder(ctx, params) - // add only the non-nil to the list - if svc != nil { - desired = append(desired, *svc) - } - } - } - - if params.Instance.Spec.TargetAllocator.Enabled { - desired = append(desired, desiredTAService(params)) - } - - // first, handle the create/update parts - if err := expectedServices(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the expected services: %w", err) - } - - // then, delete the extra objects - if err := deleteServices(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the services to be deleted: %w", err) - } - - return nil -} - -func desiredService(ctx context.Context, params Params) *corev1.Service { - labels := collector.Labels(params.Instance, []string{}) - labels["app.kubernetes.io/name"] = naming.Service(params.Instance) - - config, err := adapters.ConfigFromString(params.Instance.Spec.Config) - if err != nil { - params.Log.Error(err, "couldn't extract the configuration from the context") - return nil - } - - ports, err := adapters.ConfigToReceiverPorts(params.Log, config) - if err != nil { - params.Log.Error(err, "couldn't build the service for this instance") - return nil - } - - if len(params.Instance.Spec.Ports) > 0 { - // we should add all the ports from the CR - // there are two cases where problems might occur: - // 1) when the port number is already being used by a receiver - // 2) same, but for the port name - // - // in the first case, we remove the port we inferred from the list - // in the second case, we rename our inferred port to something like "port-%d" - portNumbers, portNames := extractPortNumbersAndNames(params.Instance.Spec.Ports) - resultingInferredPorts := []corev1.ServicePort{} - for _, inferred := range ports { - if filtered := filterPort(params.Log, inferred, portNumbers, portNames); filtered != nil { - resultingInferredPorts = append(resultingInferredPorts, *filtered) - } - } - - ports = append(params.Instance.Spec.Ports, resultingInferredPorts...) - } - - // if we have no ports, we don't need a service - if len(ports) == 0 { - params.Log.V(1).Info("the instance's configuration didn't yield any ports to open, skipping service", "instance.name", params.Instance.Name, "instance.namespace", params.Instance.Namespace) - return nil - } - - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: naming.Service(params.Instance), - Namespace: params.Instance.Namespace, - Labels: labels, - Annotations: params.Instance.Annotations, - }, - Spec: corev1.ServiceSpec{ - Selector: collector.SelectorLabels(params.Instance), - ClusterIP: "", - Ports: ports, - }, - } -} - -func desiredTAService(params Params) corev1.Service { - labels := targetallocator.Labels(params.Instance) - labels["app.kubernetes.io/name"] = naming.TAService(params.Instance) - - selector := targetallocator.Labels(params.Instance) - selector["app.kubernetes.io/name"] = naming.TargetAllocator(params.Instance) - - return corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: naming.TAService(params.Instance), - Namespace: params.Instance.Namespace, - Labels: labels, - }, - Spec: corev1.ServiceSpec{ - Selector: selector, - Ports: []corev1.ServicePort{{ - Name: "targetallocation", - Port: 80, - TargetPort: intstr.FromInt(8080), - }}, - }, - } -} - -func headless(ctx context.Context, params Params) *corev1.Service { - h := desiredService(ctx, params) - if h == nil { - return nil - } - - h.Name = naming.HeadlessService(params.Instance) - - // copy to avoid modifying params.Instance.Annotations - annotations := map[string]string{ - "service.beta.openshift.io/serving-cert-secret-name": fmt.Sprintf("%s-tls", h.Name), - } - for k, v := range h.Annotations { - annotations[k] = v - } - h.Annotations = annotations - - h.Spec.ClusterIP = "None" - return h -} - -func monitoringService(ctx context.Context, params Params) *corev1.Service { - labels := collector.Labels(params.Instance, []string{}) - labels["app.kubernetes.io/name"] = naming.MonitoringService(params.Instance) - - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: naming.MonitoringService(params.Instance), - Namespace: params.Instance.Namespace, - Labels: labels, - Annotations: params.Instance.Annotations, - }, - Spec: corev1.ServiceSpec{ - Selector: collector.SelectorLabels(params.Instance), - ClusterIP: "", - Ports: []corev1.ServicePort{{ - Name: "monitoring", - Port: 8888, - }}, - }, - } -} - -func expectedServices(ctx context.Context, params Params, expected []corev1.Service) error { - for _, obj := range expected { - desired := obj - - if err := controllerutil.SetControllerReference(¶ms.Instance, &desired, params.Scheme); err != nil { - return fmt.Errorf("failed to set controller reference: %w", err) - } - - existing := &corev1.Service{} - nns := types.NamespacedName{Namespace: desired.Namespace, Name: desired.Name} - err := params.Client.Get(ctx, nns, existing) - if err != nil && k8serrors.IsNotFound(err) { - if err := params.Client.Create(ctx, &desired); err != nil { - return fmt.Errorf("failed to create: %w", err) - } - params.Log.V(2).Info("created", "service.name", desired.Name, "service.namespace", desired.Namespace) - continue - } else if err != nil { - return fmt.Errorf("failed to get: %w", err) - } - - // it exists already, merge the two if the end result isn't identical to the existing one - updated := existing.DeepCopy() - if updated.Annotations == nil { - updated.Annotations = map[string]string{} - } - if updated.Labels == nil { - updated.Labels = map[string]string{} - } - updated.ObjectMeta.OwnerReferences = desired.ObjectMeta.OwnerReferences - - for k, v := range desired.ObjectMeta.Annotations { - updated.ObjectMeta.Annotations[k] = v - } - for k, v := range desired.ObjectMeta.Labels { - updated.ObjectMeta.Labels[k] = v - } - updated.Spec.Ports = desired.Spec.Ports - updated.Spec.Selector = desired.Spec.Selector - - patch := client.MergeFrom(existing) - - if err := params.Client.Patch(ctx, updated, patch); err != nil { - return fmt.Errorf("failed to apply changes: %w", err) - } - - params.Log.V(2).Info("applied", "service.name", desired.Name, "service.namespace", desired.Namespace) - } - - return nil -} - -func deleteServices(ctx context.Context, params Params, expected []corev1.Service) error { - opts := []client.ListOption{ - client.InNamespace(params.Instance.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - list := &corev1.ServiceList{} - if err := params.Client.List(ctx, list, opts...); err != nil { - return fmt.Errorf("failed to list: %w", err) - } - - for i := range list.Items { - existing := list.Items[i] - del := true - for _, keep := range expected { - if keep.Name == existing.Name && keep.Namespace == existing.Namespace { - del = false - break - } - } - - if del { - if err := params.Client.Delete(ctx, &existing); err != nil { - return fmt.Errorf("failed to delete: %w", err) - } - params.Log.V(2).Info("deleted", "service.name", existing.Name, "service.namespace", existing.Namespace) - } - } - - return nil -} - -func filterPort(logger logr.Logger, candidate corev1.ServicePort, portNumbers map[int32]bool, portNames map[string]bool) *corev1.ServicePort { - if portNumbers[candidate.Port] { - return nil - } - - // do we have the port name there already? - if portNames[candidate.Name] { - // there's already a port with the same name! do we have a 'port-%d' already? - fallbackName := fmt.Sprintf("port-%d", candidate.Port) - if portNames[fallbackName] { - // that wasn't expected, better skip this port - logger.V(2).Info("a port name specified in the CR clashes with an inferred port name, and the fallback port name clashes with another port name! Skipping this port.", - "inferred-port-name", candidate.Name, - "fallback-port-name", fallbackName, - ) - return nil - } - - candidate.Name = fallbackName - return &candidate - } - - // this port is unique, return as is - return &candidate -} - -func extractPortNumbersAndNames(ports []corev1.ServicePort) (map[int32]bool, map[string]bool) { - numbers := map[int32]bool{} - names := map[string]bool{} - - for _, port := range ports { - numbers[port.Port] = true - names[port.Name] = true - } - - return numbers, names -} diff --git a/pkg/collector/reconcile/serviceaccount.go b/pkg/collector/reconcile/serviceaccount.go deleted file mode 100644 index 2aa55e50d9..0000000000 --- a/pkg/collector/reconcile/serviceaccount.go +++ /dev/null @@ -1,139 +0,0 @@ -// 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 reconcile - -import ( - "context" - "fmt" - - corev1 "k8s.io/api/core/v1" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator" -) - -// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete - -// ServiceAccounts reconciles the service account(s) required for the instance in the current context. -func ServiceAccounts(ctx context.Context, params Params) error { - desired := []corev1.ServiceAccount{} - if params.Instance.Spec.Mode != v1alpha1.ModeSidecar { - desired = append(desired, collector.ServiceAccount(params.Instance)) - } - if params.Instance.Spec.TargetAllocator.Enabled { - desired = append(desired, targetallocator.ServiceAccount(params.Instance)) - } - - // first, handle the create/update parts - if err := expectedServiceAccounts(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the expected service accounts: %w", err) - } - - // then, delete the extra objects - if err := deleteServiceAccounts(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the service accounts to be deleted: %w", err) - } - - return nil -} - -func expectedServiceAccounts(ctx context.Context, params Params, expected []corev1.ServiceAccount) error { - for _, obj := range expected { - desired := obj - - if err := controllerutil.SetControllerReference(¶ms.Instance, &desired, params.Scheme); err != nil { - return fmt.Errorf("failed to set controller reference: %w", err) - } - - existing := &corev1.ServiceAccount{} - nns := types.NamespacedName{Namespace: desired.Namespace, Name: desired.Name} - err := params.Client.Get(ctx, nns, existing) - if err != nil && k8serrors.IsNotFound(err) { - if err := params.Client.Create(ctx, &desired); err != nil { - return fmt.Errorf("failed to create: %w", err) - } - params.Log.V(2).Info("created", "serviceaccount.name", desired.Name, "serviceaccount.namespace", desired.Namespace) - continue - } else if err != nil { - return fmt.Errorf("failed to get: %w", err) - } - - // it exists already, merge the two if the end result isn't identical to the existing one - updated := existing.DeepCopy() - if updated.Annotations == nil { - updated.Annotations = map[string]string{} - } - if updated.Labels == nil { - updated.Labels = map[string]string{} - } - updated.ObjectMeta.OwnerReferences = desired.ObjectMeta.OwnerReferences - - for k, v := range desired.ObjectMeta.Annotations { - updated.ObjectMeta.Annotations[k] = v - } - for k, v := range desired.ObjectMeta.Labels { - updated.ObjectMeta.Labels[k] = v - } - - patch := client.MergeFrom(existing) - - if err := params.Client.Patch(ctx, updated, patch); err != nil { - return fmt.Errorf("failed to apply changes: %w", err) - } - - params.Log.V(2).Info("applied", "serviceaccount.name", desired.Name, "serviceaccount.namespace", desired.Namespace) - } - - return nil -} - -func deleteServiceAccounts(ctx context.Context, params Params, expected []corev1.ServiceAccount) error { - opts := []client.ListOption{ - client.InNamespace(params.Instance.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - list := &corev1.ServiceAccountList{} - if err := params.Client.List(ctx, list, opts...); err != nil { - return fmt.Errorf("failed to list: %w", err) - } - - for i := range list.Items { - existing := list.Items[i] - del := true - for _, keep := range expected { - if keep.Name == existing.Name && keep.Namespace == existing.Namespace { - del = false - break - } - } - - if del { - if err := params.Client.Delete(ctx, &existing); err != nil { - return fmt.Errorf("failed to delete: %w", err) - } - params.Log.V(2).Info("deleted", "serviceaccount.name", existing.Name, "serviceaccount.namespace", existing.Namespace) - } - } - - return nil -} diff --git a/pkg/collector/reconcile/serviceaccount_test.go b/pkg/collector/reconcile/serviceaccount_test.go deleted file mode 100644 index db310237c0..0000000000 --- a/pkg/collector/reconcile/serviceaccount_test.go +++ /dev/null @@ -1,119 +0,0 @@ -// 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 reconcile - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator" -) - -func TestExpectedServiceAccounts(t *testing.T) { - t.Run("should create multiple service accounts", func(t *testing.T) { - desired := collector.ServiceAccount(params().Instance) - allocatorDesired := targetallocator.ServiceAccount(params().Instance) - err := expectedServiceAccounts(context.Background(), params(), []v1.ServiceAccount{desired, allocatorDesired}) - assert.NoError(t, err) - - exists, err := populateObjectIfExists(t, &v1.ServiceAccount{}, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - assert.NoError(t, err) - assert.True(t, exists) - - allocatorExists, err := populateObjectIfExists(t, &v1.ServiceAccount{}, types.NamespacedName{Namespace: "default", Name: "test-targetallocator"}) - assert.NoError(t, err) - assert.True(t, allocatorExists) - - }) - - t.Run("should update existing service account", func(t *testing.T) { - existing := v1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-collector", - Namespace: "default", - }, - } - createObjectIfNotExists(t, "test-collector", &existing) - exists, err := populateObjectIfExists(t, &v1.ServiceAccount{}, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - assert.NoError(t, err) - assert.True(t, exists) - - err = expectedServiceAccounts(context.Background(), params(), []v1.ServiceAccount{collector.ServiceAccount(params().Instance)}) - assert.NoError(t, err) - - actual := v1.ServiceAccount{} - _, err = populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - assert.NoError(t, err) - assert.Equal(t, instanceUID, actual.OwnerReferences[0].UID) - }) -} - -func TestDeleteServiceAccounts(t *testing.T) { - t.Run("should delete the managed service account", func(t *testing.T) { - existing := v1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-delete-collector", - Namespace: "default", - Labels: map[string]string{ - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }, - }, - } - createObjectIfNotExists(t, "test-delete-collector", &existing) - exists, err := populateObjectIfExists(t, &v1.ServiceAccount{}, types.NamespacedName{Namespace: "default", Name: "test-delete-collector"}) - assert.NoError(t, err) - assert.True(t, exists) - - err = deleteServiceAccounts(context.Background(), params(), []v1.ServiceAccount{collector.ServiceAccount(params().Instance)}) - assert.NoError(t, err) - - exists, err = populateObjectIfExists(t, &v1.ServiceAccount{}, types.NamespacedName{Namespace: "default", Name: "test-delete-collector"}) - assert.NoError(t, err) - assert.False(t, exists) - - }) - t.Run("should not delete unrelated service account", func(t *testing.T) { - existing := v1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-delete-collector", - Namespace: "default", - Labels: map[string]string{ - "app.kubernetes.io/instance": "default.testing", - "app.kubernetes.io/managed-by": "helm-opentelemetry", - }, - }, - } - createObjectIfNotExists(t, "test-delete-collector", &existing) - exists, err := populateObjectIfExists(t, &v1.ServiceAccount{}, types.NamespacedName{Namespace: "default", Name: "test-delete-collector"}) - assert.NoError(t, err) - assert.True(t, exists) - - err = deleteServiceAccounts(context.Background(), params(), []v1.ServiceAccount{collector.ServiceAccount(params().Instance)}) - assert.NoError(t, err) - - exists, err = populateObjectIfExists(t, &v1.ServiceAccount{}, types.NamespacedName{Namespace: "default", Name: "test-delete-collector"}) - assert.NoError(t, err) - assert.True(t, exists) - - }) - -} diff --git a/pkg/collector/reconcile/statefulset.go b/pkg/collector/reconcile/statefulset.go deleted file mode 100644 index e663734329..0000000000 --- a/pkg/collector/reconcile/statefulset.go +++ /dev/null @@ -1,147 +0,0 @@ -// 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 reconcile - -import ( - "context" - "fmt" - - appsv1 "k8s.io/api/apps/v1" - apiequality "k8s.io/apimachinery/pkg/api/equality" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" -) - -// +kubebuilder:rbac:groups="apps",resources=statefulsets,verbs=get;list;watch;create;update;patch;delete - -// StatefulSets reconciles the stateful set(s) required for the instance in the current context. -func StatefulSets(ctx context.Context, params Params) error { - - desired := []appsv1.StatefulSet{} - if params.Instance.Spec.Mode == "statefulset" { - desired = append(desired, collector.StatefulSet(params.Config, params.Log, params.Instance)) - } - - // first, handle the create/update parts - if err := expectedStatefulSets(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the expected stateful sets: %w", err) - } - - // then, delete the extra objects - if err := deleteStatefulSets(ctx, params, desired); err != nil { - return fmt.Errorf("failed to reconcile the stateful sets to be deleted: %w", err) - } - - return nil -} - -func expectedStatefulSets(ctx context.Context, params Params, expected []appsv1.StatefulSet) error { - for _, obj := range expected { - desired := obj - - if err := controllerutil.SetControllerReference(¶ms.Instance, &desired, params.Scheme); err != nil { - return fmt.Errorf("failed to set controller reference: %w", err) - } - - existing := &appsv1.StatefulSet{} - nns := types.NamespacedName{Namespace: desired.Namespace, Name: desired.Name} - err := params.Client.Get(ctx, nns, existing) - if err != nil && k8serrors.IsNotFound(err) { - if err := params.Client.Create(ctx, &desired); err != nil { - return fmt.Errorf("failed to create: %w", err) - } - params.Log.V(2).Info("created", "statefulset.name", desired.Name, "statefulset.namespace", desired.Namespace) - continue - } else if err != nil { - return fmt.Errorf("failed to get: %w", err) - } - - // Selector is an immutable field, if set, we cannot modify it otherwise we will face reconciliation error. - if !apiequality.Semantic.DeepEqual(desired.Spec.Selector, existing.Spec.Selector) { - params.Log.V(2).Info("Spec.Selector change detected, trying to delete, the new collector statfulset will be created in the next reconcile cycle", "statefulset.name", existing.Name, "statefulset.namespace", existing.Namespace) - - if err := params.Client.Delete(ctx, existing); err != nil { - return fmt.Errorf("failed to delete statefulset: %w", err) - } - continue - } - - // it exists already, merge the two if the end result isn't identical to the existing one - updated := existing.DeepCopy() - if updated.Annotations == nil { - updated.Annotations = map[string]string{} - } - if updated.Labels == nil { - updated.Labels = map[string]string{} - } - - updated.Spec = desired.Spec - updated.ObjectMeta.OwnerReferences = desired.ObjectMeta.OwnerReferences - - for k, v := range desired.ObjectMeta.Annotations { - updated.ObjectMeta.Annotations[k] = v - } - for k, v := range desired.ObjectMeta.Labels { - updated.ObjectMeta.Labels[k] = v - } - - patch := client.MergeFrom(existing) - if err := params.Client.Patch(ctx, updated, patch); err != nil { - return fmt.Errorf("failed to apply changes: %w", err) - } - - params.Log.V(2).Info("applied", "statefulset.name", desired.Name, "statefulset.namespace", desired.Namespace) - } - - return nil -} - -func deleteStatefulSets(ctx context.Context, params Params, expected []appsv1.StatefulSet) error { - opts := []client.ListOption{ - client.InNamespace(params.Instance.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", params.Instance.Namespace, params.Instance.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - list := &appsv1.StatefulSetList{} - if err := params.Client.List(ctx, list, opts...); err != nil { - return fmt.Errorf("failed to list: %w", err) - } - - for i := range list.Items { - existing := list.Items[i] - del := true - for _, keep := range expected { - if keep.Name == existing.Name && keep.Namespace == existing.Namespace { - del = false - break - } - } - - if del { - if err := params.Client.Delete(ctx, &existing); err != nil { - return fmt.Errorf("failed to delete: %w", err) - } - params.Log.V(2).Info("deleted", "statefulset.name", existing.Name, "statefulset.namespace", existing.Namespace) - } - } - - return nil -} diff --git a/pkg/collector/reconcile/statefulset_test.go b/pkg/collector/reconcile/statefulset_test.go deleted file mode 100644 index bf83534295..0000000000 --- a/pkg/collector/reconcile/statefulset_test.go +++ /dev/null @@ -1,165 +0,0 @@ -// 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 reconcile - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" -) - -func TestExpectedStatefulsets(t *testing.T) { - param := params() - expectedSs := collector.StatefulSet(param.Config, logger, param.Instance) - - t.Run("should create StatefulSet", func(t *testing.T) { - err := expectedStatefulSets(context.Background(), param, []v1.StatefulSet{expectedSs}) - assert.NoError(t, err) - - actual := v1.StatefulSet{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, int32(2), *actual.Spec.Replicas) - - }) - t.Run("should update statefulset", func(t *testing.T) { - createObjectIfNotExists(t, "test-collector", &expectedSs) - err := expectedStatefulSets(context.Background(), param, []v1.StatefulSet{expectedSs}) - assert.NoError(t, err) - - actual := v1.StatefulSet{} - exists, err := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "test-collector"}) - - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, instanceUID, actual.OwnerReferences[0].UID) - }) - - t.Run("should delete statefulset", func(t *testing.T) { - - labels := map[string]string{ - "app.kubernetes.io/instance": "default.test", - "app.kubernetes.io/managed-by": "opentelemetry-operator", - } - ds := v1.StatefulSet{} - ds.Name = "dummy" - ds.Namespace = "default" - ds.Labels = labels - ds.Spec = v1.StatefulSetSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "dummy", - Image: "busybox", - }}, - }, - }, - } - - createObjectIfNotExists(t, "dummy", &ds) - - err := deleteStatefulSets(context.Background(), param, []v1.StatefulSet{expectedSs}) - assert.NoError(t, err) - - actual := v1.StatefulSet{} - exists, _ := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "dummy"}) - - assert.False(t, exists) - - }) - - t.Run("should not delete statefulset", func(t *testing.T) { - - labels := map[string]string{ - "app.kubernetes.io/managed-by": "helm-opentelemetry-operator", - } - ds := v1.StatefulSet{} - ds.Name = "dummy" - ds.Namespace = "default" - ds.Labels = labels - ds.Spec = v1.StatefulSetSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "dummy", - Image: "busybox", - }}, - }, - }, - } - - createObjectIfNotExists(t, "dummy", &ds) - - err := deleteStatefulSets(context.Background(), param, []v1.StatefulSet{expectedSs}) - assert.NoError(t, err) - - actual := v1.StatefulSet{} - exists, _ := populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: "dummy"}) - - assert.True(t, exists) - - }) - - t.Run("change Spec.Selector should recreate statefulset", func(t *testing.T) { - - oldSs := collector.StatefulSet(param.Config, logger, param.Instance) - oldSs.Spec.Selector.MatchLabels["app.kubernetes.io/version"] = "latest" - oldSs.Spec.Template.Labels["app.kubernetes.io/version"] = "latest" - oldSs.Name = "update-ss" - - err := expectedStatefulSets(context.Background(), param, []v1.StatefulSet{oldSs}) - assert.NoError(t, err) - exists, err := populateObjectIfExists(t, &v1.StatefulSet{}, types.NamespacedName{Namespace: "default", Name: oldSs.Name}) - assert.NoError(t, err) - assert.True(t, exists) - - newSs := collector.StatefulSet(param.Config, logger, param.Instance) - newSs.Name = oldSs.Name - err = expectedStatefulSets(context.Background(), param, []v1.StatefulSet{newSs}) - assert.NoError(t, err) - exists, err = populateObjectIfExists(t, &v1.StatefulSet{}, types.NamespacedName{Namespace: "default", Name: oldSs.Name}) - assert.NoError(t, err) - assert.False(t, exists) - - err = expectedStatefulSets(context.Background(), param, []v1.StatefulSet{newSs}) - assert.NoError(t, err) - actual := v1.StatefulSet{} - exists, err = populateObjectIfExists(t, &actual, types.NamespacedName{Namespace: "default", Name: oldSs.Name}) - assert.NoError(t, err) - assert.True(t, exists) - assert.Equal(t, newSs.Spec.Selector.MatchLabels, actual.Spec.Selector.MatchLabels) - }) -} diff --git a/pkg/collector/reconcile/suite_test.go b/pkg/collector/reconcile/suite_test.go deleted file mode 100644 index c1640d6fe3..0000000000 --- a/pkg/collector/reconcile/suite_test.go +++ /dev/null @@ -1,277 +0,0 @@ -// 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 reconcile - -import ( - "context" - "crypto/tls" - "fmt" - "net" - "os" - "path/filepath" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/uuid" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/retry" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" -) - -var ( - k8sClient client.Client - testEnv *envtest.Environment - testScheme *runtime.Scheme = scheme.Scheme - ctx context.Context - cancel context.CancelFunc - - logger = logf.Log.WithName("unit-tests") - - instanceUID = uuid.NewUUID() -) - -const ( - defaultCollectorImage = "default-collector" - defaultTaAllocationImage = "default-ta-allocator" -) - -func TestMain(m *testing.M) { - ctx, cancel = context.WithCancel(context.TODO()) - defer cancel() - - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, - WebhookInstallOptions: envtest.WebhookInstallOptions{ - Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")}, - }, - } - cfg, err := testEnv.Start() - if err != nil { - fmt.Printf("failed to start testEnv: %v", err) - os.Exit(1) - } - - if err := v1alpha1.AddToScheme(testScheme); err != nil { - fmt.Printf("failed to register scheme: %v", err) - os.Exit(1) - } - // +kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme}) - if err != nil { - fmt.Printf("failed to setup a Kubernetes client: %v", err) - os.Exit(1) - } - - // start webhook server using Manager - webhookInstallOptions := &testEnv.WebhookInstallOptions - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: testScheme, - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - LeaderElection: false, - MetricsBindAddress: "0", - }) - if err != nil { - fmt.Printf("failed to start webhook server: %v", err) - os.Exit(1) - } - - if err := (&v1alpha1.OpenTelemetryCollector{}).SetupWebhookWithManager(mgr); err != nil { - fmt.Printf("failed to SetupWebhookWithManager: %v", err) - os.Exit(1) - } - - ctx, cancel = context.WithCancel(context.TODO()) - defer cancel() - go func() { - if err = mgr.Start(ctx); err != nil { - fmt.Printf("failed to start manager: %v", err) - os.Exit(1) - } - }() - - // wait for the webhook server to get ready - wg := &sync.WaitGroup{} - wg.Add(1) - dialer := &net.Dialer{Timeout: time.Second} - addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) - go func(wg *sync.WaitGroup) { - defer wg.Done() - if err = retry.OnError(wait.Backoff{ - Steps: 20, - Duration: 10 * time.Millisecond, - Factor: 1.5, - Jitter: 0.1, - Cap: time.Second * 30, - }, func(error) bool { - return true - }, func() error { - // #nosec G402 - conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) - if err != nil { - return err - } - _ = conn.Close() - return nil - }); err != nil { - fmt.Printf("failed to wait for webhook server to be ready: %v", err) - os.Exit(1) - } - }(wg) - wg.Wait() - - code := m.Run() - - err = testEnv.Stop() - if err != nil { - fmt.Printf("failed to stop testEnv: %v", err) - os.Exit(1) - } - - os.Exit(code) -} - -func params() Params { - replicas := int32(2) - configYAML, err := os.ReadFile("../testdata/test.yaml") - if err != nil { - fmt.Printf("Error getting yaml file: %v", err) - } - return Params{ - Config: config.New(config.WithCollectorImage(defaultCollectorImage), config.WithTargetAllocatorImage(defaultTaAllocationImage)), - Client: k8sClient, - Instance: v1alpha1.OpenTelemetryCollector{ - TypeMeta: metav1.TypeMeta{ - Kind: "opentelemetry.io", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - UID: instanceUID, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Image: "ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.47.0", - Ports: []v1.ServicePort{{ - Name: "web", - Port: 80, - TargetPort: intstr.IntOrString{ - Type: intstr.Int, - IntVal: 80, - }, - NodePort: 0, - }}, - Replicas: &replicas, - Config: string(configYAML), - }, - }, - Scheme: testScheme, - Log: logger, - Recorder: record.NewFakeRecorder(10), - } -} - -func newParams(taContainerImage string, file string) (Params, error) { - replicas := int32(1) - var configYAML []byte - var err error - - if file == "" { - configYAML, err = os.ReadFile("../testdata/test.yaml") - } else { - configYAML, err = os.ReadFile(file) - } - if err != nil { - return Params{}, fmt.Errorf("Error getting yaml file: %w", err) - } - - cfg := config.New(config.WithCollectorImage(defaultCollectorImage), config.WithTargetAllocatorImage(defaultTaAllocationImage)) - - return Params{ - Config: cfg, - Client: k8sClient, - Instance: v1alpha1.OpenTelemetryCollector{ - TypeMeta: metav1.TypeMeta{ - Kind: "opentelemetry.io", - APIVersion: "v1", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test", - Namespace: "default", - UID: instanceUID, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeStatefulSet, - Ports: []v1.ServicePort{{ - Name: "web", - Port: 80, - TargetPort: intstr.IntOrString{ - Type: intstr.Int, - IntVal: 80, - }, - NodePort: 0, - }}, - TargetAllocator: v1alpha1.OpenTelemetryTargetAllocator{ - Enabled: true, - Image: taContainerImage, - }, - Replicas: &replicas, - Config: string(configYAML), - }, - }, - Scheme: testScheme, - Log: logger, - }, nil -} - -func createObjectIfNotExists(tb testing.TB, name string, object client.Object) { - tb.Helper() - err := k8sClient.Get(context.Background(), client.ObjectKey{Namespace: "default", Name: name}, object) - if errors.IsNotFound(err) { - err := k8sClient.Create(context.Background(), object) - assert.NoError(tb, err) - } -} - -func populateObjectIfExists(t testing.TB, object client.Object, namespacedName types.NamespacedName) (bool, error) { - t.Helper() - err := k8sClient.Get(context.Background(), namespacedName, object) - if errors.IsNotFound(err) { - return false, nil - } - if err != nil { - return false, err - } - return true, nil - -} diff --git a/pkg/collector/statefulset.go b/pkg/collector/statefulset.go deleted file mode 100644 index 464f77caf5..0000000000 --- a/pkg/collector/statefulset.go +++ /dev/null @@ -1,70 +0,0 @@ -// 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 collector - -import ( - "github.com/go-logr/logr" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" -) - -// StatefulSet builds the statefulset for the given instance. -func StatefulSet(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) appsv1.StatefulSet { - labels := Labels(otelcol, cfg.LabelsFilter()) - labels["app.kubernetes.io/name"] = naming.Collector(otelcol) - - annotations := Annotations(otelcol) - podAnnotations := PodAnnotations(otelcol) - - return appsv1.StatefulSet{ - ObjectMeta: metav1.ObjectMeta{ - Name: naming.Collector(otelcol), - Namespace: otelcol.Namespace, - Labels: labels, - Annotations: annotations, - }, - Spec: appsv1.StatefulSetSpec{ - ServiceName: naming.Service(otelcol), - Selector: &metav1.LabelSelector{ - MatchLabels: SelectorLabels(otelcol), - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - Annotations: podAnnotations, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: ServiceAccountName(otelcol), - Containers: []corev1.Container{Container(cfg, logger, otelcol)}, - Volumes: Volumes(cfg, otelcol), - DNSPolicy: getDnsPolicy(otelcol), - HostNetwork: otelcol.Spec.HostNetwork, - Tolerations: otelcol.Spec.Tolerations, - NodeSelector: otelcol.Spec.NodeSelector, - SecurityContext: otelcol.Spec.PodSecurityContext, - PriorityClassName: otelcol.Spec.PriorityClassName, - }, - }, - Replicas: otelcol.Spec.Replicas, - PodManagementPolicy: "Parallel", - VolumeClaimTemplates: VolumeClaimTemplates(cfg, otelcol), - }, - } -} diff --git a/pkg/collector/upgrade/suite_test.go b/pkg/collector/upgrade/suite_test.go index 84b3e43e7c..9496b9f47d 100644 --- a/pkg/collector/upgrade/suite_test.go +++ b/pkg/collector/upgrade/suite_test.go @@ -28,12 +28,16 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" "k8s.io/client-go/util/retry" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" // +kubebuilder:scaffold:imports ) @@ -43,6 +47,9 @@ var ( testScheme *runtime.Scheme = scheme.Scheme ctx context.Context cancel context.CancelFunc + err error + cfg *rest.Config + conf = config.New() ) func TestMain(m *testing.M) { @@ -56,13 +63,13 @@ func TestMain(m *testing.M) { }, } - cfg, err := testEnv.Start() + cfg, err = testEnv.Start() if err != nil { fmt.Printf("failed to start testEnv: %v", err) os.Exit(1) } - if err := v1alpha1.AddToScheme(testScheme); err != nil { + if err = v1alpha1.AddToScheme(testScheme); err != nil { fmt.Printf("failed to register scheme: %v", err) os.Exit(1) } @@ -76,20 +83,24 @@ func TestMain(m *testing.M) { // start webhook server using Manager webhookInstallOptions := &testEnv.WebhookInstallOptions - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: testScheme, - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - LeaderElection: false, - MetricsBindAddress: "0", + mgr, mgrErr := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: testScheme, + LeaderElection: false, + WebhookServer: webhook.NewServer(webhook.Options{ + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), + Metrics: metricsserver.Options{ + BindAddress: "0", + }, }) - if err != nil { - fmt.Printf("failed to start webhook server: %v", err) + if mgrErr != nil { + fmt.Printf("failed to start webhook server: %v", mgrErr) os.Exit(1) } - if err := (&v1alpha1.OpenTelemetryCollector{}).SetupWebhookWithManager(mgr); err != nil { + if err = v1alpha1.SetupCollectorWebhook(mgr, conf); err != nil { fmt.Printf("failed to SetupWebhookWithManager: %v", err) os.Exit(1) } @@ -120,9 +131,9 @@ func TestMain(m *testing.M) { return true }, func() error { // #nosec G402 - conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) - if err != nil { - return err + conn, tlsErr := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) + if tlsErr != nil { + return tlsErr } _ = conn.Close() return nil diff --git a/pkg/collector/upgrade/upgrade.go b/pkg/collector/upgrade/upgrade.go index 7d6dae3bf2..1fed413c5a 100644 --- a/pkg/collector/upgrade/upgrade.go +++ b/pkg/collector/upgrade/upgrade.go @@ -55,6 +55,12 @@ func (u VersionUpgrade) ManagedInstances(ctx context.Context) error { for i := range list.Items { original := list.Items[i] itemLogger := u.Log.WithValues("name", original.Name, "namespace", original.Namespace) + + if original.Spec.ManagementState == v1alpha1.ManagementStateUnmanaged { + itemLogger.Info("skipping upgrade because instance is not managed") + continue + } + if original.Spec.UpgradeStrategy == v1alpha1.UpgradeStrategyNone { itemLogger.Info("skipping instance upgrade due to UpgradeStrategy") continue @@ -108,7 +114,20 @@ func (u VersionUpgrade) ManagedInstance(ctx context.Context, otelcol v1alpha1.Op } if instanceV.GreaterThan(&Latest.Version) { - u.Log.Info("skipping upgrade for OpenTelemetry Collector instance, as it's newer than our latest version", "name", otelcol.Name, "namespace", otelcol.Namespace, "version", otelcol.Status.Version, "latest", Latest.Version.String()) + // Update with the latest known version, which is what we have from versions.txt + u.Log.Info("no upgrade routines are needed for the OpenTelemetry instance", "name", otelcol.Name, "namespace", otelcol.Namespace, "version", otelcol.Status.Version, "latest", Latest.Version.String()) + + otelColV, err := semver.NewVersion(u.Version.OpenTelemetryCollector) + if err != nil { + return otelcol, err + } + if instanceV.LessThan(otelColV) { + u.Log.Info("upgraded OpenTelemetry Collector version", "name", otelcol.Name, "namespace", otelcol.Namespace, "version", otelcol.Status.Version) + otelcol.Status.Version = u.Version.OpenTelemetryCollector + } else { + u.Log.Info("skipping upgrade for OpenTelemetry Collector instance", "name", otelcol.Name, "namespace", otelcol.Namespace) + } + return otelcol, nil } @@ -126,8 +145,7 @@ func (u VersionUpgrade) ManagedInstance(ctx context.Context, otelcol v1alpha1.Op otelcol = *upgraded } } - - // at the end of the process, we are up to date with the latest known version, which is what we have from versions.txt + // Update with the latest known version, which is what we have from versions.txt otelcol.Status.Version = u.Version.OpenTelemetryCollector u.Log.V(1).Info("final version", "name", otelcol.Name, "namespace", otelcol.Namespace, "version", otelcol.Status.Version) diff --git a/pkg/collector/upgrade/upgrade_test.go b/pkg/collector/upgrade/upgrade_test.go index 9b03382b0d..500bf3068d 100644 --- a/pkg/collector/upgrade/upgrade_test.go +++ b/pkg/collector/upgrade/upgrade_test.go @@ -48,7 +48,7 @@ func TestShouldUpgradeAllToLatestBasedOnUpgradeStrategy(t *testing.T) { t.Run("spec.UpgradeStrategy = "+string(tt.strategy), func(t *testing.T) { // prepare nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - existing := makeOtelcol(nsn) + existing := makeOtelcol(nsn, v1alpha1.ManagementStateManaged) err := k8sClient.Create(context.Background(), &existing) require.NoError(t, err) @@ -84,42 +84,56 @@ func TestShouldUpgradeAllToLatestBasedOnUpgradeStrategy(t *testing.T) { } func TestUpgradeUpToLatestKnownVersion(t *testing.T) { - // prepare - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - existing := makeOtelcol(nsn) - existing.Status.Version = "0.8.0" + for _, tt := range []struct { + desc string + v string + expectedV string + }{ + {"upgrade-routine", "0.8.0", "0.10.0"}, // we don't have a 0.10.0 upgrade, but we have a 0.9.0 + {"no-upgrade-routine", "0.61.1", "0.62.0"}, // No upgrade routines between these two versions + } { + t.Run(tt.desc, func(t *testing.T) { + // prepare + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} + existing := makeOtelcol(nsn, v1alpha1.ManagementStateManaged) + existing.Status.Version = tt.v - currentV := version.Get() - currentV.OpenTelemetryCollector = "0.10.0" // we don't have a 0.10.0 upgrade, but we have a 0.9.0 - up := &upgrade.VersionUpgrade{ - Log: logger, - Version: currentV, - Client: k8sClient, - Recorder: record.NewFakeRecorder(upgrade.RecordBufferSize), - } - // test - res, err := up.ManagedInstance(context.Background(), existing) + currentV := version.Get() + currentV.OpenTelemetryCollector = tt.expectedV + up := &upgrade.VersionUpgrade{ + Log: logger, + Version: currentV, + Client: k8sClient, + Recorder: record.NewFakeRecorder(upgrade.RecordBufferSize), + } + // test + res, err := up.ManagedInstance(context.Background(), existing) - // verify - assert.NoError(t, err) - assert.Equal(t, "0.10.0", res.Status.Version) + // verify + assert.NoError(t, err) + assert.Equal(t, tt.expectedV, res.Status.Version) + }) + } } func TestVersionsShouldNotBeChanged(t *testing.T) { + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} for _, tt := range []struct { desc string v string expectedV string failureExpected bool + managementState v1alpha1.ManagementStateType }{ - {"new-instance", "", "", false}, - {"newer-than-our-newest", "100.0.0", "100.0.0", false}, - {"unparseable", "unparseable", "unparseable", true}, + {"new-instance", "", "", false, v1alpha1.ManagementStateManaged}, + {"newer-than-our-newest", "100.0.0", "100.0.0", false, v1alpha1.ManagementStateManaged}, + {"unparseable", "unparseable", "unparseable", true, v1alpha1.ManagementStateManaged}, + // Ignore unmanaged instances + {"unmanaged-instance", "1.0.0", "1.0.0", false, v1alpha1.ManagementStateUnmanaged}, } { t.Run(tt.desc, func(t *testing.T) { // prepare - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - existing := makeOtelcol(nsn) + existing := makeOtelcol(nsn, tt.managementState) existing.Status.Version = tt.v currentV := version.Get() @@ -146,8 +160,11 @@ func TestVersionsShouldNotBeChanged(t *testing.T) { } } -func makeOtelcol(nsn types.NamespacedName) v1alpha1.OpenTelemetryCollector { +func makeOtelcol(nsn types.NamespacedName, managementState v1alpha1.ManagementStateType) v1alpha1.OpenTelemetryCollector { return v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + ManagementState: managementState, + }, ObjectMeta: metav1.ObjectMeta{ Name: nsn.Name, Namespace: nsn.Namespace, diff --git a/pkg/collector/upgrade/v0_19_0.go b/pkg/collector/upgrade/v0_19_0.go index ff4f2cf7b0..40969c7060 100644 --- a/pkg/collector/upgrade/v0_19_0.go +++ b/pkg/collector/upgrade/v0_19_0.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/collector/upgrade/v0_19_0_test.go b/pkg/collector/upgrade/v0_19_0_test.go index fd33aaf755..e599469dc7 100644 --- a/pkg/collector/upgrade/v0_19_0_test.go +++ b/pkg/collector/upgrade/v0_19_0_test.go @@ -25,8 +25,8 @@ import ( "k8s.io/client-go/tools/record" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" "github.com/open-telemetry/opentelemetry-operator/internal/version" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" "github.com/open-telemetry/opentelemetry-operator/pkg/collector/upgrade" ) diff --git a/pkg/collector/upgrade/v0_24_0.go b/pkg/collector/upgrade/v0_24_0.go index cdfdee0a37..41c2777ba5 100644 --- a/pkg/collector/upgrade/v0_24_0.go +++ b/pkg/collector/upgrade/v0_24_0.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/collector/upgrade/v0_31_0.go b/pkg/collector/upgrade/v0_31_0.go index 58b7fb1f97..fc9b28514f 100644 --- a/pkg/collector/upgrade/v0_31_0.go +++ b/pkg/collector/upgrade/v0_31_0.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/collector/upgrade/v0_36_0.go b/pkg/collector/upgrade/v0_36_0.go index 2460154ebd..8cdce1f7e9 100644 --- a/pkg/collector/upgrade/v0_36_0.go +++ b/pkg/collector/upgrade/v0_36_0.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" corev1 "k8s.io/api/core/v1" ) @@ -49,32 +49,32 @@ func upgrade0_36_0(u VersionUpgrade, otelcol *v1alpha1.OpenTelemetryCollector) ( // Change tls config key from tls_settings to tls in otlp.protocols.grpc if strings.HasPrefix(k1.(string), "otlp") { - otlpConfig, ok := v1.(map[interface{}]interface{}) - if !ok { + otlpConfig, withOTLP := v1.(map[interface{}]interface{}) + if !withOTLP { // no otlpConfig? no need to fail because of that return otelcol, nil } for k2, v2 := range otlpConfig { // protocols config if k2 == "protocols" { - protocConfig, ok := v2.(map[interface{}]interface{}) - if !ok { + protocConfig, withProtocConfig := v2.(map[interface{}]interface{}) + if !withProtocConfig { // no protocolConfig? no need to fail because of that return otelcol, nil } for k3, v3 := range protocConfig { // grpc config if k3 == "grpc" || k3 == "http" { - grpcHttpConfig, ok := v3.(map[interface{}]interface{}) - if !ok { - // no grpcHttpConfig? no need to fail because of that + grpcHTTPConfig, withHTTPConfig := v3.(map[interface{}]interface{}) + if !withHTTPConfig { + // no grpcHTTPConfig? no need to fail because of that return otelcol, nil } - for k4, v4 := range grpcHttpConfig { + for k4, v4 := range grpcHTTPConfig { // change tls_settings to tls if k4.(string) == "tls_settings" { - grpcHttpConfig["tls"] = v4 - delete(grpcHttpConfig, "tls_settings") + grpcHTTPConfig["tls"] = v4 + delete(grpcHTTPConfig, "tls_settings") existing := &corev1.ConfigMap{} updated := existing.DeepCopy() u.Recorder.Event(updated, "Normal", "Upgrade", fmt.Sprintf("upgrade to v0.36.0 has changed the tls_settings field name to tls in %s protocol of %s receiver", k3, k1)) diff --git a/pkg/collector/upgrade/v0_38_0.go b/pkg/collector/upgrade/v0_38_0.go index 3dbc6875c3..901cb40c17 100644 --- a/pkg/collector/upgrade/v0_38_0.go +++ b/pkg/collector/upgrade/v0_38_0.go @@ -23,7 +23,7 @@ import ( "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/collector/upgrade/v0_39_0.go b/pkg/collector/upgrade/v0_39_0.go index 10bc0196a4..fcbc4996cc 100644 --- a/pkg/collector/upgrade/v0_39_0.go +++ b/pkg/collector/upgrade/v0_39_0.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/collector/upgrade/v0_41_0.go b/pkg/collector/upgrade/v0_41_0.go index aafc6d1e38..634b7818e3 100644 --- a/pkg/collector/upgrade/v0_41_0.go +++ b/pkg/collector/upgrade/v0_41_0.go @@ -19,7 +19,7 @@ import ( "strings" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/collector/upgrade/v0_43_0.go b/pkg/collector/upgrade/v0_43_0.go index 7a304d5471..f9ef7887a2 100644 --- a/pkg/collector/upgrade/v0_43_0.go +++ b/pkg/collector/upgrade/v0_43_0.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/collector/upgrade/v0_56_0.go b/pkg/collector/upgrade/v0_56_0.go index 9103a3d500..16ac0080d7 100644 --- a/pkg/collector/upgrade/v0_56_0.go +++ b/pkg/collector/upgrade/v0_56_0.go @@ -22,7 +22,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) func upgrade0_56_0(u VersionUpgrade, otelcol *v1alpha1.OpenTelemetryCollector) (*v1alpha1.OpenTelemetryCollector, error) { diff --git a/pkg/collector/upgrade/v0_57_2.go b/pkg/collector/upgrade/v0_57_2.go index 1d2e346516..0656e2a949 100644 --- a/pkg/collector/upgrade/v0_57_2.go +++ b/pkg/collector/upgrade/v0_57_2.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" ) func upgrade0_57_2(u VersionUpgrade, otelcol *v1alpha1.OpenTelemetryCollector) (*v1alpha1.OpenTelemetryCollector, error) { diff --git a/pkg/collector/upgrade/v0_61_0.go b/pkg/collector/upgrade/v0_61_0.go index dff6365a63..60699ecd11 100644 --- a/pkg/collector/upgrade/v0_61_0.go +++ b/pkg/collector/upgrade/v0_61_0.go @@ -20,7 +20,7 @@ import ( "strings" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" ) func upgrade0_61_0(u VersionUpgrade, otelcol *v1alpha1.OpenTelemetryCollector) (*v1alpha1.OpenTelemetryCollector, error) { diff --git a/pkg/collector/upgrade/v0_9_0.go b/pkg/collector/upgrade/v0_9_0.go index 5ba72f85dd..49a791fa8f 100644 --- a/pkg/collector/upgrade/v0_9_0.go +++ b/pkg/collector/upgrade/v0_9_0.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v2" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/adapters" corev1 "k8s.io/api/core/v1" ) diff --git a/pkg/collector/volumeclaim.go b/pkg/collector/volumeclaim.go deleted file mode 100644 index b35d830b0f..0000000000 --- a/pkg/collector/volumeclaim.go +++ /dev/null @@ -1,56 +0,0 @@ -// 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 collector handles the OpenTelemetry Collector. -package collector - -import ( - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" -) - -// VolumeClaimTemplates builds the volumeClaimTemplates for the given instance, -// including the config map volume mount. -func VolumeClaimTemplates(cfg config.Config, otelcol v1alpha1.OpenTelemetryCollector) []corev1.PersistentVolumeClaim { - - var volumeClaimTemplates []corev1.PersistentVolumeClaim - - if otelcol.Spec.Mode != "statefulset" { - return volumeClaimTemplates - } - - // Add all user specified claims or use default. - if len(otelcol.Spec.VolumeClaimTemplates) > 0 { - volumeClaimTemplates = append(volumeClaimTemplates, - otelcol.Spec.VolumeClaimTemplates...) - } else { - volumeClaimTemplates = []corev1.PersistentVolumeClaim{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "default-volume", - }, - Spec: corev1.PersistentVolumeClaimSpec{ - AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}, - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{"storage": resource.MustParse("50Mi")}, - }, - }, - }} - } - - return volumeClaimTemplates -} diff --git a/pkg/constants/env.go b/pkg/constants/env.go index 0c4070905c..93f0e98bf6 100644 --- a/pkg/constants/env.go +++ b/pkg/constants/env.go @@ -22,6 +22,15 @@ const ( EnvOTELTracesSampler = "OTEL_TRACES_SAMPLER" EnvOTELTracesSamplerArg = "OTEL_TRACES_SAMPLER_ARG" + InstrumentationPrefix = "instrumentation.opentelemetry.io/" + AnnotationDefaultAutoInstrumentationJava = InstrumentationPrefix + "default-auto-instrumentation-java-image" + AnnotationDefaultAutoInstrumentationNodeJS = InstrumentationPrefix + "default-auto-instrumentation-nodejs-image" + AnnotationDefaultAutoInstrumentationPython = InstrumentationPrefix + "default-auto-instrumentation-python-image" + AnnotationDefaultAutoInstrumentationDotNet = InstrumentationPrefix + "default-auto-instrumentation-dotnet-image" + AnnotationDefaultAutoInstrumentationGo = InstrumentationPrefix + "default-auto-instrumentation-go-image" + AnnotationDefaultAutoInstrumentationApacheHttpd = InstrumentationPrefix + "default-auto-instrumentation-apache-httpd-image" + AnnotationDefaultAutoInstrumentationNginx = InstrumentationPrefix + "default-auto-instrumentation-nginx-image" + EnvPodName = "OTEL_RESOURCE_ATTRIBUTES_POD_NAME" EnvPodUID = "OTEL_RESOURCE_ATTRIBUTES_POD_UID" EnvNodeName = "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME" diff --git a/pkg/featuregate/featuregate.go b/pkg/featuregate/featuregate.go new file mode 100644 index 0000000000..27eb61f3cd --- /dev/null +++ b/pkg/featuregate/featuregate.go @@ -0,0 +1,101 @@ +// 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 featuregate + +import ( + "flag" + + "go.opentelemetry.io/collector/featuregate" +) + +const ( + FeatureGatesFlag = "feature-gates" +) + +var ( + EnableDotnetAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister( + "operator.autoinstrumentation.dotnet", + featuregate.StageBeta, + featuregate.WithRegisterDescription("controls whether the operator supports .NET auto-instrumentation"), + featuregate.WithRegisterFromVersion("v0.76.1"), + ) + EnablePythonAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister( + "operator.autoinstrumentation.python", + featuregate.StageBeta, + featuregate.WithRegisterDescription("controls whether the operator supports Python auto-instrumentation"), + featuregate.WithRegisterFromVersion("v0.76.1"), + ) + EnableJavaAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister( + "operator.autoinstrumentation.java", + featuregate.StageBeta, + featuregate.WithRegisterDescription("controls whether the operator supports Java auto-instrumentation"), + featuregate.WithRegisterFromVersion("v0.76.1"), + ) + EnableNodeJSAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister( + "operator.autoinstrumentation.nodejs", + featuregate.StageBeta, + featuregate.WithRegisterDescription("controls whether the operator supports NodeJS auto-instrumentation"), + featuregate.WithRegisterFromVersion("v0.76.1"), + ) + EnableGoAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister( + "operator.autoinstrumentation.go", + featuregate.StageAlpha, + featuregate.WithRegisterDescription("controls whether the operator supports Golang auto-instrumentation"), + featuregate.WithRegisterFromVersion("v0.77.0"), + ) + EnableApacheHTTPAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister( + "operator.autoinstrumentation.apache-httpd", + featuregate.StageBeta, + featuregate.WithRegisterDescription("controls whether the operator supports Apache HTTPD auto-instrumentation"), + featuregate.WithRegisterFromVersion("v0.80.0"), + ) + EnableNginxAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister( + "operator.autoinstrumentation.nginx", + featuregate.StageAlpha, + featuregate.WithRegisterDescription("controls whether the operator supports Nginx auto-instrumentation"), + featuregate.WithRegisterFromVersion("v0.86.0"), + ) + + EnableMultiInstrumentationSupport = featuregate.GlobalRegistry().MustRegister( + "operator.autoinstrumentation.multi-instrumentation", + featuregate.StageAlpha, + featuregate.WithRegisterFromVersion("0.86.0"), + featuregate.WithRegisterDescription("controls whether the operator supports multi instrumentation")) + + // EnableTargetAllocatorRewrite is the feature gate that controls whether the collector's configuration should + // automatically be rewritten when the target allocator is enabled. + EnableTargetAllocatorRewrite = featuregate.GlobalRegistry().MustRegister( + "operator.collector.rewritetargetallocator", + featuregate.StageBeta, + featuregate.WithRegisterDescription("controls whether the operator should configure the collector's targetAllocator configuration"), + featuregate.WithRegisterFromVersion("v0.76.1"), + ) + + // PrometheusOperatorIsAvailable is the feature gate that enables features associated to the Prometheus Operator. + PrometheusOperatorIsAvailable = featuregate.GlobalRegistry().MustRegister( + "operator.observability.prometheus", + featuregate.StageAlpha, + featuregate.WithRegisterDescription("enables features associated to the Prometheus Operator"), + featuregate.WithRegisterFromVersion("v0.82.0"), + ) +) + +// Flags creates a new FlagSet that represents the available featuregate flags using the supplied featuregate registry. +func Flags(reg *featuregate.Registry) *flag.FlagSet { + flagSet := new(flag.FlagSet) + flagSet.Var(featuregate.NewFlag(reg), FeatureGatesFlag, + "Comma-delimited list of feature gate identifiers. Prefix with '-' to disable the feature. '+' or no prefix will enable the feature.") + return flagSet +} diff --git a/pkg/featuregate/featuregate_test.go b/pkg/featuregate/featuregate_test.go new file mode 100644 index 0000000000..3bb5113df0 --- /dev/null +++ b/pkg/featuregate/featuregate_test.go @@ -0,0 +1,89 @@ +// 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 featuregate + +import ( + "testing" + + "go.opentelemetry.io/collector/featuregate" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + basicGate = "basic" + advancedGate = "advanced" + falseGate = "false" +) + +func TestSetFlag(t *testing.T) { + featuregate.GlobalRegistry().MustRegister(basicGate, featuregate.StageAlpha) + featuregate.GlobalRegistry().MustRegister(advancedGate, featuregate.StageBeta) + featuregate.GlobalRegistry().MustRegister(falseGate, featuregate.StageStable, featuregate.WithRegisterToVersion("v0.0.1")) + tests := []struct { + name string + args []string + expectedTrue []string + expectedFalse []string + expectedErr string + }{ + { + name: "simple set", + args: []string{"--feature-gates=basic"}, + expectedTrue: []string{basicGate}, + }, + { + name: "two flags", + args: []string{"--feature-gates=basic,advanced"}, + expectedTrue: []string{basicGate, advancedGate}, + }, + { + name: "one true one false", + args: []string{"--feature-gates=basic,-advanced"}, + expectedTrue: []string{basicGate}, + expectedFalse: []string{advancedGate}, + }, + { + name: "invalid set", + args: []string{"--feature-gates=01"}, + expectedErr: `no such feature gate -01`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + flgs := Flags(featuregate.GlobalRegistry()) + err := flgs.Parse(tt.args) + if tt.expectedErr != "" { + require.Error(t, err) + } else { + require.NoError(t, err) + } + featuregate.GlobalRegistry().VisitAll(func(gate *featuregate.Gate) { + for _, id := range tt.expectedTrue { + if gate.ID() == id { + assert.True(t, gate.IsEnabled()) + } + } + for _, id := range tt.expectedFalse { + if gate.ID() == id { + assert.False(t, gate.IsEnabled()) + } + } + }) + }) + } +} diff --git a/pkg/instrumentation/annotation.go b/pkg/instrumentation/annotation.go index 53817d5c81..28ef7bf3d5 100644 --- a/pkg/instrumentation/annotation.go +++ b/pkg/instrumentation/annotation.go @@ -23,12 +23,25 @@ import ( const ( // annotationInjectJava indicates whether java auto-instrumentation should be injected or not. // Possible values are "true", "false" or "" name. - annotationInjectJava = "instrumentation.opentelemetry.io/inject-java" - annotationInjectNodeJS = "instrumentation.opentelemetry.io/inject-nodejs" - annotationInjectPython = "instrumentation.opentelemetry.io/inject-python" - annotationInjectDotNet = "instrumentation.opentelemetry.io/inject-dotnet" - annotationInjectSdk = "instrumentation.opentelemetry.io/inject-sdk" - annotationInjectContainerName = "instrumentation.opentelemetry.io/container-names" + annotationInjectContainerName = "instrumentation.opentelemetry.io/container-names" + annotationInjectJava = "instrumentation.opentelemetry.io/inject-java" + annotationInjectJavaContainersName = "instrumentation.opentelemetry.io/java-container-names" + annotationInjectNodeJS = "instrumentation.opentelemetry.io/inject-nodejs" + annotationInjectNodeJSContainersName = "instrumentation.opentelemetry.io/nodejs-container-names" + annotationInjectPython = "instrumentation.opentelemetry.io/inject-python" + annotationInjectPythonContainersName = "instrumentation.opentelemetry.io/python-container-names" + annotationInjectDotNet = "instrumentation.opentelemetry.io/inject-dotnet" + annotationDotNetRuntime = "instrumentation.opentelemetry.io/otel-dotnet-auto-runtime" + annotationInjectDotnetContainersName = "instrumentation.opentelemetry.io/dotnet-container-names" + annotationInjectGo = "instrumentation.opentelemetry.io/inject-go" + annotationInjectGoContainersName = "instrumentation.opentelemetry.io/go-container-names" + annotationGoExecPath = "instrumentation.opentelemetry.io/otel-go-auto-target-exe" + annotationInjectSdk = "instrumentation.opentelemetry.io/inject-sdk" + annotationInjectSdkContainersName = "instrumentation.opentelemetry.io/sdk-container-names" + annotationInjectApacheHttpd = "instrumentation.opentelemetry.io/inject-apache-httpd" + annotationInjectApacheHttpdContainersName = "instrumentation.opentelemetry.io/apache-httpd-container-names" + annotationInjectNginx = "instrumentation.opentelemetry.io/inject-nginx" + annotationInjectNginxContainersName = "instrumentation.opentelemetry.io/inject-nginx-container-names" ) // annotationValue returns the effective annotationInjectJava value, based on the annotations from the pod and namespace. diff --git a/pkg/instrumentation/apachehttpd.go b/pkg/instrumentation/apachehttpd.go new file mode 100644 index 0000000000..5c85dbff9a --- /dev/null +++ b/pkg/instrumentation/apachehttpd.go @@ -0,0 +1,287 @@ +// 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 instrumentation + +import ( + "fmt" + "sort" + "strings" + + "github.com/go-logr/logr" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +const ( + apacheDefaultConfigDirectory = "/usr/local/apache2/conf" + apacheConfigFile = "httpd.conf" + apacheAgentConfigFile = "opentemetry_agent.conf" + apacheAgentDirectory = "/opt/opentelemetry-webserver" + apacheAgentSubDirectory = "/agent" + apacheAgentDirFull = apacheAgentDirectory + apacheAgentSubDirectory + apacheAgentConfigDirectory = "/source-conf" + apacheAgentConfDirFull = apacheAgentDirectory + apacheAgentConfigDirectory + apacheAgentInitContainerName = "otel-agent-attach-apache" + apacheAgentCloneContainerName = "otel-agent-source-container-clone" + apacheAgentConfigVolume = "otel-apache-conf-dir" + apacheAgentVolume = "otel-apache-agent" + apacheAttributesEnvVar = "OTEL_APACHE_AGENT_CONF" + apacheServiceInstanceId = "<>" + apacheServiceInstanceIdEnvVar = "APACHE_SERVICE_INSTANCE_ID" +) + +/* + Apache injection is different from other languages in: + - OpenTelemetry parameters are not passed as environmental variables, but via a configuration file + - OpenTelemetry module needs to be specified in the Apache HTTPD config file, but that is already specified by + an author of the application image and the configuration must be preserved + + Therefore, following approach is taken: + 1) Inject an init container created as a *clone* of the application container and copy config file to an empty shared volume + 2) Inject a second init container with the OpenTelemetry module itself - i.e. instrumentation image + 3) Take the Apache HTTPD configuration file saved on volume and inject reference to OpenTelemetry module into config + 4) Create on the same volume a configuration file for OpenTelemetry module + 5) Copy OpenTelemetry module from second init container (instrumentation image) to another shared volume + 6) Inject mounting of volumes / files into appropriate directories in application container +*/ + +func injectApacheHttpdagent(_ logr.Logger, apacheSpec v1alpha1.ApacheHttpd, pod corev1.Pod, index int, otlpEndpoint string, resourceMap map[string]string) corev1.Pod { + + // caller checks if there is at least one container + container := &pod.Spec.Containers[index] + + // inject env vars + for _, env := range apacheSpec.Env { + idx := getIndexOfEnv(container.Env, env.Name) + if idx == -1 { + container.Env = append(container.Env, env) + } + } + + // First make a clone of the instrumented container to take the existing Apache configuration from + // and create init container from it + if isApacheInitContainerMissing(pod, apacheAgentCloneContainerName) { + // Inject volume for original Apache configuration + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ + Name: apacheAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: volumeSize(apacheSpec.VolumeSizeLimit), + }, + }}) + + apacheConfDir := getApacheConfDir(apacheSpec.ConfigPath) + + cloneContainer := container.DeepCopy() + cloneContainer.Name = apacheAgentCloneContainerName + cloneContainer.Command = []string{"/bin/sh", "-c"} + cloneContainer.Args = []string{"cp -r " + apacheConfDir + "/* " + apacheAgentConfDirFull} + cloneContainer.VolumeMounts = append(cloneContainer.VolumeMounts, corev1.VolumeMount{ + Name: apacheAgentConfigVolume, + MountPath: apacheAgentConfDirFull, + }) + // remove resource requirements since those are then reserved for the lifetime of a pod + // and we definitely do not need them for the init container for cp command + cloneContainer.Resources = apacheSpec.Resources + // remove livenessProbe, readinessProbe, and startupProbe, since not supported on init containers + cloneContainer.LivenessProbe = nil + cloneContainer.ReadinessProbe = nil + cloneContainer.StartupProbe = nil + + pod.Spec.InitContainers = append(pod.Spec.InitContainers, *cloneContainer) + + // drop volume mount with volume-provided Apache config from original container + // since it could over-write configuration provided by the injection + idxFound := -1 + for idx, volume := range container.VolumeMounts { + if strings.Contains(volume.MountPath, apacheConfDir) { // potentially passes config, which we want to pass to init copy only + idxFound = idx + break + } + } + if idxFound >= 0 { + volumeMounts := container.VolumeMounts + volumeMounts = append(volumeMounts[:idxFound], volumeMounts[idxFound+1:]...) + container.VolumeMounts = volumeMounts + } + + // Inject volumes info instrumented container - Apache config dir + Apache agent + container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + Name: apacheAgentVolume, + MountPath: apacheAgentDirFull, + }) + container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + Name: apacheAgentConfigVolume, + MountPath: apacheConfDir, + }) + } + + // Inject second init container with instrumentation image + // Create / update config files + // Copy OTEL module to a shared volume + if isApacheInitContainerMissing(pod, apacheAgentInitContainerName) { + // Inject volume for agent + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ + Name: apacheAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: volumeSize(apacheSpec.VolumeSizeLimit), + }, + }}) + + pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{ + Name: apacheAgentInitContainerName, + Image: apacheSpec.Image, + Command: []string{"/bin/sh", "-c"}, + Args: []string{ + // Copy agent binaries to shared volume + "cp -r /opt/opentelemetry/* " + apacheAgentDirFull + " && " + + // setup logging configuration from template + "export agentLogDir=$(echo \"" + apacheAgentDirFull + "/logs\" | sed 's,/,\\\\/,g') && " + + "cat " + apacheAgentDirFull + "/conf/appdynamics_sdk_log4cxx.xml.template | sed 's/__agent_log_dir__/'${agentLogDir}'/g' > " + apacheAgentDirFull + "/conf/appdynamics_sdk_log4cxx.xml &&" + + // Create agent configuration file by pasting content of env var to a file + "echo \"$" + apacheAttributesEnvVar + "\" > " + apacheAgentConfDirFull + "/" + apacheAgentConfigFile + " && " + + "sed -i 's/" + apacheServiceInstanceId + "/'${" + apacheServiceInstanceIdEnvVar + "}'/g' " + apacheAgentConfDirFull + "/" + apacheAgentConfigFile + " && " + + // Include a link to include Apache agent configuration file into httpd.conf + "echo 'Include " + getApacheConfDir(apacheSpec.ConfigPath) + "/" + apacheAgentConfigFile + "' >> " + apacheAgentConfDirFull + "/" + apacheConfigFile, + }, + Env: []corev1.EnvVar{ + { + Name: apacheAttributesEnvVar, + Value: getApacheOtelConfig(pod, apacheSpec, index, otlpEndpoint, resourceMap), + }, + {Name: apacheServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + Resources: apacheSpec.Resources, + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirFull, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheAgentConfDirFull, + }, + }, + }) + } + + return pod +} + +// Calculate if we already inject InitContainers. +func isApacheInitContainerMissing(pod corev1.Pod, containerName string) bool { + for _, initContainer := range pod.Spec.InitContainers { + if initContainer.Name == containerName { + return false + } + } + return true +} + +// Calculate Apache HTTPD agent configuration file based on attributes provided by the injection rules +// and by the pod values. +func getApacheOtelConfig(pod corev1.Pod, apacheSpec v1alpha1.ApacheHttpd, index int, otelEndpoint string, resourceMap map[string]string) string { + template := ` +#Load the Otel Webserver SDK +LoadFile %[1]s/sdk_lib/lib/libopentelemetry_common.so +LoadFile %[1]s/sdk_lib/lib/libopentelemetry_resources.so +LoadFile %[1]s/sdk_lib/lib/libopentelemetry_trace.so +LoadFile %[1]s/sdk_lib/lib/libopentelemetry_otlp_recordable.so +LoadFile %[1]s/sdk_lib/lib/libopentelemetry_exporter_ostream_span.so +LoadFile %[1]s/sdk_lib/lib/libopentelemetry_exporter_otlp_grpc.so +#Load the Otel ApacheModule SDK +LoadFile %[1]s/sdk_lib/lib/libopentelemetry_webserver_sdk.so +#Load the Apache Module. In this example for Apache 2.4 +#LoadModule otel_apache_module %[1]s/WebServerModule/Apache/libmod_apache_otel.so +#Load the Apache Module. In this example for Apache 2.2 +#LoadModule otel_apache_module %[1]s/WebServerModule/Apache/libmod_apache_otel22.so +LoadModule otel_apache_module %[1]s/WebServerModule/Apache/libmod_apache_otel%[2]s.so +#Attributes +` + if otelEndpoint == "" { + otelEndpoint = "http://localhost:4317/" + } + serviceName := chooseServiceName(pod, resourceMap, index) + serviceNamespace := pod.GetNamespace() + if len(serviceNamespace) == 0 { + serviceNamespace = resourceMap[string(semconv.K8SNamespaceNameKey)] + if len(serviceNamespace) == 0 { + serviceNamespace = "apache-httpd" + } + + } + // Namespace name override TBD + + // There are two versions of the OTEL modules - for Apache HTTPD 2.4 and 2.2. + // 2.4 is default and the module does not have any version suffix + // 2.2 has version suffix "22" + versionSuffix := "" + if apacheSpec.Version == "2.2" { + versionSuffix = "22" + } + + attrMap := map[string]string{ + "ApacheModuleEnabled": "ON", + // ApacheModule Otel Exporter details + "ApacheModuleOtelSpanExporter": "otlp", + "ApacheModuleOtelExporterEndpoint": otelEndpoint, + // Service name and other IDs + "ApacheModuleServiceName": serviceName, + "ApacheModuleServiceNamespace": serviceNamespace, + "ApacheModuleServiceInstanceId": apacheServiceInstanceId, + + "ApacheModuleResolveBackends": " ON", + "ApacheModuleTraceAsError": " ON", + } + for _, attr := range apacheSpec.Attrs { + attrMap[attr.Name] = attr.Value + } + + configFileContent := fmt.Sprintf(template, + apacheAgentDirectory+apacheAgentSubDirectory, + versionSuffix) + + keys := make([]string, 0, len(attrMap)) + for key := range attrMap { + keys = append(keys, key) + } + sort.Strings(keys) + + for _, key := range keys { + configFileContent += fmt.Sprintf("%s %s\n", key, attrMap[key]) + } + + return configFileContent +} + +func getApacheConfDir(configuredDir string) string { + apacheConfDir := apacheDefaultConfigDirectory + if configuredDir != "" { + apacheConfDir = configuredDir + if apacheConfDir[len(apacheConfDir)-1] == '/' { + apacheConfDir = apacheConfDir[:len(apacheConfDir)-1] + } + } + return apacheConfDir +} diff --git a/pkg/instrumentation/apachehttpd_test.go b/pkg/instrumentation/apachehttpd_test.go new file mode 100644 index 0000000000..ded356054b --- /dev/null +++ b/pkg/instrumentation/apachehttpd_test.go @@ -0,0 +1,586 @@ +// 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 instrumentation + +import ( + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +func TestInjectApacheHttpdagent(t *testing.T) { + tests := []struct { + name string + v1alpha1.ApacheHttpd + pod corev1.Pod + expected corev1.Pod + }{ + { + name: "Clone Container not present", + ApacheHttpd: v1alpha1.ApacheHttpd{Image: "foo/bar:1"}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: apacheAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: apacheAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: apacheAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /usr/local/apache2/conf/* " + apacheAgentDirectory + apacheAgentConfigDirectory}, + VolumeMounts: []corev1.VolumeMount{{ + Name: apacheAgentConfigVolume, + MountPath: apacheAgentDirectory + apacheAgentConfigDirectory, + }}, + }, + { + Name: apacheAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{ + "cp -r /opt/opentelemetry/* /opt/opentelemetry-webserver/agent && export agentLogDir=$(echo \"/opt/opentelemetry-webserver/agent/logs\" | sed 's,/,\\\\/,g') && cat /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml.template | sed 's/__agent_log_dir__/'${agentLogDir}'/g' > /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml &&echo \"$OTEL_APACHE_AGENT_CONF\" > /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && sed -i 's/<>/'${APACHE_SERVICE_INSTANCE_ID}'/g' /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && echo 'Include /usr/local/apache2/conf/opentemetry_agent.conf' >> /opt/opentelemetry-webserver/source-conf/httpd.conf"}, + Env: []corev1.EnvVar{ + { + Name: apacheAttributesEnvVar, + Value: "\n#Load the Otel Webserver SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_common.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_resources.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_trace.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_otlp_recordable.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_ostream_span.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_otlp_grpc.so\n#Load the Otel ApacheModule SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_webserver_sdk.so\n#Load the Apache Module. In this example for Apache 2.4\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Load the Apache Module. In this example for Apache 2.2\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel22.so\nLoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Attributes\nApacheModuleEnabled ON\nApacheModuleOtelExporterEndpoint http://otlp-endpoint:4317\nApacheModuleOtelSpanExporter otlp\nApacheModuleResolveBackends ON\nApacheModuleServiceInstanceId <>\nApacheModuleServiceName apache-httpd-service-name\nApacheModuleServiceNamespace req-namespace\nApacheModuleTraceAsError ON\n", + }, + {Name: apacheServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheDefaultConfigDirectory, + }, + }, + }, + }, + }, + }, + }, + // === Test ConfigPath configuration ============================= + { + name: "ConfigPath honored", + ApacheHttpd: v1alpha1.ApacheHttpd{ + Image: "foo/bar:1", + ConfigPath: "/opt/customPath", + }, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: apacheAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: apacheAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: apacheAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /opt/customPath/* " + apacheAgentDirectory + apacheAgentConfigDirectory}, + VolumeMounts: []corev1.VolumeMount{{ + Name: apacheAgentConfigVolume, + MountPath: apacheAgentDirectory + apacheAgentConfigDirectory, + }}, + }, + { + Name: apacheAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{ + "cp -r /opt/opentelemetry/* /opt/opentelemetry-webserver/agent && export agentLogDir=$(echo \"/opt/opentelemetry-webserver/agent/logs\" | sed 's,/,\\\\/,g') && cat /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml.template | sed 's/__agent_log_dir__/'${agentLogDir}'/g' > /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml &&echo \"$OTEL_APACHE_AGENT_CONF\" > /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && sed -i 's/<>/'${APACHE_SERVICE_INSTANCE_ID}'/g' /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && echo 'Include /opt/customPath/opentemetry_agent.conf' >> /opt/opentelemetry-webserver/source-conf/httpd.conf"}, + Env: []corev1.EnvVar{ + { + Name: apacheAttributesEnvVar, + Value: "\n#Load the Otel Webserver SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_common.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_resources.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_trace.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_otlp_recordable.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_ostream_span.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_otlp_grpc.so\n#Load the Otel ApacheModule SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_webserver_sdk.so\n#Load the Apache Module. In this example for Apache 2.4\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Load the Apache Module. In this example for Apache 2.2\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel22.so\nLoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Attributes\nApacheModuleEnabled ON\nApacheModuleOtelExporterEndpoint http://otlp-endpoint:4317\nApacheModuleOtelSpanExporter otlp\nApacheModuleResolveBackends ON\nApacheModuleServiceInstanceId <>\nApacheModuleServiceName apache-httpd-service-name\nApacheModuleServiceNamespace req-namespace\nApacheModuleTraceAsError ON\n", + }, + {Name: apacheServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: "/opt/customPath", + }, + }, + }, + }, + }, + }, + }, + // === Test Removal of probes ============================= + { + name: "Probes removed on clone init container", + ApacheHttpd: v1alpha1.ApacheHttpd{Image: "foo/bar:1"}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + ReadinessProbe: &corev1.Probe{}, + StartupProbe: &corev1.Probe{}, + LivenessProbe: &corev1.Probe{}, + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: apacheAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: apacheAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: apacheAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /usr/local/apache2/conf/* " + apacheAgentDirectory + apacheAgentConfigDirectory}, + VolumeMounts: []corev1.VolumeMount{{ + Name: apacheAgentConfigVolume, + MountPath: apacheAgentDirectory + apacheAgentConfigDirectory, + }}, + }, + { + Name: apacheAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{ + "cp -r /opt/opentelemetry/* /opt/opentelemetry-webserver/agent && export agentLogDir=$(echo \"/opt/opentelemetry-webserver/agent/logs\" | sed 's,/,\\\\/,g') && cat /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml.template | sed 's/__agent_log_dir__/'${agentLogDir}'/g' > /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml &&echo \"$OTEL_APACHE_AGENT_CONF\" > /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && sed -i 's/<>/'${APACHE_SERVICE_INSTANCE_ID}'/g' /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && echo 'Include /usr/local/apache2/conf/opentemetry_agent.conf' >> /opt/opentelemetry-webserver/source-conf/httpd.conf"}, + Env: []corev1.EnvVar{ + { + Name: apacheAttributesEnvVar, + Value: "\n#Load the Otel Webserver SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_common.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_resources.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_trace.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_otlp_recordable.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_ostream_span.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_otlp_grpc.so\n#Load the Otel ApacheModule SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_webserver_sdk.so\n#Load the Apache Module. In this example for Apache 2.4\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Load the Apache Module. In this example for Apache 2.2\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel22.so\nLoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Attributes\nApacheModuleEnabled ON\nApacheModuleOtelExporterEndpoint http://otlp-endpoint:4317\nApacheModuleOtelSpanExporter otlp\nApacheModuleResolveBackends ON\nApacheModuleServiceInstanceId <>\nApacheModuleServiceName apache-httpd-service-name\nApacheModuleServiceNamespace req-namespace\nApacheModuleTraceAsError ON\n", + }, + {Name: apacheServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheDefaultConfigDirectory, + }, + }, + ReadinessProbe: &corev1.Probe{}, + StartupProbe: &corev1.Probe{}, + LivenessProbe: &corev1.Probe{}, + }, + }, + }, + }, + }, + // Pod Namespace specified + { + name: "Pod Namespace specified", + ApacheHttpd: v1alpha1.ApacheHttpd{Image: "foo/bar:1"}, + pod: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "my-namespace", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "my-namespace", + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: apacheAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: apacheAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: apacheAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /usr/local/apache2/conf/* " + apacheAgentDirectory + apacheAgentConfigDirectory}, + VolumeMounts: []corev1.VolumeMount{{ + Name: apacheAgentConfigVolume, + MountPath: apacheAgentDirectory + apacheAgentConfigDirectory, + }}, + }, + { + Name: apacheAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{ + "cp -r /opt/opentelemetry/* /opt/opentelemetry-webserver/agent && export agentLogDir=$(echo \"/opt/opentelemetry-webserver/agent/logs\" | sed 's,/,\\\\/,g') && cat /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml.template | sed 's/__agent_log_dir__/'${agentLogDir}'/g' > /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml &&echo \"$OTEL_APACHE_AGENT_CONF\" > /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && sed -i 's/<>/'${APACHE_SERVICE_INSTANCE_ID}'/g' /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && echo 'Include /usr/local/apache2/conf/opentemetry_agent.conf' >> /opt/opentelemetry-webserver/source-conf/httpd.conf"}, + Env: []corev1.EnvVar{ + { + Name: apacheAttributesEnvVar, + Value: "\n#Load the Otel Webserver SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_common.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_resources.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_trace.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_otlp_recordable.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_ostream_span.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_otlp_grpc.so\n#Load the Otel ApacheModule SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_webserver_sdk.so\n#Load the Apache Module. In this example for Apache 2.4\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Load the Apache Module. In this example for Apache 2.2\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel22.so\nLoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Attributes\nApacheModuleEnabled ON\nApacheModuleOtelExporterEndpoint http://otlp-endpoint:4317\nApacheModuleOtelSpanExporter otlp\nApacheModuleResolveBackends ON\nApacheModuleServiceInstanceId <>\nApacheModuleServiceName apache-httpd-service-name\nApacheModuleServiceNamespace my-namespace\nApacheModuleTraceAsError ON\n", + }, + {Name: apacheServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheDefaultConfigDirectory, + }, + }, + }, + }, + }, + }, + }, + } + + resourceMap := map[string]string{ + string(semconv.K8SDeploymentNameKey): "apache-httpd-service-name", + string(semconv.K8SNamespaceNameKey): "req-namespace", + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := injectApacheHttpdagent(logr.Discard(), test.ApacheHttpd, test.pod, 0, "http://otlp-endpoint:4317", resourceMap) + assert.Equal(t, test.expected, pod) + }) + } +} + +func TestInjectApacheHttpdagentUnknownNamespace(t *testing.T) { + tests := []struct { + name string + v1alpha1.ApacheHttpd + pod corev1.Pod + expected corev1.Pod + }{ + { + name: "Clone Container not present, unknown namespace", + ApacheHttpd: v1alpha1.ApacheHttpd{Image: "foo/bar:1"}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: apacheAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: apacheAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: apacheAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /usr/local/apache2/conf/* " + apacheAgentDirectory + apacheAgentConfigDirectory}, + VolumeMounts: []corev1.VolumeMount{{ + Name: apacheAgentConfigVolume, + MountPath: apacheAgentDirectory + apacheAgentConfigDirectory, + }}, + }, + { + Name: apacheAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{ + "cp -r /opt/opentelemetry/* /opt/opentelemetry-webserver/agent && export agentLogDir=$(echo \"/opt/opentelemetry-webserver/agent/logs\" | sed 's,/,\\\\/,g') && cat /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml.template | sed 's/__agent_log_dir__/'${agentLogDir}'/g' > /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml &&echo \"$OTEL_APACHE_AGENT_CONF\" > /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && sed -i 's/<>/'${APACHE_SERVICE_INSTANCE_ID}'/g' /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && echo 'Include /usr/local/apache2/conf/opentemetry_agent.conf' >> /opt/opentelemetry-webserver/source-conf/httpd.conf"}, + Env: []corev1.EnvVar{ + { + Name: apacheAttributesEnvVar, + Value: "\n#Load the Otel Webserver SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_common.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_resources.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_trace.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_otlp_recordable.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_ostream_span.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_otlp_grpc.so\n#Load the Otel ApacheModule SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_webserver_sdk.so\n#Load the Apache Module. In this example for Apache 2.4\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Load the Apache Module. In this example for Apache 2.2\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel22.so\nLoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Attributes\nApacheModuleEnabled ON\nApacheModuleOtelExporterEndpoint http://otlp-endpoint:4317\nApacheModuleOtelSpanExporter otlp\nApacheModuleResolveBackends ON\nApacheModuleServiceInstanceId <>\nApacheModuleServiceName apache-httpd-service-name\nApacheModuleServiceNamespace apache-httpd\nApacheModuleTraceAsError ON\n", + }, + {Name: apacheServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheDefaultConfigDirectory, + }, + }, + }, + }, + }, + }, + }, + } + + resourceMap := map[string]string{ + string(semconv.K8SDeploymentNameKey): "apache-httpd-service-name", + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := injectApacheHttpdagent(logr.Discard(), test.ApacheHttpd, test.pod, 0, "http://otlp-endpoint:4317", resourceMap) + assert.Equal(t, test.expected, pod) + }) + } +} + +func TestApacheInitContainerMissing(t *testing.T) { + tests := []struct { + name string + pod corev1.Pod + expected bool + }{ + { + name: "InitContainer_Already_Inject", + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "istio-init", + }, + { + Name: apacheAgentInitContainerName, + }, + }, + }, + }, + expected: false, + }, + { + name: "InitContainer_Absent_1", + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "istio-init", + }, + }, + }, + }, + expected: true, + }, + { + name: "InitContainer_Absent_2", + pod: corev1.Pod{ + Spec: corev1.PodSpec{}, + }, + expected: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := isApacheInitContainerMissing(test.pod, apacheAgentInitContainerName) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/pkg/instrumentation/dotnet.go b/pkg/instrumentation/dotnet.go index c86614a47a..4837257906 100644 --- a/pkg/instrumentation/dotnet.go +++ b/pkg/instrumentation/dotnet.go @@ -32,15 +32,25 @@ const ( envDotNetStartupHook = "DOTNET_STARTUP_HOOKS" envDotNetOTelAutoHome = "OTEL_DOTNET_AUTO_HOME" dotNetCoreClrEnableProfilingEnabled = "1" - dotNetCoreClrProfilerId = "{918728DD-259F-4A6A-AC2B-B85E1B658318}" - dotNetCoreClrProfilerPath = "/otel-auto-instrumentation/OpenTelemetry.AutoInstrumentation.Native.so" - dotNetAdditionalDepsPath = "/otel-auto-instrumentation/AdditionalDeps" - dotNetOTelAutoHomePath = "/otel-auto-instrumentation" - dotNetSharedStorePath = "/otel-auto-instrumentation/store" - dotNetStartupHookPath = "/otel-auto-instrumentation/netcoreapp3.1/OpenTelemetry.AutoInstrumentation.StartupHook.dll" + dotNetCoreClrProfilerID = "{918728DD-259F-4A6A-AC2B-B85E1B658318}" + dotNetCoreClrProfilerGlibcPath = "/otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so" + dotNetCoreClrProfilerMuslPath = "/otel-auto-instrumentation-dotnet/linux-musl-x64/OpenTelemetry.AutoInstrumentation.Native.so" + dotNetAdditionalDepsPath = "/otel-auto-instrumentation-dotnet/AdditionalDeps" + dotNetOTelAutoHomePath = "/otel-auto-instrumentation-dotnet" + dotNetSharedStorePath = "/otel-auto-instrumentation-dotnet/store" + dotNetStartupHookPath = "/otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll" + dotnetVolumeName = volumeName + "-dotnet" + dotnetInitContainerName = initContainerName + "-dotnet" + dotnetInstrMountPath = "/otel-auto-instrumentation-dotnet" ) -func injectDotNetSDK(dotNetSpec v1alpha1.DotNet, pod corev1.Pod, index int) (corev1.Pod, error) { +// Supported .NET runtime identifiers (https://learn.microsoft.com/en-us/dotnet/core/rid-catalog), can be set by instrumentation.opentelemetry.io/inject-dotnet. +const ( + dotNetRuntimeLinuxGlibc = "linux-x64" + dotNetRuntimeLinuxMusl = "linux-musl-x64" +) + +func injectDotNetSDK(dotNetSpec v1alpha1.DotNet, pod corev1.Pod, index int, runtime string) (corev1.Pod, error) { // caller checks if there is at least one container. container := &pod.Spec.Containers[index] @@ -56,12 +66,22 @@ func injectDotNetSDK(dotNetSpec v1alpha1.DotNet, pod corev1.Pod, index int) (cor return pod, errors.New("OTEL_DOTNET_AUTO_HOME environment variable is already set in the container") } - // check if OTEL_DOTNET_AUTO_HOME env var is already set in the .NET instrumentatiom spec + // check if OTEL_DOTNET_AUTO_HOME env var is already set in the .NET instrumentation spec // if it is already set, then we assume that .NET Auto-instrumentation is already configured for this container if getIndexOfEnv(dotNetSpec.Env, envDotNetOTelAutoHome) > -1 { return pod, errors.New("OTEL_DOTNET_AUTO_HOME environment variable is already set in the .NET instrumentation spec") } + coreClrProfilerPath := "" + switch runtime { + case "", dotNetRuntimeLinuxGlibc: + coreClrProfilerPath = dotNetCoreClrProfilerGlibcPath + case dotNetRuntimeLinuxMusl: + coreClrProfilerPath = dotNetCoreClrProfilerMuslPath + default: + return pod, fmt.Errorf("provided instrumentation.opentelemetry.io/dotnet-runtime annotation value '%s' is not supported", runtime) + } + // inject .NET instrumentation spec env vars. for _, env := range dotNetSpec.Env { idx := getIndexOfEnv(container.Env, env.Name) @@ -77,9 +97,9 @@ func injectDotNetSDK(dotNetSpec v1alpha1.DotNet, pod corev1.Pod, index int) (cor setDotNetEnvVar(container, envDotNetCoreClrEnableProfiling, dotNetCoreClrEnableProfilingEnabled, doNotConcatEnvValues) - setDotNetEnvVar(container, envDotNetCoreClrProfiler, dotNetCoreClrProfilerId, doNotConcatEnvValues) + setDotNetEnvVar(container, envDotNetCoreClrProfiler, dotNetCoreClrProfilerID, doNotConcatEnvValues) - setDotNetEnvVar(container, envDotNetCoreClrProfilerPath, dotNetCoreClrProfilerPath, doNotConcatEnvValues) + setDotNetEnvVar(container, envDotNetCoreClrProfilerPath, coreClrProfilerPath, doNotConcatEnvValues) setDotNetEnvVar(container, envDotNetStartupHook, dotNetStartupHookPath, concatEnvValues) @@ -90,25 +110,28 @@ func injectDotNetSDK(dotNetSpec v1alpha1.DotNet, pod corev1.Pod, index int) (cor setDotNetEnvVar(container, envDotNetSharedStore, dotNetSharedStorePath, concatEnvValues) container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, }) // We just inject Volumes and init containers for the first processed container. - if isInitContainerMissing(pod) { + if isInitContainerMissing(pod, dotnetInitContainerName) { pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ - Name: volumeName, + Name: dotnetVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: volumeSize(dotNetSpec.VolumeSizeLimit), + }, }}) pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{ - Name: initContainerName, - Image: dotNetSpec.Image, - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Name: dotnetInitContainerName, + Image: dotNetSpec.Image, + Command: []string{"cp", "-a", "/autoinstrumentation/.", dotnetInstrMountPath}, + Resources: dotNetSpec.Resources, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, }}, }) } diff --git a/pkg/instrumentation/dotnet_test.go b/pkg/instrumentation/dotnet_test.go index eab7fb85c2..9da037d516 100644 --- a/pkg/instrumentation/dotnet_test.go +++ b/pkg/instrumentation/dotnet_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" ) @@ -29,12 +30,13 @@ func TestInjectDotNetSDK(t *testing.T) { name string v1alpha1.DotNet pod corev1.Pod + runtime string expected corev1.Pod err error }{ { name: "CORECLR_ENABLE_PROFILING, CORECLR_PROFILER, CORECLR_PROFILER_PATH, DOTNET_STARTUP_HOOKS, DOTNET_SHARED_STORE, DOTNET_ADDITIONAL_DEPS, OTEL_DOTNET_AUTO_HOME not defined", - DotNet: v1alpha1.DotNet{Image: "foo/bar:1", Env: []corev1.EnvVar{}}, + DotNet: v1alpha1.DotNet{Image: "foo/bar:1", Env: []corev1.EnvVar{}, Resources: testResourceRequirements}, pod: corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -46,29 +48,32 @@ func TestInjectDotNetSDK(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: "opentelemetry-auto-instrumentation-dotnet", VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-dotnet", Image: "foo/bar:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-dotnet"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-dotnet", + MountPath: "/otel-auto-instrumentation-dotnet", }}, + Resources: testResourceRequirements, }, }, Containers: []corev1.Container{ { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-dotnet", + MountPath: "/otel-auto-instrumentation-dotnet", }, }, Env: []corev1.EnvVar{ @@ -78,11 +83,11 @@ func TestInjectDotNetSDK(t *testing.T) { }, { Name: envDotNetCoreClrProfiler, - Value: dotNetCoreClrProfilerId, + Value: dotNetCoreClrProfilerID, }, { Name: envDotNetCoreClrProfilerPath, - Value: dotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, }, { Name: envDotNetStartupHook, @@ -148,20 +153,22 @@ func TestInjectDotNetSDK(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: "opentelemetry-auto-instrumentation-dotnet", VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-dotnet", Image: "foo/bar:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-dotnet"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-dotnet", + MountPath: "/otel-auto-instrumentation-dotnet", }}, }, }, @@ -169,8 +176,8 @@ func TestInjectDotNetSDK(t *testing.T) { { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-dotnet", + MountPath: "/otel-auto-instrumentation-dotnet", }, }, Env: []corev1.EnvVar{ @@ -360,11 +367,190 @@ func TestInjectDotNetSDK(t *testing.T) { }, err: fmt.Errorf("OTEL_DOTNET_AUTO_HOME environment variable is already set in the .NET instrumentation spec"), }, + { + name: "runtime linux-x64", + DotNet: v1alpha1.DotNet{Image: "foo/bar:1", Env: []corev1.EnvVar{}, Resources: testResourceRequirements}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + runtime: dotNetRuntimeLinuxGlibc, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: dotnetVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: dotnetInitContainerName, + Image: "foo/bar:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-dotnet"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: dotnetVolumeName, + MountPath: "/otel-auto-instrumentation-dotnet", + }}, + Resources: testResourceRequirements, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: "/otel-auto-instrumentation-dotnet", + }, + }, + Env: []corev1.EnvVar{ + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + }, + }, + }, + }, + }, + err: nil, + }, + { + name: "runtime linux-musl-x64", + DotNet: v1alpha1.DotNet{Image: "foo/bar:1", Env: []corev1.EnvVar{}, Resources: testResourceRequirements}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + runtime: dotNetRuntimeLinuxMusl, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: dotnetVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: dotnetInitContainerName, + Image: "foo/bar:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-dotnet"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: dotnetVolumeName, + MountPath: "/otel-auto-instrumentation-dotnet", + }}, + Resources: testResourceRequirements, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: "/otel-auto-instrumentation-dotnet", + }, + }, + Env: []corev1.EnvVar{ + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerMuslPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + }, + }, + }, + }, + }, + err: nil, + }, + { + name: "runtime not-supported", + DotNet: v1alpha1.DotNet{Image: "foo/bar:1", Env: []corev1.EnvVar{}, Resources: testResourceRequirements}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + ObjectMeta: metav1.ObjectMeta{}, + }, + runtime: "not-supported", + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + err: fmt.Errorf("provided instrumentation.opentelemetry.io/dotnet-runtime annotation value 'not-supported' is not supported"), + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - pod, err := injectDotNetSDK(test.DotNet, test.pod, 0) + pod, err := injectDotNetSDK(test.DotNet, test.pod, 0, test.runtime) assert.Equal(t, test.expected, pod) assert.Equal(t, test.err, err) }) diff --git a/pkg/instrumentation/golang.go b/pkg/instrumentation/golang.go new file mode 100644 index 0000000000..2ae2e62379 --- /dev/null +++ b/pkg/instrumentation/golang.go @@ -0,0 +1,101 @@ +// 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 instrumentation + +import ( + "fmt" + "strings" + + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" +) + +const ( + envOtelTargetExe = "OTEL_GO_AUTO_TARGET_EXE" + + kernelDebugVolumeName = "kernel-debug" + kernelDebugVolumePath = "/sys/kernel/debug" +) + +func injectGoSDK(goSpec v1alpha1.Go, pod corev1.Pod) (corev1.Pod, error) { + // skip instrumentation if share process namespaces is explicitly disabled + if pod.Spec.ShareProcessNamespace != nil && !*pod.Spec.ShareProcessNamespace { + return pod, fmt.Errorf("shared process namespace has been explicitly disabled") + } + + // skip instrumentation when more than one containers provided + containerNames := "" + ok := false + if featuregate.EnableMultiInstrumentationSupport.IsEnabled() { + containerNames, ok = pod.Annotations[annotationInjectGoContainersName] + } else { + containerNames, ok = pod.Annotations[annotationInjectContainerName] + } + + if ok && len(strings.Split(containerNames, ",")) > 1 { + return pod, fmt.Errorf("go instrumentation cannot be injected into a pod, multiple containers configured") + } + + true := true + zero := int64(0) + pod.Spec.ShareProcessNamespace = &true + + goAgent := corev1.Container{ + Name: sideCarName, + Image: goSpec.Image, + Resources: goSpec.Resources, + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &zero, + Privileged: &true, + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/sys/kernel/debug", + Name: kernelDebugVolumeName, + }, + }, + } + + // Annotation takes precedence for OTEL_GO_AUTO_TARGET_EXE + execPath, ok := pod.Annotations[annotationGoExecPath] + if ok { + goAgent.Env = append(goAgent.Env, corev1.EnvVar{ + Name: envOtelTargetExe, + Value: execPath, + }) + } + + // Inject Go instrumentation spec env vars. + // For Go, env vars must be added to the agent contain + for _, env := range goSpec.Env { + idx := getIndexOfEnv(goAgent.Env, env.Name) + if idx == -1 { + goAgent.Env = append(goAgent.Env, env) + } + } + + pod.Spec.Containers = append(pod.Spec.Containers, goAgent) + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ + Name: kernelDebugVolumeName, + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: kernelDebugVolumePath, + }, + }, + }) + return pod, nil +} diff --git a/pkg/instrumentation/golang_test.go b/pkg/instrumentation/golang_test.go new file mode 100644 index 0000000000..9cade61da9 --- /dev/null +++ b/pkg/instrumentation/golang_test.go @@ -0,0 +1,298 @@ +// 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 instrumentation + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + colfeaturegate "go.opentelemetry.io/collector/featuregate" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" +) + +func TestInjectGoSDK(t *testing.T) { + falsee := false + true := true + zero := int64(0) + + tests := []struct { + name string + v1alpha1.Go + pod corev1.Pod + expected corev1.Pod + err error + setFeatureGates func(t *testing.T) + }{ + { + name: "shared process namespace disabled", + Go: v1alpha1.Go{Image: "foo/bar:1", Env: []corev1.EnvVar{}}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + ShareProcessNamespace: &falsee, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + ShareProcessNamespace: &falsee, + }, + }, + err: fmt.Errorf("shared process namespace has been explicitly disabled"), + }, + { + name: "using go-container-names", + Go: v1alpha1.Go{Image: "foo/bar:1", Env: []corev1.EnvVar{}}, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/go-container-names": "foo,bar", + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/go-container-names": "foo,bar", + }, + }, + }, + err: fmt.Errorf("go instrumentation cannot be injected into a pod, multiple containers configured"), + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableMultiInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "using container-names", + Go: v1alpha1.Go{Image: "foo/bar:1", Env: []corev1.EnvVar{}}, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/container-names": "foo,bar", + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/container-names": "foo,bar", + }, + }, + }, + err: fmt.Errorf("go instrumentation cannot be injected into a pod, multiple containers configured"), + }, + { + name: "pod annotation takes precedence", + Go: v1alpha1.Go{ + Image: "foo/bar:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_GO_AUTO_TARGET_EXE", + Value: "foo", + }, + }, + Resources: testResourceRequirements, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/otel-go-auto-target-exe": "bar", + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/otel-go-auto-target-exe": "bar", + }, + }, + Spec: corev1.PodSpec{ + ShareProcessNamespace: &true, + Containers: []corev1.Container{ + { + Name: sideCarName, + Resources: testResourceRequirements, + Image: "foo/bar:1", + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &zero, + Privileged: &true, + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/sys/kernel/debug", + Name: kernelDebugVolumeName, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_GO_AUTO_TARGET_EXE", + Value: "bar", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: kernelDebugVolumeName, + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: kernelDebugVolumePath, + }, + }, + }, + }, + }, + }, + }, + { + name: "use instrumentation env var", + Go: v1alpha1.Go{ + Image: "foo/bar:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_GO_AUTO_TARGET_EXE", + Value: "foo", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/go-container-names": "foo", + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/go-container-names": "foo", + }, + }, + Spec: corev1.PodSpec{ + ShareProcessNamespace: &true, + Containers: []corev1.Container{ + { + Name: sideCarName, + Image: "foo/bar:1", + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &zero, + Privileged: &true, + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/sys/kernel/debug", + Name: kernelDebugVolumeName, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_GO_AUTO_TARGET_EXE", + Value: "foo", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: kernelDebugVolumeName, + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: kernelDebugVolumePath, + }, + }, + }, + }, + }, + }, + }, + { + name: "inject env vars", + Go: v1alpha1.Go{ + Image: "foo/bar:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_1", + Value: "foo", + }, + { + Name: "OTEL_2", + Value: "bar", + }, + }, + }, + pod: corev1.Pod{}, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + ShareProcessNamespace: &true, + Containers: []corev1.Container{ + { + Name: sideCarName, + Image: "foo/bar:1", + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &zero, + Privileged: &true, + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/sys/kernel/debug", + Name: kernelDebugVolumeName, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_1", + Value: "foo", + }, + { + Name: "OTEL_2", + Value: "bar", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: kernelDebugVolumeName, + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: kernelDebugVolumePath, + }, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.setFeatureGates != nil { + test.setFeatureGates(t) + } + pod, err := injectGoSDK(test.Go, test.pod) + assert.Equal(t, test.expected, pod) + assert.Equal(t, test.err, err) + }) + } +} diff --git a/pkg/instrumentation/helper.go b/pkg/instrumentation/helper.go index 7ade3b7b3f..8306f773ae 100644 --- a/pkg/instrumentation/helper.go +++ b/pkg/instrumentation/helper.go @@ -14,12 +14,25 @@ package instrumentation -import corev1 "k8s.io/api/core/v1" +import ( + "fmt" + "sort" + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/utils/strings/slices" + + "github.com/open-telemetry/opentelemetry-operator/internal/naming" + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" +) + +var defaultSize = resource.MustParse("200Mi") // Calculate if we already inject InitContainers. -func isInitContainerMissing(pod corev1.Pod) bool { +func isInitContainerMissing(pod corev1.Pod, containerName string) bool { for _, initContainer := range pod.Spec.InitContainers { - if initContainer.Name == initContainerName { + if initContainer.Name == containerName { return false } } @@ -29,9 +42,94 @@ func isInitContainerMissing(pod corev1.Pod) bool { // Checks if Pod is already instrumented by checking Instrumentation InitContainer presence. func isAutoInstrumentationInjected(pod corev1.Pod) bool { for _, cont := range pod.Spec.InitContainers { - if cont.Name == initContainerName { + if slices.Contains([]string{ + dotnetInitContainerName, + javaInitContainerName, + nodejsInitContainerName, + pythonInitContainerName, + apacheAgentInitContainerName, + apacheAgentCloneContainerName, + }, cont.Name) { return true } } + + for _, cont := range pod.Spec.Containers { + // Go uses a sidecar + if cont.Name == sideCarName { + return true + } + + // This environment variable is set in the sidecar and in the + // collector containers. We look for it in any container that is not + // the sidecar container to check if we already injected the + // instrumentation or not + if cont.Name != naming.Container() { + for _, envVar := range cont.Env { + if envVar.Name == constants.EnvNodeName { + return true + } + } + } + } return false } + +// Look for duplicates in the provided containers. +func findDuplicatedContainers(ctrs []string) error { + // Merge is needed because of multiple containers can be provided for single instrumentation. + mergedContainers := strings.Join(ctrs, ",") + + // Split all containers. + splitContainers := strings.Split(mergedContainers, ",") + + countMap := make(map[string]int) + var duplicates []string + for _, str := range splitContainers { + countMap[str]++ + } + + // Find and collect the duplicates + for str, count := range countMap { + // omit empty container names + if str == "" { + continue + } + + if count > 1 { + duplicates = append(duplicates, str) + } + } + + if duplicates != nil { + sort.Strings(duplicates) + return fmt.Errorf("duplicated container names detected: %s", duplicates) + } + + return nil +} + +// Return positive for instrumentation with defined containers. +func isInstrWithContainers(inst instrumentationWithContainers) int { + if inst.Containers != "" { + return 1 + } + + return 0 +} + +// Return positive for instrumentation without defined containers. +func isInstrWithoutContainers(inst instrumentationWithContainers) int { + if inst.Containers == "" { + return 1 + } + + return 0 +} + +func volumeSize(quantity *resource.Quantity) *resource.Quantity { + if quantity == nil { + return &defaultSize + } + return quantity +} diff --git a/pkg/instrumentation/helper_test.go b/pkg/instrumentation/helper_test.go index b2f37d1c0b..72db0b4f21 100644 --- a/pkg/instrumentation/helper_test.go +++ b/pkg/instrumentation/helper_test.go @@ -15,10 +15,13 @@ package instrumentation import ( + "fmt" "testing" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" ) func TestInitContainerMissing(t *testing.T) { @@ -36,7 +39,7 @@ func TestInitContainerMissing(t *testing.T) { Name: "istio-init", }, { - Name: initContainerName, + Name: javaInitContainerName, }, }, }, @@ -67,7 +70,7 @@ func TestInitContainerMissing(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - result := isInitContainerMissing(test.pod) + result := isInitContainerMissing(test.pod, javaInitContainerName) assert.Equal(t, test.expected, result) }) } @@ -88,7 +91,41 @@ func TestAutoInstrumentationInjected(t *testing.T) { Name: "magic-init", }, { - Name: initContainerName, + Name: nodejsInitContainerName, + }, + }, + }, + }, + expected: true, + }, + { + name: "AutoInstrumentation_Already_Inject_go", + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{}, + Containers: []corev1.Container{ + { + Name: sideCarName, + }, + }, + }, + }, + expected: true, + }, + { + name: "AutoInstrumentation_Already_Inject_no_init_containers", + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{}, + Containers: []corev1.Container{ + { + Name: "my-app", + Env: []corev1.EnvVar{ + { + Name: constants.EnvNodeName, + Value: "value", + }, + }, }, }, }, @@ -124,3 +161,81 @@ func TestAutoInstrumentationInjected(t *testing.T) { }) } } + +func TestDuplicatedContainers(t *testing.T) { + tests := []struct { + name string + containers []string + expectedDuplicates error + }{ + { + name: "No duplicates", + containers: []string{"app1,app2", "app3", "app4,app5"}, + expectedDuplicates: nil, + }, + { + name: "Duplicates in containers", + containers: []string{"app1,app2", "app1", "app1,app3,app4", "app4"}, + expectedDuplicates: fmt.Errorf("duplicated container names detected: [app1 app4]"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ok := findDuplicatedContainers(test.containers) + assert.Equal(t, test.expectedDuplicates, ok) + }) + } +} + +func TestInstrWithContainers(t *testing.T) { + tests := []struct { + name string + containers instrumentationWithContainers + expectedResult int + }{ + { + name: "No containers", + containers: instrumentationWithContainers{Containers: ""}, + expectedResult: 0, + }, + { + name: "With containers", + containers: instrumentationWithContainers{Containers: "ct1"}, + expectedResult: 1, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + res := isInstrWithContainers(test.containers) + assert.Equal(t, test.expectedResult, res) + }) + } +} + +func TestInstrWithoutContainers(t *testing.T) { + tests := []struct { + name string + containers instrumentationWithContainers + expectedResult int + }{ + { + name: "No containers", + containers: instrumentationWithContainers{Containers: ""}, + expectedResult: 1, + }, + { + name: "With containers", + containers: instrumentationWithContainers{Containers: "ct1"}, + expectedResult: 0, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + res := isInstrWithoutContainers(test.containers) + assert.Equal(t, test.expectedResult, res) + }) + } +} diff --git a/pkg/instrumentation/instrumentation_suite_test.go b/pkg/instrumentation/instrumentation_suite_test.go index a5fb3c46b0..4877f7355a 100644 --- a/pkg/instrumentation/instrumentation_suite_test.go +++ b/pkg/instrumentation/instrumentation_suite_test.go @@ -21,28 +21,33 @@ import ( "testing" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" ) -var k8sClient client.Client -var testEnv *envtest.Environment -var testScheme = scheme.Scheme +var ( + k8sClient client.Client + testEnv *envtest.Environment + testScheme = scheme.Scheme + err error + cfg *rest.Config +) func TestMain(m *testing.M) { testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, } - cfg, err := testEnv.Start() + cfg, err = testEnv.Start() if err != nil { fmt.Printf("failed to start testEnv: %v", err) os.Exit(1) } - if err := v1alpha1.AddToScheme(testScheme); err != nil { + if err = v1alpha1.AddToScheme(testScheme); err != nil { fmt.Printf("failed to register scheme: %v", err) os.Exit(1) } diff --git a/pkg/instrumentation/javaagent.go b/pkg/instrumentation/javaagent.go index 02688ac0d9..5422cc08ad 100644 --- a/pkg/instrumentation/javaagent.go +++ b/pkg/instrumentation/javaagent.go @@ -21,8 +21,11 @@ import ( ) const ( - envJavaToolsOptions = "JAVA_TOOL_OPTIONS" - javaJVMArgument = " -javaagent:/otel-auto-instrumentation/javaagent.jar" + envJavaToolsOptions = "JAVA_TOOL_OPTIONS" + javaJVMArgument = " -javaagent:/otel-auto-instrumentation-java/javaagent.jar" + javaInitContainerName = initContainerName + "-java" + javaVolumeName = volumeName + "-java" + javaInstrMountPath = "/otel-auto-instrumentation-java" ) func injectJavaagent(javaSpec v1alpha1.Java, pod corev1.Pod, index int) (corev1.Pod, error) { @@ -53,25 +56,28 @@ func injectJavaagent(javaSpec v1alpha1.Java, pod corev1.Pod, index int) (corev1. } container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: javaVolumeName, + MountPath: javaInstrMountPath, }) // We just inject Volumes and init containers for the first processed container. - if isInitContainerMissing(pod) { + if isInitContainerMissing(pod, javaInitContainerName) { pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ - Name: volumeName, + Name: javaVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: volumeSize(javaSpec.VolumeSizeLimit), + }, }}) pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{ - Name: initContainerName, - Image: javaSpec.Image, - Command: []string{"cp", "/javaagent.jar", "/otel-auto-instrumentation/javaagent.jar"}, + Name: javaInitContainerName, + Image: javaSpec.Image, + Command: []string{"cp", "/javaagent.jar", javaInstrMountPath + "/javaagent.jar"}, + Resources: javaSpec.Resources, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: javaVolumeName, + MountPath: javaInstrMountPath, }}, }) } diff --git a/pkg/instrumentation/javaagent_test.go b/pkg/instrumentation/javaagent_test.go index ab3a6f885d..3a8c43b84f 100644 --- a/pkg/instrumentation/javaagent_test.go +++ b/pkg/instrumentation/javaagent_test.go @@ -46,20 +46,22 @@ func TestInjectJavaagent(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: "opentelemetry-auto-instrumentation-java", VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-java", Image: "foo/bar:1", - Command: []string{"cp", "/javaagent.jar", "/otel-auto-instrumentation/javaagent.jar"}, + Command: []string{"cp", "/javaagent.jar", "/otel-auto-instrumentation-java/javaagent.jar"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-java", + MountPath: "/otel-auto-instrumentation-java", }}, }, }, @@ -67,8 +69,8 @@ func TestInjectJavaagent(t *testing.T) { { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-java", + MountPath: "/otel-auto-instrumentation-java", }, }, Env: []corev1.EnvVar{ @@ -85,7 +87,7 @@ func TestInjectJavaagent(t *testing.T) { }, { name: "JAVA_TOOL_OPTIONS defined", - Java: v1alpha1.Java{Image: "foo/bar:1"}, + Java: v1alpha1.Java{Image: "foo/bar:1", Resources: testResourceRequirements}, pod: corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -104,29 +106,32 @@ func TestInjectJavaagent(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: "opentelemetry-auto-instrumentation-java", VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-java", Image: "foo/bar:1", - Command: []string{"cp", "/javaagent.jar", "/otel-auto-instrumentation/javaagent.jar"}, + Command: []string{"cp", "/javaagent.jar", "/otel-auto-instrumentation-java/javaagent.jar"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-java", + MountPath: "/otel-auto-instrumentation-java", }}, + Resources: testResourceRequirements, }, }, Containers: []corev1.Container{ { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-java", + MountPath: "/otel-auto-instrumentation-java", }, }, Env: []corev1.EnvVar{ diff --git a/pkg/instrumentation/nginx.go b/pkg/instrumentation/nginx.go new file mode 100644 index 0000000000..4855e5a54f --- /dev/null +++ b/pkg/instrumentation/nginx.go @@ -0,0 +1,354 @@ +// 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 instrumentation + +import ( + "fmt" + "path/filepath" + "sort" + "strings" + + "github.com/go-logr/logr" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +const ( + nginxDefaultConfigFile = "/etc/nginx/nginx.conf" + nginxAgentCloneContainerName = "otel-agent-source-container-clone" + nginxAgentInitContainerName = "otel-agent-attach-nginx" + nginxAgentVolume = "otel-nginx-agent" + nginxAgentConfigVolume = "otel-nginx-conf-dir" + nginxAgentConfigFile = "opentemetry_agent.conf" + nginxAgentDirectory = "/opt/opentelemetry-webserver" + nginxAgentSubDirectory = "/agent" + nginxAgentDirFull = nginxAgentDirectory + nginxAgentSubDirectory + nginxAgentConfigDirectory = "/source-conf" + nginxAgentConfDirFull = nginxAgentDirectory + nginxAgentConfigDirectory + nginxAttributesEnvVar = "OTEL_NGINX_AGENT_CONF" + nginxServiceInstanceId = "<>" + nginxServiceInstanceIdEnvVar = "OTEL_NGINX_SERVICE_INSTANCE_ID" + nginxVersionEnvVar = "NGINX_VERSION" + nginxLibraryPathEnv = "LD_LIBRARY_PATH" +) + +/* + Nginx injection is different from other languages in: + - OpenTelemetry parameters are not passed as environmental variables, but via a configuration file + - OpenTelemetry module needs to be specified in the Nginx config file, but that is already specified by + an author of the original image configuration and the configuration must be preserved + + Therefore, following approach is taken: + 1) Inject an init container created as a *clone* of the application container and copy config file and referenced + configuration directory to an empty shared volume + 2) Inject a second init container with the OpenTelemetry module itself - i.e. instrumentation image + 3) Take the Nginx configuration file saved on volume and inject reference to OpenTelemetry module into the config + 4) On the same volume, inject a configuration file for OpenTelemetry module + 5) Copy OpenTelemetry module from second init container (instrumentation image) to another shared volume + 6) Inject mounting of volumes / files into appropriate directories in the application container +*/ + +func injectNginxSDK(_ logr.Logger, nginxSpec v1alpha1.Nginx, pod corev1.Pod, index int, otlpEndpoint string, resourceMap map[string]string) corev1.Pod { + + // caller checks if there is at least one container + container := &pod.Spec.Containers[index] + + // inject env vars + for _, env := range nginxSpec.Env { + idx := getIndexOfEnv(container.Env, env.Name) + if idx == -1 { + container.Env = append(container.Env, env) + } + } + + // First make a clone of the instrumented container to take the existing Nginx configuration from + // and create init container from it + if isNginxInitContainerMissing(pod, nginxAgentCloneContainerName) { + // Inject volume for original Nginx configuration + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ + Name: nginxAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }}) + + nginxConfFile := getNginxConfFile(nginxSpec.ConfigFile) + nginxConfDir := getNginxConfDir(nginxSpec.ConfigFile) + + // from original Nginx container, we need + // 1) original configuration files, which then get modified in the instrumentation process + // 2) version of Nginx to select the proper version of OTel modules. + // - run Nginx with -v to get the version + // - store the version into a file where instrumentation initContainer can pick it up + nginxCloneScriptTemplate := + ` +cp -r %[2]s/* %[3]s && +export %[4]s=$( { nginx -v ; } 2>&1 ) && echo ${%[4]s##*/} > %[3]s/version.txt +` + nginxAgentCommands := prepareCommandFromTemplate(nginxCloneScriptTemplate, + nginxConfFile, + nginxConfDir, + nginxAgentConfDirFull, + nginxVersionEnvVar, + ) + + cloneContainer := container.DeepCopy() + cloneContainer.Name = nginxAgentCloneContainerName + cloneContainer.Command = []string{"/bin/sh", "-c"} + cloneContainer.Args = []string{nginxAgentCommands} + cloneContainer.VolumeMounts = append(cloneContainer.VolumeMounts, corev1.VolumeMount{ + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }) + // remove resource requirements since those are then reserved for the lifetime of a pod + // and we definitely do not need them for the init container for cp command + cloneContainer.Resources = nginxSpec.Resources + // remove livenessProbe, readinessProbe, and startupProbe, since not supported on init containers + cloneContainer.LivenessProbe = nil + cloneContainer.ReadinessProbe = nil + cloneContainer.StartupProbe = nil + // remove lifecycle, since not supported on init containers + cloneContainer.Lifecycle = nil + + pod.Spec.InitContainers = append(pod.Spec.InitContainers, *cloneContainer) + + // drop volume mount with volume-provided Nginx config from original container + // since it could over-write configuration provided by the injection + idxFound := -1 + for idx, volume := range container.VolumeMounts { + if strings.Contains(volume.MountPath, nginxConfDir) { // potentially passes config, which we want to pass to init copy only + idxFound = idx + break + } + } + if idxFound >= 0 { + volumeMounts := container.VolumeMounts + volumeMounts = append(volumeMounts[:idxFound], volumeMounts[idxFound+1:]...) + container.VolumeMounts = volumeMounts + } + + // Inject volumes info instrumented container - Nginx config dir + Nginx agent + container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }) + container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + Name: nginxAgentConfigVolume, + MountPath: nginxConfDir, + }) + } + + // Inject second init container with instrumentation image + // Create / update config files + // Copy OTEL module to a shared volume + if isNginxInitContainerMissing(pod, nginxAgentInitContainerName) { + // Inject volume for agent + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ + Name: nginxAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }}) + + // Following is the template for a shell script, which does the actual instrumentation + // It does following: + // 1) Copies Nginx OTel modules from the webserver agent image + // 2) Picks-up the Nginx version stored by the clone of original container (see comment there) + // 3) Finds out which directory to use for logs + // 4) Configures the directory in logging configuration file of OTel modules + // 5) Creates a configuration file for OTel modules + // 6) In that configuration file, set SID parameter to pod name (in env var OTEL_NGINX_SERVICE_INSTANCE_ID) + // 7) In Nginx config file, inject directive to load OTel module + // 8) In Nginx config file, enable use of env var OTEL_RESOURCE_ATTRIBUTES in Nginx process + // (by default, env vars are hidden to Nginx process, they need to be enabled specifically) + // 9) Move OTel module configuration file to Nginx configuration directory. + + // Each line of the script MUST end with \n ! + nginxAgentI13nScript := + ` +NGINX_AGENT_DIR_FULL=$1 \n +NGINX_AGENT_CONF_DIR_FULL=$2 \n +NGINX_CONFIG_FILE=$3 \n +NGINX_SID_PLACEHOLDER=$4 \n +NGINX_SID_VALUE=$5 \n +echo "Input Parameters: $@" \n +set -x \n +\n +cp -ar /opt/opentelemetry/* ${NGINX_AGENT_DIR_FULL} \n +\n +NGINX_VERSION=$(cat ${NGINX_AGENT_CONF_DIR_FULL}/version.txt) \n +NGINX_AGENT_LOG_DIR=$(echo "${NGINX_AGENT_DIR_FULL}/logs" | sed 's,/,\\/,g') \n +\n +cat ${NGINX_AGENT_DIR_FULL}/conf/appdynamics_sdk_log4cxx.xml.template | sed 's,__agent_log_dir__,'${NGINX_AGENT_LOG_DIR}',g' > ${NGINX_AGENT_DIR_FULL}/conf/appdynamics_sdk_log4cxx.xml \n +echo -e $OTEL_NGINX_AGENT_CONF > ${NGINX_AGENT_CONF_DIR_FULL}/opentelemetry_agent.conf \n +sed -i "s,${NGINX_SID_PLACEHOLDER},${OTEL_NGINX_SERVICE_INSTANCE_ID},g" ${NGINX_AGENT_CONF_DIR_FULL}/opentelemetry_agent.conf \n +sed -i "1s,^,load_module ${NGINX_AGENT_DIR_FULL}/WebServerModule/Nginx/${NGINX_VERSION}/ngx_http_opentelemetry_module.so;\\n,g" ${NGINX_AGENT_CONF_DIR_FULL}/${NGINX_CONFIG_FILE} \n +sed -i "1s,^,env OTEL_RESOURCE_ATTRIBUTES;\\n,g" ${NGINX_AGENT_CONF_DIR_FULL}/${NGINX_CONFIG_FILE} \n +mv ${NGINX_AGENT_CONF_DIR_FULL}/opentelemetry_agent.conf ${NGINX_AGENT_CONF_DIR_FULL}/conf.d \n + ` + + nginxAgentI13nCommand := "echo -e $OTEL_NGINX_I13N_SCRIPT > " + nginxAgentDirFull + "/nginx_instrumentation.sh && " + + "chmod +x " + nginxAgentDirFull + "/nginx_instrumentation.sh && " + + "cat " + nginxAgentDirFull + "/nginx_instrumentation.sh && " + + fmt.Sprintf(nginxAgentDirFull+"/nginx_instrumentation.sh \"%s\" \"%s\" \"%s\" \"%s\"", + nginxAgentDirFull, + nginxAgentConfDirFull, + getNginxConfFile(nginxSpec.ConfigFile), + nginxServiceInstanceId, + ) + + pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{ + Name: nginxAgentInitContainerName, + Image: nginxSpec.Image, + Command: []string{"/bin/sh", "-c"}, + Args: []string{nginxAgentI13nCommand}, + Env: []corev1.EnvVar{ + { + Name: nginxAttributesEnvVar, + Value: getNginxOtelConfig(pod, nginxSpec, index, otlpEndpoint, resourceMap), + }, + { + Name: "OTEL_NGINX_I13N_SCRIPT", + Value: nginxAgentI13nScript, + }, + { + Name: nginxServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + Resources: nginxSpec.Resources, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }, + }, + SecurityContext: pod.Spec.Containers[index].SecurityContext, + }) + + found := false + for i, e := range container.Env { + if e.Name == nginxLibraryPathEnv { + container.Env[i].Value = e.Value + ":" + nginxAgentDirFull + "/sdk_lib/lib" + found = true + break + } + } + if !found { + container.Env = append(container.Env, corev1.EnvVar{ + Name: nginxLibraryPathEnv, + Value: nginxAgentDirFull + "/sdk_lib/lib", + }) + } + } + + return pod +} + +// Calculate if we already inject InitContainers. +func isNginxInitContainerMissing(pod corev1.Pod, containerName string) bool { + for _, initContainer := range pod.Spec.InitContainers { + if initContainer.Name == containerName { + return false + } + } + return true +} + +// Calculate Nginx agent configuration file based on attributes provided by the injection rules +// and by the pod values. +func getNginxOtelConfig(pod corev1.Pod, nginxSpec v1alpha1.Nginx, index int, otelEndpoint string, resourceMap map[string]string) string { + + if otelEndpoint == "" { + otelEndpoint = "http://localhost:4317/" + } + serviceName := chooseServiceName(pod, resourceMap, index) + serviceNamespace := pod.GetNamespace() + if len(serviceNamespace) == 0 { + serviceNamespace = resourceMap[string(semconv.K8SNamespaceNameKey)] + if len(serviceNamespace) == 0 { + serviceNamespace = "nginx" + } + } + + // Namespace name override TBD + + attrMap := map[string]string{ + "NginxModuleEnabled": "ON", + "NginxModuleOtelSpanExporter": "otlp", + "NginxModuleOtelExporterEndpoint": otelEndpoint, + "NginxModuleServiceName": serviceName, + "NginxModuleServiceNamespace": serviceNamespace, + "NginxModuleServiceInstanceId": nginxServiceInstanceId, + "NginxModuleResolveBackends": "ON", + "NginxModuleTraceAsError": "ON", + } + for _, attr := range nginxSpec.Attrs { + attrMap[attr.Name] = attr.Value + } + + configFileContent := "" + + keys := make([]string, 0, len(attrMap)) + for key := range attrMap { + keys = append(keys, key) + } + sort.Strings(keys) + + for _, key := range keys { + configFileContent += fmt.Sprintf("%s %s;\n", key, attrMap[key]) + } + + return configFileContent +} + +func getNginxConfDir(configuredFile string) string { + nginxConfFile := nginxDefaultConfigFile + if configuredFile != "" { + nginxConfFile = configuredFile + } + configDir := filepath.Dir(nginxConfFile) + return configDir +} + +func getNginxConfFile(configuredFile string) string { + nginxConfFile := nginxDefaultConfigFile + if configuredFile != "" { + nginxConfFile = configuredFile + } + configFilenameOnly := filepath.Base(nginxConfFile) + return configFilenameOnly +} + +func prepareCommandFromTemplate(template string, params ...any) string { + command := fmt.Sprintf(template, + params..., + ) + + command = strings.Replace(command, "\n", " ", -1) + command = strings.Replace(command, "\t", " ", -1) + command = strings.TrimLeft(command, " ") + command = strings.TrimRight(command, " ") + + return command +} diff --git a/pkg/instrumentation/nginx_test.go b/pkg/instrumentation/nginx_test.go new file mode 100644 index 0000000000..6284f430bc --- /dev/null +++ b/pkg/instrumentation/nginx_test.go @@ -0,0 +1,659 @@ +// 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 instrumentation + +import ( + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +var nginxSdkInitContainerTestCommand = "echo -e $OTEL_NGINX_I13N_SCRIPT > /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && chmod +x /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && cat /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh \"/opt/opentelemetry-webserver/agent\" \"/opt/opentelemetry-webserver/source-conf\" \"nginx.conf\" \"<>\"" +var nginxSdkInitContainerTestCommandCustomFile = "echo -e $OTEL_NGINX_I13N_SCRIPT > /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && chmod +x /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && cat /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh \"/opt/opentelemetry-webserver/agent\" \"/opt/opentelemetry-webserver/source-conf\" \"custom-nginx.conf\" \"<>\"" +var nginxSdkInitContainerI13nScript = "\nNGINX_AGENT_DIR_FULL=$1\t\\n\nNGINX_AGENT_CONF_DIR_FULL=$2 \\n\nNGINX_CONFIG_FILE=$3 \\n\nNGINX_SID_PLACEHOLDER=$4 \\n\nNGINX_SID_VALUE=$5 \\n\necho \"Input Parameters: $@\" \\n\nset -x \\n\n\\n\ncp -ar /opt/opentelemetry/* ${NGINX_AGENT_DIR_FULL} \\n\n\\n\nNGINX_VERSION=$(cat ${NGINX_AGENT_CONF_DIR_FULL}/version.txt) \\n\nNGINX_AGENT_LOG_DIR=$(echo \"${NGINX_AGENT_DIR_FULL}/logs\" | sed 's,/,\\\\/,g') \\n\n\\n\ncat ${NGINX_AGENT_DIR_FULL}/conf/appdynamics_sdk_log4cxx.xml.template | sed 's,__agent_log_dir__,'${NGINX_AGENT_LOG_DIR}',g' > ${NGINX_AGENT_DIR_FULL}/conf/appdynamics_sdk_log4cxx.xml \\n\necho -e $OTEL_NGINX_AGENT_CONF > ${NGINX_AGENT_CONF_DIR_FULL}/opentelemetry_agent.conf \\n\nsed -i \"s,${NGINX_SID_PLACEHOLDER},${OTEL_NGINX_SERVICE_INSTANCE_ID},g\" ${NGINX_AGENT_CONF_DIR_FULL}/opentelemetry_agent.conf \\n\nsed -i \"1s,^,load_module ${NGINX_AGENT_DIR_FULL}/WebServerModule/Nginx/${NGINX_VERSION}/ngx_http_opentelemetry_module.so;\\\\n,g\" ${NGINX_AGENT_CONF_DIR_FULL}/${NGINX_CONFIG_FILE} \\n\nsed -i \"1s,^,env OTEL_RESOURCE_ATTRIBUTES;\\\\n,g\" ${NGINX_AGENT_CONF_DIR_FULL}/${NGINX_CONFIG_FILE} \\n\nmv ${NGINX_AGENT_CONF_DIR_FULL}/opentelemetry_agent.conf ${NGINX_AGENT_CONF_DIR_FULL}/conf.d \\n\n\t\t" + +func TestInjectNginxSDK(t *testing.T) { + + tests := []struct { + name string + v1alpha1.Nginx + pod corev1.Pod + expected corev1.Pod + }{ + { + name: "Clone Container not present", + Nginx: v1alpha1.Nginx{ + Image: "foo/bar:1", + Attrs: []corev1.EnvVar{ + { + Name: "NginxModuleOtelMaxQueueSize", + Value: "4096", + }, + }, + }, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + ObjectMeta: v1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + }, + expected: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: nginxAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: nginxAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: nginxAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /etc/nginx/* /opt/opentelemetry-webserver/source-conf && export NGINX_VERSION=$( { nginx -v ; } 2>&1 ) && echo ${NGINX_VERSION##*/} > /opt/opentelemetry-webserver/source-conf/version.txt"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }}, + }, + { + Name: nginxAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{nginxSdkInitContainerTestCommand}, + Env: []corev1.EnvVar{ + { + Name: nginxAttributesEnvVar, + Value: "NginxModuleEnabled ON;\nNginxModuleOtelExporterEndpoint http://otlp-endpoint:4317;\nNginxModuleOtelMaxQueueSize 4096;\nNginxModuleOtelSpanExporter otlp;\nNginxModuleResolveBackends ON;\nNginxModuleServiceInstanceId <>;\nNginxModuleServiceName nginx-service-name;\nNginxModuleServiceNamespace req-namespace;\nNginxModuleTraceAsError ON;\n", + }, + { + Name: "OTEL_NGINX_I13N_SCRIPT", + Value: nginxSdkInitContainerI13nScript, + }, + { + Name: nginxServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: "/etc/nginx", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "LD_LIBRARY_PATH", + Value: "/opt/opentelemetry-webserver/agent/sdk_lib/lib", + }, + }, + }, + }, + }, + }, + }, + // === Test ConfigFile configuration ============================= + { + name: "ConfigFile honored", + Nginx: v1alpha1.Nginx{ + Image: "foo/bar:1", + ConfigFile: "/opt/nginx/custom-nginx.conf", + }, + pod: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: nginxAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: nginxAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: nginxAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /opt/nginx/* /opt/opentelemetry-webserver/source-conf && export NGINX_VERSION=$( { nginx -v ; } 2>&1 ) && echo ${NGINX_VERSION##*/} > /opt/opentelemetry-webserver/source-conf/version.txt"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }}, + }, + { + Name: nginxAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{nginxSdkInitContainerTestCommandCustomFile}, + Env: []corev1.EnvVar{ + { + Name: nginxAttributesEnvVar, + Value: "NginxModuleEnabled ON;\nNginxModuleOtelExporterEndpoint http://otlp-endpoint:4317;\nNginxModuleOtelSpanExporter otlp;\nNginxModuleResolveBackends ON;\nNginxModuleServiceInstanceId <>;\nNginxModuleServiceName nginx-service-name;\nNginxModuleServiceNamespace req-namespace;\nNginxModuleTraceAsError ON;\n", + }, + { + Name: "OTEL_NGINX_I13N_SCRIPT", + Value: nginxSdkInitContainerI13nScript, + }, + { + Name: nginxServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: "/opt/nginx", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "LD_LIBRARY_PATH", + Value: "/opt/opentelemetry-webserver/agent/sdk_lib/lib", + }, + }, + }, + }, + }, + }}, + // === Test Removal of probes and lifecycle ============================= + { + name: "Probes removed on clone init container", + Nginx: v1alpha1.Nginx{ + Image: "foo/bar:1", + }, + pod: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + ReadinessProbe: &corev1.Probe{}, + StartupProbe: &corev1.Probe{}, + LivenessProbe: &corev1.Probe{}, + Lifecycle: &corev1.Lifecycle{}, + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: nginxAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: nginxAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: nginxAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /etc/nginx/* /opt/opentelemetry-webserver/source-conf && export NGINX_VERSION=$( { nginx -v ; } 2>&1 ) && echo ${NGINX_VERSION##*/} > /opt/opentelemetry-webserver/source-conf/version.txt"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }}, + }, + { + Name: nginxAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{nginxSdkInitContainerTestCommand}, + Env: []corev1.EnvVar{ + { + Name: nginxAttributesEnvVar, + Value: "NginxModuleEnabled ON;\nNginxModuleOtelExporterEndpoint http://otlp-endpoint:4317;\nNginxModuleOtelSpanExporter otlp;\nNginxModuleResolveBackends ON;\nNginxModuleServiceInstanceId <>;\nNginxModuleServiceName nginx-service-name;\nNginxModuleServiceNamespace req-namespace;\nNginxModuleTraceAsError ON;\n", + }, + { + Name: "OTEL_NGINX_I13N_SCRIPT", + Value: nginxSdkInitContainerI13nScript, + }, + { + Name: nginxServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: "/etc/nginx", + }, + }, + ReadinessProbe: &corev1.Probe{}, + StartupProbe: &corev1.Probe{}, + LivenessProbe: &corev1.Probe{}, + Lifecycle: &corev1.Lifecycle{}, + Env: []corev1.EnvVar{ + { + Name: "LD_LIBRARY_PATH", + Value: "/opt/opentelemetry-webserver/agent/sdk_lib/lib", + }, + }, + }, + }, + }, + }, + }, + // Pod Namespace specified + { + name: "Pod Namespace specified", + Nginx: v1alpha1.Nginx{Image: "foo/bar:1"}, + pod: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "my-namespace", + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Namespace: "my-namespace", + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: nginxAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: nginxAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: nginxAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /etc/nginx/* /opt/opentelemetry-webserver/source-conf && export NGINX_VERSION=$( { nginx -v ; } 2>&1 ) && echo ${NGINX_VERSION##*/} > /opt/opentelemetry-webserver/source-conf/version.txt"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }}, + }, + { + Name: nginxAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{nginxSdkInitContainerTestCommand}, + Env: []corev1.EnvVar{ + { + Name: nginxAttributesEnvVar, + Value: "NginxModuleEnabled ON;\nNginxModuleOtelExporterEndpoint http://otlp-endpoint:4317;\nNginxModuleOtelSpanExporter otlp;\nNginxModuleResolveBackends ON;\nNginxModuleServiceInstanceId <>;\nNginxModuleServiceName nginx-service-name;\nNginxModuleServiceNamespace my-namespace;\nNginxModuleTraceAsError ON;\n", + }, + { + Name: "OTEL_NGINX_I13N_SCRIPT", + Value: nginxSdkInitContainerI13nScript, + }, + { + Name: nginxServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: "/etc/nginx", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "LD_LIBRARY_PATH", + Value: "/opt/opentelemetry-webserver/agent/sdk_lib/lib", + }, + }, + }, + }, + }, + }, + }, + } + + resourceMap := map[string]string{ + string(semconv.K8SDeploymentNameKey): "nginx-service-name", + string(semconv.K8SNamespaceNameKey): "req-namespace", + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := injectNginxSDK(logr.Discard(), test.Nginx, test.pod, 0, "http://otlp-endpoint:4317", resourceMap) + assert.Equal(t, test.expected, pod) + }) + } +} + +func TestInjectNginxUnknownNamespace(t *testing.T) { + + tests := []struct { + name string + v1alpha1.Nginx + pod corev1.Pod + expected corev1.Pod + }{ + { + name: "Clone Container not present, unknown namespace", + Nginx: v1alpha1.Nginx{Image: "foo/bar:1"}, + pod: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: v1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: nginxAgentConfigVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: nginxAgentVolume, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: nginxAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /etc/nginx/* /opt/opentelemetry-webserver/source-conf && export NGINX_VERSION=$( { nginx -v ; } 2>&1 ) && echo ${NGINX_VERSION##*/} > /opt/opentelemetry-webserver/source-conf/version.txt"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }}, + }, + { + Name: nginxAgentInitContainerName, + Image: "foo/bar:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{nginxSdkInitContainerTestCommand}, + Env: []corev1.EnvVar{ + { + Name: nginxAttributesEnvVar, + Value: "NginxModuleEnabled ON;\nNginxModuleOtelExporterEndpoint http://otlp-endpoint:4317;\nNginxModuleOtelSpanExporter otlp;\nNginxModuleResolveBackends ON;\nNginxModuleServiceInstanceId <>;\nNginxModuleServiceName nginx-service-name;\nNginxModuleServiceNamespace nginx;\nNginxModuleTraceAsError ON;\n", + }, + { + Name: "OTEL_NGINX_I13N_SCRIPT", + Value: nginxSdkInitContainerI13nScript, + }, + { + Name: nginxServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: "/etc/nginx", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "LD_LIBRARY_PATH", + Value: "/opt/opentelemetry-webserver/agent/sdk_lib/lib", + }, + }, + }, + }, + }, + }, + }, + } + + resourceMap := map[string]string{ + string(semconv.K8SDeploymentNameKey): "nginx-service-name", + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := injectNginxSDK(logr.Discard(), test.Nginx, test.pod, 0, "http://otlp-endpoint:4317", resourceMap) + assert.Equal(t, test.expected, pod) + }) + } +} + +func TestNginxInitContainerMissing(t *testing.T) { + tests := []struct { + name string + pod corev1.Pod + expected bool + }{ + { + name: "InitContainer_Already_Inject", + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "istio-init", + }, + { + Name: nginxAgentInitContainerName, + }, + }, + }, + }, + expected: false, + }, + { + name: "InitContainer_Absent_1", + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "istio-init", + }, + }, + }, + }, + expected: true, + }, + { + name: "InitContainer_Absent_2", + pod: corev1.Pod{ + Spec: corev1.PodSpec{}, + }, + expected: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := isNginxInitContainerMissing(test.pod, nginxAgentInitContainerName) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/pkg/instrumentation/nodejs.go b/pkg/instrumentation/nodejs.go index d1a4e180e0..6d06bea363 100644 --- a/pkg/instrumentation/nodejs.go +++ b/pkg/instrumentation/nodejs.go @@ -21,8 +21,11 @@ import ( ) const ( - envNodeOptions = "NODE_OPTIONS" - nodeRequireArgument = " --require /otel-auto-instrumentation/autoinstrumentation.js" + envNodeOptions = "NODE_OPTIONS" + nodeRequireArgument = " --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js" + nodejsInitContainerName = initContainerName + "-nodejs" + nodejsVolumeName = volumeName + "-nodejs" + nodejsInstrMountPath = "/otel-auto-instrumentation-nodejs" ) func injectNodeJSSDK(nodeJSSpec v1alpha1.NodeJS, pod corev1.Pod, index int) (corev1.Pod, error) { @@ -53,25 +56,28 @@ func injectNodeJSSDK(nodeJSSpec v1alpha1.NodeJS, pod corev1.Pod, index int) (cor } container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, }) // We just inject Volumes and init containers for the first processed container - if isInitContainerMissing(pod) { + if isInitContainerMissing(pod, nodejsInitContainerName) { pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ - Name: volumeName, + Name: nodejsVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: volumeSize(nodeJSSpec.VolumeSizeLimit), + }, }}) pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{ - Name: initContainerName, - Image: nodeJSSpec.Image, - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Name: nodejsInitContainerName, + Image: nodeJSSpec.Image, + Command: []string{"cp", "-a", "/autoinstrumentation/.", nodejsInstrMountPath}, + Resources: nodeJSSpec.Resources, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, }}, }) } diff --git a/pkg/instrumentation/nodejs_test.go b/pkg/instrumentation/nodejs_test.go index 3054f029a9..4b5d22dd89 100644 --- a/pkg/instrumentation/nodejs_test.go +++ b/pkg/instrumentation/nodejs_test.go @@ -46,20 +46,22 @@ func TestInjectNodeJSSDK(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: "opentelemetry-auto-instrumentation-nodejs", VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-nodejs", Image: "foo/bar:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-nodejs"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-nodejs", + MountPath: "/otel-auto-instrumentation-nodejs", }}, }, }, @@ -67,14 +69,14 @@ func TestInjectNodeJSSDK(t *testing.T) { { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-nodejs", + MountPath: "/otel-auto-instrumentation-nodejs", }, }, Env: []corev1.EnvVar{ { Name: "NODE_OPTIONS", - Value: nodeRequireArgument, + Value: " --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js", }, }, }, @@ -85,7 +87,7 @@ func TestInjectNodeJSSDK(t *testing.T) { }, { name: "NODE_OPTIONS defined", - NodeJS: v1alpha1.NodeJS{Image: "foo/bar:1"}, + NodeJS: v1alpha1.NodeJS{Image: "foo/bar:1", Resources: testResourceRequirements}, pod: corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -104,35 +106,38 @@ func TestInjectNodeJSSDK(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: "opentelemetry-auto-instrumentation-nodejs", VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-nodejs", Image: "foo/bar:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-nodejs"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-nodejs", + MountPath: "/otel-auto-instrumentation-nodejs", }}, + Resources: testResourceRequirements, }, }, Containers: []corev1.Container{ { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-nodejs", + MountPath: "/otel-auto-instrumentation-nodejs", }, }, Env: []corev1.EnvVar{ { Name: "NODE_OPTIONS", - Value: "-Dbaz=bar" + nodeRequireArgument, + Value: "-Dbaz=bar" + " --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js", }, }, }, diff --git a/pkg/instrumentation/podmutator.go b/pkg/instrumentation/podmutator.go index c18c661326..0a7878ae19 100644 --- a/pkg/instrumentation/podmutator.go +++ b/pkg/instrumentation/podmutator.go @@ -17,15 +17,18 @@ package instrumentation import ( "context" "errors" + "fmt" "strings" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/webhookhandler" + "github.com/open-telemetry/opentelemetry-operator/internal/webhook/podmutation" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" ) var ( @@ -37,19 +40,160 @@ type instPodMutator struct { Client client.Client sdkInjector *sdkInjector Logger logr.Logger + Recorder record.EventRecorder +} + +type instrumentationWithContainers struct { + Instrumentation *v1alpha1.Instrumentation + Containers string + AdditionalAnnotations map[string]string } type languageInstrumentations struct { - Java *v1alpha1.Instrumentation - NodeJS *v1alpha1.Instrumentation - Python *v1alpha1.Instrumentation - DotNet *v1alpha1.Instrumentation - Sdk *v1alpha1.Instrumentation + Java instrumentationWithContainers + NodeJS instrumentationWithContainers + Python instrumentationWithContainers + DotNet instrumentationWithContainers + ApacheHttpd instrumentationWithContainers + Nginx instrumentationWithContainers + Go instrumentationWithContainers + Sdk instrumentationWithContainers +} + +// Check if single instrumentation is configured for Pod and return which is configured. +func (langInsts languageInstrumentations) isSingleInstrumentationEnabled() bool { + count := 0 + + if langInsts.Java.Instrumentation != nil { + count++ + } + if langInsts.NodeJS.Instrumentation != nil { + count++ + } + if langInsts.Python.Instrumentation != nil { + count++ + } + if langInsts.DotNet.Instrumentation != nil { + count++ + } + if langInsts.ApacheHttpd.Instrumentation != nil { + count++ + } + if langInsts.Nginx.Instrumentation != nil { + count++ + } + if langInsts.Go.Instrumentation != nil { + count++ + } + if langInsts.Sdk.Instrumentation != nil { + count++ + } + + return count == 1 } -var _ webhookhandler.PodMutator = (*instPodMutator)(nil) +// Check if specific containers are provided for configured instrumentation. +func (langInsts languageInstrumentations) areContainerNamesConfiguredForMultipleInstrumentations() (bool, error) { + var instrWithoutContainers int + var instrWithContainers int + var allContainers []string + + // Check for instrumentations with and without containers. + if langInsts.Java.Instrumentation != nil { + instrWithContainers += isInstrWithContainers(langInsts.Java) + instrWithoutContainers += isInstrWithoutContainers(langInsts.Java) + allContainers = append(allContainers, langInsts.Java.Containers) + } + if langInsts.NodeJS.Instrumentation != nil { + instrWithContainers += isInstrWithContainers(langInsts.NodeJS) + instrWithoutContainers += isInstrWithoutContainers(langInsts.NodeJS) + allContainers = append(allContainers, langInsts.NodeJS.Containers) + } + if langInsts.Python.Instrumentation != nil { + instrWithContainers += isInstrWithContainers(langInsts.Python) + instrWithoutContainers += isInstrWithoutContainers(langInsts.Python) + allContainers = append(allContainers, langInsts.Python.Containers) + } + if langInsts.DotNet.Instrumentation != nil { + instrWithContainers += isInstrWithContainers(langInsts.DotNet) + instrWithoutContainers += isInstrWithoutContainers(langInsts.DotNet) + allContainers = append(allContainers, langInsts.DotNet.Containers) + } + if langInsts.ApacheHttpd.Instrumentation != nil { + instrWithContainers += isInstrWithContainers(langInsts.ApacheHttpd) + instrWithoutContainers += isInstrWithoutContainers(langInsts.ApacheHttpd) + allContainers = append(allContainers, langInsts.ApacheHttpd.Containers) + } + if langInsts.Nginx.Instrumentation != nil { + instrWithContainers += isInstrWithContainers(langInsts.Nginx) + instrWithoutContainers += isInstrWithoutContainers(langInsts.Nginx) + allContainers = append(allContainers, langInsts.Nginx.Containers) + } + if langInsts.Go.Instrumentation != nil { + instrWithContainers += isInstrWithContainers(langInsts.Go) + instrWithoutContainers += isInstrWithoutContainers(langInsts.Go) + allContainers = append(allContainers, langInsts.Go.Containers) + } + if langInsts.Sdk.Instrumentation != nil { + instrWithContainers += isInstrWithContainers(langInsts.Sdk) + instrWithoutContainers += isInstrWithoutContainers(langInsts.Sdk) + allContainers = append(allContainers, langInsts.Sdk.Containers) + } + + // Look for duplicated containers. + containerDuplicates := findDuplicatedContainers(allContainers) + if containerDuplicates != nil { + return false, containerDuplicates + } + + // Look for mixed multiple instrumentations with and without container names. + if instrWithoutContainers > 0 && instrWithContainers > 0 { + return false, fmt.Errorf("incorrect instrumentation configuration - please provide container names for all instrumentations") + } + + // Look for multiple instrumentations without container names. + if instrWithoutContainers > 1 && instrWithContainers == 0 { + return false, fmt.Errorf("incorrect instrumentation configuration - please provide container names for all instrumentations") + } + + if instrWithoutContainers == 0 && instrWithContainers == 0 { + return false, fmt.Errorf("instrumentation configuration not provided") + } + + return true, nil +} -func NewMutator(logger logr.Logger, client client.Client) *instPodMutator { +// Set containers for configured instrumentation. +func (langInsts *languageInstrumentations) setInstrumentationLanguageContainers(containers string) { + if langInsts.Java.Instrumentation != nil { + langInsts.Java.Containers = containers + } + if langInsts.NodeJS.Instrumentation != nil { + langInsts.NodeJS.Containers = containers + } + if langInsts.Python.Instrumentation != nil { + langInsts.Python.Containers = containers + } + if langInsts.DotNet.Instrumentation != nil { + langInsts.DotNet.Containers = containers + } + if langInsts.ApacheHttpd.Instrumentation != nil { + langInsts.ApacheHttpd.Containers = containers + } + if langInsts.Nginx.Instrumentation != nil { + langInsts.Nginx.Containers = containers + } + if langInsts.Go.Instrumentation != nil { + langInsts.Go.Containers = containers + } + if langInsts.Sdk.Instrumentation != nil { + langInsts.Sdk.Containers = containers + } +} + +var _ podmutation.PodMutator = (*instPodMutator)(nil) + +func NewMutator(logger logr.Logger, client client.Client, recorder record.EventRecorder) *instPodMutator { return &instPodMutator{ Logger: logger, Client: client, @@ -57,6 +201,7 @@ func NewMutator(logger logr.Logger, client client.Client) *instPodMutator { logger: logger, client: client, }, + Recorder: recorder, } } @@ -81,50 +226,138 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod") return pod, err } - insts.Java = inst + if featuregate.EnableJavaAutoInstrumentationSupport.IsEnabled() || inst == nil { + insts.Java.Instrumentation = inst + } else { + logger.Error(nil, "support for Java auto instrumentation is not enabled") + pm.Recorder.Event(pod.DeepCopy(), "Warning", "InstrumentationRequestRejected", "support for Java auto instrumentation is not enabled") + } if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectNodeJS); err != nil { // we still allow the pod to be created, but we log a message to the operator's logs logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod") return pod, err } - insts.NodeJS = inst + if featuregate.EnableNodeJSAutoInstrumentationSupport.IsEnabled() || inst == nil { + insts.NodeJS.Instrumentation = inst + } else { + logger.Error(nil, "support for NodeJS auto instrumentation is not enabled") + pm.Recorder.Event(pod.DeepCopy(), "Warning", "InstrumentationRequestRejected", "support for NodeJS auto instrumentation is not enabled") + } if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectPython); err != nil { // we still allow the pod to be created, but we log a message to the operator's logs logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod") return pod, err } - insts.Python = inst + if featuregate.EnablePythonAutoInstrumentationSupport.IsEnabled() || inst == nil { + insts.Python.Instrumentation = inst + } else { + logger.Error(nil, "support for Python auto instrumentation is not enabled") + pm.Recorder.Event(pod.DeepCopy(), "Warning", "InstrumentationRequestRejected", "support for Python auto instrumentation is not enabled") + } if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectDotNet); err != nil { // we still allow the pod to be created, but we log a message to the operator's logs logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod") return pod, err } - insts.DotNet = inst + if featuregate.EnableDotnetAutoInstrumentationSupport.IsEnabled() || inst == nil { + insts.DotNet.Instrumentation = inst + insts.DotNet.AdditionalAnnotations = map[string]string{annotationDotNetRuntime: annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationDotNetRuntime)} + } else { + logger.Error(nil, "support for .NET auto instrumentation is not enabled") + pm.Recorder.Event(pod.DeepCopy(), "Warning", "InstrumentationRequestRejected", "support for .NET auto instrumentation is not enabled") + } + + if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectGo); err != nil { + // we still allow the pod to be created, but we log a message to the operator's logs + logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod") + return pod, err + } + if featuregate.EnableGoAutoInstrumentationSupport.IsEnabled() || inst == nil { + insts.Go.Instrumentation = inst + } else { + logger.Error(err, "support for Go auto instrumentation is not enabled") + pm.Recorder.Event(pod.DeepCopy(), "Warning", "InstrumentationRequestRejected", "support for Go auto instrumentation is not enabled") + } + + if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectApacheHttpd); err != nil { + // we still allow the pod to be created, but we log a message to the operator's logs + logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod") + return pod, err + } + if featuregate.EnableApacheHTTPAutoInstrumentationSupport.IsEnabled() || inst == nil { + insts.ApacheHttpd.Instrumentation = inst + } else { + logger.Error(nil, "support for Apache HTTPD auto instrumentation is not enabled") + pm.Recorder.Event(pod.DeepCopy(), "Warning", "InstrumentationRequestRejected", "support for Apache HTTPD auto instrumentation is not enabled") + } + + if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectNginx); err != nil { + // we still allow the pod to be created, but we log a message to the operator's logs + logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod") + return pod, err + } + if featuregate.EnableNginxAutoInstrumentationSupport.IsEnabled() || inst == nil { + insts.Nginx.Instrumentation = inst + } else { + logger.Error(nil, "support for Nginx auto instrumentation is not enabled") + pm.Recorder.Event(pod.DeepCopy(), "Warning", "InstrumentationRequestRejected", "support for Nginx auto instrumentation is not enabled") + } if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectSdk); err != nil { // we still allow the pod to be created, but we log a message to the operator's logs logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod") return pod, err } - insts.Sdk = inst + insts.Sdk.Instrumentation = inst + + if insts.Java.Instrumentation == nil && insts.NodeJS.Instrumentation == nil && insts.Python.Instrumentation == nil && + insts.DotNet.Instrumentation == nil && insts.Go.Instrumentation == nil && insts.ApacheHttpd.Instrumentation == nil && + insts.Nginx.Instrumentation == nil && + insts.Sdk.Instrumentation == nil { - if insts.Java == nil && insts.NodeJS == nil && insts.Python == nil && insts.DotNet == nil && insts.Sdk == nil { logger.V(1).Info("annotation not present in deployment, skipping instrumentation injection") return pod, nil } // We retrieve the annotation for podname - var targetContainers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectContainerName) + if featuregate.EnableMultiInstrumentationSupport.IsEnabled() { + // We use annotations specific for instrumentation language + insts.Java.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectJavaContainersName) + insts.NodeJS.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectNodeJSContainersName) + insts.Python.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectPythonContainersName) + insts.DotNet.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectDotnetContainersName) + insts.Go.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectGoContainersName) + insts.ApacheHttpd.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectApacheHttpdContainersName) + insts.Nginx.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectNginxContainersName) + insts.Sdk.Containers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectSdkContainersName) + + // We check if provided annotations and instrumentations are valid + ok, msg := insts.areContainerNamesConfiguredForMultipleInstrumentations() + if !ok { + logger.V(1).Error(msg, "skipping instrumentation injection") + return pod, nil + } + } else { + // We use general annotation for container names + // only when multi instrumentation is disabled + singleInstrEnabled := insts.isSingleInstrumentationEnabled() + if singleInstrEnabled { + generalContainerNames := annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectContainerName) + insts.setInstrumentationLanguageContainers(generalContainerNames) + } else { + logger.V(1).Error(fmt.Errorf("multiple injection annotations present"), "skipping instrumentation injection") + return pod, nil + } + + } // once it's been determined that instrumentation is desired, none exists yet, and we know which instance it should talk to, // we should inject the instrumentation. modifiedPod := pod - for _, currentContainer := range strings.Split(targetContainers, ",") { - modifiedPod = pm.sdkInjector.inject(ctx, insts, ns, modifiedPod, strings.TrimSpace(currentContainer)) - } + modifiedPod = pm.sdkInjector.inject(ctx, insts, ns, modifiedPod) return modifiedPod, nil } diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index 3418b5533b..7e764abf07 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -22,23 +22,30 @@ import ( "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + colfeaturegate "go.opentelemetry.io/collector/featuregate" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" ) func TestMutatePod(t *testing.T) { - mutator := NewMutator(logr.Discard(), k8sClient) + mutator := NewMutator(logr.Discard(), k8sClient, record.NewFakeRecorder(100)) require.NotNil(t, mutator) + true := true + zero := int64(0) + tests := []struct { - name string - err string - pod corev1.Pod - expected corev1.Pod - inst v1alpha1.Instrumentation - ns corev1.Namespace + name string + err string + pod corev1.Pod + expected corev1.Pod + inst v1alpha1.Instrumentation + ns corev1.Namespace + setFeatureGates func(t *testing.T) }{ { name: "javaagent injection, true", @@ -68,6 +75,7 @@ func TestMutatePod(t *testing.T) { Value: "false", }, }, + Resources: testResourceRequirements, }, Env: []corev1.EnvVar{ { @@ -123,20 +131,23 @@ func TestMutatePod(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: "opentelemetry-auto-instrumentation", + Name: javaVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, - Command: []string{"cp", "/javaagent.jar", "/otel-auto-instrumentation/javaagent.jar"}, + Name: javaInitContainerName, + Command: []string{"cp", "/javaagent.jar", javaInstrMountPath + "/javaagent.jar"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: javaVolumeName, + MountPath: javaInstrMountPath, }}, + Resources: testResourceRequirements, }, }, Containers: []corev1.Container{ @@ -210,8 +221,8 @@ func TestMutatePod(t *testing.T) { }, VolumeMounts: []corev1.VolumeMount{ { - Name: "opentelemetry-auto-instrumentation", - MountPath: "/otel-auto-instrumentation", + Name: javaVolumeName, + MountPath: javaInstrMountPath, }, }, }, @@ -220,26 +231,34 @@ func TestMutatePod(t *testing.T) { }, }, { - name: "nodejs injection, true", + name: "javaagent injection multiple containers, true", ns: corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "nodejs", + Name: "javaagent-multiple-containers", }, }, inst: v1alpha1.Instrumentation{ ObjectMeta: metav1.ObjectMeta{ Name: "example-inst", - Namespace: "nodejs", + Namespace: "javaagent-multiple-containers", }, Spec: v1alpha1.InstrumentationSpec{ - NodeJS: v1alpha1.NodeJS{ - Image: "otel/nodejs:1", + Java: v1alpha1.Java{ Env: []corev1.EnvVar{ { - Name: "OTEL_NODEJS_DEBUG", + Name: "OTEL_JAVAAGENT_DEBUG", Value: "true", }, + { + Name: "OTEL_INSTRUMENTATION_JDBC_ENABLED", + Value: "false", + }, + { + Name: "SPLUNK_PROFILER_ENABLED", + Value: "false", + }, }, + Resources: testResourceRequirements, }, Env: []corev1.EnvVar{ { @@ -275,13 +294,17 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectNodeJS: "true", + annotationInjectJava: "true", + annotationInjectContainerName: "app1,app2", }, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "app", + Name: "app1", + }, + { + Name: "app2", }, }, }, @@ -289,40 +312,51 @@ func TestMutatePod(t *testing.T) { expected: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectNodeJS: "true", + annotationInjectJava: "true", + annotationInjectContainerName: "app1,app2", }, }, Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: "opentelemetry-auto-instrumentation", + Name: javaVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, - Image: "otel/nodejs:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Name: javaInitContainerName, + Command: []string{"cp", "/javaagent.jar", javaInstrMountPath + "/javaagent.jar"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: javaVolumeName, + MountPath: javaInstrMountPath, }}, + Resources: testResourceRequirements, }, }, Containers: []corev1.Container{ { - Name: "app", + Name: "app1", Env: []corev1.EnvVar{ { - Name: "OTEL_NODEJS_DEBUG", + Name: "OTEL_JAVAAGENT_DEBUG", Value: "true", }, { - Name: "NODE_OPTIONS", - Value: nodeRequireArgument, + Name: "OTEL_INSTRUMENTATION_JDBC_ENABLED", + Value: "false", + }, + { + Name: "SPLUNK_PROFILER_ENABLED", + Value: "false", + }, + { + Name: "JAVA_TOOL_OPTIONS", + Value: javaJVMArgument, }, { Name: "OTEL_TRACES_EXPORTER", @@ -350,7 +384,7 @@ func TestMutatePod(t *testing.T) { }, { Name: "OTEL_SERVICE_NAME", - Value: "app", + Value: "app1", }, { Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", @@ -370,13 +404,88 @@ func TestMutatePod(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=app,k8s.namespace.name=nodejs,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + Value: "k8s.container.name=app1,k8s.namespace.name=javaagent-multiple-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: javaVolumeName, + MountPath: javaInstrMountPath, + }, + }, + }, + { + Name: "app2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_JAVAAGENT_DEBUG", + Value: "true", + }, + { + Name: "OTEL_INSTRUMENTATION_JDBC_ENABLED", + Value: "false", + }, + { + Name: "SPLUNK_PROFILER_ENABLED", + Value: "false", + }, + { + Name: "JAVA_TOOL_OPTIONS", + Value: javaJVMArgument, + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app2", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app2,k8s.namespace.name=javaagent-multiple-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", }, }, VolumeMounts: []corev1.VolumeMount{ { - Name: "opentelemetry-auto-instrumentation", - MountPath: "/otel-auto-instrumentation", + Name: javaVolumeName, + MountPath: javaInstrMountPath, }, }, }, @@ -385,43 +494,132 @@ func TestMutatePod(t *testing.T) { }, }, { - name: "python injection, true", + name: "javaagent injection feature gate disabled", ns: corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "python", + Name: "javaagent-disabled", }, }, inst: v1alpha1.Instrumentation{ ObjectMeta: metav1.ObjectMeta{ Name: "example-inst", - Namespace: "python", + Namespace: "javaagent-disabled", }, Spec: v1alpha1.InstrumentationSpec{ - Python: v1alpha1.Python{ - Image: "otel/python:1", + Java: v1alpha1.Java{ Env: []corev1.EnvVar{ { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: "OTEL_TRACES_EXPORTER", - Value: "otlp_proto_http", + Name: "OTEL_JAVAAGENT_DEBUG", + Value: "true", }, { - Name: "OTEL_METRICS_EXPORTER", - Value: "none", + Name: "OTEL_INSTRUMENTATION_JDBC_ENABLED", + Value: "false", }, { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://localhost:4317", + Name: "SPLUNK_PROFILER_ENABLED", + Value: "false", }, }, }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, Exporter: v1alpha1.Exporter{ Endpoint: "http://collector:12345", }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectJava: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectJava: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableJavaAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableJavaAutoInstrumentationSupport.ID(), false)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableJavaAutoInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "nodejs injection, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nodejs", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "nodejs", + }, + Spec: v1alpha1.InstrumentationSpec{ + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_NODEJS_DEBUG", + Value: "true", + }, + }, + }, Env: []corev1.EnvVar{ + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, { Name: "OTEL_EXPORTER_OTLP_TIMEOUT", Value: "20", @@ -439,12 +637,15 @@ func TestMutatePod(t *testing.T) { Value: "true", }, }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, }, }, pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectPython: "true", + annotationInjectNodeJS: "true", }, }, Spec: corev1.PodSpec{ @@ -458,26 +659,28 @@ func TestMutatePod(t *testing.T) { expected: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectPython: "true", + annotationInjectNodeJS: "true", }, }, Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: "opentelemetry-auto-instrumentation", + Name: nodejsVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, - Image: "otel/python:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Name: nodejsInitContainerName, + Image: "otel/nodejs:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", nodejsInstrMountPath}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, }}, }, }, @@ -486,25 +689,21 @@ func TestMutatePod(t *testing.T) { Name: "app", Env: []corev1.EnvVar{ { - Name: "OTEL_LOG_LEVEL", - Value: "debug", + Name: "OTEL_NODEJS_DEBUG", + Value: "true", }, { - Name: "OTEL_TRACES_EXPORTER", - Value: "otlp_proto_http", + Name: "NODE_OPTIONS", + Value: nodeRequireArgument, }, { - Name: "OTEL_METRICS_EXPORTER", - Value: "none", + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", }, { Name: "OTEL_EXPORTER_OTLP_ENDPOINT", Value: "http://localhost:4317", }, - { - Name: "PYTHONPATH", - Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), - }, { Name: "OTEL_EXPORTER_OTLP_TIMEOUT", Value: "20", @@ -543,13 +742,13 @@ func TestMutatePod(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=app,k8s.namespace.name=python,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + Value: "k8s.container.name=app,k8s.namespace.name=nodejs,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", }, }, VolumeMounts: []corev1.VolumeMount{ { - Name: "opentelemetry-auto-instrumentation", - MountPath: "/otel-auto-instrumentation", + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, }, }, }, @@ -558,35 +757,36 @@ func TestMutatePod(t *testing.T) { }, }, { - name: "dotnet injection, true", + name: "nodejs injection multiple containers, true", ns: corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "dotnet", + Name: "nodejs-multiple-containers", }, }, inst: v1alpha1.Instrumentation{ ObjectMeta: metav1.ObjectMeta{ Name: "example-inst", - Namespace: "dotnet", + Namespace: "nodejs-multiple-containers", }, Spec: v1alpha1.InstrumentationSpec{ - DotNet: v1alpha1.DotNet{ - Image: "otel/dotnet:1", + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", Env: []corev1.EnvVar{ { - Name: "OTEL_LOG_LEVEL", - Value: "debug", - }, - { - Name: "OTEL_EXPORTER_OTLP_ENDPOINT", - Value: "http://localhost:4317", + Name: "OTEL_NODEJS_DEBUG", + Value: "true", }, }, }, - Exporter: v1alpha1.Exporter{ - Endpoint: "http://collector:12345", - }, Env: []corev1.EnvVar{ + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, { Name: "OTEL_EXPORTER_OTLP_TIMEOUT", Value: "20", @@ -604,18 +804,25 @@ func TestMutatePod(t *testing.T) { Value: "true", }, }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, }, }, pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectDotNet: "true", + annotationInjectNodeJS: "true", + annotationInjectContainerName: "app1,app2", }, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "app", + Name: "app1", + }, + { + Name: "app2", }, }, }, @@ -623,68 +830,118 @@ func TestMutatePod(t *testing.T) { expected: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectDotNet: "true", + annotationInjectNodeJS: "true", + annotationInjectContainerName: "app1,app2", }, }, Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: "opentelemetry-auto-instrumentation", + Name: nodejsVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, - Image: "otel/dotnet:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Name: nodejsInitContainerName, + Image: "otel/nodejs:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", nodejsInstrMountPath}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, }}, }, }, Containers: []corev1.Container{ { - Name: "app", + Name: "app1", Env: []corev1.EnvVar{ { - Name: "OTEL_LOG_LEVEL", - Value: "debug", + Name: "OTEL_NODEJS_DEBUG", + Value: "true", + }, + { + Name: "NODE_OPTIONS", + Value: nodeRequireArgument, + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", }, { Name: "OTEL_EXPORTER_OTLP_ENDPOINT", Value: "http://localhost:4317", }, { - Name: envDotNetCoreClrEnableProfiling, - Value: dotNetCoreClrEnableProfilingEnabled, + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", }, { - Name: envDotNetCoreClrProfiler, - Value: dotNetCoreClrProfilerId, + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", }, { - Name: envDotNetCoreClrProfilerPath, - Value: dotNetCoreClrProfilerPath, + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", }, { - Name: envDotNetStartupHook, - Value: dotNetStartupHookPath, + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", }, { - Name: envDotNetAdditionalDeps, - Value: dotNetAdditionalDepsPath, + Name: "OTEL_SERVICE_NAME", + Value: "app1", }, { - Name: envDotNetOTelAutoHome, - Value: dotNetOTelAutoHomePath, + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, }, { - Name: envDotNetSharedStore, - Value: dotNetSharedStorePath, + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app1,k8s.namespace.name=nodejs-multiple-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, + }, + }, + }, + { + Name: "app2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_NODEJS_DEBUG", + Value: "true", + }, + { + Name: "NODE_OPTIONS", + Value: nodeRequireArgument, + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", }, { Name: "OTEL_EXPORTER_OTLP_TIMEOUT", @@ -704,7 +961,7 @@ func TestMutatePod(t *testing.T) { }, { Name: "OTEL_SERVICE_NAME", - Value: "app", + Value: "app2", }, { Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", @@ -724,13 +981,13 @@ func TestMutatePod(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=app,k8s.namespace.name=dotnet,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + Value: "k8s.container.name=app2,k8s.namespace.name=nodejs-multiple-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", }, }, VolumeMounts: []corev1.VolumeMount{ { - Name: "opentelemetry-auto-instrumentation", - MountPath: "/otel-auto-instrumentation", + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, }, }, }, @@ -739,60 +996,52 @@ func TestMutatePod(t *testing.T) { }, }, { - name: "missing annotation", + name: "nodejs injection feature gate disabled", ns: corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "missing-annotation", + Name: "nodejs-disabled", }, }, inst: v1alpha1.Instrumentation{ ObjectMeta: metav1.ObjectMeta{ Name: "example-inst", - Namespace: "missing-annotation", + Namespace: "nodejs-disabled", }, Spec: v1alpha1.InstrumentationSpec{ - Java: v1alpha1.Java{ - Image: "otel/java:1", - }, - Exporter: v1alpha1.Exporter{ - Endpoint: "http://collector:12345", + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_NODEJS_DEBUG", + Value: "true", + }, + }, }, - }, - }, - pod: corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ + Env: []corev1.EnvVar{ { - Name: "app", + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", }, - }, - }, - }, - expected: corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ { - Name: "app", + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", }, - }, - }, - }, - }, - { - name: "annotation set to false", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "annotation-false", - }, - }, - inst: v1alpha1.Instrumentation{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-inst", - Namespace: "annotation-false", - }, - Spec: v1alpha1.InstrumentationSpec{ - Java: v1alpha1.Java{ - Image: "otel/java:1", }, Exporter: v1alpha1.Exporter{ Endpoint: "http://collector:12345", @@ -802,7 +1051,7 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "false", + annotationInjectNodeJS: "true", }, }, Spec: corev1.PodSpec{ @@ -816,7 +1065,7 @@ func TestMutatePod(t *testing.T) { expected: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "false", + annotationInjectNodeJS: "true", }, }, Spec: corev1.PodSpec{ @@ -827,32 +1076,75 @@ func TestMutatePod(t *testing.T) { }, }, }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableNodeJSAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNodeJSAutoInstrumentationSupport.ID(), false)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNodeJSAutoInstrumentationSupport.ID(), originalVal)) + }) + }, }, { - name: "annotation set to non existing instance", + name: "python injection, true", ns: corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "non-existing-instance", + Name: "python", }, }, inst: v1alpha1.Instrumentation{ ObjectMeta: metav1.ObjectMeta{ Name: "example-inst", - Namespace: "non-existing-instance", + Namespace: "python", }, Spec: v1alpha1.InstrumentationSpec{ - Java: v1alpha1.Java{ - Image: "otel/java:1", + Python: v1alpha1.Python{ + Image: "otel/python:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4318", + }, + }, }, Exporter: v1alpha1.Exporter{ Endpoint: "http://collector:12345", }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, }, }, pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "doesnotexists", + annotationInjectPython: "true", }, }, Spec: corev1.PodSpec{ @@ -863,27 +1155,4170 @@ func TestMutatePod(t *testing.T) { }, }, }, - err: `instrumentations.opentelemetry.io "doesnotexists" not found`, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := k8sClient.Create(context.Background(), &test.ns) - require.NoError(t, err) - defer func() { - _ = k8sClient.Delete(context.Background(), &test.ns) - }() - err = k8sClient.Create(context.Background(), &test.inst) - require.NoError(t, err) - - pod, err := mutator.Mutate(context.Background(), test.ns, test.pod) - if test.err == "" { - require.NoError(t, err) - assert.Equal(t, test.expected, pod) - } else { - assert.Contains(t, err.Error(), test.err) - } + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectPython: "true", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: pythonVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: pythonInitContainerName, + Image: "otel/python:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", pythonInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "app", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4318", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=python,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }, + }, + }, + }, + }, + }, + }, + { + name: "python injection multiple containers, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "python-multiple-containers", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "python-multiple-containers", + }, + Spec: v1alpha1.InstrumentationSpec{ + Python: v1alpha1.Python{ + Image: "otel/python:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4318", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectPython: "true", + annotationInjectContainerName: "app1,app2", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app1", + }, + { + Name: "app2", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectPython: "true", + annotationInjectContainerName: "app1,app2", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: pythonVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: pythonInitContainerName, + Image: "otel/python:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", pythonInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "app1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4318", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app1", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app1,k8s.namespace.name=python-multiple-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }, + }, + }, + { + Name: "app2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4318", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app2", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app2,k8s.namespace.name=python-multiple-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }, + }, + }, + }, + }, + }, + }, + { + name: "python injection feature gate disabled", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "python-disabled", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "python-disabled", + }, + Spec: v1alpha1.InstrumentationSpec{ + Python: v1alpha1.Python{ + Image: "otel/python:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4318", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectPython: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectPython: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnablePythonAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnablePythonAutoInstrumentationSupport.ID(), false)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnablePythonAutoInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "dotnet injection, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dotnet", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "dotnet", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationDotNetRuntime: dotNetRuntimeLinuxMusl, + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationDotNetRuntime: dotNetRuntimeLinuxMusl, + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: dotnetVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: dotnetInitContainerName, + Image: "otel/dotnet:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", dotnetInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "app", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerMuslPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=dotnet,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }, + }, + }, + }, + }, + }, + }, + { + name: "dotnet injection, by namespace annotations", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dotnet-by-namespace-annotation", + Annotations: map[string]string{ + annotationInjectDotNet: "example-inst", + annotationDotNetRuntime: dotNetRuntimeLinuxMusl, + }, + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "dotnet-by-namespace-annotation", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{}, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: dotnetVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: dotnetInitContainerName, + Image: "otel/dotnet:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", dotnetInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "app", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerMuslPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=dotnet-by-namespace-annotation,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }, + }, + }, + }, + }, + }, + }, + { + name: "dotnet injection multiple containers, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dotnet-multiple-containers", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "dotnet-multiple-containers", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectContainerName: "app1,app2", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app1", + }, + { + Name: "app2", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectContainerName: "app1,app2", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: dotnetVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: dotnetInitContainerName, + Image: "otel/dotnet:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", dotnetInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "app1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app1", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app1,k8s.namespace.name=dotnet-multiple-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }, + }, + }, + { + Name: "app2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app2", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app2,k8s.namespace.name=dotnet-multiple-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }, + }, + }, + }, + }, + }, + }, + { + name: "dotnet injection feature gate disabled", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "dotnet-disabled", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "dotnet-disabled", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableDotnetAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableDotnetAutoInstrumentationSupport.ID(), false)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableDotnetAutoInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "go injection, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "go", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "go", + }, + Spec: v1alpha1.InstrumentationSpec{ + Go: v1alpha1.Go{ + Image: "otel/go:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectGo: "true", + annotationGoExecPath: "/app", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectGo: "true", + annotationGoExecPath: "/app", + }, + }, + Spec: corev1.PodSpec{ + ShareProcessNamespace: &true, + Containers: []corev1.Container{ + { + Name: "app", + }, + { + Name: sideCarName, + Image: "otel/go:1", + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &zero, + Privileged: &true, + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/sys/kernel/debug", + Name: kernelDebugVolumeName, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_GO_AUTO_TARGET_EXE", + Value: "/app", + }, + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=go,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: kernelDebugVolumeName, + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: kernelDebugVolumePath, + }, + }, + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableGoAutoInstrumentationSupport.IsEnabled() + mtVal := featuregate.EnableMultiInstrumentationSupport.IsEnabled() + + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), true)) + + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableGoAutoInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableGoAutoInstrumentationSupport.ID(), originalVal)) + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), mtVal)) + + }) + }, + }, + { + name: "go injection feature gate disabled", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "go-disabled", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "go-disabled", + }, + Spec: v1alpha1.InstrumentationSpec{ + Go: v1alpha1.Go{ + Image: "otel/go:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectGo: "true", + annotationGoExecPath: "/app", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectGo: "true", + annotationGoExecPath: "/app", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + }, + { + name: "apache httpd injection, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "apache-httpd", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "apache-httpd", + }, + Spec: v1alpha1.InstrumentationSpec{ + ApacheHttpd: v1alpha1.ApacheHttpd{ + Image: "otel/apache-httpd:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{}, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectApacheHttpd: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectApacheHttpd: "true", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "otel-apache-conf-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: "otel-apache-agent", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: apacheAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /usr/local/apache2/conf/* " + apacheAgentDirectory + apacheAgentConfigDirectory}, + VolumeMounts: []corev1.VolumeMount{{ + Name: apacheAgentConfigVolume, + MountPath: apacheAgentDirectory + apacheAgentConfigDirectory, + }}, + }, + { + Name: apacheAgentInitContainerName, + Image: "otel/apache-httpd:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{ + "cp -r /opt/opentelemetry/* /opt/opentelemetry-webserver/agent && export agentLogDir=$(echo \"/opt/opentelemetry-webserver/agent/logs\" | sed 's,/,\\\\/,g') && cat /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml.template | sed 's/__agent_log_dir__/'${agentLogDir}'/g' > /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml &&echo \"$OTEL_APACHE_AGENT_CONF\" > /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && sed -i 's/<>/'${APACHE_SERVICE_INSTANCE_ID}'/g' /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && echo 'Include /usr/local/apache2/conf/opentemetry_agent.conf' >> /opt/opentelemetry-webserver/source-conf/httpd.conf"}, + Env: []corev1.EnvVar{ + { + Name: apacheAttributesEnvVar, + Value: "\n#Load the Otel Webserver SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_common.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_resources.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_trace.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_otlp_recordable.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_ostream_span.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_otlp_grpc.so\n#Load the Otel ApacheModule SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_webserver_sdk.so\n#Load the Apache Module. In this example for Apache 2.4\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Load the Apache Module. In this example for Apache 2.2\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel22.so\nLoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Attributes\nApacheModuleEnabled ON\nApacheModuleOtelExporterEndpoint http://collector:12345\nApacheModuleOtelSpanExporter otlp\nApacheModuleResolveBackends ON\nApacheModuleServiceInstanceId <>\nApacheModuleServiceName app\nApacheModuleServiceNamespace apache-httpd\nApacheModuleTraceAsError ON\n", + }, + { + Name: apacheServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "app", + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheDefaultConfigDirectory, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=apache-httpd,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableApacheHTTPAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableApacheHTTPAutoInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableApacheHTTPAutoInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "apache httpd injection feature gate disabled", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "apache-httpd-disabled", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "apache-httpd-disabled", + }, + Spec: v1alpha1.InstrumentationSpec{ + ApacheHttpd: v1alpha1.ApacheHttpd{ + Image: "otel/apache-httpd:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectApacheHttpd: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectApacheHttpd: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableApacheHTTPAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableApacheHTTPAutoInstrumentationSupport.ID(), false)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableApacheHTTPAutoInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + + { + name: "nginx injection, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "req-namespace", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + Namespace: "req-namespace", + }, + Spec: v1alpha1.InstrumentationSpec{ + Nginx: v1alpha1.Nginx{ + Image: "otel/nginx-inj:1", + Attrs: []corev1.EnvVar{{ + Name: "NginxModuleOtelMaxQueueSize", + Value: "4096", + }}, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://otlp-endpoint:4317", + }, + Env: []corev1.EnvVar{}, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + Annotations: map[string]string{ + annotationInjectNginx: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + Annotations: map[string]string{ + annotationInjectNginx: "true", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "otel-nginx-conf-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "otel-nginx-agent", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: nginxAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /etc/nginx/* /opt/opentelemetry-webserver/source-conf && export NGINX_VERSION=$( { nginx -v ; } 2>&1 ) && echo ${NGINX_VERSION##*/} > /opt/opentelemetry-webserver/source-conf/version.txt"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }}, + }, + { + Name: nginxAgentInitContainerName, + Image: "otel/nginx-inj:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{nginxSdkInitContainerTestCommand}, + Env: []corev1.EnvVar{ + { + Name: nginxAttributesEnvVar, + Value: "NginxModuleEnabled ON;\nNginxModuleOtelExporterEndpoint http://otlp-endpoint:4317;\nNginxModuleOtelMaxQueueSize 4096;\nNginxModuleOtelSpanExporter otlp;\nNginxModuleResolveBackends ON;\nNginxModuleServiceInstanceId <>;\nNginxModuleServiceName my-nginx-6c44bcbdd;\nNginxModuleServiceNamespace req-namespace;\nNginxModuleTraceAsError ON;\n", + }, + { + Name: "OTEL_NGINX_I13N_SCRIPT", + Value: nginxSdkInitContainerI13nScript, + }, { + Name: nginxServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "nginx", + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: "/etc/nginx", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "LD_LIBRARY_PATH", + Value: "/opt/opentelemetry-webserver/agent/sdk_lib/lib", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "my-nginx-6c44bcbdd", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://otlp-endpoint:4317", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=nginx,k8s.namespace.name=req-namespace,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=my-nginx-6c44bcbdd,service.instance.id=req-namespace.my-nginx-6c44bcbdd.nginx", + }, + }, + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableNginxAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNginxAutoInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNginxAutoInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "nginx injection feature gate disabled", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nginx-disabled", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + Namespace: "nginx-disabled", + }, + Spec: v1alpha1.InstrumentationSpec{ + Nginx: v1alpha1.Nginx{ + Image: "otel/nginx-inj:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + }, + Attrs: []corev1.EnvVar{{ + Name: "NginxModuleOtelMaxQueueSize", + Value: "4096", + }}, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://otlp-endpoint:4317", + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + Annotations: map[string]string{ + annotationInjectNginx: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + Annotations: map[string]string{ + annotationInjectNginx: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "nginx", + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableNginxAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNginxAutoInstrumentationSupport.ID(), false)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNginxAutoInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + + { + name: "missing annotation", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "missing-annotation", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "missing-annotation", + }, + Spec: v1alpha1.InstrumentationSpec{ + Java: v1alpha1.Java{ + Image: "otel/java:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + }, + { + name: "annotation set to false", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "annotation-false", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "annotation-false", + }, + Spec: v1alpha1.InstrumentationSpec{ + Java: v1alpha1.Java{ + Image: "otel/java:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectJava: "false", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectJava: "false", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + }, + { + name: "annotation set to non existing instance", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "non-existing-instance", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "non-existing-instance", + }, + Spec: v1alpha1.InstrumentationSpec{ + Java: v1alpha1.Java{ + Image: "otel/java:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectJava: "doesnotexists", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + err: `instrumentations.opentelemetry.io "doesnotexists" not found`, + }, + { + name: "multi instrumentation for multiple containers feature gate enabled", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "multi-instrumentation-multi-containers", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "multi-instrumentation-multi-containers", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Java: v1alpha1.Java{ + Image: "otel/java:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Python: v1alpha1.Python{ + Image: "otel/python:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectJava: "true", + annotationInjectNodeJS: "true", + annotationInjectPython: "true", + annotationInjectDotnetContainersName: "dotnet1,dotnet2", + annotationInjectJavaContainersName: "java1,java2", + annotationInjectNodeJSContainersName: "nodejs1,nodejs2", + annotationInjectPythonContainersName: "python1,python2", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dotnet1", + }, + { + Name: "dotnet2", + }, + { + Name: "java1", + }, + { + Name: "java2", + }, + { + Name: "nodejs1", + }, + { + Name: "nodejs2", + }, + { + Name: "python1", + }, + { + Name: "python2", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectJava: "true", + annotationInjectNodeJS: "true", + annotationInjectPython: "true", + annotationInjectDotnetContainersName: "dotnet1,dotnet2", + annotationInjectJavaContainersName: "java1,java2", + annotationInjectNodeJSContainersName: "nodejs1,nodejs2", + annotationInjectPythonContainersName: "python1,python2", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: javaVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: nodejsVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: pythonVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: dotnetVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: javaInitContainerName, + Image: "otel/java:1", + Command: []string{"cp", "/javaagent.jar", javaInstrMountPath + "/javaagent.jar"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: javaVolumeName, + MountPath: javaInstrMountPath, + }}, + }, + { + Name: nodejsInitContainerName, + Image: "otel/nodejs:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", nodejsInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, + }}, + }, + { + Name: pythonInitContainerName, + Image: "otel/python:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", pythonInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }}, + }, + { + Name: dotnetInitContainerName, + Image: "otel/dotnet:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", dotnetInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "dotnet1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "dotnet1", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=dotnet1,k8s.namespace.name=multi-instrumentation-multi-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }, + }, + }, + { + Name: "dotnet2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "dotnet2", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=dotnet2,k8s.namespace.name=multi-instrumentation-multi-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }, + }, + }, + { + Name: "java1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "JAVA_TOOL_OPTIONS", + Value: javaJVMArgument, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "java1", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=java1,k8s.namespace.name=multi-instrumentation-multi-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: javaVolumeName, + MountPath: javaInstrMountPath, + }, + }, + }, + { + Name: "java2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "JAVA_TOOL_OPTIONS", + Value: javaJVMArgument, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "java2", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=java2,k8s.namespace.name=multi-instrumentation-multi-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: javaVolumeName, + MountPath: javaInstrMountPath, + }, + }, + }, + { + Name: "nodejs1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "NODE_OPTIONS", + Value: nodeRequireArgument, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "nodejs1", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=nodejs1,k8s.namespace.name=multi-instrumentation-multi-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, + }, + }, + }, + { + Name: "nodejs2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "NODE_OPTIONS", + Value: nodeRequireArgument, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "nodejs2", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=nodejs2,k8s.namespace.name=multi-instrumentation-multi-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, + }, + }, + }, + { + Name: "python1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "python1", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=python1,k8s.namespace.name=multi-instrumentation-multi-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }, + }, + }, + { + Name: "python2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "python2", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=python2,k8s.namespace.name=multi-instrumentation-multi-containers,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }, + }, + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableMultiInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "multi instrumentation for multiple containers feature gate enabled, container-names not used", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "multi-instrumentation-multi-containers-cn", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "multi-instrumentation-multi-containers-cn", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Java: v1alpha1.Java{ + Image: "otel/java:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Python: v1alpha1.Python{ + Image: "otel/python:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectJava: "true", + annotationInjectNodeJS: "true", + annotationInjectPython: "true", + annotationInjectDotnetContainersName: "dotnet1,dotnet2", + annotationInjectJavaContainersName: "java1,java2", + annotationInjectNodeJSContainersName: "nodejs1,nodejs2", + annotationInjectPythonContainersName: "python1,python2", + annotationInjectContainerName: "should-not-be-instrumented1,should-not-be-instrumented2", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dotnet1", + }, + { + Name: "dotnet2", + }, + { + Name: "java1", + }, + { + Name: "java2", + }, + { + Name: "nodejs1", + }, + { + Name: "nodejs2", + }, + { + Name: "python1", + }, + { + Name: "python2", + }, + { + Name: "should-not-be-instrumented1", + }, + { + Name: "should-not-be-instrumented2", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectJava: "true", + annotationInjectNodeJS: "true", + annotationInjectPython: "true", + annotationInjectDotnetContainersName: "dotnet1,dotnet2", + annotationInjectJavaContainersName: "java1,java2", + annotationInjectNodeJSContainersName: "nodejs1,nodejs2", + annotationInjectPythonContainersName: "python1,python2", + annotationInjectContainerName: "should-not-be-instrumented1,should-not-be-instrumented2", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: javaVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: nodejsVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: pythonVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: dotnetVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: javaInitContainerName, + Image: "otel/java:1", + Command: []string{"cp", "/javaagent.jar", javaInstrMountPath + "/javaagent.jar"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: javaVolumeName, + MountPath: javaInstrMountPath, + }}, + }, + { + Name: nodejsInitContainerName, + Image: "otel/nodejs:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", nodejsInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, + }}, + }, + { + Name: pythonInitContainerName, + Image: "otel/python:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", pythonInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }}, + }, + { + Name: dotnetInitContainerName, + Image: "otel/dotnet:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", dotnetInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "dotnet1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "dotnet1", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=dotnet1,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }, + }, + }, + { + Name: "dotnet2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "dotnet2", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=dotnet2,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }, + }, + }, + { + Name: "java1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "JAVA_TOOL_OPTIONS", + Value: javaJVMArgument, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "java1", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=java1,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: javaVolumeName, + MountPath: javaInstrMountPath, + }, + }, + }, + { + Name: "java2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "JAVA_TOOL_OPTIONS", + Value: javaJVMArgument, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "java2", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=java2,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: javaVolumeName, + MountPath: javaInstrMountPath, + }, + }, + }, + { + Name: "nodejs1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "NODE_OPTIONS", + Value: nodeRequireArgument, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "nodejs1", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=nodejs1,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, + }, + }, + }, + { + Name: "nodejs2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "NODE_OPTIONS", + Value: nodeRequireArgument, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "nodejs2", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=nodejs2,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, + }, + }, + }, + { + Name: "python1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "python1", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=python1,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }, + }, + }, + { + Name: "python2", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_METRICS_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "python2", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=python2,k8s.namespace.name=multi-instrumentation-multi-containers-cn,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, + }, + }, + }, + { + Name: "should-not-be-instrumented1", + }, + { + Name: "should-not-be-instrumented2", + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableMultiInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "multi instrumentation for multiple containers feature gate disabled, multiple instrumentation annotations set", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "multi-instrumentation-multi-containers-dis-cn", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "multi-instrumentation-multi-containers-dis-cn", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Java: v1alpha1.Java{ + Image: "otel/java:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Python: v1alpha1.Python{ + Image: "otel/python:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectJava: "true", + annotationInjectNodeJS: "true", + annotationInjectPython: "true", + annotationInjectDotnetContainersName: "dotnet1,dotnet2", + annotationInjectJavaContainersName: "java1,java2", + annotationInjectNodeJSContainersName: "nodejs1,nodejs2", + annotationInjectPythonContainersName: "python1,python2", + annotationInjectContainerName: "should-not-be-instrumented1,should-not-be-instrumented2", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dotnet1", + }, + { + Name: "dotnet2", + }, + { + Name: "java1", + }, + { + Name: "java2", + }, + { + Name: "nodejs1", + }, + { + Name: "nodejs2", + }, + { + Name: "python1", + }, + { + Name: "python2", + }, + { + Name: "should-not-be-instrumented1", + }, + { + Name: "should-not-be-instrumented2", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectJava: "true", + annotationInjectNodeJS: "true", + annotationInjectPython: "true", + annotationInjectDotnetContainersName: "dotnet1,dotnet2", + annotationInjectJavaContainersName: "java1,java2", + annotationInjectNodeJSContainersName: "nodejs1,nodejs2", + annotationInjectPythonContainersName: "python1,python2", + annotationInjectContainerName: "should-not-be-instrumented1,should-not-be-instrumented2", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dotnet1", + }, + { + Name: "dotnet2", + }, + { + Name: "java1", + }, + { + Name: "java2", + }, + { + Name: "nodejs1", + }, + { + Name: "nodejs2", + }, + { + Name: "python1", + }, + { + Name: "python2", + }, + { + Name: "should-not-be-instrumented1", + }, + { + Name: "should-not-be-instrumented2", + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableMultiInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), false)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "multi instrumentation feature gate enabled, multiple instrumentation annotations set, no containers", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "multi-instrumentation-multi-containers-no-cont", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "multi-instrumentation-multi-containers-no-cont", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Java: v1alpha1.Java{ + Image: "otel/java:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Python: v1alpha1.Python{ + Image: "otel/python:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectJava: "true", + annotationInjectNodeJS: "true", + annotationInjectPython: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dotnet1", + }, + { + Name: "dotnet2", + }, + { + Name: "java1", + }, + { + Name: "java2", + }, + { + Name: "nodejs1", + }, + { + Name: "nodejs2", + }, + { + Name: "python1", + }, + { + Name: "python2", + }, + { + Name: "should-not-be-instrumented1", + }, + { + Name: "should-not-be-instrumented2", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectJava: "true", + annotationInjectNodeJS: "true", + annotationInjectPython: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dotnet1", + }, + { + Name: "dotnet2", + }, + { + Name: "java1", + }, + { + Name: "java2", + }, + { + Name: "nodejs1", + }, + { + Name: "nodejs2", + }, + { + Name: "python1", + }, + { + Name: "python2", + }, + { + Name: "should-not-be-instrumented1", + }, + { + Name: "should-not-be-instrumented2", + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableMultiInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "multi instrumentation feature gate enabled, single instrumentation annotation set, no containers", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "multi-instrumentation-single-container-no-cont", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "multi-instrumentation-single-container-no-cont", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Java: v1alpha1.Java{ + Image: "otel/java:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Python: v1alpha1.Python{ + Image: "otel/python:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dotnet1", + }, + { + Name: "should-not-be-instrumented1", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: dotnetVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: dotnetInitContainerName, + Image: "otel/dotnet:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", dotnetInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "dotnet1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + { + Name: envDotNetCoreClrEnableProfiling, + Value: dotNetCoreClrEnableProfilingEnabled, + }, + { + Name: envDotNetCoreClrProfiler, + Value: dotNetCoreClrProfilerID, + }, + { + Name: envDotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, + }, + { + Name: envDotNetStartupHook, + Value: dotNetStartupHookPath, + }, + { + Name: envDotNetAdditionalDeps, + Value: dotNetAdditionalDepsPath, + }, + { + Name: envDotNetOTelAutoHome, + Value: dotNetOTelAutoHomePath, + }, + { + Name: envDotNetSharedStore, + Value: dotNetSharedStorePath, + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "dotnet1", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=dotnet1,k8s.namespace.name=multi-instrumentation-single-container-no-cont,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, + }, + }, + }, + { + Name: "should-not-be-instrumented1", + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalVal := featuregate.EnableMultiInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), originalVal)) + }) + }, + }, + { + name: "multi instrumentation feature gate disabled, instrumentation feature gate disabled and annotation set, multiple specific containers set", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "multi-instrumentation-single-container-spec-cont", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "multi-instrumentation-single-container-spec-cont", + }, + Spec: v1alpha1.InstrumentationSpec{ + DotNet: v1alpha1.DotNet{ + Image: "otel/dotnet:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_LOG_LEVEL", + Value: "debug", + }, + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectDotnetContainersName: "dotnet1", + annotationInjectNodeJSContainersName: "should-not-be-instrumented1", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dotnet1", + Env: []corev1.EnvVar{ + { + Name: "TEST", + Value: "debug", + }, + }, + }, + { + Name: "should-not-be-instrumented1", + Env: []corev1.EnvVar{ + { + Name: "TEST", + Value: "debug", + }, + }, + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectDotNet: "true", + annotationInjectDotnetContainersName: "dotnet1", + annotationInjectNodeJSContainersName: "should-not-be-instrumented1", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "dotnet1", + Env: []corev1.EnvVar{ + { + Name: "TEST", + Value: "debug", + }, + }, + }, + { + Name: "should-not-be-instrumented1", + Env: []corev1.EnvVar{ + { + Name: "TEST", + Value: "debug", + }, + }, + }, + }, + }, + }, + setFeatureGates: func(t *testing.T) { + originalValMultiInstr := featuregate.EnableMultiInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), true)) + originalValDotNetInstr := featuregate.EnableDotnetAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableDotnetAutoInstrumentationSupport.ID(), false)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableMultiInstrumentationSupport.ID(), originalValMultiInstr)) + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableDotnetAutoInstrumentationSupport.ID(), originalValDotNetInstr)) + }) + }, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + if test.setFeatureGates != nil { + test.setFeatureGates(t) + } + + err := k8sClient.Create(context.Background(), &test.ns) + require.NoError(t, err) + defer func() { + _ = k8sClient.Delete(context.Background(), &test.ns) + }() + err = k8sClient.Create(context.Background(), &test.inst) + require.NoError(t, err) + + pod, err := mutator.Mutate(context.Background(), test.ns, test.pod) + if test.err == "" { + require.NoError(t, err) + assert.Equal(t, test.expected, pod) + } else { + assert.Contains(t, err.Error(), test.err) + } + }) + } +} + +func TestSingleInstrumentationEnabled(t *testing.T) { + tests := []struct { + name string + instrumentations languageInstrumentations + expectedStatus bool + expectedMsg string + }{ + { + name: "Single instrumentation enabled", + instrumentations: languageInstrumentations{ + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, + NodeJS: instrumentationWithContainers{Instrumentation: nil}, + }, + expectedStatus: true, + expectedMsg: "Java", + }, + { + name: "Multiple instrumentations enabled", + instrumentations: languageInstrumentations{ + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, + NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, + }, + expectedStatus: false, + expectedMsg: "", + }, + { + name: "Instrumentations disabled", + instrumentations: languageInstrumentations{ + Java: instrumentationWithContainers{Instrumentation: nil}, + NodeJS: instrumentationWithContainers{Instrumentation: nil}, + }, + expectedStatus: false, + expectedMsg: "", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ok := test.instrumentations.isSingleInstrumentationEnabled() + assert.Equal(t, test.expectedStatus, ok) + }) + } +} + +func TestContainerNamesConfiguredForMultipleInstrumentations(t *testing.T) { + tests := []struct { + name string + instrumentations languageInstrumentations + expectedStatus bool + expectedMsg error + }{ + { + name: "Single instrumentation enabled without containers", + instrumentations: languageInstrumentations{ + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, + NodeJS: instrumentationWithContainers{Instrumentation: nil}, + }, + expectedStatus: true, + expectedMsg: nil, + }, + { + name: "Multiple instrumentations enabled with containers", + instrumentations: languageInstrumentations{ + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "java"}, + NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "nodejs"}, + }, + expectedStatus: true, + expectedMsg: nil, + }, + { + name: "Multiple instrumentations enabled without containers", + instrumentations: languageInstrumentations{ + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, + NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, + }, + expectedStatus: false, + expectedMsg: fmt.Errorf("incorrect instrumentation configuration - please provide container names for all instrumentations"), + }, + { + name: "Multiple instrumentations enabled with containers for single instrumentation", + instrumentations: languageInstrumentations{ + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "test"}, + NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, + }, + expectedStatus: false, + expectedMsg: fmt.Errorf("incorrect instrumentation configuration - please provide container names for all instrumentations"), + }, + { + name: "Disabled instrumentations", + instrumentations: languageInstrumentations{ + NodeJS: instrumentationWithContainers{Instrumentation: nil}, + }, + expectedStatus: false, + expectedMsg: fmt.Errorf("instrumentation configuration not provided"), + }, + { + name: "Multiple instrumentations enabled with duplicated containers", + instrumentations: languageInstrumentations{ + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "app,app1,java"}, + NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "app1,app,nodejs"}, + }, + expectedStatus: false, + expectedMsg: fmt.Errorf("duplicated container names detected: [app app1]"), + }, + { + name: "Multiple instrumentations enabled with duplicated containers for single instrumentation", + instrumentations: languageInstrumentations{ + Java: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "app,app,java"}, + NodeJS: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "nodejs"}, + }, + expectedStatus: false, + expectedMsg: fmt.Errorf("duplicated container names detected: [app]"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ok, msg := test.instrumentations.areContainerNamesConfiguredForMultipleInstrumentations() + assert.Equal(t, test.expectedStatus, ok) + assert.Equal(t, test.expectedMsg, msg) + }) + } +} + +func TestInstrumentationLanguageContainersSet(t *testing.T) { + tests := []struct { + name string + instrumentations languageInstrumentations + containers string + expectedInstrumentations languageInstrumentations + }{ + { + name: "Set containers for enabled instrumentation", + instrumentations: languageInstrumentations{ + NodeJS: instrumentationWithContainers{Instrumentation: nil}, + Python: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}}, + }, + containers: "python,python1", + expectedInstrumentations: languageInstrumentations{ + NodeJS: instrumentationWithContainers{Instrumentation: nil}, + Python: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{}, Containers: "python,python1"}, + }, + }, + { + name: "Set containers when all instrumentations disabled", + instrumentations: languageInstrumentations{}, + containers: "python,python1", + expectedInstrumentations: languageInstrumentations{}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + test.instrumentations.setInstrumentationLanguageContainers(test.containers) + assert.Equal(t, test.expectedInstrumentations, test.instrumentations) }) } } diff --git a/pkg/instrumentation/python.go b/pkg/instrumentation/python.go index 2662ddc877..0be23ee413 100644 --- a/pkg/instrumentation/python.go +++ b/pkg/instrumentation/python.go @@ -23,11 +23,16 @@ import ( ) const ( - envPythonPath = "PYTHONPATH" - envOtelTracesExporter = "OTEL_TRACES_EXPORTER" - envOtelMetricsExporter = "OTEL_METRICS_EXPORTER" - pythonPathPrefix = "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation" - pythonPathSuffix = "/otel-auto-instrumentation" + envPythonPath = "PYTHONPATH" + envOtelTracesExporter = "OTEL_TRACES_EXPORTER" + envOtelMetricsExporter = "OTEL_METRICS_EXPORTER" + envOtelExporterOTLPTracesProtocol = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL" + envOtelExporterOTLPMetricsProtocol = "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL" + pythonPathPrefix = "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation" + pythonPathSuffix = "/otel-auto-instrumentation-python" + pythonInstrMountPath = "/otel-auto-instrumentation-python" + pythonVolumeName = volumeName + "-python" + pythonInitContainerName = initContainerName + "-python" ) func injectPythonSDK(pythonSpec v1alpha1.Python, pod corev1.Pod, index int) (corev1.Pod, error) { @@ -62,42 +67,60 @@ func injectPythonSDK(pythonSpec v1alpha1.Python, pod corev1.Pod, index int) (cor if idx == -1 { container.Env = append(container.Env, corev1.EnvVar{ Name: envOtelTracesExporter, - Value: "otlp_proto_http", + Value: "otlp", }) } - // TODO: https://github.com/open-telemetry/opentelemetry-python/issues/2447 this should - // also be set to `otlp_proto_http` once an exporter is implemented. For now, set - // OTEL_METRICS_EXPORTER to none if not set by user to prevent using the default grpc - // exporter which is not included in the image. + // Set OTEL_EXPORTER_OTLP_TRACES_PROTOCOL to http/protobuf if not set by user because it is what our autoinstrumentation supports. + idx = getIndexOfEnv(container.Env, envOtelExporterOTLPTracesProtocol) + if idx == -1 { + container.Env = append(container.Env, corev1.EnvVar{ + Name: envOtelExporterOTLPTracesProtocol, + Value: "http/protobuf", + }) + } + + // Set OTEL_METRICS_EXPORTER to HTTP exporter if not set by user because it is what our autoinstrumentation supports. idx = getIndexOfEnv(container.Env, envOtelMetricsExporter) if idx == -1 { container.Env = append(container.Env, corev1.EnvVar{ Name: envOtelMetricsExporter, - Value: "none", + Value: "otlp", + }) + } + + // Set OTEL_EXPORTER_OTLP_METRICS_PROTOCOL to http/protobuf if not set by user because it is what our autoinstrumentation supports. + idx = getIndexOfEnv(container.Env, envOtelExporterOTLPMetricsProtocol) + if idx == -1 { + container.Env = append(container.Env, corev1.EnvVar{ + Name: envOtelExporterOTLPMetricsProtocol, + Value: "http/protobuf", }) } container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, }) // We just inject Volumes and init containers for the first processed container. - if isInitContainerMissing(pod) { + if isInitContainerMissing(pod, pythonInitContainerName) { pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ - Name: volumeName, + Name: pythonVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: volumeSize(pythonSpec.VolumeSizeLimit), + }, }}) pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{ - Name: initContainerName, - Image: pythonSpec.Image, - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Name: pythonInitContainerName, + Image: pythonSpec.Image, + Command: []string{"cp", "-a", "/autoinstrumentation/.", pythonInstrMountPath}, + Resources: pythonSpec.Resources, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, }}, }) } diff --git a/pkg/instrumentation/python_test.go b/pkg/instrumentation/python_test.go index 910a35ba61..7f7510c719 100644 --- a/pkg/instrumentation/python_test.go +++ b/pkg/instrumentation/python_test.go @@ -46,20 +46,22 @@ func TestInjectPythonSDK(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: pythonVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-python", Image: "foo/bar:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-python"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-python", + MountPath: "/otel-auto-instrumentation-python", }}, }, }, @@ -67,22 +69,30 @@ func TestInjectPythonSDK(t *testing.T) { { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-python", + MountPath: "/otel-auto-instrumentation-python", }, }, Env: []corev1.EnvVar{ { Name: "PYTHONPATH", - Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + Value: fmt.Sprintf("%s:%s", "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation", "/otel-auto-instrumentation-python"), }, { Name: "OTEL_TRACES_EXPORTER", - Value: "otlp_proto_http", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", }, { Name: "OTEL_METRICS_EXPORTER", - Value: "none", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", }, }, }, @@ -93,7 +103,7 @@ func TestInjectPythonSDK(t *testing.T) { }, { name: "PYTHONPATH defined", - Python: v1alpha1.Python{Image: "foo/bar:1"}, + Python: v1alpha1.Python{Image: "foo/bar:1", Resources: testResourceRequirements}, pod: corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ @@ -112,43 +122,54 @@ func TestInjectPythonSDK(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: "opentelemetry-auto-instrumentation-python", VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-python", Image: "foo/bar:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-python"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-python", + MountPath: "/otel-auto-instrumentation-python", }}, + Resources: testResourceRequirements, }, }, Containers: []corev1.Container{ { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-python", + MountPath: "/otel-auto-instrumentation-python", }, }, Env: []corev1.EnvVar{ { Name: "PYTHONPATH", - Value: fmt.Sprintf("%s:%s:%s", pythonPathPrefix, "/foo:/bar", pythonPathSuffix), + Value: fmt.Sprintf("%s:%s:%s", "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation", "/foo:/bar", "/otel-auto-instrumentation-python"), }, { Name: "OTEL_TRACES_EXPORTER", - Value: "otlp_proto_http", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", }, { Name: "OTEL_METRICS_EXPORTER", - Value: "none", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", }, }, }, @@ -178,20 +199,22 @@ func TestInjectPythonSDK(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: pythonVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-python", Image: "foo/bar:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-python"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-python", + MountPath: "/otel-auto-instrumentation-python", }}, }, }, @@ -199,8 +222,8 @@ func TestInjectPythonSDK(t *testing.T) { { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-python", + MountPath: "/otel-auto-instrumentation-python", }, }, Env: []corev1.EnvVar{ @@ -210,11 +233,19 @@ func TestInjectPythonSDK(t *testing.T) { }, { Name: "PYTHONPATH", - Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + Value: fmt.Sprintf("%s:%s", "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation", "/otel-auto-instrumentation-python"), + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", }, { Name: "OTEL_METRICS_EXPORTER", - Value: "none", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", }, }, }, @@ -244,20 +275,22 @@ func TestInjectPythonSDK(t *testing.T) { Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: "opentelemetry-auto-instrumentation-python", VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: "opentelemetry-auto-instrumentation-python", Image: "foo/bar:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-python"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-python", + MountPath: "/otel-auto-instrumentation-python", }}, }, }, @@ -265,8 +298,8 @@ func TestInjectPythonSDK(t *testing.T) { { VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: "opentelemetry-auto-instrumentation-python", + MountPath: "/otel-auto-instrumentation-python", }, }, Env: []corev1.EnvVar{ @@ -276,11 +309,19 @@ func TestInjectPythonSDK(t *testing.T) { }, { Name: "PYTHONPATH", - Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + Value: fmt.Sprintf("%s:%s", "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation", "/otel-auto-instrumentation-python"), }, { Name: "OTEL_TRACES_EXPORTER", - Value: "otlp_proto_http", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", }, }, }, diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go index b126427d75..0f6de945fe 100644 --- a/pkg/instrumentation/sdk.go +++ b/pkg/instrumentation/sdk.go @@ -23,6 +23,10 @@ import ( "unsafe" "github.com/go-logr/logr" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" + "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.7.0" appsv1 "k8s.io/api/apps/v1" @@ -33,14 +37,12 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/pkg/constants" ) const ( volumeName = "opentelemetry-auto-instrumentation" initContainerName = "opentelemetry-auto-instrumentation" + sideCarName = "opentelemetry-auto-instrumentation" ) // inject a new sidecar container to the given pod, based on the given OpenTelemetryCollector. @@ -50,77 +52,186 @@ type sdkInjector struct { logger logr.Logger } -func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations, ns corev1.Namespace, pod corev1.Pod, containerName string) corev1.Pod { +func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations, ns corev1.Namespace, pod corev1.Pod) corev1.Pod { if len(pod.Spec.Containers) < 1 { return pod } - // We search for specific container to inject variables and if no one is found - // We fallback to first container - var index = 0 - for idx, ctnair := range pod.Spec.Containers { - if ctnair.Name == containerName { - index = idx - } - } - - if insts.Java != nil { - otelinst := *insts.Java + if insts.Java.Instrumentation != nil { + otelinst := *insts.Java.Instrumentation var err error i.logger.V(1).Info("injecting Java instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - pod, err = injectJavaagent(otelinst.Spec.Java, pod, index) - if err != nil { - i.logger.Info("Skipping javaagent injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) - } else { - pod = i.injectCommonEnvVar(otelinst, pod, index) - pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index) + + javaContainers := insts.Java.Containers + + for _, container := range strings.Split(javaContainers, ",") { + index := getContainerIndex(container, pod) + pod, err = injectJavaagent(otelinst.Spec.Java, pod, index) + if err != nil { + i.logger.Info("Skipping javaagent injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) + } else { + pod = i.injectCommonEnvVar(otelinst, pod, index) + pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index, index) + pod = i.setInitContainerSecurityContext(pod, pod.Spec.Containers[index].SecurityContext, javaInitContainerName) + } } } - if insts.NodeJS != nil { - otelinst := *insts.NodeJS + if insts.NodeJS.Instrumentation != nil { + otelinst := *insts.NodeJS.Instrumentation var err error i.logger.V(1).Info("injecting NodeJS instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - pod, err = injectNodeJSSDK(otelinst.Spec.NodeJS, pod, index) - if err != nil { - i.logger.Info("Skipping NodeJS SDK injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) - } else { - pod = i.injectCommonEnvVar(otelinst, pod, index) - pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index) + + nodejsContainers := insts.NodeJS.Containers + + for _, container := range strings.Split(nodejsContainers, ",") { + index := getContainerIndex(container, pod) + pod, err = injectNodeJSSDK(otelinst.Spec.NodeJS, pod, index) + if err != nil { + i.logger.Info("Skipping NodeJS SDK injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) + } else { + pod = i.injectCommonEnvVar(otelinst, pod, index) + pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index, index) + pod = i.setInitContainerSecurityContext(pod, pod.Spec.Containers[index].SecurityContext, nodejsInitContainerName) + } } } - if insts.Python != nil { - otelinst := *insts.Python + if insts.Python.Instrumentation != nil { + otelinst := *insts.Python.Instrumentation var err error i.logger.V(1).Info("injecting Python instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - pod, err = injectPythonSDK(otelinst.Spec.Python, pod, index) - if err != nil { - i.logger.Info("Skipping Python SDK injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) - } else { - pod = i.injectCommonEnvVar(otelinst, pod, index) - pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index) + + pythonContainers := insts.Python.Containers + + for _, container := range strings.Split(pythonContainers, ",") { + index := getContainerIndex(container, pod) + pod, err = injectPythonSDK(otelinst.Spec.Python, pod, index) + if err != nil { + i.logger.Info("Skipping Python SDK injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) + } else { + pod = i.injectCommonEnvVar(otelinst, pod, index) + pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index, index) + pod = i.setInitContainerSecurityContext(pod, pod.Spec.Containers[index].SecurityContext, pythonInitContainerName) + } } } - if insts.DotNet != nil { - otelinst := *insts.DotNet + if insts.DotNet.Instrumentation != nil { + otelinst := *insts.DotNet.Instrumentation var err error i.logger.V(1).Info("injecting DotNet instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - pod, err = injectDotNetSDK(otelinst.Spec.DotNet, pod, index) + + dotnetContainers := insts.DotNet.Containers + + for _, container := range strings.Split(dotnetContainers, ",") { + index := getContainerIndex(container, pod) + pod, err = injectDotNetSDK(otelinst.Spec.DotNet, pod, index, insts.DotNet.AdditionalAnnotations[annotationDotNetRuntime]) + if err != nil { + i.logger.Info("Skipping DotNet SDK injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) + } else { + pod = i.injectCommonEnvVar(otelinst, pod, index) + pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index, index) + pod = i.setInitContainerSecurityContext(pod, pod.Spec.Containers[index].SecurityContext, dotnetInitContainerName) + } + } + } + if insts.Go.Instrumentation != nil { + origPod := pod + otelinst := *insts.Go.Instrumentation + var err error + i.logger.V(1).Info("injecting Go instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + + goContainers := insts.Go.Containers + + // Go instrumentation supports only single container instrumentation. + index := getContainerIndex(goContainers, pod) + pod, err = injectGoSDK(otelinst.Spec.Go, pod) if err != nil { - i.logger.Info("Skipping DotNet SDK injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) + i.logger.Info("Skipping Go SDK injection", "reason", err.Error(), "container", pod.Spec.Containers[index].Name) } else { + // Common env vars and config need to be applied to the agent contain. + pod = i.injectCommonEnvVar(otelinst, pod, len(pod.Spec.Containers)-1) + pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, len(pod.Spec.Containers)-1, 0) + + // Ensure that after all the env var coalescing we have a value for OTEL_GO_AUTO_TARGET_EXE + idx := getIndexOfEnv(pod.Spec.Containers[len(pod.Spec.Containers)-1].Env, envOtelTargetExe) + if idx == -1 { + i.logger.Info("Skipping Go SDK injection", "reason", "OTEL_GO_AUTO_TARGET_EXE not set", "container", pod.Spec.Containers[index].Name) + pod = origPod + } + } + } + if insts.ApacheHttpd.Instrumentation != nil { + otelinst := *insts.ApacheHttpd.Instrumentation + i.logger.V(1).Info("injecting Apache Httpd instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + + apacheHttpdContainers := insts.ApacheHttpd.Containers + + for _, container := range strings.Split(apacheHttpdContainers, ",") { + index := getContainerIndex(container, pod) + // Apache agent is configured via config files rather than env vars. + // Therefore, service name, otlp endpoint and other attributes are passed to the agent injection method + pod = injectApacheHttpdagent(i.logger, otelinst.Spec.ApacheHttpd, pod, index, otelinst.Spec.Endpoint, i.createResourceMap(ctx, otelinst, ns, pod, index)) + pod = i.injectCommonEnvVar(otelinst, pod, index) + pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index, index) + pod = i.setInitContainerSecurityContext(pod, pod.Spec.Containers[index].SecurityContext, apacheAgentInitContainerName) + pod = i.setInitContainerSecurityContext(pod, pod.Spec.Containers[index].SecurityContext, apacheAgentCloneContainerName) + } + } + + if insts.Nginx.Instrumentation != nil { + otelinst := *insts.Nginx.Instrumentation + i.logger.V(1).Info("injecting Nginx instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + + nginxContainers := insts.Nginx.Containers + + for _, container := range strings.Split(nginxContainers, ",") { + index := getContainerIndex(container, pod) + // Nginx agent is configured via config files rather than env vars. + // Therefore, service name, otlp endpoint and other attributes are passed to the agent injection method + pod = injectNginxSDK(i.logger, otelinst.Spec.Nginx, pod, index, otelinst.Spec.Endpoint, i.createResourceMap(ctx, otelinst, ns, pod, index)) pod = i.injectCommonEnvVar(otelinst, pod, index) - pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index) + pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index, index) } } - if insts.Sdk != nil { - otelinst := *insts.Sdk + + if insts.Sdk.Instrumentation != nil { + otelinst := *insts.Sdk.Instrumentation i.logger.V(1).Info("injecting sdk-only instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - pod = i.injectCommonEnvVar(otelinst, pod, index) - pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index) + + sdkContainers := insts.Sdk.Containers + + for _, container := range strings.Split(sdkContainers, ",") { + index := getContainerIndex(container, pod) + pod = i.injectCommonEnvVar(otelinst, pod, index) + pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index, index) + } + } + + return pod +} + +func (i *sdkInjector) setInitContainerSecurityContext(pod corev1.Pod, securityContext *corev1.SecurityContext, instrInitContainerName string) corev1.Pod { + for i, initContainer := range pod.Spec.InitContainers { + if initContainer.Name == instrInitContainerName { + pod.Spec.InitContainers[i].SecurityContext = securityContext + } } + return pod } +func getContainerIndex(containerName string, pod corev1.Pod) int { + // We search for specific container to inject variables and if no one is found + // We fallback to first container + var index = 0 + for idx, ctnair := range pod.Spec.Containers { + if ctnair.Name == containerName { + index = idx + } + } + + return index +} + func (i *sdkInjector) injectCommonEnvVar(otelinst v1alpha1.Instrumentation, pod corev1.Pod, index int) corev1.Pod { container := &pod.Spec.Containers[index] for _, env := range otelinst.Spec.Env { @@ -132,14 +243,21 @@ func (i *sdkInjector) injectCommonEnvVar(otelinst v1alpha1.Instrumentation, pod return pod } -func (i *sdkInjector) injectCommonSDKConfig(ctx context.Context, otelinst v1alpha1.Instrumentation, ns corev1.Namespace, pod corev1.Pod, index int) corev1.Pod { - container := &pod.Spec.Containers[index] - resourceMap := i.createResourceMap(ctx, otelinst, ns, pod, index) +// injectCommonSDKConfig adds common SDK configuration environment variables to the necessary pod +// agentIndex represents the index of the pod the needs the env vars to instrument the application. +// appIndex represents the index of the pod the will produce the telemetry. +// When the pod handling the instrumentation is the same as the pod producing the telemetry agentIndex +// and appIndex should be the same value. This is true for dotnet, java, nodejs, and python instrumentations. +// Go requires the agent to be a different container in the pod, so the agentIndex should represent this new sidecar +// and appIndex should represent the application being instrumented. +func (i *sdkInjector) injectCommonSDKConfig(ctx context.Context, otelinst v1alpha1.Instrumentation, ns corev1.Namespace, pod corev1.Pod, agentIndex int, appIndex int) corev1.Pod { + container := &pod.Spec.Containers[agentIndex] + resourceMap := i.createResourceMap(ctx, otelinst, ns, pod, appIndex) idx := getIndexOfEnv(container.Env, constants.EnvOTELServiceName) if idx == -1 { container.Env = append(container.Env, corev1.EnvVar{ Name: constants.EnvOTELServiceName, - Value: chooseServiceName(pod, resourceMap, index), + Value: chooseServiceName(pod, resourceMap, appIndex), }) } if otelinst.Spec.Exporter.Endpoint != "" { @@ -177,6 +295,15 @@ func (i *sdkInjector) injectCommonSDKConfig(ctx context.Context, otelinst v1alph resourceMap[string(semconv.K8SPodUIDKey)] = fmt.Sprintf("$(%s)", constants.EnvPodUID) } } + + idx = getIndexOfEnv(container.Env, constants.EnvOTELResourceAttrs) + if idx == -1 || !strings.Contains(container.Env[idx].Value, string(semconv.ServiceVersionKey)) { + vsn := chooseServiceVersion(pod, appIndex) + if vsn != "" { + resourceMap[string(semconv.ServiceVersionKey)] = vsn + } + } + if resourceMap[string(semconv.K8SNodeNameKey)] == "" { container.Env = append(container.Env, corev1.EnvVar{ Name: constants.EnvNodeName, @@ -249,6 +376,9 @@ func chooseServiceName(pod corev1.Pod, resources map[string]string, index int) s if name := resources[string(semconv.K8SStatefulSetNameKey)]; name != "" { return name } + if name := resources[string(semconv.K8SDaemonSetNameKey)]; name != "" { + return name + } if name := resources[string(semconv.K8SJobNameKey)]; name != "" { return name } @@ -261,6 +391,28 @@ func chooseServiceName(pod corev1.Pod, resources map[string]string, index int) s return pod.Spec.Containers[index].Name } +// obtains version by splitting image string on ":" and extracting final element from resulting array. +func chooseServiceVersion(pod corev1.Pod, index int) string { + parts := strings.Split(pod.Spec.Containers[index].Image, ":") + tag := parts[len(parts)-1] + //guard statement to handle case where image name has a port number + if strings.Contains(tag, "/") { + return "" + } + return tag +} + +// creates the service.instance.id following the semantic defined by +// https://github.com/open-telemetry/semantic-conventions/pull/312. +func createServiceInstanceId(namespaceName, podName, containerName string) string { + var serviceInstanceId string + if namespaceName != "" && podName != "" && containerName != "" { + resNames := []string{namespaceName, podName, containerName} + serviceInstanceId = strings.Join(resNames, ".") + } + return serviceInstanceId +} + // createResourceMap creates resource attribute map. // User defined attributes (in explicitly set env var) have higher precedence. func (i *sdkInjector) createResourceMap(ctx context.Context, otelinst v1alpha1.Instrumentation, ns corev1.Namespace, pod corev1.Pod, index int) map[string]string { @@ -284,7 +436,6 @@ func (i *sdkInjector) createResourceMap(ctx context.Context, otelinst v1alpha1.I res[k] = v } } - k8sResources := map[attribute.Key]string{} k8sResources[semconv.K8SNamespaceNameKey] = ns.Name k8sResources[semconv.K8SContainerNameKey] = pod.Spec.Containers[index].Name @@ -293,6 +444,7 @@ func (i *sdkInjector) createResourceMap(ctx context.Context, otelinst v1alpha1.I k8sResources[semconv.K8SPodNameKey] = pod.Name k8sResources[semconv.K8SPodUIDKey] = string(pod.UID) k8sResources[semconv.K8SNodeNameKey] = pod.Spec.NodeName + k8sResources[semconv.ServiceInstanceIDKey] = createServiceInstanceId(ns.Name, pod.Name, pod.Spec.Containers[index].Name) i.addParentResourceLabels(ctx, otelinst.Spec.Resource.AddK8sUIDAttributes, ns, pod.ObjectMeta, k8sResources) for k, v := range k8sResources { if !existingRes[string(k)] && v != "" { diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go index c900193f2d..013a5ffc37 100644 --- a/pkg/instrumentation/sdk_test.go +++ b/pkg/instrumentation/sdk_test.go @@ -25,11 +25,25 @@ import ( "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" ) +var defaultVolumeLimitSize = resource.MustParse("200Mi") + +var testResourceRequirements = corev1.ResourceRequirements{ + Limits: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, + Requests: corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + corev1.ResourceMemory: resource.MustParse("128Mi"), + }, +} + func TestSDKInjection(t *testing.T) { ns := corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -131,7 +145,8 @@ func TestSDKInjection(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "application-name", + Name: "application-name", + Image: "app:latest", }, }, }, @@ -153,7 +168,8 @@ func TestSDKInjection(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "application-name", + Name: "application-name", + Image: "app:latest", Env: []corev1.EnvVar{ { Name: "OTEL_SERVICE_NAME", @@ -185,7 +201,7 @@ func TestSDKInjection(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=application-name,k8s.deployment.name=my-deployment,k8s.deployment.uid=depuid,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=app,k8s.pod.uid=pod-uid,k8s.replicaset.name=my-replicaset,k8s.replicaset.uid=rsuid", + Value: "k8s.container.name=application-name,k8s.deployment.name=my-deployment,k8s.deployment.uid=depuid,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=app,k8s.pod.uid=pod-uid,k8s.replicaset.name=my-replicaset,k8s.replicaset.uid=rsuid,service.instance.id=project1.app.application-name,service.version=latest", }, }, }, @@ -220,6 +236,7 @@ func TestSDKInjection(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { + Image: "app:latest", Env: []corev1.EnvVar{ { Name: "OTEL_SERVICE_NAME", @@ -239,7 +256,7 @@ func TestSDKInjection(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "foo=bar,k8s.container.name=other,", + Value: "foo=bar,k8s.container.name=other,service.version=explicitly_set,", }, }, }, @@ -254,6 +271,7 @@ func TestSDKInjection(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { + Image: "app:latest", Env: []corev1.EnvVar{ { Name: "OTEL_SERVICE_NAME", @@ -281,7 +299,7 @@ func TestSDKInjection(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "foo=bar,k8s.container.name=other,fromcr=val,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=app", + Value: "foo=bar,k8s.container.name=other,service.version=explicitly_set,fromcr=val,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=app", }, }, }, @@ -311,7 +329,8 @@ func TestSDKInjection(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "application-name", + Name: "application-name", + Image: "app:latest", }, }, }, @@ -333,7 +352,8 @@ func TestSDKInjection(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "application-name", + Name: "application-name", + Image: "app:latest", Env: []corev1.EnvVar{ { Name: "OTEL_SERVICE_NAME", @@ -349,7 +369,103 @@ func TestSDKInjection(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=application-name,k8s.deployment.name=my-deployment,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=app,k8s.pod.uid=pod-uid,k8s.replicaset.name=my-replicaset", + Value: "k8s.container.name=application-name,k8s.deployment.name=my-deployment,k8s.namespace.name=project1,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=app,k8s.pod.uid=pod-uid,k8s.replicaset.name=my-replicaset,service.instance.id=project1.app.application-name,service.version=latest", + }, + }, + }, + }, + }, + }, + }, + { + name: "SDK image with port number, no version", + inst: v1alpha1.Instrumentation{}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "fictional.registry.example:10443/imagename", + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "fictional.registry.example:10443/imagename", + Env: []corev1.EnvVar{ + { + Name: "OTEL_SERVICE_NAME", + Value: "", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + }, + }, + }, + }, + }, + { + name: "SDK image with port number, with version", + inst: v1alpha1.Instrumentation{}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "fictional.registry.example:10443/imagename:latest", + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "fictional.registry.example:10443/imagename:latest", + Env: []corev1.EnvVar{ + { + Name: "OTEL_SERVICE_NAME", + Value: "", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.version=latest", }, }, }, @@ -364,7 +480,7 @@ func TestSDKInjection(t *testing.T) { inj := sdkInjector{ client: k8sClient, } - pod := inj.injectCommonSDKConfig(context.Background(), test.inst, corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: test.pod.Namespace}}, test.pod, 0) + pod := inj.injectCommonSDKConfig(context.Background(), test.inst, corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: test.pod.Namespace}}, test.pod, 0, 0) _, err = json.MarshalIndent(pod, "", " ") assert.NoError(t, err) assert.Equal(t, test.expected, pod) @@ -376,7 +492,8 @@ func TestInjectJava(t *testing.T) { inst := v1alpha1.Instrumentation{ Spec: v1alpha1.InstrumentationSpec{ Java: v1alpha1.Java{ - Image: "img:1", + Image: "img:1", + Resources: testResourceRequirements, }, Exporter: v1alpha1.Exporter{ Endpoint: "https://collector:4317", @@ -384,7 +501,7 @@ func TestInjectJava(t *testing.T) { }, } insts := languageInstrumentations{ - Java: &inst, + Java: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, } inj := sdkInjector{ logger: logr.Discard(), @@ -395,39 +512,44 @@ func TestInjectJava(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", }, }, }, - }, "") + }) assert.Equal(t, corev1.Pod{ Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: javaVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: javaInitContainerName, Image: "img:1", - Command: []string{"cp", "/javaagent.jar", "/otel-auto-instrumentation/javaagent.jar"}, + Command: []string{"cp", "/javaagent.jar", javaInstrMountPath + "/javaagent.jar"}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: javaVolumeName, + MountPath: javaInstrMountPath, }}, + Resources: testResourceRequirements, }, }, Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: javaVolumeName, + MountPath: javaInstrMountPath, }, }, Env: []corev1.EnvVar{ @@ -461,7 +583,7 @@ func TestInjectJava(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.version=latest", }, }, }, @@ -474,7 +596,8 @@ func TestInjectNodeJS(t *testing.T) { inst := v1alpha1.Instrumentation{ Spec: v1alpha1.InstrumentationSpec{ NodeJS: v1alpha1.NodeJS{ - Image: "img:1", + Image: "img:1", + Resources: testResourceRequirements, }, Exporter: v1alpha1.Exporter{ Endpoint: "https://collector:4318", @@ -482,7 +605,7 @@ func TestInjectNodeJS(t *testing.T) { }, } insts := languageInstrumentations{ - NodeJS: &inst, + NodeJS: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, } inj := sdkInjector{ logger: logr.Discard(), @@ -493,39 +616,44 @@ func TestInjectNodeJS(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", }, }, }, - }, "") + }) assert.Equal(t, corev1.Pod{ Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: nodejsVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: nodejsInitContainerName, Image: "img:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", nodejsInstrMountPath}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, }}, + Resources: testResourceRequirements, }, }, Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, }, }, Env: []corev1.EnvVar{ @@ -559,7 +687,7 @@ func TestInjectNodeJS(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.version=latest", }, }, }, @@ -580,7 +708,7 @@ func TestInjectPython(t *testing.T) { }, } insts := languageInstrumentations{ - Python: &inst, + Python: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, } inj := sdkInjector{ @@ -592,39 +720,43 @@ func TestInjectPython(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", }, }, }, - }, "") + }) assert.Equal(t, corev1.Pod{ Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: pythonVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: pythonInitContainerName, Image: "img:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", pythonInstrMountPath}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, }}, }, }, Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: pythonVolumeName, + MountPath: pythonInstrMountPath, }, }, Env: []corev1.EnvVar{ @@ -634,11 +766,19 @@ func TestInjectPython(t *testing.T) { }, { Name: "OTEL_TRACES_EXPORTER", - Value: "otlp_proto_http", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", + Value: "http/protobuf", }, { Name: "OTEL_METRICS_EXPORTER", - Value: "none", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", + Value: "http/protobuf", }, { Name: "OTEL_SERVICE_NAME", @@ -666,7 +806,7 @@ func TestInjectPython(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.version=latest", }, }, }, @@ -687,7 +827,7 @@ func TestInjectDotNet(t *testing.T) { }, } insts := languageInstrumentations{ - DotNet: &inst, + DotNet: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, } inj := sdkInjector{ logger: logr.Discard(), @@ -698,39 +838,43 @@ func TestInjectDotNet(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", }, }, }, - }, "") + }) assert.Equal(t, corev1.Pod{ Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ { - Name: volumeName, + Name: dotnetVolumeName, VolumeSource: corev1.VolumeSource{ - EmptyDir: &corev1.EmptyDirVolumeSource{}, + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, }, }, }, InitContainers: []corev1.Container{ { - Name: initContainerName, + Name: dotnetInitContainerName, Image: "img:1", - Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", dotnetInstrMountPath}, VolumeMounts: []corev1.VolumeMount{{ - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, }}, }, }, Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", VolumeMounts: []corev1.VolumeMount{ { - Name: volumeName, - MountPath: "/otel-auto-instrumentation", + Name: dotnetVolumeName, + MountPath: dotnetInstrMountPath, }, }, Env: []corev1.EnvVar{ @@ -740,11 +884,11 @@ func TestInjectDotNet(t *testing.T) { }, { Name: envDotNetCoreClrProfiler, - Value: dotNetCoreClrProfilerId, + Value: dotNetCoreClrProfilerID, }, { Name: envDotNetCoreClrProfilerPath, - Value: dotNetCoreClrProfilerPath, + Value: dotNetCoreClrProfilerGlibcPath, }, { Name: envDotNetStartupHook, @@ -788,7 +932,7 @@ func TestInjectDotNet(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.version=latest", }, }, }, @@ -797,6 +941,609 @@ func TestInjectDotNet(t *testing.T) { }, pod) } +func TestInjectGo(t *testing.T) { + falsee := false + true := true + zero := int64(0) + + tests := []struct { + name string + insts languageInstrumentations + pod corev1.Pod + expected corev1.Pod + }{ + { + name: "shared process namespace disabled", + insts: languageInstrumentations{ + Go: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Go: v1alpha1.Go{ + Image: "otel/go:1", + }, + }, + }, + }, + }, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + ShareProcessNamespace: &falsee, + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + ShareProcessNamespace: &falsee, + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + }, + { + name: "OTEL_GO_AUTO_TARGET_EXE not set", + insts: languageInstrumentations{ + Go: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Go: v1alpha1.Go{ + Image: "otel/go:1", + }, + }, + }, + }, + }, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + }, + { + name: "OTEL_GO_AUTO_TARGET_EXE set by inst", + insts: languageInstrumentations{ + Go: instrumentationWithContainers{Instrumentation: &v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Go: v1alpha1.Go{ + Image: "otel/go:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_GO_AUTO_TARGET_EXE", + Value: "foo", + }, + }, + }, + }, + }, + }, + }, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + Image: "app:latest", + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + ShareProcessNamespace: &true, + Containers: []corev1.Container{ + { + Name: "app", + Image: "app:latest", + }, + { + Name: sideCarName, + Image: "otel/go:1", + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &zero, + Privileged: &true, + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/sys/kernel/debug", + Name: kernelDebugVolumeName, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_GO_AUTO_TARGET_EXE", + Value: "foo", + }, + + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.version=latest", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: kernelDebugVolumeName, + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: kernelDebugVolumePath, + }, + }, + }, + }, + }, + }, + }, + { + name: "OTEL_GO_AUTO_TARGET_EXE set by annotation", + insts: languageInstrumentations{ + Go: instrumentationWithContainers{ + Containers: "", + Instrumentation: &v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Go: v1alpha1.Go{ + Image: "otel/go:1", + }, + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/otel-go-auto-target-exe": "foo", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + Image: "app:latest", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + "instrumentation.opentelemetry.io/otel-go-auto-target-exe": "foo", + }, + }, + Spec: corev1.PodSpec{ + ShareProcessNamespace: &true, + Containers: []corev1.Container{ + { + Name: "app", + Image: "app:latest", + }, + { + Name: sideCarName, + Image: "otel/go:1", + SecurityContext: &corev1.SecurityContext{ + RunAsUser: &zero, + Privileged: &true, + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/sys/kernel/debug", + Name: kernelDebugVolumeName, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_GO_AUTO_TARGET_EXE", + Value: "foo", + }, + + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.version=latest", + }, + }, + }, + }, + Volumes: []corev1.Volume{ + { + Name: kernelDebugVolumeName, + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: kernelDebugVolumePath, + }, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + inj := sdkInjector{ + logger: logr.Discard(), + } + pod := inj.inject(context.Background(), test.insts, corev1.Namespace{}, test.pod) + assert.Equal(t, test.expected, pod) + }) + } +} + +func TestInjectApacheHttpd(t *testing.T) { + + tests := []struct { + name string + insts languageInstrumentations + pod corev1.Pod + expected corev1.Pod + }{ + { + name: "injection enabled, exporter set", + insts: languageInstrumentations{ + ApacheHttpd: instrumentationWithContainers{ + Instrumentation: &v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + ApacheHttpd: v1alpha1.ApacheHttpd{ + Image: "img:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "https://collector:4318", + }, + }, + }, + Containers: "", + }, + }, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "otel-apache-conf-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + { + Name: "otel-apache-agent", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: apacheAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /usr/local/apache2/conf/* " + apacheAgentDirectory + apacheAgentConfigDirectory}, + VolumeMounts: []corev1.VolumeMount{{ + Name: apacheAgentConfigVolume, + MountPath: apacheAgentDirectory + apacheAgentConfigDirectory, + }}, + }, + { + Name: apacheAgentInitContainerName, + Image: "img:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{ + "cp -r /opt/opentelemetry/* /opt/opentelemetry-webserver/agent && export agentLogDir=$(echo \"/opt/opentelemetry-webserver/agent/logs\" | sed 's,/,\\\\/,g') && cat /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml.template | sed 's/__agent_log_dir__/'${agentLogDir}'/g' > /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml &&echo \"$OTEL_APACHE_AGENT_CONF\" > /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && sed -i 's/<>/'${APACHE_SERVICE_INSTANCE_ID}'/g' /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && echo 'Include /usr/local/apache2/conf/opentemetry_agent.conf' >> /opt/opentelemetry-webserver/source-conf/httpd.conf"}, + Env: []corev1.EnvVar{ + { + Name: apacheAttributesEnvVar, + Value: "\n#Load the Otel Webserver SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_common.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_resources.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_trace.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_otlp_recordable.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_ostream_span.so\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_exporter_otlp_grpc.so\n#Load the Otel ApacheModule SDK\nLoadFile /opt/opentelemetry-webserver/agent/sdk_lib/lib/libopentelemetry_webserver_sdk.so\n#Load the Apache Module. In this example for Apache 2.4\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Load the Apache Module. In this example for Apache 2.2\n#LoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel22.so\nLoadModule otel_apache_module /opt/opentelemetry-webserver/agent/WebServerModule/Apache/libmod_apache_otel.so\n#Attributes\nApacheModuleEnabled ON\nApacheModuleOtelExporterEndpoint https://collector:4318\nApacheModuleOtelSpanExporter otlp\nApacheModuleResolveBackends ON\nApacheModuleServiceInstanceId <>\nApacheModuleServiceName app\nApacheModuleServiceNamespace apache-httpd\nApacheModuleTraceAsError ON\n", + }, + {Name: apacheServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "app", + VolumeMounts: []corev1.VolumeMount{ + { + Name: apacheAgentVolume, + MountPath: apacheAgentDirectory + apacheAgentSubDirectory, + }, + { + Name: apacheAgentConfigVolume, + MountPath: apacheDefaultConfigDirectory, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "https://collector:4318", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + }, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + inj := sdkInjector{ + logger: logr.Discard(), + } + + pod := inj.inject(context.Background(), test.insts, corev1.Namespace{}, test.pod) + assert.Equal(t, test.expected, pod) + }) + } +} + +func TestInjectNginx(t *testing.T) { + + tests := []struct { + name string + insts languageInstrumentations + pod corev1.Pod + expected corev1.Pod + }{ + { + name: "injection enabled, exporter set", + insts: languageInstrumentations{ + Nginx: instrumentationWithContainers{ + Instrumentation: &v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Nginx: v1alpha1.Nginx{ + Image: "img:1", + Attrs: []corev1.EnvVar{{ + Name: "NginxModuleOtelMaxQueueSize", + Value: "4096", + }}, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://otlp-endpoint:4317", + }, + }, + }, + Containers: "", + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-nginx-6c44bcbdd", + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "otel-nginx-conf-dir", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "otel-nginx-agent", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: nginxAgentCloneContainerName, + Image: "", + Command: []string{"/bin/sh", "-c"}, + Args: []string{"cp -r /etc/nginx/* /opt/opentelemetry-webserver/source-conf && export NGINX_VERSION=$( { nginx -v ; } 2>&1 ) && echo ${NGINX_VERSION##*/} > /opt/opentelemetry-webserver/source-conf/version.txt"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }}, + }, + { + Name: nginxAgentInitContainerName, + Image: "img:1", + Command: []string{"/bin/sh", "-c"}, + Args: []string{nginxSdkInitContainerTestCommand}, + Env: []corev1.EnvVar{ + { + Name: nginxAttributesEnvVar, + Value: "NginxModuleEnabled ON;\nNginxModuleOtelExporterEndpoint http://otlp-endpoint:4317;\nNginxModuleOtelMaxQueueSize 4096;\nNginxModuleOtelSpanExporter otlp;\nNginxModuleResolveBackends ON;\nNginxModuleServiceInstanceId <>;\nNginxModuleServiceName my-nginx-6c44bcbdd;\nNginxModuleServiceNamespace nginx;\nNginxModuleTraceAsError ON;\n", + }, + { + Name: "OTEL_NGINX_I13N_SCRIPT", + Value: nginxSdkInitContainerI13nScript, + }, + { + Name: nginxServiceInstanceIdEnvVar, + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: nginxAgentConfDirFull, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + Name: "app", + VolumeMounts: []corev1.VolumeMount{ + { + Name: nginxAgentVolume, + MountPath: nginxAgentDirFull, + }, + { + Name: nginxAgentConfigVolume, + MountPath: "/etc/nginx", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "LD_LIBRARY_PATH", + Value: "/opt/opentelemetry-webserver/agent/sdk_lib/lib", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "my-nginx-6c44bcbdd", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://otlp-endpoint:4317", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=my-nginx-6c44bcbdd", + }, + }, + }, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + inj := sdkInjector{ + logger: logr.Discard(), + } + pod := inj.inject(context.Background(), test.insts, corev1.Namespace{}, test.pod) + assert.Equal(t, test.expected, pod) + }) + } +} + func TestInjectSdkOnly(t *testing.T) { inst := v1alpha1.Instrumentation{ Spec: v1alpha1.InstrumentationSpec{ @@ -806,7 +1553,7 @@ func TestInjectSdkOnly(t *testing.T) { }, } insts := languageInstrumentations{ - Sdk: &inst, + Sdk: instrumentationWithContainers{Instrumentation: &inst, Containers: ""}, } inj := sdkInjector{ @@ -818,16 +1565,18 @@ func TestInjectSdkOnly(t *testing.T) { Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", }, }, }, - }, "") + }) assert.Equal(t, corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ { - Name: "app", + Name: "app", + Image: "app:latest", Env: []corev1.EnvVar{ { Name: "OTEL_SERVICE_NAME", @@ -855,7 +1604,7 @@ func TestInjectSdkOnly(t *testing.T) { }, { Name: "OTEL_RESOURCE_ATTRIBUTES", - Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.version=latest", }, }, }, diff --git a/pkg/instrumentation/upgrade/upgrade.go b/pkg/instrumentation/upgrade/upgrade.go index 1dc9c7f54d..d9dbec6c6f 100644 --- a/pkg/instrumentation/upgrade/upgrade.go +++ b/pkg/instrumentation/upgrade/upgrade.go @@ -20,21 +20,41 @@ import ( "reflect" "github.com/go-logr/logr" + featuregate2 "go.opentelemetry.io/collector/featuregate" + "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" +) + +var ( + defaultAnnotationToGate = map[string]*featuregate2.Gate{ + constants.AnnotationDefaultAutoInstrumentationJava: featuregate.EnableJavaAutoInstrumentationSupport, + constants.AnnotationDefaultAutoInstrumentationNodeJS: featuregate.EnableNodeJSAutoInstrumentationSupport, + constants.AnnotationDefaultAutoInstrumentationPython: featuregate.EnablePythonAutoInstrumentationSupport, + constants.AnnotationDefaultAutoInstrumentationDotNet: featuregate.EnableDotnetAutoInstrumentationSupport, + constants.AnnotationDefaultAutoInstrumentationGo: featuregate.EnableGoAutoInstrumentationSupport, + constants.AnnotationDefaultAutoInstrumentationApacheHttpd: featuregate.EnableApacheHTTPAutoInstrumentationSupport, + constants.AnnotationDefaultAutoInstrumentationNginx: featuregate.EnableNginxAutoInstrumentationSupport, + } ) type InstrumentationUpgrade struct { - Client client.Client - Logger logr.Logger - DefaultAutoInstJava string - DefaultAutoInstNodeJS string - DefaultAutoInstPython string - DefaultAutoInstDotNet string + Client client.Client + Logger logr.Logger + Recorder record.EventRecorder + DefaultAutoInstJava string + DefaultAutoInstNodeJS string + DefaultAutoInstPython string + DefaultAutoInstDotNet string + DefaultAutoInstApacheHttpd string + DefaultAutoInstNginx string + DefaultAutoInstGo string } -//+kubebuilder:rbac:groups=opentelemetry.io,resources=instrumentations,verbs=get;list;watch;update;patch +// +kubebuilder:rbac:groups=opentelemetry.io,resources=instrumentations,verbs=get;list;watch;update;patch // ManagedInstances upgrades managed instances by the opentelemetry-operator. func (u *InstrumentationUpgrade) ManagedInstances(ctx context.Context) error { @@ -55,7 +75,7 @@ func (u *InstrumentationUpgrade) ManagedInstances(ctx context.Context) error { upgraded := u.upgrade(ctx, toUpgrade) if !reflect.DeepEqual(upgraded, toUpgrade) { // use update instead of patch because the patch does not upgrade annotations - if err := u.Client.Update(ctx, &upgraded); err != nil { + if err := u.Client.Update(ctx, upgraded); err != nil { u.Logger.Error(err, "failed to apply changes to instance", "name", upgraded.Name, "namespace", upgraded.Namespace) continue } @@ -68,38 +88,54 @@ func (u *InstrumentationUpgrade) ManagedInstances(ctx context.Context) error { return nil } -func (u *InstrumentationUpgrade) upgrade(_ context.Context, inst v1alpha1.Instrumentation) v1alpha1.Instrumentation { - autoInstJava := inst.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationJava] - if autoInstJava != "" { - // upgrade the image only if the image matches the annotation - if inst.Spec.Java.Image == autoInstJava { - inst.Spec.Java.Image = u.DefaultAutoInstJava - inst.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationJava] = u.DefaultAutoInstJava - } - } - autoInstNodeJS := inst.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationNodeJS] - if autoInstNodeJS != "" { - // upgrade the image only if the image matches the annotation - if inst.Spec.NodeJS.Image == autoInstNodeJS { - inst.Spec.NodeJS.Image = u.DefaultAutoInstNodeJS - inst.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationNodeJS] = u.DefaultAutoInstNodeJS - } - } - autoInstPython := inst.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationPython] - if autoInstPython != "" { - // upgrade the image only if the image matches the annotation - if inst.Spec.Python.Image == autoInstPython { - inst.Spec.Python.Image = u.DefaultAutoInstPython - inst.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationPython] = u.DefaultAutoInstPython - } - } - autoInstDotnet := inst.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationDotNet] - if autoInstDotnet != "" { - // upgrade the image only if the image matches the annotation - if inst.Spec.DotNet.Image == autoInstDotnet { - inst.Spec.DotNet.Image = u.DefaultAutoInstDotNet - inst.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationDotNet] = u.DefaultAutoInstDotNet +func (u *InstrumentationUpgrade) upgrade(_ context.Context, inst v1alpha1.Instrumentation) *v1alpha1.Instrumentation { + upgraded := inst.DeepCopy() + for annotation, gate := range defaultAnnotationToGate { + autoInst := upgraded.Annotations[annotation] + if autoInst != "" { + if gate.IsEnabled() { + switch annotation { + case constants.AnnotationDefaultAutoInstrumentationJava: + if inst.Spec.Java.Image == autoInst { + upgraded.Spec.Java.Image = u.DefaultAutoInstJava + upgraded.Annotations[annotation] = u.DefaultAutoInstJava + } + case constants.AnnotationDefaultAutoInstrumentationNodeJS: + if inst.Spec.NodeJS.Image == autoInst { + upgraded.Spec.NodeJS.Image = u.DefaultAutoInstNodeJS + upgraded.Annotations[annotation] = u.DefaultAutoInstNodeJS + } + case constants.AnnotationDefaultAutoInstrumentationPython: + if inst.Spec.Python.Image == autoInst { + upgraded.Spec.Python.Image = u.DefaultAutoInstPython + upgraded.Annotations[annotation] = u.DefaultAutoInstPython + } + case constants.AnnotationDefaultAutoInstrumentationDotNet: + if inst.Spec.DotNet.Image == autoInst { + upgraded.Spec.DotNet.Image = u.DefaultAutoInstDotNet + upgraded.Annotations[annotation] = u.DefaultAutoInstDotNet + } + case constants.AnnotationDefaultAutoInstrumentationGo: + if inst.Spec.Go.Image == autoInst { + upgraded.Spec.Go.Image = u.DefaultAutoInstGo + upgraded.Annotations[annotation] = u.DefaultAutoInstGo + } + case constants.AnnotationDefaultAutoInstrumentationApacheHttpd: + if inst.Spec.ApacheHttpd.Image == autoInst { + upgraded.Spec.ApacheHttpd.Image = u.DefaultAutoInstApacheHttpd + upgraded.Annotations[annotation] = u.DefaultAutoInstApacheHttpd + } + case constants.AnnotationDefaultAutoInstrumentationNginx: + if inst.Spec.Nginx.Image == autoInst { + upgraded.Spec.Nginx.Image = u.DefaultAutoInstNginx + upgraded.Annotations[annotation] = u.DefaultAutoInstNginx + } + } + } else { + u.Logger.Error(nil, "autoinstrumentation not enabled for this language", "flag", gate.ID()) + u.Recorder.Event(upgraded, "Warning", "InstrumentationUpgradeRejected", fmt.Sprintf("support for is not enabled for %s", gate.ID())) + } } } - return inst + return upgraded } diff --git a/pkg/instrumentation/upgrade/upgrade_suite_test.go b/pkg/instrumentation/upgrade/upgrade_suite_test.go index fb3891f9ec..11f62ea128 100644 --- a/pkg/instrumentation/upgrade/upgrade_suite_test.go +++ b/pkg/instrumentation/upgrade/upgrade_suite_test.go @@ -21,6 +21,7 @@ import ( "testing" "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" @@ -30,6 +31,8 @@ import ( var k8sClient client.Client var testEnv *envtest.Environment var testScheme = scheme.Scheme +var err error +var cfg *rest.Config func TestMain(m *testing.M) { testEnv = &envtest.Environment{ @@ -38,13 +41,13 @@ func TestMain(m *testing.M) { }, } - cfg, err := testEnv.Start() + cfg, err = testEnv.Start() if err != nil { fmt.Printf("failed to start testEnv: %v", err) os.Exit(1) } - if err := v1alpha1.AddToScheme(testScheme); err != nil { + if err = v1alpha1.AddToScheme(testScheme); err != nil { fmt.Printf("failed to register scheme: %v", err) os.Exit(1) } diff --git a/pkg/instrumentation/upgrade/upgrade_test.go b/pkg/instrumentation/upgrade/upgrade_test.go index 89a1c251d5..e4f6448e45 100644 --- a/pkg/instrumentation/upgrade/upgrade_test.go +++ b/pkg/instrumentation/upgrade/upgrade_test.go @@ -22,14 +22,36 @@ import ( "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + colfeaturegate "go.opentelemetry.io/collector/featuregate" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/config" + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" + "github.com/open-telemetry/opentelemetry-operator/pkg/featuregate" ) func TestUpgrade(t *testing.T) { + originalVal := featuregate.EnableGoAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableGoAutoInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableGoAutoInstrumentationSupport.ID(), originalVal)) + }) + + originalVal = featuregate.EnableApacheHTTPAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableApacheHTTPAutoInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableApacheHTTPAutoInstrumentationSupport.ID(), originalVal)) + }) + + originalVal = featuregate.EnableNginxAutoInstrumentationSupport.IsEnabled() + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNginxAutoInstrumentationSupport.ID(), true)) + t.Cleanup(func() { + require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNginxAutoInstrumentationSupport.ID(), originalVal)) + }) + nsName := strings.ToLower(t.Name()) err := k8sClient.Create(context.Background(), &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -42,12 +64,6 @@ func TestUpgrade(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "my-inst", Namespace: nsName, - Annotations: map[string]string{ - v1alpha1.AnnotationDefaultAutoInstrumentationJava: "java:1", - v1alpha1.AnnotationDefaultAutoInstrumentationNodeJS: "nodejs:1", - v1alpha1.AnnotationDefaultAutoInstrumentationPython: "python:1", - v1alpha1.AnnotationDefaultAutoInstrumentationDotNet: "dotnet:1", - }, }, Spec: v1alpha1.InstrumentationSpec{ Sampler: v1alpha1.Sampler{ @@ -55,21 +71,40 @@ func TestUpgrade(t *testing.T) { }, }, } - inst.Default() + err = v1alpha1.NewInstrumentationWebhook( + logr.Discard(), + testScheme, + config.New( + config.WithAutoInstrumentationJavaImage("java:1"), + config.WithAutoInstrumentationNodeJSImage("nodejs:1"), + config.WithAutoInstrumentationPythonImage("python:1"), + config.WithAutoInstrumentationDotNetImage("dotnet:1"), + config.WithAutoInstrumentationGoImage("go:1"), + config.WithAutoInstrumentationApacheHttpdImage("apache-httpd:1"), + config.WithAutoInstrumentationNginxImage("nginx:1"), + ), + ).Default(context.Background(), inst) + assert.Nil(t, err) assert.Equal(t, "java:1", inst.Spec.Java.Image) assert.Equal(t, "nodejs:1", inst.Spec.NodeJS.Image) assert.Equal(t, "python:1", inst.Spec.Python.Image) assert.Equal(t, "dotnet:1", inst.Spec.DotNet.Image) + assert.Equal(t, "go:1", inst.Spec.Go.Image) + assert.Equal(t, "apache-httpd:1", inst.Spec.ApacheHttpd.Image) + assert.Equal(t, "nginx:1", inst.Spec.Nginx.Image) err = k8sClient.Create(context.Background(), inst) require.NoError(t, err) up := &InstrumentationUpgrade{ - Logger: logr.Discard(), - DefaultAutoInstJava: "java:2", - DefaultAutoInstNodeJS: "nodejs:2", - DefaultAutoInstPython: "python:2", - DefaultAutoInstDotNet: "dotnet:2", - Client: k8sClient, + Logger: logr.Discard(), + DefaultAutoInstJava: "java:2", + DefaultAutoInstNodeJS: "nodejs:2", + DefaultAutoInstPython: "python:2", + DefaultAutoInstDotNet: "dotnet:2", + DefaultAutoInstGo: "go:2", + DefaultAutoInstApacheHttpd: "apache-httpd:2", + DefaultAutoInstNginx: "nginx:2", + Client: k8sClient, } err = up.ManagedInstances(context.Background()) require.NoError(t, err) @@ -80,12 +115,18 @@ func TestUpgrade(t *testing.T) { Name: "my-inst", }, &updated) require.NoError(t, err) - assert.Equal(t, "java:2", updated.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationJava]) + assert.Equal(t, "java:2", updated.Annotations[constants.AnnotationDefaultAutoInstrumentationJava]) assert.Equal(t, "java:2", updated.Spec.Java.Image) - assert.Equal(t, "nodejs:2", updated.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationNodeJS]) + assert.Equal(t, "nodejs:2", updated.Annotations[constants.AnnotationDefaultAutoInstrumentationNodeJS]) assert.Equal(t, "nodejs:2", updated.Spec.NodeJS.Image) - assert.Equal(t, "python:2", updated.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationPython]) + assert.Equal(t, "python:2", updated.Annotations[constants.AnnotationDefaultAutoInstrumentationPython]) assert.Equal(t, "python:2", updated.Spec.Python.Image) - assert.Equal(t, "dotnet:2", updated.Annotations[v1alpha1.AnnotationDefaultAutoInstrumentationDotNet]) + assert.Equal(t, "dotnet:2", updated.Annotations[constants.AnnotationDefaultAutoInstrumentationDotNet]) assert.Equal(t, "dotnet:2", updated.Spec.DotNet.Image) + assert.Equal(t, "go:2", updated.Annotations[constants.AnnotationDefaultAutoInstrumentationGo]) + assert.Equal(t, "go:2", updated.Spec.Go.Image) + assert.Equal(t, "apache-httpd:2", updated.Annotations[constants.AnnotationDefaultAutoInstrumentationApacheHttpd]) + assert.Equal(t, "apache-httpd:2", updated.Spec.ApacheHttpd.Image) + assert.Equal(t, "nginx:2", updated.Annotations[constants.AnnotationDefaultAutoInstrumentationNginx]) + assert.Equal(t, "nginx:2", updated.Spec.Nginx.Image) } diff --git a/pkg/naming/main.go b/pkg/naming/main.go deleted file mode 100644 index 59b8029a8e..0000000000 --- a/pkg/naming/main.go +++ /dev/null @@ -1,110 +0,0 @@ -// 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 naming is for determining the names for components (containers, services, ...). -package naming - -import ( - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" -) - -// ConfigMap builds the name for the config map used in the OpenTelemetryCollector containers. -func ConfigMap(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-collector", 63, otelcol.Name)) -} - -// TAConfigMap returns the name for the config map used in the TargetAllocator. -func TAConfigMap(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-targetallocator", 63, otelcol.Name)) -} - -// ConfigMapVolume returns the name to use for the config map's volume in the pod. -func ConfigMapVolume() string { - return "otc-internal" -} - -// TAConfigMapVolume returns the name to use for the config map's volume in the TargetAllocator pod. -func TAConfigMapVolume() string { - return "ta-internal" -} - -// Container returns the name to use for the container in the pod. -func Container() string { - return "otc-container" -} - -// TAContainer returns the name to use for the container in the TargetAllocator pod. -func TAContainer() string { - return "ta-container" -} - -// Collector builds the collector (deployment/daemonset) name based on the instance. -func Collector(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-collector", 63, otelcol.Name)) -} - -// HorizontalPodAutoscaler builds the autoscaler name based on the instance. -func HorizontalPodAutoscaler(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-collector", 63, otelcol.Name)) -} - -// HorizontalPodAutoscaler builds the collector (deployment/daemonset) name based on the instance. -func OpenTelemetryCollector(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s", 63, otelcol.Name)) -} - -// HorizontalPodAutoscaler builds the collector (deployment/daemonset) name based on the instance. -func OpenTelemetryCollectorName(otelcolName string) string { - return DNSName(Truncate("%s", 63, otelcolName)) -} - -// TargetAllocator returns the TargetAllocator deployment resource name. -func TargetAllocator(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-targetallocator", 63, otelcol.Name)) -} - -// HeadlessService builds the name for the headless service based on the instance. -func HeadlessService(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-headless", 63, Service(otelcol))) -} - -// MonitoringService builds the name for the monitoring service based on the instance. -func MonitoringService(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-monitoring", 63, Service(otelcol))) -} - -// Service builds the service name based on the instance. -func Service(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-collector", 63, otelcol.Name)) -} - -// Ingress builds the ingress name based on the instance. -func Ingress(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-ingress", 63, otelcol.Name)) -} - -// TAService returns the name to use for the TargetAllocator service. -func TAService(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-targetallocator", 63, otelcol.Name)) -} - -// ServiceAccount builds the service account name based on the instance. -func ServiceAccount(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-collector", 63, otelcol.Name)) -} - -// TargetAllocatorServiceAccount returns the TargetAllocator service account resource name. -func TargetAllocatorServiceAccount(otelcol v1alpha1.OpenTelemetryCollector) string { - return DNSName(Truncate("%s-targetallocator", 63, otelcol.Name)) -} diff --git a/pkg/sidecar/pod.go b/pkg/sidecar/pod.go index 593d3d0c3a..6c119fc59c 100644 --- a/pkg/sidecar/pod.go +++ b/pkg/sidecar/pod.go @@ -23,29 +23,37 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) const ( - label = "sidecar.opentelemetry.io/injected" + injectedLabel = "sidecar.opentelemetry.io/injected" + confEnvVar = "OTEL_CONFIG" ) // add a new sidecar container to the given pod, based on the given OpenTelemetryCollector. func add(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector, pod corev1.Pod, attributes []corev1.EnvVar) (corev1.Pod, error) { - // add the container - volumes := collector.Volumes(cfg, otelcol) - container := collector.Container(cfg, logger, otelcol) + otelColCfg, err := collector.ReplaceConfig(otelcol) + if err != nil { + return pod, err + } + + container := collector.Container(cfg, logger, otelcol, false) + container.Args = append(container.Args, fmt.Sprintf("--config=env:%s", confEnvVar)) + + container.Env = append(container.Env, corev1.EnvVar{Name: confEnvVar, Value: otelColCfg}) if !hasResourceAttributeEnvVar(container.Env) { container.Env = append(container.Env, attributes...) } + pod.Spec.InitContainers = append(pod.Spec.InitContainers, otelcol.Spec.InitContainers...) pod.Spec.Containers = append(pod.Spec.Containers, container) - pod.Spec.Volumes = append(pod.Spec.Volumes, volumes...) + pod.Spec.Volumes = append(pod.Spec.Volumes, otelcol.Spec.Volumes...) if pod.Labels == nil { pod.Labels = map[string]string{} } - pod.Labels[label] = fmt.Sprintf("%s.%s", otelcol.Namespace, otelcol.Name) + pod.Labels[injectedLabel] = naming.Truncate("%s.%s", 63, otelcol.Namespace, otelcol.Name) return pod, nil } diff --git a/pkg/sidecar/pod_test.go b/pkg/sidecar/pod_test.go index 1982e53153..a6d6070f78 100644 --- a/pkg/sidecar/pod_test.go +++ b/pkg/sidecar/pod_test.go @@ -18,13 +18,14 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" ) var logger = logf.Log.WithName("unit-tests") @@ -36,15 +37,39 @@ func TestAddSidecarWhenNoSidecarExists(t *testing.T) { Containers: []corev1.Container{ {Name: "my-app"}, }, + InitContainers: []corev1.Container{ + { + Name: "my-init", + }, + }, // cross-test: the pod has a volume already, make sure we don't remove it Volumes: []corev1.Volume{{}}, }, } otelcol := v1alpha1.OpenTelemetryCollector{ ObjectMeta: metav1.ObjectMeta{ - Name: "otelcol-sample", + Name: "otelcol-sample-with-a-name-that-is-longer-than-sixty-three-characters", Namespace: "some-app", }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Ports: []corev1.ServicePort{ + { + Name: "metrics", + Port: 8888, + Protocol: corev1.ProtocolTCP, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "test", + }, + }, + Config: ` +receivers: +exporters: +processors: +`, + }, } cfg := config.New(config.WithCollectorImage("some-default-image")) @@ -53,9 +78,37 @@ func TestAddSidecarWhenNoSidecarExists(t *testing.T) { // verify assert.NoError(t, err) - assert.Len(t, changed.Spec.Containers, 2) - assert.Len(t, changed.Spec.Volumes, 2) - assert.Equal(t, "some-app.otelcol-sample", changed.Labels["sidecar.opentelemetry.io/injected"]) + require.Len(t, changed.Spec.Containers, 2) + require.Len(t, changed.Spec.InitContainers, 2) + require.Len(t, changed.Spec.Volumes, 1) + assert.Equal(t, "otelcol-sample-with-a-name-that-is-longer-than-sixty-three-cha", + changed.Labels["sidecar.opentelemetry.io/injected"]) + assert.Equal(t, corev1.Container{ + Name: "otc-container", + Image: "some-default-image", + Args: []string{"--config=env:OTEL_CONFIG"}, + Env: []corev1.EnvVar{ + { + Name: "POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_CONFIG", + Value: otelcol.Spec.Config, + }, + }, + Ports: []corev1.ContainerPort{ + { + Name: "metrics", + ContainerPort: 8888, + Protocol: corev1.ProtocolTCP, + }, + }, + }, changed.Spec.Containers[1]) } // this situation should never happen in the current code path, but it should not fail diff --git a/pkg/sidecar/podmutator.go b/pkg/sidecar/podmutator.go index 55743db2e6..a7a0737b53 100644 --- a/pkg/sidecar/podmutator.go +++ b/pkg/sidecar/podmutator.go @@ -28,7 +28,7 @@ import ( "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/internal/webhookhandler" + "github.com/open-telemetry/opentelemetry-operator/internal/webhook/podmutation" ) var ( @@ -43,7 +43,7 @@ type sidecarPodMutator struct { config config.Config } -var _ webhookhandler.PodMutator = (*sidecarPodMutator)(nil) +var _ podmutation.PodMutator = (*sidecarPodMutator)(nil) func NewMutator(logger logr.Logger, config config.Config, client client.Client) *sidecarPodMutator { return &sidecarPodMutator{ @@ -79,7 +79,7 @@ func (p *sidecarPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod // which instance should it talk to? otelcol, err := p.getCollectorInstance(ctx, ns, annValue) if err != nil { - if err == errMultipleInstancesPossible || err == errNoInstancesAvailable || err == errInstanceNotSidecar { + if errors.Is(err, errMultipleInstancesPossible) || errors.Is(err, errNoInstancesAvailable) || errors.Is(err, errInstanceNotSidecar) { // we still allow the pod to be created, but we log a message to the operator's logs logger.Error(err, "failed to select an OpenTelemetry Collector instance for this pod's sidecar") return pod, nil @@ -106,7 +106,14 @@ func (p *sidecarPodMutator) getCollectorInstance(ctx context.Context, ns corev1. } otelcol := v1alpha1.OpenTelemetryCollector{} - err := p.client.Get(ctx, types.NamespacedName{Name: ann, Namespace: ns.Name}, &otelcol) + var nsnOtelcol types.NamespacedName + instNamespace, instName, namespaced := strings.Cut(ann, "/") + if namespaced { + nsnOtelcol = types.NamespacedName{Name: instName, Namespace: instNamespace} + } else { + nsnOtelcol = types.NamespacedName{Name: ann, Namespace: ns.Name} + } + err := p.client.Get(ctx, nsnOtelcol, &otelcol) if err != nil { return otelcol, err } diff --git a/pkg/targetallocator/adapters/config_to_prom_config.go b/pkg/targetallocator/adapters/config_to_prom_config.go deleted file mode 100644 index b4ba5f4d59..0000000000 --- a/pkg/targetallocator/adapters/config_to_prom_config.go +++ /dev/null @@ -1,69 +0,0 @@ -// 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 adapters - -import ( - "fmt" - - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" -) - -func errorNoComponent(component string) error { - return fmt.Errorf("no %s available as part of the configuration", component) -} - -func errorNotAMap(component string) error { - return fmt.Errorf("%s property in the configuration doesn't contain valid %s", component, component) -} - -// ConfigToPromConfig converts the incoming configuration object into a the Prometheus receiver config. -func ConfigToPromConfig(cfg string) (map[interface{}]interface{}, error) { - config, err := adapters.ConfigFromString(cfg) - if err != nil { - return nil, err - } - - receiversProperty, ok := config["receivers"] - if !ok { - return nil, errorNoComponent("receivers") - } - - receivers, ok := receiversProperty.(map[interface{}]interface{}) - if !ok { - return nil, errorNotAMap("receivers") - } - - prometheusProperty, ok := receivers["prometheus"] - if !ok { - return nil, errorNoComponent("prometheus") - } - - prometheus, ok := prometheusProperty.(map[interface{}]interface{}) - if !ok { - return nil, errorNotAMap("prometheus") - } - - prometheusConfigProperty, ok := prometheus["config"] - if !ok { - return nil, errorNoComponent("prometheusConfig") - } - - prometheusConfig, ok := prometheusConfigProperty.(map[interface{}]interface{}) - if !ok { - return nil, errorNotAMap("prometheusConfig") - } - - return prometheusConfig, nil -} diff --git a/pkg/targetallocator/adapters/config_to_prom_config_test.go b/pkg/targetallocator/adapters/config_to_prom_config_test.go deleted file mode 100644 index ba5cc2978a..0000000000 --- a/pkg/targetallocator/adapters/config_to_prom_config_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// 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 adapters_test - -import ( - "fmt" - "reflect" - "testing" - - "github.com/stretchr/testify/assert" - - ta "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator/adapters" -) - -func TestExtractPromConfigFromConfig(t *testing.T) { - configStr := `receivers: - examplereceiver: - endpoint: "0.0.0.0:12345" - examplereceiver/settings: - endpoint: "0.0.0.0:12346" - prometheus: - config: - scrape_config: - job_name: otel-collector - scrape_interval: 10s - jaeger/custom: - protocols: - thrift_http: - endpoint: 0.0.0.0:15268 -` - expectedData := map[interface{}]interface{}{ - "scrape_config": map[interface{}]interface{}{ - "job_name": "otel-collector", - "scrape_interval": "10s", - }, - } - - // test - promConfig, err := ta.ConfigToPromConfig(configStr) - assert.NoError(t, err) - - // verify - assert.Equal(t, expectedData, promConfig) -} - -func TestExtractPromConfigFromNullConfig(t *testing.T) { - configStr := `receivers: - examplereceiver: - endpoint: "0.0.0.0:12345" - examplereceiver/settings: - endpoint: "0.0.0.0:12346" - prometheus: - config: - jaeger/custom: - protocols: - thrift_http: - endpoint: 0.0.0.0:15268 -` - - // test - promConfig, err := ta.ConfigToPromConfig(configStr) - assert.Equal(t, err, fmt.Errorf("%s property in the configuration doesn't contain valid %s", "prometheusConfig", "prometheusConfig")) - - // verify - assert.True(t, reflect.ValueOf(promConfig).IsNil()) -} diff --git a/pkg/targetallocator/container.go b/pkg/targetallocator/container.go deleted file mode 100644 index b485f68cfc..0000000000 --- a/pkg/targetallocator/container.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package targetallocator - -import ( - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" -) - -// Container builds a container for the given TargetAllocator. -func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) corev1.Container { - image := otelcol.Spec.TargetAllocator.Image - if len(image) == 0 { - image = cfg.TargetAllocatorImage() - } - - volumeMounts := []corev1.VolumeMount{{ - Name: naming.TAConfigMapVolume(), - MountPath: "/conf", - }} - - envVars := []corev1.EnvVar{} - - envVars = append(envVars, corev1.EnvVar{ - Name: "OTELCOL_NAMESPACE", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.namespace", - }, - }, - }) - - var args []string - if otelcol.Spec.TargetAllocator.PrometheusCR.Enabled { - args = append(args, "--enable-prometheus-cr-watcher") - } - return corev1.Container{ - Name: naming.TAContainer(), - Image: image, - Env: envVars, - VolumeMounts: volumeMounts, - Args: args, - } -} diff --git a/pkg/targetallocator/deployment.go b/pkg/targetallocator/deployment.go deleted file mode 100644 index 0eb9198534..0000000000 --- a/pkg/targetallocator/deployment.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package targetallocator - -import ( - "github.com/go-logr/logr" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" - "github.com/open-telemetry/opentelemetry-operator/pkg/naming" -) - -// Deployment builds the deployment for the given instance. -func Deployment(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) appsv1.Deployment { - labels := Labels(otelcol) - labels["app.kubernetes.io/name"] = naming.TargetAllocator(otelcol) - - return appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: naming.TargetAllocator(otelcol), - Namespace: otelcol.Namespace, - Labels: labels, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: otelcol.Spec.TargetAllocator.Replicas, - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - Annotations: otelcol.Spec.PodAnnotations, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: ServiceAccountName(otelcol), - Containers: []corev1.Container{Container(cfg, logger, otelcol)}, - Volumes: Volumes(cfg, otelcol), - }, - }, - }, - } -} diff --git a/pkg/targetallocator/deployment_test.go b/pkg/targetallocator/deployment_test.go deleted file mode 100644 index 46b2322629..0000000000 --- a/pkg/targetallocator/deployment_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package targetallocator - -import ( - "testing" - - "github.com/stretchr/testify/assert" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" - "github.com/open-telemetry/opentelemetry-operator/internal/config" -) - -func TestDeploymentNewDefault(t *testing.T) { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - cfg := config.New() - - // test - d := Deployment(cfg, logger, otelcol) - - // verify - assert.Equal(t, "my-instance-targetallocator", d.Name) - assert.Equal(t, "my-instance-targetallocator", d.Labels["app.kubernetes.io/name"]) - - assert.Len(t, d.Spec.Template.Spec.Containers, 1) - - // none of the default annotations should propagate down to the pod - assert.Empty(t, d.Spec.Template.Annotations) - - // the pod selector should match the pod spec's labels - assert.Equal(t, d.Spec.Template.Labels, d.Spec.Selector.MatchLabels) -} - -func TestDeploymentPodAnnotations(t *testing.T) { - // prepare - testPodAnnotationValues := map[string]string{"annotation-key": "annotation-value"} - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - PodAnnotations: testPodAnnotationValues, - }, - } - cfg := config.New() - - // test - ds := Deployment(cfg, logger, otelcol) - - // verify - assert.Equal(t, "my-instance-targetallocator", ds.Name) - assert.Equal(t, testPodAnnotationValues, ds.Spec.Template.Annotations) -} diff --git a/tests/e2e-autoscale/autoscale/00-assert.yaml b/tests/e2e-autoscale/autoscale/00-assert.yaml new file mode 100644 index 0000000000..2546b5462d --- /dev/null +++ b/tests/e2e-autoscale/autoscale/00-assert.yaml @@ -0,0 +1,86 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simplest-collector +status: + readyReplicas: 1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simplest-set-utilization-collector +status: + readyReplicas: 1 +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: simplest-collector +spec: + scaleTargetRef: + kind: OpenTelemetryCollector + name: simplest + apiVersion: opentelemetry.io/v1alpha1 + minReplicas: 1 + maxReplicas: 2 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 90 + behavior: + scaleUp: + stabilizationWindowSeconds: 10 + selectPolicy: Max + policies: + - type: Pods + value: 4 + periodSeconds: 15 + - type: Percent + value: 100 + periodSeconds: 15 + scaleDown: + stabilizationWindowSeconds: 15 + selectPolicy: Max + policies: + - type: Percent + value: 100 + periodSeconds: 15 +--- +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: simplest-set-utilization-collector +spec: + scaleTargetRef: + kind: OpenTelemetryCollector + name: simplest-set-utilization + apiVersion: opentelemetry.io/v1alpha1 + minReplicas: 1 + maxReplicas: 2 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 99 + behavior: + scaleUp: + stabilizationWindowSeconds: 300 + selectPolicy: Max + policies: + - type: Pods + value: 4 + periodSeconds: 15 + - type: Percent + value: 100 + periodSeconds: 15 + scaleDown: + selectPolicy: Max + policies: + - type: Percent + value: 100 + periodSeconds: 15 diff --git a/tests/e2e/autoscale/00-install.yaml b/tests/e2e-autoscale/autoscale/00-install.yaml similarity index 61% rename from tests/e2e/autoscale/00-install.yaml rename to tests/e2e-autoscale/autoscale/00-install.yaml index 04e52e7be9..8f5f5fa92d 100644 --- a/tests/e2e/autoscale/00-install.yaml +++ b/tests/e2e-autoscale/autoscale/00-install.yaml @@ -1,8 +1,17 @@ +# This creates two different deployments: +# * The first one is to ensure an autoscaler is created without +# setting any metrics in the Spec. +# * The second is to check that scaling works properly and that +# spec updates are working as expected. +# apiVersion: opentelemetry.io/v1alpha1 kind: OpenTelemetryCollector metadata: name: simplest spec: +# TODO: these tests use .Spec.MaxReplicas and .Spec.MinReplicas. These fields are +# deprecated and moved to .Spec.Autoscaler. Fine to use these fields to test that old CRD is +# still supported but should eventually be updated. minReplicas: 1 maxReplicas: 2 autoscaler: @@ -28,14 +37,14 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] processors: [] - exporters: [logging] + exporters: [debug] --- apiVersion: opentelemetry.io/v1alpha1 kind: OpenTelemetryCollector @@ -45,10 +54,13 @@ spec: minReplicas: 1 maxReplicas: 2 autoscaler: - targetCPUUtilization: 50 + targetCPUUtilization: 99 + behavior: + scaleUp: + stabilizationWindowSeconds: 300 resources: limits: - cpu: 500m + cpu: 250m memory: 128Mi requests: cpu: 5m @@ -63,11 +75,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e-autoscale/autoscale/01-assert.yaml b/tests/e2e-autoscale/autoscale/01-assert.yaml new file mode 100644 index 0000000000..faae4da95b --- /dev/null +++ b/tests/e2e-autoscale/autoscale/01-assert.yaml @@ -0,0 +1,36 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: simplest-set-utilization-collector +spec: + scaleTargetRef: + kind: OpenTelemetryCollector + name: simplest-set-utilization + apiVersion: opentelemetry.io/v1alpha1 + minReplicas: 1 + maxReplicas: 2 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 60 + behavior: + scaleUp: + stabilizationWindowSeconds: 1 + selectPolicy: Max + policies: + - type: Pods + value: 4 + periodSeconds: 15 + - type: Percent + value: 100 + periodSeconds: 15 + scaleDown: + stabilizationWindowSeconds: 15 + selectPolicy: Max + policies: + - type: Percent + value: 100 + periodSeconds: 15 diff --git a/tests/e2e-autoscale/autoscale/01-install.yaml b/tests/e2e-autoscale/autoscale/01-install.yaml new file mode 100644 index 0000000000..a9a6d549c2 --- /dev/null +++ b/tests/e2e-autoscale/autoscale/01-install.yaml @@ -0,0 +1,44 @@ +# This updates an existing deployment with a new Spec. +# Target memory utilization is now added. +# scaleDown and scaleUp behavior is now added. +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest-set-utilization +spec: + minReplicas: 1 + maxReplicas: 2 + autoscaler: + targetCPUUtilization: 60 + # Without this behavior the HPA will default to a scaledown stabilization + # window of 300 seconds. Tests should fail if this update is not successful. + behavior: + scaleDown: + stabilizationWindowSeconds: 15 + scaleUp: + stabilizationWindowSeconds: 1 + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] \ No newline at end of file diff --git a/tests/e2e-autoscale/autoscale/02-assert.yaml b/tests/e2e-autoscale/autoscale/02-assert.yaml new file mode 100644 index 0000000000..8dc83f76b8 --- /dev/null +++ b/tests/e2e-autoscale/autoscale/02-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest-set-utilization +status: + scale: + replicas: 2 diff --git a/tests/e2e-autoscale/autoscale/02-install.yaml b/tests/e2e-autoscale/autoscale/02-install.yaml new file mode 100644 index 0000000000..b3a5b51193 --- /dev/null +++ b/tests/e2e-autoscale/autoscale/02-install.yaml @@ -0,0 +1,22 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: telemetrygen-set-utilization +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen + command: + - "./telemetrygen" + args: + - -otlp-endpoint=simplest-set-utilization-collector-headless:4317 + - -otlp-insecure + # High duration to ensure the trace creation doesn't stop. + # It'll be stopped in step 4 + - -duration=1m + - -workers=20 + - traces + restartPolicy: Never + backoffLimit: 4 diff --git a/tests/e2e/autoscale/02-assert.yaml b/tests/e2e-autoscale/autoscale/03-assert.yaml similarity index 76% rename from tests/e2e/autoscale/02-assert.yaml rename to tests/e2e-autoscale/autoscale/03-assert.yaml index fb4e052f2b..d67919fa37 100644 --- a/tests/e2e/autoscale/02-assert.yaml +++ b/tests/e2e-autoscale/autoscale/03-assert.yaml @@ -1,8 +1,7 @@ apiVersion: opentelemetry.io/v1alpha1 kind: OpenTelemetryCollector - metadata: - name: simplest + name: simplest-set-utilization status: scale: replicas: 1 diff --git a/tests/e2e-autoscale/autoscale/03-delete.yaml b/tests/e2e-autoscale/autoscale/03-delete.yaml new file mode 100644 index 0000000000..5e39ab40db --- /dev/null +++ b/tests/e2e-autoscale/autoscale/03-delete.yaml @@ -0,0 +1,8 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: +- apiVersion: batch/v1 + kind: Job + propagationPolicy: Background + metadata: + name: telemetrygen-set-utilization \ No newline at end of file diff --git a/tests/e2e-instrumentation/instrumentation-apache-httpd/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-apache-httpd/00-install-collector.yaml new file mode 100644 index 0000000000..5b0d06576c --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-httpd/00-install-collector.yaml @@ -0,0 +1,31 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + exporters: + debug: + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] + +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=3000/1000 --overwrite diff --git a/tests/e2e-instrumentation/instrumentation-apache-httpd/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-apache-httpd/00-install-instrumentation.yaml new file mode 100644 index 0000000000..26bcbaf9eb --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-httpd/00-install-instrumentation.yaml @@ -0,0 +1,17 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: apache +spec: + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + sampler: + type: parentbased_traceidratio + argument: "0.25" + apacheHttpd: + attrs: + - name: ApacheModuleOtelMaxQueueSize + value: "4096" diff --git a/tests/e2e-instrumentation/instrumentation-apache-httpd/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-apache-httpd/01-assert.yaml new file mode 100644 index 0000000000..3634f82b55 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-httpd/01-assert.yaml @@ -0,0 +1,51 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/inject-apache-httpd: 'true' + sidecar.opentelemetry.io/inject: 'true' + labels: + app: my-apache +spec: + containers: + - env: + - name: OTEL_SERVICE_NAME + value: my-apache + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: '0.25' + - name: OTEL_RESOURCE_ATTRIBUTES + name: myapp + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - name: otel-apache-agent + mountPath: /opt/opentelemetry-webserver/agent + - name: otel-apache-conf-dir + mountPath: /usr/local/apache2/conf + - args: + - --config=env:OTEL_CONFIG + name: otc-container + initContainers: + - args: + - cp -r /usr/local/apache2/conf/* /opt/opentelemetry-webserver/source-conf + name: otel-agent-source-container-clone + - args: + - cp -r /opt/opentelemetry/* /opt/opentelemetry-webserver/agent && export agentLogDir=$(echo "/opt/opentelemetry-webserver/agent/logs" | sed 's,/,\\/,g') && cat /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml.template | sed 's/__agent_log_dir__/'${agentLogDir}'/g' > /opt/opentelemetry-webserver/agent/conf/appdynamics_sdk_log4cxx.xml &&echo "$OTEL_APACHE_AGENT_CONF" > /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && sed -i 's/<>/'${APACHE_SERVICE_INSTANCE_ID}'/g' /opt/opentelemetry-webserver/source-conf/opentemetry_agent.conf && echo 'Include /usr/local/apache2/conf/opentemetry_agent.conf' >> /opt/opentelemetry-webserver/source-conf/httpd.conf + name: otel-agent-attach-apache +status: + phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-apache-httpd/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-apache-httpd/01-install-app.yaml new file mode 100644 index 0000000000..96f1326117 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-httpd/01-install-app.yaml @@ -0,0 +1,37 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-apache +spec: + selector: + matchLabels: + app: my-apache + replicas: 1 + template: + metadata: + labels: + app: my-apache + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-apache-httpd: "true" + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - name: myapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-apache-httpd:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + ports: + - containerPort: 8080 + resources: + limits: + cpu: "1" + memory: 500Mi + requests: + cpu: 250m + memory: 100Mi diff --git a/tests/e2e-instrumentation/instrumentation-apache-multicontainer/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/00-install-collector.yaml new file mode 100644 index 0000000000..ee92c16567 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/00-install-collector.yaml @@ -0,0 +1,22 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +#apiVersion: opentelemetry.io/v1alpha1 +#kind: OpenTelemetryCollector +#metadata: +# name: sidecar +#spec: +# mode: sidecar +# config: | +# receivers: +# otlp: +# protocols: +# grpc: +# http: +# processors: +# exporters: +# debug: +# service: +# pipelines: +# traces: +# receivers: [otlp] +# processors: [] +# exporters: [debug] diff --git a/tests/e2e-instrumentation/instrumentation-apache-multicontainer/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/00-install-instrumentation.yaml new file mode 100644 index 0000000000..4d7a2b22f3 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/00-install-instrumentation.yaml @@ -0,0 +1,18 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +#apiVersion: opentelemetry.io/v1alpha1 +#kind: Instrumentation +#metadata: +# name: apache +#spec: +# exporter: +# endpoint: http://localhost:4317 +# propagators: +# - jaeger +# - b3 +# sampler: +# type: parentbased_traceidratio +# argument: "0.25" +# apacheHttpd: +# attrs: +# - name: ApacheModuleOtelMaxQueueSize +# value: "4096" diff --git a/tests/e2e-instrumentation/instrumentation-apache-multicontainer/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/01-assert.yaml new file mode 100644 index 0000000000..c2f7496e9f --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/01-assert.yaml @@ -0,0 +1,71 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +#apiVersion: v1 +#kind: Pod +#metadata: +# annotations: +# sidecar.opentelemetry.io/inject: "true" +# instrumentation.opentelemetry.io/inject-apache-httpd: "true" +# instrumentation.opentelemetry.io/container-names: "myapp,myrabbit" +# labels: +# app: my-apache +#spec: +# containers: +# - env: +# - name: OTEL_SERVICE_NAME +# value: my-apache +# - name: OTEL_EXPORTER_OTLP_ENDPOINT +# value: http://localhost:4317 +# - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME +# valueFrom: +# fieldRef: +# apiVersion: v1 +# fieldPath: metadata.name +# - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME +# valueFrom: +# fieldRef: +# apiVersion: v1 +# fieldPath: spec.nodeName +# - name: OTEL_PROPAGATORS +# value: jaeger,b3 +# - name: OTEL_TRACES_SAMPLER +# value: parentbased_traceidratio +# - name: OTEL_TRACES_SAMPLER_ARG +# value: "0.25" +# - name: OTEL_RESOURCE_ATTRIBUTES +# name: myapp +# volumeMounts: +# - mountPath: /var/run/secrets/kubernetes.io/serviceaccount +# - mountPath: /opt/opentelemetry-webserver/agent +# name: otel-apache-agent +# - mountPath: /usr/local/apache2/conf +# name: otel-apache-conf-dir +# - env: +# - name: OTEL_SERVICE_NAME +# value: my-apache +# - name: OTEL_EXPORTER_OTLP_ENDPOINT +# value: http://localhost:4317 +# - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME +# valueFrom: +# fieldRef: +# apiVersion: v1 +# fieldPath: metadata.name +# - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME +# valueFrom: +# fieldRef: +# apiVersion: v1 +# fieldPath: spec.nodeName +# - name: OTEL_PROPAGATORS +# value: jaeger,b3 +# - name: OTEL_TRACES_SAMPLER +# value: parentbased_traceidratio +# - name: OTEL_TRACES_SAMPLER_ARG +# value: "0.25" +# - name: OTEL_RESOURCE_ATTRIBUTES +# name: myrabbit +# volumeMounts: +# - mountPath: /var/run/secrets/kubernetes.io/serviceaccount +# - args: +# - --config=env:OTEL_CONFIG +# name: otc-container +#status: +# phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-apache-multicontainer/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/01-install-app.yaml new file mode 100644 index 0000000000..1d7fbb0251 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/01-install-app.yaml @@ -0,0 +1,36 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +#apiVersion: apps/v1 +#kind: Deployment +#metadata: +# name: my-apache +#spec: +# selector: +# matchLabels: +# app: my-apache +# replicas: 1 +# template: +# metadata: +# labels: +# app: my-apache +# annotations: +# sidecar.opentelemetry.io/inject: "true" +# instrumentation.opentelemetry.io/inject-apache-httpd: "true" +# instrumentation.opentelemetry.io/container-names: "myapp,myrabbit" +# spec: +# containers: +# - name: myapp +# image: docker.io/chrlic/apache-test@sha256:fad58c6ce7a4f477b455bece2a1980741fa6f81cef1e1093a3b72f9b2ffa7b8e +# # image source at https://github.com/cisco-open/appdynamics-k8s-webhook-instrumentor/tree/main/testWorkloads/apache-httpd +# # licensed under Apache 2.0 +# imagePullPolicy: Always +# ports: +# - containerPort: 8080 +# resources: +# limits: +# cpu: "1" +# memory: 500Mi +# requests: +# cpu: 250m +# memory: 100Mi +# - name: myrabbit +# image: rabbitmq diff --git a/tests/e2e-instrumentation/instrumentation-apache-multicontainer/02-assert.yaml b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/02-assert.yaml new file mode 100644 index 0000000000..dd29818998 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/02-assert.yaml @@ -0,0 +1,68 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +#apiVersion: v1 +#kind: Pod +#metadata: +# annotations: +# instrumentation.opentelemetry.io/inject-apache-httpd: "true" +# sidecar.opentelemetry.io/inject: "true" +# labels: +# app: my-apache +#spec: +# containers: +# - env: +# - name: OTEL_SERVICE_NAME +# value: my-apache +# - name: OTEL_EXPORTER_OTLP_ENDPOINT +# value: http://localhost:4317 +# - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME +# valueFrom: +# fieldRef: +# apiVersion: v1 +# fieldPath: metadata.name +# - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME +# valueFrom: +# fieldRef: +# apiVersion: v1 +# fieldPath: spec.nodeName +# - name: OTEL_PROPAGATORS +# value: jaeger,b3 +# - name: OTEL_TRACES_SAMPLER +# value: parentbased_traceidratio +# - name: OTEL_TRACES_SAMPLER_ARG +# value: "0.25" +# - name: OTEL_RESOURCE_ATTRIBUTES +# name: myapp +# volumeMounts: +# - mountPath: /var/run/secrets/kubernetes.io/serviceaccount +# - mountPath: /opt/opentelemetry-webserver/agent +# name: otel-apache-agent +# - mountPath: /usr/local/apache2/conf +# name: otel-apache-conf-dir +# - env: +# - name: OTEL_SERVICE_NAME +# value: my-apache +# - name: OTEL_EXPORTER_OTLP_ENDPOINT +# value: http://localhost:4317 +# - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME +# valueFrom: +# fieldRef: +# apiVersion: v1 +# fieldPath: metadata.name +# - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME +# valueFrom: +# fieldRef: +# apiVersion: v1 +# fieldPath: spec.nodeName +# - name: OTEL_PROPAGATORS +# value: jaeger,b3 +# - name: OTEL_TRACES_SAMPLER +# value: parentbased_traceidratio +# - name: OTEL_TRACES_SAMPLER_ARG +# value: "0.25" +# - name: OTEL_RESOURCE_ATTRIBUTES +# name: myrabbit +# - args: +# - --config=env:OTEL_CONFIG +# name: otc-container +#status: +# phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-apache-multicontainer/02-install-app.yaml b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/02-install-app.yaml new file mode 100644 index 0000000000..8044cda2f2 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-apache-multicontainer/02-install-app.yaml @@ -0,0 +1,36 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +#apiVersion: apps/v1 +#kind: Deployment +#metadata: +# name: my-apache +#spec: +# selector: +# matchLabels: +# app: my-apache +# replicas: 1 +# template: +# metadata: +# labels: +# app: my-apache +# annotations: +# sidecar.opentelemetry.io/inject: "true" +# instrumentation.opentelemetry.io/inject-apache-httpd: "true" +# instrumentation.opentelemetry.io/container-names: "myrabbit" +# spec: +# containers: +# - name: myapp +# image: docker.io/chrlic/apache-test@sha256:fad58c6ce7a4f477b455bece2a1980741fa6f81cef1e1093a3b72f9b2ffa7b8e +# # image source at https://github.com/cisco-open/appdynamics-k8s-webhook-instrumentor/tree/main/testWorkloads/apache-httpd +# # licensed under Apache 2.0 +# imagePullPolicy: Always +# ports: +# - containerPort: 8080 +# resources: +# limits: +# cpu: "1" +# memory: 500Mi +# requests: +# cpu: 250m +# memory: 100Mi +# - name: myrabbit +# image: rabbitmq diff --git a/tests/e2e/instrumentation-java/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/00-install-collector.yaml similarity index 87% rename from tests/e2e/instrumentation-java/00-install-collector.yaml rename to tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/00-install-collector.yaml index f8e1e98e07..b00affd4ce 100644 --- a/tests/e2e/instrumentation-java/00-install-collector.yaml +++ b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/00-install-collector.yaml @@ -13,11 +13,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/instrumentation-dotnet-multicontainer/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/00-install-instrumentation.yaml similarity index 100% rename from tests/e2e/instrumentation-dotnet-multicontainer/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/00-install-instrumentation.yaml diff --git a/tests/e2e/instrumentation-dotnet-multicontainer/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/01-assert.yaml similarity index 72% rename from tests/e2e/instrumentation-dotnet-multicontainer/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/01-assert.yaml index 48dc57916c..c3083f377c 100644 --- a/tests/e2e/instrumentation-dotnet-multicontainer/01-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/01-assert.yaml @@ -18,15 +18,15 @@ spec: - name: CORECLR_PROFILER value: "{918728DD-259F-4A6A-AC2B-B85E1B658318}" - name: CORECLR_PROFILER_PATH - value: /otel-auto-instrumentation/OpenTelemetry.AutoInstrumentation.Native.so + value: /otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so - name: DOTNET_STARTUP_HOOKS - value: /otel-auto-instrumentation/netcoreapp3.1/OpenTelemetry.AutoInstrumentation.StartupHook.dll + value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll - name: DOTNET_ADDITIONAL_DEPS - value: /otel-auto-instrumentation/AdditionalDeps + value: /otel-auto-instrumentation-dotnet/AdditionalDeps - name: OTEL_DOTNET_AUTO_HOME - value: /otel-auto-instrumentation + value: /otel-auto-instrumentation-dotnet - name: DOTNET_SHARED_STORE - value: /otel-auto-instrumentation/store + value: /otel-auto-instrumentation-dotnet/store - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -48,8 +48,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-dotnet + name: opentelemetry-auto-instrumentation-dotnet - name: myrabbit env: - name: OTEL_LOG_LEVEL @@ -59,15 +59,15 @@ spec: - name: CORECLR_PROFILER value: "{918728DD-259F-4A6A-AC2B-B85E1B658318}" - name: CORECLR_PROFILER_PATH - value: /otel-auto-instrumentation/OpenTelemetry.AutoInstrumentation.Native.so + value: /otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so - name: DOTNET_STARTUP_HOOKS - value: /otel-auto-instrumentation/netcoreapp3.1/OpenTelemetry.AutoInstrumentation.StartupHook.dll + value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll - name: DOTNET_ADDITIONAL_DEPS - value: /otel-auto-instrumentation/AdditionalDeps + value: /otel-auto-instrumentation-dotnet/AdditionalDeps - name: OTEL_DOTNET_AUTO_HOME - value: /otel-auto-instrumentation + value: /otel-auto-instrumentation-dotnet - name: DOTNET_SHARED_STORE - value: /otel-auto-instrumentation/store + value: /otel-auto-instrumentation-dotnet/store - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -89,8 +89,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-dotnet + name: opentelemetry-auto-instrumentation-dotnet - name: otc-container status: phase: Running \ No newline at end of file diff --git a/tests/e2e/instrumentation-dotnet-multicontainer/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/01-install-app.yaml similarity index 73% rename from tests/e2e/instrumentation-dotnet-multicontainer/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/01-install-app.yaml index e64bc4f569..b77a2a7443 100644 --- a/tests/e2e/instrumentation-dotnet-multicontainer/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/01-install-app.yaml @@ -18,6 +18,6 @@ spec: spec: containers: - name: myapp - image: docker.io/avadhutp123/aspnetapp:latest # source code of the application: https://github.com/dotnet/dotnet-docker/tree/main/samples/aspnetapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-dotnet:main - name: myrabbit - image: rabbitmq + image: rabbitmq:3 diff --git a/tests/e2e/instrumentation-dotnet-multicontainer/02-assert.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/02-assert.yaml similarity index 76% rename from tests/e2e/instrumentation-dotnet-multicontainer/02-assert.yaml rename to tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/02-assert.yaml index ff8be2ac3d..4cd7320900 100644 --- a/tests/e2e/instrumentation-dotnet-multicontainer/02-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/02-assert.yaml @@ -21,15 +21,15 @@ spec: - name: CORECLR_PROFILER value: "{918728DD-259F-4A6A-AC2B-B85E1B658318}" - name: CORECLR_PROFILER_PATH - value: /otel-auto-instrumentation/OpenTelemetry.AutoInstrumentation.Native.so + value: /otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so - name: DOTNET_STARTUP_HOOKS - value: /otel-auto-instrumentation/netcoreapp3.1/OpenTelemetry.AutoInstrumentation.StartupHook.dll + value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll - name: DOTNET_ADDITIONAL_DEPS - value: /otel-auto-instrumentation/AdditionalDeps + value: /otel-auto-instrumentation-dotnet/AdditionalDeps - name: OTEL_DOTNET_AUTO_HOME - value: /otel-auto-instrumentation + value: /otel-auto-instrumentation-dotnet - name: DOTNET_SHARED_STORE - value: /otel-auto-instrumentation/store + value: /otel-auto-instrumentation-dotnet/store - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -51,8 +51,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-dotnet + name: opentelemetry-auto-instrumentation-dotnet - name: otc-container status: phase: Running \ No newline at end of file diff --git a/tests/e2e/instrumentation-dotnet-multicontainer/02-install-app.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/02-install-app.yaml similarity index 73% rename from tests/e2e/instrumentation-dotnet-multicontainer/02-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/02-install-app.yaml index 9b86d167ca..87930b15f1 100644 --- a/tests/e2e/instrumentation-dotnet-multicontainer/02-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-dotnet-multicontainer/02-install-app.yaml @@ -18,6 +18,6 @@ spec: spec: containers: - name: myapp - image: docker.io/avadhutp123/aspnetapp:latest # source code of the application: https://github.com/dotnet/dotnet-docker/tree/main/samples/aspnetapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-dotnet:main - name: myrabbit - image: rabbitmq + image: rabbitmq:3 diff --git a/tests/e2e-instrumentation/instrumentation-dotnet-musl/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-musl/00-install-collector.yaml new file mode 100644 index 0000000000..b03a72e60e --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-dotnet-musl/00-install-collector.yaml @@ -0,0 +1,32 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=2000/1000 --overwrite diff --git a/tests/e2e/instrumentation-dotnet/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-musl/00-install-instrumentation.yaml similarity index 100% rename from tests/e2e/instrumentation-dotnet/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-dotnet-musl/00-install-instrumentation.yaml diff --git a/tests/e2e-instrumentation/instrumentation-dotnet-musl/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-musl/01-assert.yaml new file mode 100644 index 0000000000..40fedff849 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-dotnet-musl/01-assert.yaml @@ -0,0 +1,66 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-dotnet: "true" + instrumentation.opentelemetry.io/otel-dotnet-auto-runtime: "linux-musl-x64" + labels: + app: my-pod-with-sidecar +spec: + containers: + - name: myapp + env: + - name: CORECLR_ENABLE_PROFILING + value: "1" + - name: CORECLR_PROFILER + value: "{918728DD-259F-4A6A-AC2B-B85E1B658318}" + - name: CORECLR_PROFILER_PATH + value: /otel-auto-instrumentation-dotnet/linux-musl-x64/OpenTelemetry.AutoInstrumentation.Native.so + - name: DOTNET_STARTUP_HOOKS + value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll + - name: DOTNET_ADDITIONAL_DEPS + value: /otel-auto-instrumentation-dotnet/AdditionalDeps + - name: OTEL_DOTNET_AUTO_HOME + value: /otel-auto-instrumentation-dotnet + - name: DOTNET_SHARED_STORE + value: /otel-auto-instrumentation-dotnet/store + - name: OTEL_TRACES_EXPORTER + value: otlp + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_EXPORTER_OTLP_TIMEOUT + value: "20" + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED + value: "true" + - name: OTEL_SERVICE_NAME + value: my-deployment-with-sidecar + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_PROPAGATORS + value: jaeger,b3multi + - name: OTEL_RESOURCE_ATTRIBUTES + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - mountPath: /otel-auto-instrumentation-dotnet + name: opentelemetry-auto-instrumentation-dotnet + - name: otc-container + initContainers: + - name: opentelemetry-auto-instrumentation-dotnet + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + resources: + limits: + cpu: "500m" + memory: "128Mi" + requests: + cpu: "50m" + memory: "128Mi" +status: + phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-dotnet-musl/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-dotnet-musl/01-install-app.yaml new file mode 100644 index 0000000000..e8d32f7b41 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-dotnet-musl/01-install-app.yaml @@ -0,0 +1,29 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment-with-sidecar +spec: + selector: + matchLabels: + app: my-pod-with-sidecar + replicas: 1 + template: + metadata: + labels: + app: my-pod-with-sidecar + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-dotnet: "true" + instrumentation.opentelemetry.io/otel-dotnet-auto-runtime: "linux-musl-x64" + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + containers: + - name: myapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-dotnet:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] diff --git a/tests/e2e-instrumentation/instrumentation-dotnet/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-dotnet/00-install-collector.yaml new file mode 100644 index 0000000000..2875bde640 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-dotnet/00-install-collector.yaml @@ -0,0 +1,32 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=2000/1000 --overwrite diff --git a/tests/e2e-instrumentation/instrumentation-dotnet/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-dotnet/00-install-instrumentation.yaml new file mode 100644 index 0000000000..80ca25ca55 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-dotnet/00-install-instrumentation.yaml @@ -0,0 +1,26 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: dotnet +spec: + env: + - name: OTEL_TRACES_EXPORTER + value: otlp + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_EXPORTER_OTLP_TIMEOUT + value: "20" + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED + value: "true" + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3multi + sampler: + type: parentbased_traceidratio + argument: "0.25" diff --git a/tests/e2e/instrumentation-dotnet/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-dotnet/01-assert.yaml similarity index 63% rename from tests/e2e/instrumentation-dotnet/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-dotnet/01-assert.yaml index 29bbb3a6fe..68178cc2d5 100644 --- a/tests/e2e/instrumentation-dotnet/01-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-dotnet/01-assert.yaml @@ -15,15 +15,15 @@ spec: - name: CORECLR_PROFILER value: "{918728DD-259F-4A6A-AC2B-B85E1B658318}" - name: CORECLR_PROFILER_PATH - value: /otel-auto-instrumentation/OpenTelemetry.AutoInstrumentation.Native.so + value: /otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so - name: DOTNET_STARTUP_HOOKS - value: /otel-auto-instrumentation/netcoreapp3.1/OpenTelemetry.AutoInstrumentation.StartupHook.dll + value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll - name: DOTNET_ADDITIONAL_DEPS - value: /otel-auto-instrumentation/AdditionalDeps + value: /otel-auto-instrumentation-dotnet/AdditionalDeps - name: OTEL_DOTNET_AUTO_HOME - value: /otel-auto-instrumentation + value: /otel-auto-instrumentation-dotnet - name: DOTNET_SHARED_STORE - value: /otel-auto-instrumentation/store + value: /otel-auto-instrumentation-dotnet/store - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -45,10 +45,21 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-dotnet + name: opentelemetry-auto-instrumentation-dotnet - name: otc-container initContainers: - - name: opentelemetry-auto-instrumentation + - name: opentelemetry-auto-instrumentation-dotnet + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + resources: + limits: + cpu: "500m" + memory: "128Mi" + requests: + cpu: "50m" + memory: "128Mi" status: phase: Running diff --git a/tests/e2e/instrumentation-dotnet/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-dotnet/01-install-app.yaml similarity index 71% rename from tests/e2e/instrumentation-dotnet/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-dotnet/01-install-app.yaml index 589b4d9c83..15e58506fc 100644 --- a/tests/e2e/instrumentation-dotnet/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-dotnet/01-install-app.yaml @@ -21,4 +21,8 @@ spec: fsGroup: 2000 containers: - name: myapp - image: docker.io/avadhutp123/aspnetapp:latest # source code of the application: https://github.com/dotnet/dotnet-docker/tree/main/samples/aspnetapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-dotnet:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] diff --git a/tests/e2e/instrumentation-nodejs-multicontainer/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-go/00-install-collector.yaml similarity index 87% rename from tests/e2e/instrumentation-nodejs-multicontainer/00-install-collector.yaml rename to tests/e2e-instrumentation/instrumentation-go/00-install-collector.yaml index f8e1e98e07..b00affd4ce 100644 --- a/tests/e2e/instrumentation-nodejs-multicontainer/00-install-collector.yaml +++ b/tests/e2e-instrumentation/instrumentation-go/00-install-collector.yaml @@ -13,11 +13,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e-instrumentation/instrumentation-go/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-go/00-install-instrumentation.yaml new file mode 100644 index 0000000000..f5f640dbfa --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-go/00-install-instrumentation.yaml @@ -0,0 +1,26 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: go +spec: + env: + - name: OTEL_TRACES_EXPORTER + value: otlp + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_EXPORTER_OTLP_TIMEOUT + value: "20" + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED + value: "true" + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + sampler: + type: parentbased_traceidratio + argument: "0.25" diff --git a/tests/e2e-instrumentation/instrumentation-go/01-add-scc.yaml b/tests/e2e-instrumentation/instrumentation-go/01-add-scc.yaml new file mode 100644 index 0000000000..e8a82d749e --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-go/01-add-scc.yaml @@ -0,0 +1,16 @@ +# Create a SA to apply the SCC policy +apiVersion: v1 +kind: ServiceAccount +metadata: + name: otel-instrumentation-go +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - script: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=0/0 --overwrite + - script: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=2000/1000 --overwrite + # Add the SCC + - script: ./add-scc.sh diff --git a/tests/e2e-instrumentation/instrumentation-go/02-assert.yaml b/tests/e2e-instrumentation/instrumentation-go/02-assert.yaml new file mode 100644 index 0000000000..1d0787dc34 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-go/02-assert.yaml @@ -0,0 +1,51 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-go: "true" + instrumentation.opentelemetry.io/otel-go-auto-target-exe: /usr/src/app/productcatalogservice + labels: + app: my-pod-with-sidecar +spec: + containers: + - name: productcatalogservice + - name: otc-container + - name: opentelemetry-auto-instrumentation + resources: + limits: + cpu: "500m" + memory: "32Mi" + requests: + cpu: "50m" + memory: "32Mi" + image: ghcr.io/open-telemetry/opentelemetry-go-instrumentation/autoinstrumentation-go:v0.8.0-alpha + env: + - name: OTEL_GO_AUTO_TARGET_EXE + value: /usr/src/app/productcatalogservice + - name: OTEL_TRACES_EXPORTER + value: otlp + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_EXPORTER_OTLP_TIMEOUT + value: "20" + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED + value: "true" + - name: OTEL_SERVICE_NAME + value: my-deployment-with-sidecar + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + volumeMounts: + - name: kernel-debug + mountPath: "/sys/kernel/debug" + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + +status: + phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-go/02-install-app.yaml b/tests/e2e-instrumentation/instrumentation-go/02-install-app.yaml new file mode 100644 index 0000000000..1a4eb45ceb --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-go/02-install-app.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment-with-sidecar +spec: + selector: + matchLabels: + app: my-pod-with-sidecar + replicas: 1 + template: + metadata: + labels: + app: my-pod-with-sidecar + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-go: "true" + instrumentation.opentelemetry.io/otel-go-auto-target-exe: /usr/src/app/productcatalogservice + spec: + serviceAccountName: otel-instrumentation-go + securityContext: + runAsUser: 0 + runAsGroup: 3000 + fsGroup: 2000 + containers: + - name: productcatalogservice + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-golang:main diff --git a/tests/e2e-instrumentation/instrumentation-go/add-scc.sh b/tests/e2e-instrumentation/instrumentation-go/add-scc.sh new file mode 100755 index 0000000000..d5bb738b36 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-go/add-scc.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +if [[ "$(kubectl api-resources --api-group=operator.openshift.io -o name)" ]]; then + kubectl apply -f scc.yaml + oc adm policy add-scc-to-user otel-go-instrumentation -z otel-instrumentation-go -n $NAMESPACE +fi diff --git a/tests/e2e-instrumentation/instrumentation-go/scc.yaml b/tests/e2e-instrumentation/instrumentation-go/scc.yaml new file mode 100644 index 0000000000..b32985664a --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-go/scc.yaml @@ -0,0 +1,17 @@ +apiVersion: security.openshift.io/v1 +kind: SecurityContextConstraints +metadata: + name: otel-go-instrumentation +allowHostDirVolumePlugin: true +allowPrivilegeEscalation: true +allowPrivilegedContainer: true +fsGroup: + type: RunAsAny +runAsUser: + type: RunAsAny +seLinuxContext: + type: RunAsAny +seccompProfiles: +- '*' +supplementalGroups: + type: RunAsAny diff --git a/tests/e2e/instrumentation-nodejs/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-java-multicontainer/00-install-collector.yaml similarity index 87% rename from tests/e2e/instrumentation-nodejs/00-install-collector.yaml rename to tests/e2e-instrumentation/instrumentation-java-multicontainer/00-install-collector.yaml index f8e1e98e07..b00affd4ce 100644 --- a/tests/e2e/instrumentation-nodejs/00-install-collector.yaml +++ b/tests/e2e-instrumentation/instrumentation-java-multicontainer/00-install-collector.yaml @@ -13,11 +13,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/instrumentation-java-multicontainer/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-java-multicontainer/00-install-instrumentation.yaml similarity index 100% rename from tests/e2e/instrumentation-java-multicontainer/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-java-multicontainer/00-install-instrumentation.yaml diff --git a/tests/e2e/instrumentation-java-multicontainer/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-java-multicontainer/01-assert.yaml similarity index 86% rename from tests/e2e/instrumentation-java-multicontainer/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-java-multicontainer/01-assert.yaml index 5e4f02fcc1..bb15aa2698 100644 --- a/tests/e2e/instrumentation-java-multicontainer/01-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-java-multicontainer/01-assert.yaml @@ -18,7 +18,7 @@ spec: - name: SPLUNK_PROFILER_ENABLED value: "false" - name: JAVA_TOOL_OPTIONS - value: " -javaagent:/otel-auto-instrumentation/javaagent.jar" + value: " -javaagent:/otel-auto-instrumentation-java/javaagent.jar" - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -40,8 +40,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-java + name: opentelemetry-auto-instrumentation-java - name: myrabbit env: - name: OTEL_JAVAAGENT_DEBUG @@ -51,7 +51,7 @@ spec: - name: SPLUNK_PROFILER_ENABLED value: "false" - name: JAVA_TOOL_OPTIONS - value: " -javaagent:/otel-auto-instrumentation/javaagent.jar" + value: " -javaagent:/otel-auto-instrumentation-java/javaagent.jar" - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -73,8 +73,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-java + name: opentelemetry-auto-instrumentation-java - name: otc-container status: phase: Running \ No newline at end of file diff --git a/tests/e2e/instrumentation-java-multicontainer/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-java-multicontainer/01-install-app.yaml similarity index 82% rename from tests/e2e/instrumentation-java-multicontainer/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-java-multicontainer/01-install-app.yaml index b051624357..b3b563780e 100644 --- a/tests/e2e/instrumentation-java-multicontainer/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-java-multicontainer/01-install-app.yaml @@ -18,6 +18,6 @@ spec: spec: containers: - name: myapp - image: ghcr.io/pavolloffay/spring-petclinic:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-java:main - name: myrabbit - image: rabbitmq + image: rabbitmq:3 diff --git a/tests/e2e/instrumentation-java-multicontainer/02-assert.yaml b/tests/e2e-instrumentation/instrumentation-java-multicontainer/02-assert.yaml similarity index 88% rename from tests/e2e/instrumentation-java-multicontainer/02-assert.yaml rename to tests/e2e-instrumentation/instrumentation-java-multicontainer/02-assert.yaml index 0df16471ad..f93b4e752f 100644 --- a/tests/e2e/instrumentation-java-multicontainer/02-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-java-multicontainer/02-assert.yaml @@ -21,7 +21,7 @@ spec: - name: SPLUNK_PROFILER_ENABLED value: "false" - name: JAVA_TOOL_OPTIONS - value: " -javaagent:/otel-auto-instrumentation/javaagent.jar" + value: " -javaagent:/otel-auto-instrumentation-java/javaagent.jar" - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -43,8 +43,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-java + name: opentelemetry-auto-instrumentation-java - name: otc-container status: phase: Running \ No newline at end of file diff --git a/tests/e2e/instrumentation-java-multicontainer/02-install-app.yaml b/tests/e2e-instrumentation/instrumentation-java-multicontainer/02-install-app.yaml similarity index 82% rename from tests/e2e/instrumentation-java-multicontainer/02-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-java-multicontainer/02-install-app.yaml index c963b87377..4c7a1339c9 100644 --- a/tests/e2e/instrumentation-java-multicontainer/02-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-java-multicontainer/02-install-app.yaml @@ -18,6 +18,6 @@ spec: spec: containers: - name: myapp - image: ghcr.io/pavolloffay/spring-petclinic:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-java:main - name: myrabbit - image: rabbitmq + image: rabbitmq:3 diff --git a/tests/e2e/instrumentation-java-other-ns/00-cleanup-other-ns.yaml b/tests/e2e-instrumentation/instrumentation-java-other-ns/01-cleanup-other-ns.yaml similarity index 100% rename from tests/e2e/instrumentation-java-other-ns/00-cleanup-other-ns.yaml rename to tests/e2e-instrumentation/instrumentation-java-other-ns/01-cleanup-other-ns.yaml diff --git a/tests/e2e-instrumentation/instrumentation-java-other-ns/02-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-java-other-ns/02-install-collector.yaml new file mode 100644 index 0000000000..2875bde640 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-other-ns/02-install-collector.yaml @@ -0,0 +1,32 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=2000/1000 --overwrite diff --git a/tests/e2e/instrumentation-java-other-ns/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-java-other-ns/02-install-instrumentation.yaml similarity index 100% rename from tests/e2e/instrumentation-java-other-ns/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-java-other-ns/02-install-instrumentation.yaml diff --git a/tests/e2e/instrumentation-java-other-ns/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-java-other-ns/03-assert.yaml similarity index 79% rename from tests/e2e/instrumentation-java-other-ns/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-java-other-ns/03-assert.yaml index fc4dd75353..99252fdf9c 100644 --- a/tests/e2e/instrumentation-java-other-ns/01-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-java-other-ns/03-assert.yaml @@ -17,7 +17,7 @@ spec: - name: SPLUNK_PROFILER_ENABLED value: "false" - name: JAVA_TOOL_OPTIONS - value: " -javaagent:/otel-auto-instrumentation/javaagent.jar" + value: " -javaagent:/otel-auto-instrumentation-java/javaagent.jar" - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -39,10 +39,14 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-java + name: opentelemetry-auto-instrumentation-java - name: otc-container initContainers: - - name: opentelemetry-auto-instrumentation + - name: opentelemetry-auto-instrumentation-java + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] status: phase: Running diff --git a/tests/e2e/instrumentation-java-other-ns/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-java-other-ns/03-install-app.yaml similarity index 72% rename from tests/e2e/instrumentation-java-other-ns/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-java-other-ns/03-install-app.yaml index f566eb4c02..962207dd8c 100644 --- a/tests/e2e/instrumentation-java-other-ns/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-java-other-ns/03-install-app.yaml @@ -21,4 +21,8 @@ spec: fsGroup: 2000 containers: - name: myapp - image: ghcr.io/pavolloffay/spring-petclinic:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-java:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] diff --git a/tests/e2e-instrumentation/instrumentation-java-other-ns/04-cleanup-other-ns.yaml b/tests/e2e-instrumentation/instrumentation-java-other-ns/04-cleanup-other-ns.yaml new file mode 100644 index 0000000000..ee6bc7f70e --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-other-ns/04-cleanup-other-ns.yaml @@ -0,0 +1,6 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: +- apiVersion: v1 + kind: Namespace + name: my-other-ns diff --git a/tests/e2e-instrumentation/instrumentation-java/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-java/00-install-collector.yaml new file mode 100644 index 0000000000..2875bde640 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java/00-install-collector.yaml @@ -0,0 +1,32 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=2000/1000 --overwrite diff --git a/tests/e2e/instrumentation-java/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-java/00-install-instrumentation.yaml similarity index 100% rename from tests/e2e/instrumentation-java/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-java/00-install-instrumentation.yaml diff --git a/tests/e2e/instrumentation-java/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-java/01-assert.yaml similarity index 72% rename from tests/e2e/instrumentation-java/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-java/01-assert.yaml index d3505b046c..5dec65babc 100644 --- a/tests/e2e/instrumentation-java/01-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-java/01-assert.yaml @@ -17,7 +17,7 @@ spec: - name: SPLUNK_PROFILER_ENABLED value: "false" - name: JAVA_TOOL_OPTIONS - value: " -javaagent:/otel-auto-instrumentation/javaagent.jar" + value: " -javaagent:/otel-auto-instrumentation-java/javaagent.jar" - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -39,10 +39,21 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-java + name: opentelemetry-auto-instrumentation-java - name: otc-container initContainers: - - name: opentelemetry-auto-instrumentation + - name: opentelemetry-auto-instrumentation-java + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + resources: + limits: + cpu: "500m" + memory: "64Mi" + requests: + cpu: "50m" + memory: "64Mi" status: phase: Running diff --git a/tests/e2e/instrumentation-java/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-java/01-install-app.yaml similarity index 71% rename from tests/e2e/instrumentation-java/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-java/01-install-app.yaml index 093bb8b4c1..17be6d12eb 100644 --- a/tests/e2e/instrumentation-java/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-java/01-install-app.yaml @@ -21,4 +21,8 @@ spec: fsGroup: 2000 containers: - name: myapp - image: ghcr.io/pavolloffay/spring-petclinic:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-java:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] diff --git a/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/00-install-collector.yaml new file mode 100644 index 0000000000..c18f418bfb --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/00-install-collector.yaml @@ -0,0 +1,31 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + exporters: + logging: + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] + +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=3000/1000 --overwrite diff --git a/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/00-install-instrumentation.yaml new file mode 100644 index 0000000000..9c8297f897 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/00-install-instrumentation.yaml @@ -0,0 +1,17 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: nginx +spec: + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + sampler: + type: parentbased_traceidratio + argument: "0.25" + nginx: + attrs: + - name: NginxModuleOtelMaxQueueSize + value: "4096" diff --git a/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/01-assert.yaml new file mode 100644 index 0000000000..ff21ccd679 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/01-assert.yaml @@ -0,0 +1,59 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/inject-nginx: 'true' + sidecar.opentelemetry.io/inject: 'true' + labels: + app: my-nginx +spec: + containers: + - env: + - name: LD_LIBRARY_PATH + value: /opt:/opt/opentelemetry-webserver/agent/sdk_lib/lib + - name: OTEL_SERVICE_NAME + value: my-nginx + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: '0.25' + - name: OTEL_RESOURCE_ATTRIBUTES + name: myapp + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - name: otel-nginx-agent + mountPath: /opt/opentelemetry-webserver/agent + - name: otel-nginx-conf-dir + mountPath: /etc/nginx + - args: + - --config=env:OTEL_CONFIG + name: otc-container + initContainers: + - args: + - cp -r /etc/nginx/* /opt/opentelemetry-webserver/source-conf && export NGINX_VERSION=$( { nginx -v ; } 2>&1 ) && echo ${NGINX_VERSION##*/} > /opt/opentelemetry-webserver/source-conf/version.txt + name: otel-agent-source-container-clone + - name: otel-agent-attach-nginx + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + args: + - echo -e $OTEL_NGINX_I13N_SCRIPT > /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && chmod +x /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && cat /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh "/opt/opentelemetry-webserver/agent" "/opt/opentelemetry-webserver/source-conf" "nginx.conf" "<>" +status: + phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/01-install-app.yaml new file mode 100644 index 0000000000..70fcc626a4 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-contnr-secctx/01-install-app.yaml @@ -0,0 +1,94 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-nginx +spec: + selector: + matchLabels: + app: my-nginx + replicas: 1 + template: + metadata: + labels: + app: my-nginx + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-nginx: "true" + spec: + containers: + - name: myapp + image: nginxinc/nginx-unprivileged:1.23.1 + imagePullPolicy: Always + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + ports: + - containerPort: 8765 + env: + - name: LD_LIBRARY_PATH + value: /opt + volumeMounts: + - name: nginx-conf + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + readOnly: true + imagePullPolicy: Always + resources: + limits: + cpu: "1" + memory: 500Mi + requests: + cpu: 250m + memory: 100Mi + volumes: + - name: nginx-conf + configMap: + name: nginx-conf + items: + - key: nginx.conf + path: nginx.conf + +--- + +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-conf +data: + nginx.conf: | + # user nginx; + pid /tmp/nginx.pid; + worker_processes 1; + events { + worker_connections 10240; + } + http { + include /etc/nginx/conf.d/*.conf; + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp_path; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + server { + listen 8765; + server_name localhost; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + location /api/customer/ { + proxy_pass http://localhost:8282/api/customer/; + } + location /api/vendor/ { + proxy_pass http://localhost:8383/api/vendor/; + } + + location /seznam { + proxy_pass http://www.seznam.cz/; + } + } + + } + +--- diff --git a/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/00-install-collector.yaml new file mode 100644 index 0000000000..cb02aa005d --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/00-install-collector.yaml @@ -0,0 +1,32 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + exporters: + logging: + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] + +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=3000/1000 --overwrite diff --git a/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/00-install-instrumentation.yaml new file mode 100644 index 0000000000..1f5296bc0b --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/00-install-instrumentation.yaml @@ -0,0 +1,19 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: nginx +spec: + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + sampler: + type: parentbased_traceidratio + argument: "0.25" + nginx: + attrs: + - name: NginxModuleOtelMaxQueueSize + value: "4096" + diff --git a/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/01-assert.yaml new file mode 100644 index 0000000000..6d8a744ae3 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/01-assert.yaml @@ -0,0 +1,77 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +apiVersion: v1 +kind: Pod +metadata: + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-nginx: "true" + instrumentation.opentelemetry.io/container-names: "myapp,myrabbit" + labels: + app: my-nginx +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - env: + - name: LD_LIBRARY_PATH + value: /opt:/opt/opentelemetry-webserver/agent/sdk_lib/lib + - name: OTEL_SERVICE_NAME + value: my-nginx + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.25" + - name: OTEL_RESOURCE_ATTRIBUTES + name: myapp + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - mountPath: /opt/opentelemetry-webserver/agent + name: otel-nginx-agent + - mountPath: /etc/nginx + name: otel-nginx-conf-dir + - env: + - name: OTEL_SERVICE_NAME + value: my-nginx + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.25" + - name: OTEL_RESOURCE_ATTRIBUTES + name: myrabbit + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - args: + - --config=env:OTEL_CONFIG + name: otc-container +status: + phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/01-install-app.yaml new file mode 100644 index 0000000000..198cf20e1a --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/01-install-app.yaml @@ -0,0 +1,119 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-nginx +spec: + selector: + matchLabels: + app: my-nginx + replicas: 1 + template: + metadata: + labels: + app: my-nginx + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-nginx: "true" + instrumentation.opentelemetry.io/container-names: "myapp,myrabbit" + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - name: myapp + image: nginxinc/nginx-unprivileged:1.23.1 + imagePullPolicy: Always + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + runAsNonRoot: true + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: ["ALL"] + ports: + - containerPort: 8765 + env: + - name: LD_LIBRARY_PATH + value: /opt + volumeMounts: + - name: nginx-conf + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + readOnly: true + imagePullPolicy: Always + resources: + limits: + cpu: 500m + memory: 500Mi + requests: + cpu: 100m + memory: 100Mi + - name: myrabbit + image: rabbitmq + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + volumes: + - name: nginx-conf + configMap: + name: nginx-conf + items: + - key: nginx.conf + path: nginx.conf + +--- + +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-conf +data: + nginx.conf: | + # user nginx; + pid /tmp/nginx.pid; + worker_processes 1; + events { + worker_connections 10240; + } + http { + include /etc/nginx/conf.d/*.conf; + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp_path; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + server { + listen 8765; + server_name localhost; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + location /api/customer/ { + proxy_pass http://localhost:8282/api/customer/; + } + location /api/vendor/ { + proxy_pass http://localhost:8383/api/vendor/; + } + + location /seznam { + proxy_pass http://www.seznam.cz/; + } + } + + } + +--- diff --git a/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/02-assert.yaml b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/02-assert.yaml new file mode 100644 index 0000000000..1e13e4de4f --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/02-assert.yaml @@ -0,0 +1,74 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/inject-nginx: "true" + sidecar.opentelemetry.io/inject: "true" + labels: + app: my-nginx +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - env: + - name: LD_LIBRARY_PATH + value: /opt:/opt/opentelemetry-webserver/agent/sdk_lib/lib + - name: OTEL_SERVICE_NAME + value: my-nginx + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.25" + - name: OTEL_RESOURCE_ATTRIBUTES + name: myapp + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - mountPath: /opt/opentelemetry-webserver/agent + name: otel-nginx-agent + - mountPath: /etc/nginx + name: otel-nginx-conf-dir + - env: + - name: OTEL_SERVICE_NAME + value: my-nginx + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.25" + - name: OTEL_RESOURCE_ATTRIBUTES + name: myrabbit + - args: + - --config=env:OTEL_CONFIG + name: otc-container +status: + phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/02-install-app.yaml b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/02-install-app.yaml new file mode 100644 index 0000000000..bc4ff47531 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx-multicontainer/02-install-app.yaml @@ -0,0 +1,118 @@ +# skipping test, see https://github.com/open-telemetry/opentelemetry-operator/issues/1936 +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-nginx +spec: + selector: + matchLabels: + app: my-nginx + replicas: 1 + template: + metadata: + labels: + app: my-nginx + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-nginx: "true" + instrumentation.opentelemetry.io/container-names: "myrabbit" + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - name: myapp + image: nginxinc/nginx-unprivileged:1.23.1 + imagePullPolicy: Always + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + runAsNonRoot: true + allowPrivilegeEscalation: false + seccompProfile: + type: RuntimeDefault + capabilities: + drop: ["ALL"] + ports: + - containerPort: 8765 + env: + - name: LD_LIBRARY_PATH + value: /opt + volumeMounts: + - name: nginx-conf + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + readOnly: true + imagePullPolicy: Always + resources: + limits: + cpu: 500m + memory: 500Mi + requests: + cpu: 100m + memory: 100Mi + - name: myrabbit + image: rabbitmq + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + volumes: + - name: nginx-conf + configMap: + name: nginx-conf + items: + - key: nginx.conf + path: nginx.conf +--- + +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-conf +data: + nginx.conf: | + # user nginx; + pid /tmp/nginx.pid; + worker_processes 1; + events { + worker_connections 10240; + } + http { + include /etc/nginx/conf.d/*.conf; + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp_path; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + server { + listen 8765; + server_name localhost; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + location /api/customer/ { + proxy_pass http://localhost:8282/api/customer/; + } + location /api/vendor/ { + proxy_pass http://localhost:8383/api/vendor/; + } + + location /seznam { + proxy_pass http://www.seznam.cz/; + } + } + + } + +--- diff --git a/tests/e2e-instrumentation/instrumentation-nginx/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-nginx/00-install-collector.yaml new file mode 100644 index 0000000000..c18f418bfb --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx/00-install-collector.yaml @@ -0,0 +1,31 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + exporters: + logging: + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] + +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=3000/1000 --overwrite diff --git a/tests/e2e-instrumentation/instrumentation-nginx/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-nginx/00-install-instrumentation.yaml new file mode 100644 index 0000000000..9c8297f897 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx/00-install-instrumentation.yaml @@ -0,0 +1,17 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: nginx +spec: + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + sampler: + type: parentbased_traceidratio + argument: "0.25" + nginx: + attrs: + - name: NginxModuleOtelMaxQueueSize + value: "4096" diff --git a/tests/e2e-instrumentation/instrumentation-nginx/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-nginx/01-assert.yaml new file mode 100644 index 0000000000..ff3846d228 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx/01-assert.yaml @@ -0,0 +1,57 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/inject-nginx: 'true' + sidecar.opentelemetry.io/inject: 'true' + labels: + app: my-nginx +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - env: + - name: LD_LIBRARY_PATH + value: /opt:/opt/opentelemetry-webserver/agent/sdk_lib/lib + - name: OTEL_SERVICE_NAME + value: my-nginx + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: '0.25' + - name: OTEL_RESOURCE_ATTRIBUTES + name: myapp + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - name: otel-nginx-agent + mountPath: /opt/opentelemetry-webserver/agent + - name: otel-nginx-conf-dir + mountPath: /etc/nginx + - args: + - --config=env:OTEL_CONFIG + name: otc-container + initContainers: + - args: + - cp -r /etc/nginx/* /opt/opentelemetry-webserver/source-conf && export NGINX_VERSION=$( { nginx -v ; } 2>&1 ) && echo ${NGINX_VERSION##*/} > /opt/opentelemetry-webserver/source-conf/version.txt + name: otel-agent-source-container-clone + - name: otel-agent-attach-nginx + args: + - echo -e $OTEL_NGINX_I13N_SCRIPT > /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && chmod +x /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && cat /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh && /opt/opentelemetry-webserver/agent/nginx_instrumentation.sh "/opt/opentelemetry-webserver/agent" "/opt/opentelemetry-webserver/source-conf" "nginx.conf" "<>" +status: + phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-nginx/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-nginx/01-install-app.yaml new file mode 100644 index 0000000000..a7d86790ee --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nginx/01-install-app.yaml @@ -0,0 +1,104 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-nginx +spec: + selector: + matchLabels: + app: my-nginx + replicas: 1 + template: + metadata: + labels: + app: my-nginx + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-nginx: "true" + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - name: myapp + image: nginxinc/nginx-unprivileged:1.23.1 + imagePullPolicy: Always + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + # following to test lifecycle removal in cloned init container + lifecycle: + postStart: + exec: + command: ["/bin/sh", "-c", "echo Hello from the postStart handler"] + ports: + - containerPort: 8765 + env: + - name: LD_LIBRARY_PATH + value: /opt + volumeMounts: + - name: nginx-conf + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + readOnly: true + imagePullPolicy: Always + resources: + limits: + cpu: "1" + memory: 500Mi + requests: + cpu: 250m + memory: 100Mi + volumes: + - name: nginx-conf + configMap: + name: nginx-conf + items: + - key: nginx.conf + path: nginx.conf + +--- + +apiVersion: v1 +kind: ConfigMap +metadata: + name: nginx-conf +data: + nginx.conf: | + # user nginx; + worker_processes 1; + pid /tmp/nginx.pid; + events { + worker_connections 10240; + } + http { + include /etc/nginx/conf.d/*.conf; + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp_path; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + server { + listen 8765; + server_name localhost; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + location /api/customer/ { + proxy_pass http://localhost:8282/api/customer/; + } + location /api/vendor/ { + proxy_pass http://localhost:8383/api/vendor/; + } + + location /seznam { + proxy_pass http://www.seznam.cz/; + } + } + + } + +--- diff --git a/tests/e2e/instrumentation-java-other-ns/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/00-install-collector.yaml similarity index 87% rename from tests/e2e/instrumentation-java-other-ns/00-install-collector.yaml rename to tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/00-install-collector.yaml index f8e1e98e07..b00affd4ce 100644 --- a/tests/e2e/instrumentation-java-other-ns/00-install-collector.yaml +++ b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/00-install-collector.yaml @@ -13,11 +13,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/instrumentation-nodejs-multicontainer/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/00-install-instrumentation.yaml similarity index 100% rename from tests/e2e/instrumentation-nodejs-multicontainer/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/00-install-instrumentation.yaml diff --git a/tests/e2e/instrumentation-nodejs-multicontainer/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/01-assert.yaml similarity index 83% rename from tests/e2e/instrumentation-nodejs-multicontainer/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/01-assert.yaml index 17f8bd73ac..ff68ac36c8 100644 --- a/tests/e2e/instrumentation-nodejs-multicontainer/01-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/01-assert.yaml @@ -12,7 +12,7 @@ spec: - name: myapp env: - name: NODE_OPTIONS - value: ' --require /otel-auto-instrumentation/autoinstrumentation.js' + value: ' --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js' - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -34,12 +34,12 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-nodejs + name: opentelemetry-auto-instrumentation-nodejs - name: myrabbit env: - name: NODE_OPTIONS - value: ' --require /otel-auto-instrumentation/autoinstrumentation.js' + value: ' --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js' - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -61,8 +61,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-nodejs + name: opentelemetry-auto-instrumentation-nodejs - name: otc-container status: phase: Running \ No newline at end of file diff --git a/tests/e2e/instrumentation-nodejs-multicontainer/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/01-install-app.yaml similarity index 82% rename from tests/e2e/instrumentation-nodejs-multicontainer/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/01-install-app.yaml index 2514ba5e5f..a850a3cfcf 100644 --- a/tests/e2e/instrumentation-nodejs-multicontainer/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/01-install-app.yaml @@ -18,6 +18,6 @@ spec: spec: containers: - name: myapp - image: ghcr.io/anuraaga/express-hello-world:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main - name: myrabbit - image: rabbitmq + image: rabbitmq:3 diff --git a/tests/e2e/instrumentation-nodejs-multicontainer/02-assert.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/02-assert.yaml similarity index 86% rename from tests/e2e/instrumentation-nodejs-multicontainer/02-assert.yaml rename to tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/02-assert.yaml index d9688c06ec..5b60ac8f18 100644 --- a/tests/e2e/instrumentation-nodejs-multicontainer/02-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/02-assert.yaml @@ -15,7 +15,7 @@ spec: - name: myrabbit env: - name: NODE_OPTIONS - value: ' --require /otel-auto-instrumentation/autoinstrumentation.js' + value: ' --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js' - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -37,8 +37,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-nodejs + name: opentelemetry-auto-instrumentation-nodejs - name: otc-container status: phase: Running \ No newline at end of file diff --git a/tests/e2e/instrumentation-nodejs-multicontainer/02-install-app.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/02-install-app.yaml similarity index 81% rename from tests/e2e/instrumentation-nodejs-multicontainer/02-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/02-install-app.yaml index ea8f74d0b0..7de601d685 100644 --- a/tests/e2e/instrumentation-nodejs-multicontainer/02-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-nodejs-multicontainer/02-install-app.yaml @@ -18,6 +18,6 @@ spec: spec: containers: - name: myapp - image: ghcr.io/anuraaga/express-hello-world:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main - name: myrabbit - image: rabbitmq + image: rabbitmq:3 diff --git a/tests/e2e-instrumentation/instrumentation-nodejs/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-nodejs/00-install-collector.yaml new file mode 100644 index 0000000000..2875bde640 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nodejs/00-install-collector.yaml @@ -0,0 +1,32 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=2000/1000 --overwrite diff --git a/tests/e2e/instrumentation-nodejs/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-nodejs/00-install-instrumentation.yaml similarity index 92% rename from tests/e2e/instrumentation-nodejs/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-nodejs/00-install-instrumentation.yaml index 35154afb2b..a87939b6c2 100644 --- a/tests/e2e/instrumentation-nodejs/00-install-instrumentation.yaml +++ b/tests/e2e-instrumentation/instrumentation-nodejs/00-install-instrumentation.yaml @@ -16,6 +16,8 @@ spec: value: "0.85" - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED value: "true" + - name: OTEL_METRICS_EXPORTER + value: prometheus exporter: endpoint: http://localhost:4317 propagators: diff --git a/tests/e2e/instrumentation-nodejs/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-nodejs/01-assert.yaml similarity index 67% rename from tests/e2e/instrumentation-nodejs/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-nodejs/01-assert.yaml index 3190895a59..fa71d4a6e8 100644 --- a/tests/e2e/instrumentation-nodejs/01-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-nodejs/01-assert.yaml @@ -13,7 +13,7 @@ spec: - name: OTEL_NODEJS_DEBUG value: "true" - name: NODE_OPTIONS - value: " --require /otel-auto-instrumentation/autoinstrumentation.js" + value: " --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js" - name: OTEL_TRACES_EXPORTER value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT @@ -26,6 +26,8 @@ spec: value: "0.85" - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED value: "true" + - name: OTEL_METRICS_EXPORTER + value: prometheus - name: OTEL_SERVICE_NAME value: my-deployment-with-sidecar - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME @@ -35,10 +37,22 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-nodejs + name: opentelemetry-auto-instrumentation-nodejs - name: otc-container initContainers: - - name: opentelemetry-auto-instrumentation + - name: opentelemetry-auto-instrumentation-nodejs + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + resources: + limits: + cpu: "500m" + memory: "128Mi" + requests: + cpu: "50m" + memory: "128Mi" + status: phase: Running diff --git a/tests/e2e/instrumentation-nodejs/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-nodejs/01-install-app.yaml similarity index 71% rename from tests/e2e/instrumentation-nodejs/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-nodejs/01-install-app.yaml index 1a1a298883..5e5e749448 100644 --- a/tests/e2e/instrumentation-nodejs/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-nodejs/01-install-app.yaml @@ -21,4 +21,8 @@ spec: fsGroup: 2000 containers: - name: myapp - image: ghcr.io/anuraaga/express-hello-world:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] diff --git a/tests/e2e-instrumentation/instrumentation-python-multicontainer/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-python-multicontainer/00-install-collector.yaml new file mode 100644 index 0000000000..b00affd4ce --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-python-multicontainer/00-install-collector.yaml @@ -0,0 +1,23 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/tests/e2e/instrumentation-python-multicontainer/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-python-multicontainer/00-install-instrumentation.yaml similarity index 100% rename from tests/e2e/instrumentation-python-multicontainer/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-python-multicontainer/00-install-instrumentation.yaml diff --git a/tests/e2e/instrumentation-python-multicontainer/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-python-multicontainer/01-assert.yaml similarity index 69% rename from tests/e2e/instrumentation-python-multicontainer/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-python-multicontainer/01-assert.yaml index 41d2a13ea6..99ccda9a6c 100644 --- a/tests/e2e/instrumentation-python-multicontainer/01-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-python-multicontainer/01-assert.yaml @@ -12,11 +12,15 @@ spec: - name: myapp env: - name: PYTHONPATH - value: /otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation + value: /otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation-python - name: OTEL_TRACES_EXPORTER - value: otlp_proto_http + value: otlp + - name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL + value: http/protobuf - name: OTEL_METRICS_EXPORTER - value: none + value: otlp + - name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL + value: http/protobuf - name: OTEL_EXPORTER_OTLP_ENDPOINT value: http://localhost:4317 - name: OTEL_EXPORTER_OTLP_TIMEOUT @@ -36,16 +40,20 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-python + name: opentelemetry-auto-instrumentation-python - name: myrabbit env: - name: PYTHONPATH - value: /otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation + value: /otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation-python - name: OTEL_TRACES_EXPORTER - value: otlp_proto_http + value: otlp + - name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL + value: http/protobuf - name: OTEL_METRICS_EXPORTER - value: none + value: otlp + - name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL + value: http/protobuf - name: OTEL_EXPORTER_OTLP_ENDPOINT value: http://localhost:4317 - name: OTEL_EXPORTER_OTLP_TIMEOUT @@ -65,8 +73,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-python + name: opentelemetry-auto-instrumentation-python - name: otc-container status: phase: Running diff --git a/tests/e2e/instrumentation-python-multicontainer/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-python-multicontainer/01-install-app.yaml similarity index 82% rename from tests/e2e/instrumentation-python-multicontainer/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-python-multicontainer/01-install-app.yaml index 82910ec979..cb6d4f5ccc 100644 --- a/tests/e2e/instrumentation-python-multicontainer/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-python-multicontainer/01-install-app.yaml @@ -18,6 +18,6 @@ spec: spec: containers: - name: myapp - image: ghcr.io/anuraaga/flask-hello-world:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main - name: myrabbit - image: rabbitmq + image: rabbitmq:3 diff --git a/tests/e2e/instrumentation-python-multicontainer/02-assert.yaml b/tests/e2e-instrumentation/instrumentation-python-multicontainer/02-assert.yaml similarity index 73% rename from tests/e2e/instrumentation-python-multicontainer/02-assert.yaml rename to tests/e2e-instrumentation/instrumentation-python-multicontainer/02-assert.yaml index 3bfcbfcc35..f5c3191074 100644 --- a/tests/e2e/instrumentation-python-multicontainer/02-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-python-multicontainer/02-assert.yaml @@ -15,11 +15,15 @@ spec: - name: myrabbit env: - name: PYTHONPATH - value: /otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation + value: /otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation-python - name: OTEL_TRACES_EXPORTER - value: otlp_proto_http + value: otlp + - name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL + value: http/protobuf - name: OTEL_METRICS_EXPORTER - value: none + value: otlp + - name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL + value: http/protobuf - name: OTEL_EXPORTER_OTLP_ENDPOINT value: http://localhost:4317 - name: OTEL_EXPORTER_OTLP_TIMEOUT @@ -39,8 +43,8 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-python + name: opentelemetry-auto-instrumentation-python - name: otc-container status: phase: Running \ No newline at end of file diff --git a/tests/e2e/instrumentation-python-multicontainer/02-install-app.yaml b/tests/e2e-instrumentation/instrumentation-python-multicontainer/02-install-app.yaml similarity index 81% rename from tests/e2e/instrumentation-python-multicontainer/02-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-python-multicontainer/02-install-app.yaml index 8101f7d169..7e4a4595e6 100644 --- a/tests/e2e/instrumentation-python-multicontainer/02-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-python-multicontainer/02-install-app.yaml @@ -18,6 +18,6 @@ spec: spec: containers: - name: myapp - image: ghcr.io/anuraaga/flask-hello-world:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main - name: myrabbit - image: rabbitmq + image: rabbitmq:3 diff --git a/tests/e2e-instrumentation/instrumentation-python/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-python/00-install-collector.yaml new file mode 100644 index 0000000000..2875bde640 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-python/00-install-collector.yaml @@ -0,0 +1,32 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=2000/1000 --overwrite diff --git a/tests/e2e/instrumentation-python/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-python/00-install-instrumentation.yaml similarity index 90% rename from tests/e2e/instrumentation-python/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-python/00-install-instrumentation.yaml index ab4bd669c9..a2b7d65a2d 100644 --- a/tests/e2e/instrumentation-python/00-install-instrumentation.yaml +++ b/tests/e2e-instrumentation/instrumentation-python/00-install-instrumentation.yaml @@ -25,6 +25,6 @@ spec: - name: OTEL_LOG_LEVEL value: "debug" - name: OTEL_TRACES_EXPORTER - value: otlp_proto_http + value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT - value: http://localhost:4317 + value: http://localhost:4318 diff --git a/tests/e2e/instrumentation-python/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-python/01-assert.yaml similarity index 58% rename from tests/e2e/instrumentation-python/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-python/01-assert.yaml index a1eb66123c..b2d8d02aaa 100644 --- a/tests/e2e/instrumentation-python/01-assert.yaml +++ b/tests/e2e-instrumentation/instrumentation-python/01-assert.yaml @@ -13,13 +13,17 @@ spec: - name: OTEL_LOG_LEVEL value: "debug" - name: OTEL_TRACES_EXPORTER - value: otlp_proto_http + value: otlp - name: OTEL_EXPORTER_OTLP_ENDPOINT - value: http://localhost:4317 + value: http://localhost:4318 - name: PYTHONPATH - value: "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation" + value: "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation-python" + - name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL + value: http/protobuf - name: OTEL_METRICS_EXPORTER - value: none + value: otlp + - name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL + value: http/protobuf - name: OTEL_EXPORTER_OTLP_TIMEOUT value: "20" - name: OTEL_TRACES_SAMPLER @@ -37,10 +41,21 @@ spec: - name: OTEL_RESOURCE_ATTRIBUTES volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount - - mountPath: /otel-auto-instrumentation - name: opentelemetry-auto-instrumentation + - mountPath: /otel-auto-instrumentation-python + name: opentelemetry-auto-instrumentation-python - name: otc-container initContainers: - - name: opentelemetry-auto-instrumentation + - name: opentelemetry-auto-instrumentation-python + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + resources: + limits: + cpu: "500m" + memory: "32Mi" + requests: + cpu: "50m" + memory: "32Mi" status: phase: Running diff --git a/tests/e2e/instrumentation-python/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-python/01-install-app.yaml similarity index 71% rename from tests/e2e/instrumentation-python/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-python/01-install-app.yaml index e8e9e0f940..982b439460 100644 --- a/tests/e2e/instrumentation-python/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-python/01-install-app.yaml @@ -21,4 +21,8 @@ spec: fsGroup: 2000 containers: - name: myapp - image: ghcr.io/anuraaga/flask-hello-world:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] diff --git a/tests/e2e-instrumentation/instrumentation-sdk/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-sdk/00-install-collector.yaml new file mode 100644 index 0000000000..2875bde640 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-sdk/00-install-collector.yaml @@ -0,0 +1,32 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + # Annotate the namespace to allow the application to run using an specific group and user in OpenShift + # https://docs.openshift.com/dedicated/authentication/managing-security-context-constraints.html + # This annotation has no effect in Kubernetes + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.uid-range=1000/1000 --overwrite + - command: kubectl annotate namespace ${NAMESPACE} openshift.io/sa.scc.supplemental-groups=2000/1000 --overwrite diff --git a/tests/e2e/instrumentation-sdk/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-sdk/00-install-instrumentation.yaml similarity index 100% rename from tests/e2e/instrumentation-sdk/00-install-instrumentation.yaml rename to tests/e2e-instrumentation/instrumentation-sdk/00-install-instrumentation.yaml diff --git a/tests/e2e/instrumentation-sdk/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-sdk/01-assert.yaml similarity index 100% rename from tests/e2e/instrumentation-sdk/01-assert.yaml rename to tests/e2e-instrumentation/instrumentation-sdk/01-assert.yaml diff --git a/tests/e2e/instrumentation-sdk/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-sdk/01-install-app.yaml similarity index 85% rename from tests/e2e/instrumentation-sdk/01-install-app.yaml rename to tests/e2e-instrumentation/instrumentation-sdk/01-install-app.yaml index a57db32a58..3ce4568bbb 100644 --- a/tests/e2e/instrumentation-sdk/01-install-app.yaml +++ b/tests/e2e-instrumentation/instrumentation-sdk/01-install-app.yaml @@ -21,4 +21,4 @@ spec: fsGroup: 2000 containers: - name: myapp - image: ghcr.io/anuraaga/express-hello-world:latest + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main diff --git a/tests/e2e/instrumentation-dotnet-multicontainer/00-install-collector.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/00-install-collector.yaml similarity index 100% rename from tests/e2e/instrumentation-dotnet-multicontainer/00-install-collector.yaml rename to tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/00-install-collector.yaml diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/00-install-instrumentation.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/00-install-instrumentation.yaml new file mode 100644 index 0000000000..3bf65bfff0 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/00-install-instrumentation.yaml @@ -0,0 +1,40 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: multi +spec: + env: + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + sampler: + type: parentbased_traceidratio + argument: "0.25" + java: + env: + - name: OTEL_SERVICE_NAME + value: "javaapp" + python: + env: + - name: OTEL_SERVICE_NAME + value: "pythonapp" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4318 + nodejs: + env: + - name: OTEL_SERVICE_NAME + value: "nodejsapp" + dotnet: + env: + - name: OTEL_SERVICE_NAME + value: "dotnetapp" + go: + env: + - name: OTEL_SERVICE_NAME + value: "goapp" diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml new file mode 100644 index 0000000000..136b7abaf9 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml @@ -0,0 +1,129 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/container-names: "shouldnt-be-instrumented" + instrumentation.opentelemetry.io/dotnet-container-names: "dotnetapp" + instrumentation.opentelemetry.io/inject-dotnet: "true" + instrumentation.opentelemetry.io/inject-java: "true" + instrumentation.opentelemetry.io/inject-nodejs: "true" + instrumentation.opentelemetry.io/inject-python: "true" + instrumentation.opentelemetry.io/java-container-names: "javaapp" + instrumentation.opentelemetry.io/nodejs-container-names: "nodejsapp" + instrumentation.opentelemetry.io/python-container-names: "pythonapp" + sidecar.opentelemetry.io/inject: "true" + labels: + app: pod-with-multi-instrumentation +spec: + containers: + - name: dotnetapp + env: + - name: OTEL_SERVICE_NAME + value: dotnetapp + - name: CORECLR_ENABLE_PROFILING + value: "1" + - name: CORECLR_PROFILER + value: "{918728DD-259F-4A6A-AC2B-B85E1B658318}" + - name: CORECLR_PROFILER_PATH + value: /otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so + - name: DOTNET_STARTUP_HOOKS + value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll + - name: DOTNET_ADDITIONAL_DEPS + value: /otel-auto-instrumentation-dotnet/AdditionalDeps + - name: OTEL_DOTNET_AUTO_HOME + value: /otel-auto-instrumentation-dotnet + - name: DOTNET_SHARED_STORE + value: /otel-auto-instrumentation-dotnet/store + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - mountPath: /otel-auto-instrumentation-dotnet + name: opentelemetry-auto-instrumentation-dotnet + - name: javaapp + env: + - name: OTEL_SERVICE_NAME + value: javaapp + - name: JAVA_TOOL_OPTIONS + value: " -javaagent:/otel-auto-instrumentation-java/javaagent.jar" + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - mountPath: /otel-auto-instrumentation-java + name: opentelemetry-auto-instrumentation-java + - name: nodejsapp + env: + - name: OTEL_SERVICE_NAME + value: nodejsapp + - name: NODE_OPTIONS + value: ' --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js' + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - mountPath: /otel-auto-instrumentation-nodejs + name: opentelemetry-auto-instrumentation-nodejs + - name: pythonapp + env: + - name: OTEL_SERVICE_NAME + value: pythonapp + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4318 + - name: PYTHONPATH + value: /otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation-python + - name: OTEL_TRACES_EXPORTER + value: otlp + - name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL + value: http/protobuf + - name: OTEL_METRICS_EXPORTER + value: otlp + - name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL + value: http/protobuf + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - mountPath: /otel-auto-instrumentation-python + name: opentelemetry-auto-instrumentation-python + - name: shouldnt-be-instrumented + env: + - name: TEST + value: test + - name: otc-container +status: + phase: Running diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml new file mode 100644 index 0000000000..652eb0a348 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: deployment-with-multi-instrumentation +spec: + selector: + matchLabels: + app: pod-with-multi-instrumentation + replicas: 1 + template: + metadata: + labels: + app: pod-with-multi-instrumentation + annotations: + instrumentation.opentelemetry.io/container-names: "shouldnt-be-instrumented" + instrumentation.opentelemetry.io/dotnet-container-names: "dotnetapp" + instrumentation.opentelemetry.io/inject-dotnet: "true" + instrumentation.opentelemetry.io/inject-java: "true" + instrumentation.opentelemetry.io/inject-nodejs: "true" + instrumentation.opentelemetry.io/inject-python: "true" + instrumentation.opentelemetry.io/java-container-names: "javaapp" + instrumentation.opentelemetry.io/nodejs-container-names: "nodejsapp" + instrumentation.opentelemetry.io/python-container-names: "pythonapp" + sidecar.opentelemetry.io/inject: "true" + spec: + containers: + - name: dotnetapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-dotnet:main + - name: javaapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-java:main + - name: nodejsapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main + - name: pythonapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main + - name: shouldnt-be-instrumented + image: rabbitmq:3 + env: + - name: TEST + value: test diff --git a/tests/e2e/instrumentation-dotnet/00-install-collector.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/00-install-collector.yaml similarity index 100% rename from tests/e2e/instrumentation-dotnet/00-install-collector.yaml rename to tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/00-install-collector.yaml diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/00-install-instrumentation.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/00-install-instrumentation.yaml new file mode 100644 index 0000000000..3bf65bfff0 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/00-install-instrumentation.yaml @@ -0,0 +1,40 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: multi +spec: + env: + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + sampler: + type: parentbased_traceidratio + argument: "0.25" + java: + env: + - name: OTEL_SERVICE_NAME + value: "javaapp" + python: + env: + - name: OTEL_SERVICE_NAME + value: "pythonapp" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4318 + nodejs: + env: + - name: OTEL_SERVICE_NAME + value: "nodejsapp" + dotnet: + env: + - name: OTEL_SERVICE_NAME + value: "dotnetapp" + go: + env: + - name: OTEL_SERVICE_NAME + value: "goapp" diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-assert.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-assert.yaml new file mode 100644 index 0000000000..f1af37c665 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-assert.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/inject-nodejs: "true" + instrumentation.opentelemetry.io/inject-python: "true" + sidecar.opentelemetry.io/inject: "true" + labels: + app: pod-multi-instr-no-containers +spec: + containers: + - name: nodejsapp + env: + - name: TEST + value: test + - name: pythonapp + env: + - name: TEST + value: test + - name: otc-container +status: + phase: Running diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-install-app.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-install-app.yaml new file mode 100644 index 0000000000..9921c8791c --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-install-app.yaml @@ -0,0 +1,29 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep-multi-instr-no-containers +spec: + selector: + matchLabels: + app: pod-multi-instr-no-containers + replicas: 1 + template: + metadata: + labels: + app: pod-multi-instr-no-containers + annotations: + instrumentation.opentelemetry.io/inject-nodejs: "true" + instrumentation.opentelemetry.io/inject-python: "true" + sidecar.opentelemetry.io/inject: "true" + spec: + containers: + - name: nodejsapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main + env: + - name: TEST + value: test + - name: pythonapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main + env: + - name: TEST + value: test diff --git a/tests/e2e/instrumentation-java-multicontainer/00-install-collector.yaml b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/00-install-collector.yaml similarity index 100% rename from tests/e2e/instrumentation-java-multicontainer/00-install-collector.yaml rename to tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/00-install-collector.yaml diff --git a/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/00-install-instrumentation.yaml b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/00-install-instrumentation.yaml new file mode 100644 index 0000000000..3bf65bfff0 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/00-install-instrumentation.yaml @@ -0,0 +1,40 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: multi +spec: + env: + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + sampler: + type: parentbased_traceidratio + argument: "0.25" + java: + env: + - name: OTEL_SERVICE_NAME + value: "javaapp" + python: + env: + - name: OTEL_SERVICE_NAME + value: "pythonapp" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4318 + nodejs: + env: + - name: OTEL_SERVICE_NAME + value: "nodejsapp" + dotnet: + env: + - name: OTEL_SERVICE_NAME + value: "dotnetapp" + go: + env: + - name: OTEL_SERVICE_NAME + value: "goapp" diff --git a/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-assert.yaml b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-assert.yaml new file mode 100644 index 0000000000..bec9919253 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-assert.yaml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/inject-nodejs: "true" + sidecar.opentelemetry.io/inject: "true" + labels: + app: pod-single-instr-first-container +spec: + containers: + - name: nodejsapp + env: + - name: OTEL_SERVICE_NAME + value: nodejsapp + - name: NODE_OPTIONS + value: ' --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js' + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "0.85" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - mountPath: /otel-auto-instrumentation-nodejs + name: opentelemetry-auto-instrumentation-nodejs + - name: pythonapp + env: + - name: TEST + value: test + - name: otc-container +status: + phase: Running diff --git a/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-install-app.yaml b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-install-app.yaml new file mode 100644 index 0000000000..e33830e2e7 --- /dev/null +++ b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-install-app.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dep-single-instr-first-container +spec: + selector: + matchLabels: + app: pod-single-instr-first-container + replicas: 1 + template: + metadata: + labels: + app: pod-single-instr-first-container + annotations: + instrumentation.opentelemetry.io/inject-nodejs: "true" + sidecar.opentelemetry.io/inject: "true" + spec: + containers: + - name: nodejsapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main + - name: pythonapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main + env: + - name: TEST + value: test diff --git a/tests/e2e-multi-instrumentation/manager_deployment_feature_gate.yaml b/tests/e2e-multi-instrumentation/manager_deployment_feature_gate.yaml new file mode 100644 index 0000000000..c0f277d00b --- /dev/null +++ b/tests/e2e-multi-instrumentation/manager_deployment_feature_gate.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager + name: opentelemetry-operator-system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: opentelemetry-operator-controller-manager + namespace: opentelemetry-operator-system + labels: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager +spec: + selector: + matchLabels: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager + replicas: 1 + template: + metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager + spec: + containers: + - name: kube-rbac-proxy + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 + args: + - "--secure-listen-address=0.0.0.0:8443" + - "--upstream=http://127.0.0.1:8080/" + - "--logtostderr=true" + - "--v=0" + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + - name: manager + image: "local/opentelemetry-operator:e2e" + args: + - "--metrics-addr=127.0.0.1:8080" + - "--zap-log-level=debug" + - "--enable-leader-election" + - "--feature-gates=+operator.autoinstrumentation.multi-instrumentation" + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: opentelemetry-operator-controller-manager-service-cert diff --git a/tests/e2e-opampbridge/opampbridge/00-assert.yaml b/tests/e2e-opampbridge/opampbridge/00-assert.yaml new file mode 100644 index 0000000000..f4022a72d4 --- /dev/null +++ b/tests/e2e-opampbridge/opampbridge/00-assert.yaml @@ -0,0 +1,70 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-opamp-bridge +spec: + template: + spec: + containers: + - name: opamp-bridge-container + env: + - name: OTELCOL_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + volumeMounts: + - mountPath: /conf + name: opamp-bridge-internal + volumes: + - configMap: + items: + - key: remoteconfiguration.yaml + path: remoteconfiguration.yaml + name: test-opamp-bridge + name: opamp-bridge-internal +status: + replicas: 1 + readyReplicas: 1 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: test-opamp-bridge +data: + remoteconfiguration.yaml: | + capabilities: + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRemoteConfig: true + AcceptsRestartCommand: true + ReportsEffectiveConfig: true + ReportsHealth: true + ReportsOwnLogs: true + ReportsOwnMetrics: true + ReportsOwnTraces: true + ReportsRemoteConfig: true + ReportsStatus: true + componentsAllowed: + exporters: + - logging + processors: + - memory_limiter + receivers: + - otlp + endpoint: ws://opamp-server:4320/v1/opamp +--- +apiVersion: v1 +kind: Service +metadata: + name: test-opamp-bridge +spec: + ports: + - name: opamp-bridge + port: 80 + protocol: TCP + targetPort: 8080 +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +opampbridges: + - selector: app.kubernetes.io/component=opentelemetry-opamp-bridge diff --git a/tests/e2e-opampbridge/opampbridge/00-install.yaml b/tests/e2e-opampbridge/opampbridge/00-install.yaml new file mode 100644 index 0000000000..3f4f353f15 --- /dev/null +++ b/tests/e2e-opampbridge/opampbridge/00-install.yaml @@ -0,0 +1,49 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: opamp-bridge +automountServiceAccountToken: true +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: opamp-bridge +rules: +- apiGroups: + - opentelemetry.io + resources: + - opentelemetrycollectors + verbs: + - '*' +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl -n $NAMESPACE create rolebinding default-opamp-bridge-$NAMESPACE --role=opamp-bridge --serviceaccount=$NAMESPACE:opamp-bridge +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpAMPBridge +metadata: + name: test +spec: + image: "local/opentelemetry-operator-opamp-bridge:e2e" + endpoint: ws://opamp-server:4320/v1/opamp + capabilities: + AcceptsOpAMPConnectionSettings: true + AcceptsOtherConnectionSettings: true + AcceptsRemoteConfig: true + AcceptsRestartCommand: true + ReportsEffectiveConfig: true + ReportsHealth: true + ReportsOwnLogs: true + ReportsOwnMetrics: true + ReportsOwnTraces: true + ReportsRemoteConfig: true + ReportsStatus: true + componentsAllowed: + receivers: + - otlp + processors: + - memory_limiter + exporters: + - logging \ No newline at end of file diff --git a/tests/e2e-openshift/Dockerfile b/tests/e2e-openshift/Dockerfile new file mode 100644 index 0000000000..17e9b46e4b --- /dev/null +++ b/tests/e2e-openshift/Dockerfile @@ -0,0 +1,35 @@ +# The Dockerfile's resulting image is purpose-built for executing OpenTelemetry Operator e2e tests within the OpenShift release (https://github.com/openshift/release) using Prow CI. + +FROM golang:1.20 + +# Copy the repository files +COPY . /tmp/opentelemetry-operator + +WORKDIR /tmp + +# Set the Go path and Go cache environment variables +ENV GOPATH=/tmp/go +ENV GOBIN=/tmp/go/bin +ENV GOCACHE=/tmp/.cache/go-build +ENV PATH=$PATH:$GOBIN + +# Create the /tmp/go/bin and build cache directories, and grant read and write permissions to all users +RUN mkdir -p /tmp/go/bin $GOCACHE \ + && chmod -R 777 /tmp/go/bin $GOPATH $GOCACHE + +# Install dependencies required by test cases and debugging +RUN apt-get update && apt-get install -y jq vim libreadline-dev + +# Install kuttl +RUN curl -LO https://github.com/kudobuilder/kuttl/releases/download/v0.15.0/kubectl-kuttl_0.15.0_linux_x86_64 \ + && chmod +x kubectl-kuttl_0.15.0_linux_x86_64 \ + && mv kubectl-kuttl_0.15.0_linux_x86_64 /usr/local/bin/kuttl + +# Install kubectl and oc +RUN curl -LO https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/latest/openshift-client-linux.tar.gz \ + && tar -xzf openshift-client-linux.tar.gz \ + && chmod +x oc kubectl \ + && mv oc kubectl /usr/local/bin/ + +# Set the working directory +WORKDIR /tmp/opentelemetry-operator diff --git a/tests/e2e-openshift/kafka/00-assert.yaml b/tests/e2e-openshift/kafka/00-assert.yaml new file mode 100644 index 0000000000..7474e7da98 --- /dev/null +++ b/tests/e2e-openshift/kafka/00-assert.yaml @@ -0,0 +1,180 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: kuttl-kafka + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/instance: my-cluster + app.kubernetes.io/managed-by: strimzi-cluster-operator + app.kubernetes.io/name: entity-operator + app.kubernetes.io/part-of: strimzi-my-cluster + strimzi.io/cluster: my-cluster + strimzi.io/component-type: entity-operator + strimzi.io/kind: Kafka + strimzi.io/name: my-cluster-entity-operator + name: my-cluster-entity-operator + namespace: kuttl-kafka +status: + availableReplicas: 1 + readyReplicas: 1 + replicas: 1 + +--- +apiVersion: v1 +kind: Pod +metadata: + name: my-cluster-kafka-0 + namespace: kuttl-kafka +status: + phase: Running + +--- +apiVersion: v1 +kind: Pod +metadata: + name: my-cluster-zookeeper-0 + namespace: kuttl-kafka +status: + phase: Running + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/instance: my-cluster + app.kubernetes.io/managed-by: strimzi-cluster-operator + app.kubernetes.io/name: kafka + app.kubernetes.io/part-of: strimzi-my-cluster + strimzi.io/cluster: my-cluster + strimzi.io/component-type: kafka + strimzi.io/discovery: "true" + strimzi.io/kind: Kafka + strimzi.io/name: my-cluster-kafka + name: my-cluster-kafka-bootstrap + namespace: kuttl-kafka +spec: + ports: + - name: tcp-replication + port: 9091 + protocol: TCP + targetPort: 9091 + - name: tcp-clients + port: 9092 + protocol: TCP + targetPort: 9092 + - name: tcp-clientstls + port: 9093 + protocol: TCP + targetPort: 9093 + selector: + strimzi.io/cluster: my-cluster + strimzi.io/kind: Kafka + strimzi.io/name: my-cluster-kafka + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/instance: my-cluster + app.kubernetes.io/managed-by: strimzi-cluster-operator + app.kubernetes.io/name: kafka + app.kubernetes.io/part-of: strimzi-my-cluster + strimzi.io/cluster: my-cluster + strimzi.io/component-type: kafka + strimzi.io/kind: Kafka + strimzi.io/name: my-cluster-kafka + name: my-cluster-kafka-brokers + namespace: kuttl-kafka +spec: + ports: + - name: tcp-ctrlplane + port: 9090 + protocol: TCP + targetPort: 9090 + - name: tcp-replication + port: 9091 + protocol: TCP + targetPort: 9091 + - name: tcp-kafkaagent + port: 8443 + protocol: TCP + targetPort: 8443 + - name: tcp-clients + port: 9092 + protocol: TCP + targetPort: 9092 + - name: tcp-clientstls + port: 9093 + protocol: TCP + targetPort: 9093 + publishNotReadyAddresses: true + selector: + strimzi.io/cluster: my-cluster + strimzi.io/kind: Kafka + strimzi.io/name: my-cluster-kafka + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/instance: my-cluster + app.kubernetes.io/managed-by: strimzi-cluster-operator + app.kubernetes.io/name: zookeeper + app.kubernetes.io/part-of: strimzi-my-cluster + strimzi.io/cluster: my-cluster + strimzi.io/component-type: zookeeper + strimzi.io/kind: Kafka + strimzi.io/name: my-cluster-zookeeper + name: my-cluster-zookeeper-client + namespace: kuttl-kafka +spec: + ports: + - name: tcp-clients + port: 2181 + protocol: TCP + targetPort: 2181 + selector: + strimzi.io/cluster: my-cluster + strimzi.io/kind: Kafka + strimzi.io/name: my-cluster-zookeeper + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/instance: my-cluster + app.kubernetes.io/managed-by: strimzi-cluster-operator + app.kubernetes.io/name: zookeeper + app.kubernetes.io/part-of: strimzi-my-cluster + strimzi.io/cluster: my-cluster + strimzi.io/component-type: zookeeper + strimzi.io/kind: Kafka + strimzi.io/name: my-cluster-zookeeper + name: my-cluster-zookeeper-nodes + namespace: kuttl-kafka +spec: + ports: + - name: tcp-clients + port: 2181 + protocol: TCP + targetPort: 2181 + - name: tcp-clustering + port: 2888 + protocol: TCP + targetPort: 2888 + - name: tcp-election + port: 3888 + protocol: TCP + targetPort: 3888 + selector: + strimzi.io/cluster: my-cluster + strimzi.io/kind: Kafka + strimzi.io/name: my-cluster-zookeeper diff --git a/tests/e2e-openshift/kafka/00-create-kafka-instance.yaml b/tests/e2e-openshift/kafka/00-create-kafka-instance.yaml new file mode 100644 index 0000000000..22b30c0759 --- /dev/null +++ b/tests/e2e-openshift/kafka/00-create-kafka-instance.yaml @@ -0,0 +1,60 @@ +#For creating the Kafka instance, install the AMQ streams operator https://access.redhat.com/documentation/en-us/red_hat_amq_streams/2.5/html/getting_started_with_amq_streams_on_openshift/proc-deploying-cluster-operator-hub-str + +apiVersion: v1 +kind: Namespace +metadata: + name: kuttl-kafka + +--- +apiVersion: kafka.strimzi.io/v1beta2 +kind: Kafka +metadata: + name: my-cluster + namespace: kuttl-kafka +spec: + entityOperator: + topicOperator: + reconciliationIntervalSeconds: 90 + userOperator: + reconciliationIntervalSeconds: 120 + kafka: + config: + log.message.format.version: 3.5.0 + message.max.bytes: 10485760 + offsets.topic.replication.factor: 1 + ssl.cipher.suites: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + ssl.enabled.protocols: TLSv1.2 + ssl.protocol: TLSv1.2 + transaction.state.log.min.isr: 1 + transaction.state.log.replication.factor: 1 + jvmOptions: + -Xms: 1024m + -Xmx: 1024m + listeners: + - configuration: + useServiceDnsDomain: true + name: plain + port: 9092 + tls: false + type: internal + - authentication: + type: tls + name: tls + port: 9093 + tls: true + type: internal + replicas: 1 + resources: + limits: + cpu: "1" + memory: 4Gi + requests: + cpu: "1" + memory: 4Gi + storage: + type: ephemeral + version: 3.5.0 + zookeeper: + replicas: 1 + storage: + type: ephemeral diff --git a/tests/e2e-openshift/kafka/01-assert.yaml b/tests/e2e-openshift/kafka/01-assert.yaml new file mode 100644 index 0000000000..eceae60c45 --- /dev/null +++ b/tests/e2e-openshift/kafka/01-assert.yaml @@ -0,0 +1,13 @@ +apiVersion: kafka.strimzi.io/v1beta2 +kind: KafkaTopic +metadata: + name: otlp-spans + namespace: kuttl-kafka +spec: + config: + retention.ms: 300000 + segment.bytes: 1073741824 + partitions: 1 + replicas: 1 +status: + topicName: otlp-spans diff --git a/tests/e2e-openshift/kafka/01-create-kafka-topics.yaml b/tests/e2e-openshift/kafka/01-create-kafka-topics.yaml new file mode 100644 index 0000000000..e7cfcc23d4 --- /dev/null +++ b/tests/e2e-openshift/kafka/01-create-kafka-topics.yaml @@ -0,0 +1,13 @@ +apiVersion: kafka.strimzi.io/v1beta1 +kind: KafkaTopic +metadata: + labels: + strimzi.io/cluster: my-cluster + name: otlp-spans + namespace: kuttl-kafka +spec: + config: + retention.ms: 300000 + segment.bytes: 1073741824 + partitions: 1 + replicas: 1 diff --git a/tests/e2e-openshift/kafka/02-assert.yaml b/tests/e2e-openshift/kafka/02-assert.yaml new file mode 100644 index 0000000000..5fa0ca1403 --- /dev/null +++ b/tests/e2e-openshift/kafka/02-assert.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-kafka.kafka-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: kafka-receiver-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + name: kafka-receiver-collector + namespace: kuttl-kafka +status: + availableReplicas: 1 + readyReplicas: 1 + replicas: 1 + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-kafka.kafka-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: kafka-receiver-collector-monitoring + name: kafka-receiver-collector-monitoring + namespace: kuttl-kafka +spec: + ports: + - name: monitoring + port: 8888 + protocol: TCP + targetPort: 8888 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-kafka.kafka-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + type: ClusterIP diff --git a/tests/e2e-openshift/kafka/02-otel-kakfa-receiver.yaml b/tests/e2e-openshift/kafka/02-otel-kakfa-receiver.yaml new file mode 100644 index 0000000000..800732cefb --- /dev/null +++ b/tests/e2e-openshift/kafka/02-otel-kakfa-receiver.yaml @@ -0,0 +1,22 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: kafka-receiver + namespace: kuttl-kafka +spec: + mode: "deployment" + config: | + receivers: + kafka/traces: + brokers: ["my-cluster-kafka-brokers.kuttl-kafka.svc:9092"] + protocol_version: 3.5.0 + topic: otlp-spans + exporters: + debug: + verbosity: detailed + service: + pipelines: + traces: + receivers: [kafka/traces] + processors: [] + exporters: [debug] diff --git a/tests/e2e-openshift/kafka/03-assert.yaml b/tests/e2e-openshift/kafka/03-assert.yaml new file mode 100644 index 0000000000..6881d17302 --- /dev/null +++ b/tests/e2e-openshift/kafka/03-assert.yaml @@ -0,0 +1,75 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-kafka.kafka-exporter + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: kafka-exporter-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + name: kafka-exporter-collector + namespace: kuttl-kafka +status: + availableReplicas: 1 + readyReplicas: 1 + replicas: 1 + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-kafka.kafka-exporter + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: kafka-exporter-collector + name: kafka-exporter-collector + namespace: kuttl-kafka +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-kafka.kafka-exporter + app.kubernetes.io/managed-by: opentelemetry-operator + sessionAffinity: None + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-kafka.kafka-exporter + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: kafka-exporter-collector + operator.opentelemetry.io/collector-headless-service: Exists + name: kafka-exporter-collector-headless + namespace: kuttl-kafka +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-kafka.kafka-exporter + app.kubernetes.io/managed-by: opentelemetry-operator + type: ClusterIP diff --git a/tests/e2e-openshift/kafka/03-otel-kakfa-exporter.yaml b/tests/e2e-openshift/kafka/03-otel-kakfa-exporter.yaml new file mode 100644 index 0000000000..65f1f114f5 --- /dev/null +++ b/tests/e2e-openshift/kafka/03-otel-kakfa-exporter.yaml @@ -0,0 +1,25 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: kafka-exporter + namespace: kuttl-kafka +spec: + mode: deployment + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + exporters: + kafka/traces: + brokers: ["my-cluster-kafka-brokers.kuttl-kafka.svc:9092"] + protocol_version: 3.5.0 + topic: otlp-spans + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [kafka/traces] diff --git a/tests/e2e-openshift/kafka/04-assert.yaml b/tests/e2e-openshift/kafka/04-assert.yaml new file mode 100644 index 0000000000..942ada3c58 --- /dev/null +++ b/tests/e2e-openshift/kafka/04-assert.yaml @@ -0,0 +1,9 @@ +apiVersion: batch/v1 +kind: Job +metadata: + labels: + app: telemetrygen-traces + job-name: telemetrygen-traces + name: telemetrygen-traces +status: + succeeded: 1 diff --git a/tests/e2e-openshift/kafka/04-generate-traces.yaml b/tests/e2e-openshift/kafka/04-generate-traces.yaml new file mode 100644 index 0000000000..11f2d1fd7f --- /dev/null +++ b/tests/e2e-openshift/kafka/04-generate-traces.yaml @@ -0,0 +1,26 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: telemetrygen-traces +spec: + completions: 1 + parallelism: 1 + template: + metadata: + labels: + app: telemetrygen-traces + spec: + containers: + - name: telemetrygen-traces + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest + command: ["./telemetrygen"] + args: + - "--otlp-endpoint=kafka-exporter-collector-headless.kuttl-kafka.svc:4317" + - "--otlp-insecure=true" + - "--rate=1" + - "--duration=30s" + - "--otlp-attributes=test=\"kuttl-kafka\"" + - "--otlp-header=kafka-topic=\"otlp-spans\"" + - "--service=\"kafka\"" + - "traces" + restartPolicy: Never diff --git a/tests/e2e-openshift/kafka/05-assert.yaml b/tests/e2e-openshift/kafka/05-assert.yaml new file mode 100644 index 0000000000..0dfd963f94 --- /dev/null +++ b/tests/e2e-openshift/kafka/05-assert.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 30 +commands: +- script: ./tests/e2e-openshift/kafka/check_traces.sh diff --git a/tests/e2e-openshift/kafka/check_traces.sh b/tests/e2e-openshift/kafka/check_traces.sh new file mode 100755 index 0000000000..05b4755d07 --- /dev/null +++ b/tests/e2e-openshift/kafka/check_traces.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# This script checks the kafka-receiver OTEL collector pod for the presence of Traces. + +# Define the label selector +LABEL_SELECTOR="app.kubernetes.io/instance=kuttl-kafka.kafka-receiver" + +# Define the search strings +SEARCH_STRING1='-> service.name: Str("kafka")' +SEARCH_STRING2='-> test: Str(kuttl-kafka)' + +# Get the list of pods with the specified label +PODS=$(kubectl -n kuttl-kafka get pods -l $LABEL_SELECTOR -o jsonpath='{.items[*].metadata.name}') + +# Initialize flags to track if strings are found +FOUND1=false +FOUND2=false + +# Loop through each pod and search for the strings in the logs +for POD in $PODS; do + # Search for the first string + if ! $FOUND1 && kubectl -n kuttl-kafka logs $POD | grep -q -- "$SEARCH_STRING1"; then + echo "\"$SEARCH_STRING1\" found in $POD" + FOUND1=true + fi + # Search for the second string + if ! $FOUND2 && kubectl -n kuttl-kafka logs $POD | grep -q -- "$SEARCH_STRING2"; then + echo "\"$SEARCH_STRING2\" found in $POD" + FOUND2=true + fi +done + +# Check if either of the strings was not found +if ! $FOUND1 || ! $FOUND2; then + echo "No Traces with service name Kafka and attribute test=kuttl-kafka found." + exit 1 +else + echo "Traces with service name Kafka and attribute test=kuttl-kafka found." +fi + diff --git a/tests/e2e-openshift/monitoring/00-assert.yaml b/tests/e2e-openshift/monitoring/00-assert.yaml new file mode 100644 index 0000000000..f9cbcf22cc --- /dev/null +++ b/tests/e2e-openshift/monitoring/00-assert.yaml @@ -0,0 +1,4 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: ./tests/e2e-openshift/monitoring/check_user_workload_monitoring.sh diff --git a/tests/e2e-openshift/monitoring/00-workload-monitoring.yaml b/tests/e2e-openshift/monitoring/00-workload-monitoring.yaml new file mode 100644 index 0000000000..af526ecbe1 --- /dev/null +++ b/tests/e2e-openshift/monitoring/00-workload-monitoring.yaml @@ -0,0 +1,13 @@ +# oc -n openshift-user-workload-monitoring get pod +# https://docs.openshift.com/container-platform/4.13/monitoring/enabling-monitoring-for-user-defined-projects.html#accessing-metrics-from-outside-cluster_enabling-monitoring-for-user-defined-projects + +apiVersion: v1 +kind: ConfigMap +metadata: + name: cluster-monitoring-config + namespace: openshift-monitoring +data: + config.yaml: | + enableUserWorkload: true + alertmanagerMain: + enableUserAlertmanagerConfig: true diff --git a/tests/e2e-openshift/monitoring/01-assert.yaml b/tests/e2e-openshift/monitoring/01-assert.yaml new file mode 100644 index 0000000000..170c0f4148 --- /dev/null +++ b/tests/e2e-openshift/monitoring/01-assert.yaml @@ -0,0 +1,111 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + prometheus.io/path: /metrics + prometheus.io/port: "8888" + prometheus.io/scrape: "true" + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: cluster-collector-collector + app.kubernetes.io/part-of: opentelemetry + name: cluster-collector-collector +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 +spec: + endpoints: + - port: monitoring + selector: + matchLabels: + app.kubernetes.io/managed-by: opentelemetry-operator + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: cluster-collector-collector + app.kubernetes.io/part-of: opentelemetry + name: cluster-collector-collector +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: cluster-collector-collector + app.kubernetes.io/part-of: opentelemetry + operator.opentelemetry.io/collector-headless-service: Exists + name: cluster-collector-collector-headless +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: cluster-collector-collector-monitoring + app.kubernetes.io/part-of: opentelemetry + name: cluster-collector-collector-monitoring +spec: + ports: + - name: monitoring + port: 8888 + protocol: TCP + targetPort: 8888 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + type: ClusterIP diff --git a/tests/e2e-openshift/monitoring/01-otel-collector.yaml b/tests/e2e-openshift/monitoring/01-otel-collector.yaml new file mode 100644 index 0000000000..8420879089 --- /dev/null +++ b/tests/e2e-openshift/monitoring/01-otel-collector.yaml @@ -0,0 +1,24 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: cluster-collector +spec: + mode: deployment + observability: + metrics: + enableMetrics: true + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + exporters: + debug: + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/tests/e2e-openshift/monitoring/02-assert.yaml b/tests/e2e-openshift/monitoring/02-assert.yaml new file mode 100644 index 0000000000..701adbdf76 --- /dev/null +++ b/tests/e2e-openshift/monitoring/02-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: telemetrygen-traces +status: + active: 1 diff --git a/tests/e2e-openshift/monitoring/02-generate-traces.yaml b/tests/e2e-openshift/monitoring/02-generate-traces.yaml new file mode 100644 index 0000000000..1f3b23778f --- /dev/null +++ b/tests/e2e-openshift/monitoring/02-generate-traces.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: telemetrygen-traces +spec: + completions: 1 + parallelism: 1 + template: + metadata: + labels: + app: telemetrygen-traces + spec: + containers: + - name: telemetrygen-traces + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest + command: ["./telemetrygen"] + args: + - "--otlp-endpoint=cluster-collector-collector-headless:4317" + - "--otlp-insecure=true" + - "--rate=1" + - "--duration=3m" + - "--otlp-attributes=telemetrygen=\"traces\"" + - "--otlp-header=telemetrygen=\"traces\"" + - "--span-duration=1s" + - "--workers=1" + - "traces" + restartPolicy: Never diff --git a/tests/e2e-openshift/monitoring/03-assert.yaml b/tests/e2e-openshift/monitoring/03-assert.yaml new file mode 100644 index 0000000000..46f97913db --- /dev/null +++ b/tests/e2e-openshift/monitoring/03-assert.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: ./tests/e2e-openshift/monitoring/check_metrics.sh diff --git a/tests/e2e-openshift/monitoring/check_metrics.sh b/tests/e2e-openshift/monitoring/check_metrics.sh new file mode 100755 index 0000000000..1d88c58413 --- /dev/null +++ b/tests/e2e-openshift/monitoring/check_metrics.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +SECRET=$(oc get secret -n openshift-user-workload-monitoring | grep prometheus-user-workload-token | head -n 1 | awk '{print $1}') +TOKEN=$(echo $(oc get secret $SECRET -n openshift-user-workload-monitoring -o json | jq -r '.data.token') | base64 -d) +THANOS_QUERIER_HOST=$(oc get route thanos-querier -n openshift-monitoring -o json | jq -r '.spec.host') + +#Check metrics for OpenTelemetry collector instance. +metrics="otelcol_process_uptime otelcol_process_runtime_total_sys_memory_bytes otelcol_process_memory_rss otelcol_exporter_sent_spans otelcol_process_cpu_seconds otelcol_process_memory_rss otelcol_process_runtime_heap_alloc_bytes otelcol_process_runtime_total_alloc_bytes otelcol_process_runtime_total_sys_memory_bytes otelcol_process_uptime otelcol_receiver_accepted_spans otelcol_receiver_refused_spans" + +for metric in $metrics; do + query="$metric" + + response=$(curl -k -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" "https://$THANOS_QUERIER_HOST/api/v1/query?query=$query") + + count=$(echo "$response" | jq -r '.data.result | length') + + if [[ $count -eq 0 ]]; then + echo "No metric '$metric' with value present. Exiting with status 1." + exit 1 + else + echo "Metric '$metric' with value is present." + fi +done diff --git a/tests/e2e-openshift/monitoring/check_user_workload_monitoring.sh b/tests/e2e-openshift/monitoring/check_user_workload_monitoring.sh new file mode 100755 index 0000000000..5ecaebe3df --- /dev/null +++ b/tests/e2e-openshift/monitoring/check_user_workload_monitoring.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +check_replicas() { + replicas=$(oc get $1 $2 -n openshift-user-workload-monitoring -o 'jsonpath={.status.availableReplicas} {.status.readyReplicas} {.status.replicas}') + for count in $replicas; do + if [[ $count =~ ^[0-9]+$ ]]; then + if ((count < 1)); then + echo "The number of replicas is 0 for $1 $2" + exit 1 + fi + else + echo "Error: Replica count is not a valid number for $1 $2" + exit 1 + fi + done +} + +check_replicas deployment prometheus-operator +check_replicas statefulset prometheus-user-workload +check_replicas statefulset thanos-ruler-user-workload diff --git a/tests/e2e-openshift/multi-cluster/00-assert.yaml b/tests/e2e-openshift/multi-cluster/00-assert.yaml new file mode 100644 index 0000000000..cf530e1d45 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/00-assert.yaml @@ -0,0 +1,13 @@ +apiVersion: project.openshift.io/v1 +kind: Project +metadata: + name: kuttl-multi-cluster-send +status: + phase: Active + +apiVersion: project.openshift.io/v1 +kind: Project +metadata: + name: kuttl-multi-cluster-receive +status: + phase: Active diff --git a/tests/e2e-openshift/multi-cluster/00-create-namespaces.yaml b/tests/e2e-openshift/multi-cluster/00-create-namespaces.yaml new file mode 100644 index 0000000000..15931a4001 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/00-create-namespaces.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: kuttl-multi-cluster-send + +--- +apiVersion: v1 +kind: Namespace +metadata: + name: kuttl-multi-cluster-receive diff --git a/tests/e2e-openshift/multi-cluster/01-assert.yaml b/tests/e2e-openshift/multi-cluster/01-assert.yaml new file mode 100644 index 0000000000..f2c8714806 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/01-assert.yaml @@ -0,0 +1,126 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: jaeger + app.kubernetes.io/component: all-in-one + app.kubernetes.io/instance: jaeger-allinone + app.kubernetes.io/managed-by: jaeger-operator + app.kubernetes.io/name: jaeger-allinone + app.kubernetes.io/part-of: jaeger + name: jaeger-allinone + namespace: kuttl-multi-cluster-receive +status: + availableReplicas: 1 + readyReplicas: 1 + replicas: 1 + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: jaeger + app.kubernetes.io/component: service-collector + app.kubernetes.io/instance: jaeger-allinone + app.kubernetes.io/managed-by: jaeger-operator + app.kubernetes.io/name: jaeger-allinone-collector + app.kubernetes.io/part-of: jaeger + name: jaeger-allinone-collector + namespace: kuttl-multi-cluster-receive +spec: + ports: + - name: http-zipkin + port: 9411 + protocol: TCP + targetPort: 9411 + - name: tls-grpc-jaeger + port: 14250 + protocol: TCP + targetPort: 14250 + - name: http-c-tchan-trft + port: 14267 + protocol: TCP + targetPort: 14267 + - name: http-c-binary-trft + port: 14268 + protocol: TCP + targetPort: 14268 + - name: admin-http + port: 14269 + protocol: TCP + targetPort: 14269 + - name: grpc-otlp + port: 4317 + protocol: TCP + targetPort: 4317 + - name: http-otlp + port: 4318 + protocol: TCP + targetPort: 4318 + selector: + app: jaeger + app.kubernetes.io/component: all-in-one + app.kubernetes.io/instance: jaeger-allinone + app.kubernetes.io/managed-by: jaeger-operator + app.kubernetes.io/name: jaeger-allinone + app.kubernetes.io/part-of: jaeger + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: jaeger + app.kubernetes.io/component: service-query + app.kubernetes.io/instance: jaeger-allinone + app.kubernetes.io/managed-by: jaeger-operator + app.kubernetes.io/name: jaeger-allinone-query + app.kubernetes.io/part-of: jaeger + name: jaeger-allinone-query + namespace: kuttl-multi-cluster-receive +spec: + ports: + - name: http-query + port: 16686 + protocol: TCP + targetPort: 16686 + - name: grpc-query + port: 16685 + protocol: TCP + targetPort: 16685 + - name: admin-http + port: 16687 + protocol: TCP + targetPort: 16687 + selector: + app: jaeger + app.kubernetes.io/component: all-in-one + app.kubernetes.io/instance: jaeger-allinone + app.kubernetes.io/managed-by: jaeger-operator + app.kubernetes.io/name: jaeger-allinone + app.kubernetes.io/part-of: jaeger + type: ClusterIP + +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + labels: + app: jaeger + app.kubernetes.io/component: query-route + app.kubernetes.io/instance: jaeger-allinone + app.kubernetes.io/managed-by: jaeger-operator + app.kubernetes.io/name: jaeger-allinone + app.kubernetes.io/part-of: jaeger + name: jaeger-allinone + namespace: kuttl-multi-cluster-receive +spec: + port: + targetPort: http-query + tls: + termination: edge + to: + kind: Service + name: jaeger-allinone-query diff --git a/tests/e2e-openshift/multi-cluster/01-create-jaeger.yaml b/tests/e2e-openshift/multi-cluster/01-create-jaeger.yaml new file mode 100644 index 0000000000..bac7266626 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/01-create-jaeger.yaml @@ -0,0 +1,9 @@ +apiVersion: jaegertracing.io/v1 +kind: Jaeger +metadata: + name: jaeger-allinone + namespace: kuttl-multi-cluster-receive +spec: + strategy: allinone + ingress: + security: none diff --git a/tests/e2e-openshift/multi-cluster/02-assert.yaml b/tests/e2e-openshift/multi-cluster/02-assert.yaml new file mode 100644 index 0000000000..9650dd59f3 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/02-assert.yaml @@ -0,0 +1,147 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-receive.otlp-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otlp-receiver-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + name: otlp-receiver-collector + namespace: kuttl-multi-cluster-receive +status: + availableReplicas: 1 + readyReplicas: 1 + replicas: 1 + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-receive.otlp-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otlp-receiver-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + name: otlp-receiver-collector + namespace: kuttl-multi-cluster-receive +spec: + ports: + - appProtocol: h2c + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-receive.otlp-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-receive.otlp-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otlp-receiver-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + operator.opentelemetry.io/collector-headless-service: Exists + name: otlp-receiver-collector-headless + namespace: kuttl-multi-cluster-receive +spec: + ports: + - appProtocol: h2c + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-receive.otlp-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-receive.otlp-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otlp-receiver-collector-monitoring + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + name: otlp-receiver-collector-monitoring + namespace: kuttl-multi-cluster-receive +spec: + ports: + - name: monitoring + port: 8888 + protocol: TCP + targetPort: 8888 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-receive.otlp-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + type: ClusterIP + +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + labels: + app.kubernetes.io/instance: kuttl-multi-cluster-receive.otlp-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otlp-grpc-otlp-receiver-route + name: otlp-grpc-otlp-receiver-route + namespace: kuttl-multi-cluster-receive +spec: + port: + targetPort: otlp-grpc + tls: + termination: passthrough + to: + kind: Service + name: otlp-receiver-collector + weight: 100 + +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + labels: + app.kubernetes.io/instance: kuttl-multi-cluster-receive.otlp-receiver + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otlp-http-otlp-receiver-route + name: otlp-http-otlp-receiver-route + namespace: kuttl-multi-cluster-receive +spec: + port: + targetPort: otlp-http + tls: + termination: passthrough + to: + kind: Service + name: otlp-receiver-collector + weight: 100 diff --git a/tests/e2e-openshift/multi-cluster/02-otlp-receiver.yaml b/tests/e2e-openshift/multi-cluster/02-otlp-receiver.yaml new file mode 100644 index 0000000000..d300458082 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/02-otlp-receiver.yaml @@ -0,0 +1,49 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: +- script: ./generate_certs.sh + +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: otlp-receiver + namespace: kuttl-multi-cluster-receive +spec: + mode: "deployment" + ingress: + type: route + route: + termination: "passthrough" + volumes: + - name: kuttl-certs + configMap: + name: kuttl-certs + volumeMounts: + - name: kuttl-certs + mountPath: /certs + config: | + receivers: + otlp: + protocols: + http: + tls: + cert_file: /certs/server.crt + key_file: /certs/server.key + client_ca_file: /certs/ca.crt + grpc: + tls: + cert_file: /certs/server.crt + key_file: /certs/server.key + client_ca_file: /certs/ca.crt + exporters: + otlp: + endpoint: "jaeger-allinone-collector.kuttl-multi-cluster-receive.svc:4317" + tls: + insecure: true + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [otlp] diff --git a/tests/e2e-openshift/multi-cluster/03-assert.yaml b/tests/e2e-openshift/multi-cluster/03-assert.yaml new file mode 100644 index 0000000000..8edce42aa8 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/03-assert.yaml @@ -0,0 +1,107 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-send.otel-sender + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otel-sender-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + name: otel-sender-collector + namespace: kuttl-multi-cluster-send +status: + availableReplicas: 1 + readyReplicas: 1 + replicas: 1 + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-send.otel-sender + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otel-sender-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + name: otel-sender-collector + namespace: kuttl-multi-cluster-send +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-send.otel-sender + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-send.otel-sender + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otel-sender-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + operator.opentelemetry.io/collector-headless-service: Exists + name: otel-sender-collector-headless + namespace: kuttl-multi-cluster-send +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-send.otel-sender + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-send.otel-sender + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otel-sender-collector-monitoring + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + name: otel-sender-collector-monitoring + namespace: kuttl-multi-cluster-send +spec: + ports: + - name: monitoring + port: 8888 + protocol: TCP + targetPort: 8888 + selector: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: kuttl-multi-cluster-send.otel-sender + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/part-of: opentelemetry + type: ClusterIP diff --git a/tests/e2e-openshift/multi-cluster/03-otlp-sender.yaml b/tests/e2e-openshift/multi-cluster/03-otlp-sender.yaml new file mode 100644 index 0000000000..2c1d45273f --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/03-otlp-sender.yaml @@ -0,0 +1,46 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kuttl-multi-cluster + namespace: kuttl-multi-cluster-send + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kuttl-multi-cluster +rules: +- apiGroups: ["config.openshift.io"] + resources: ["infrastructures", "infrastructures/status"] + verbs: ["get", "watch", "list"] +- apiGroups: ["apps"] + resources: ["replicasets"] + verbs: ["get", "watch", "list"] +- apiGroups: [""] + resources: ["pods"] + verbs: ["get", "watch", "list"] +- apiGroups: [""] + resources: ["namespaces"] + verbs: ["get", "watch", "list"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kuttl-multi-cluster +subjects: +- kind: ServiceAccount + name: kuttl-multi-cluster + namespace: kuttl-multi-cluster-send +roleRef: + kind: ClusterRole + name: kuttl-multi-cluster + apiGroup: rbac.authorization.k8s.io + +--- +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: +- script: ./create_otlp_sender.sh diff --git a/tests/e2e-openshift/multi-cluster/04-assert.yaml b/tests/e2e-openshift/multi-cluster/04-assert.yaml new file mode 100644 index 0000000000..2a2a3b6221 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/04-assert.yaml @@ -0,0 +1,20 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http + namespace: kuttl-multi-cluster-send +status: + conditions: + - status: "True" + type: Complete + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc + namespace: kuttl-multi-cluster-send +status: + conditions: + - status: "True" + type: Complete diff --git a/tests/e2e-openshift/multi-cluster/04-generate-traces.yaml b/tests/e2e-openshift/multi-cluster/04-generate-traces.yaml new file mode 100644 index 0000000000..4c2d6b10fc --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/04-generate-traces.yaml @@ -0,0 +1,41 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-http + namespace: kuttl-multi-cluster-send +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.75.0 + args: + - traces + - --otlp-endpoint=otel-sender-collector:4318 + - --traces=100 + - --otlp-http + - --otlp-insecure=true + - --service=telemetrygen-http + - --otlp-attributes=protocol="http" + restartPolicy: Never + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: generate-traces-grpc + namespace: kuttl-multi-cluster-send +spec: + template: + spec: + containers: + - name: telemetrygen + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:v0.75.0 + args: + - traces + - --otlp-endpoint=otel-sender-collector:4317 + - --traces=100 + - --otlp-insecure=true + - --service=telemetrygen-grpc + - --otlp-attributes=protocol="grpc" + restartPolicy: Never diff --git a/tests/e2e-openshift/multi-cluster/05-assert.yaml b/tests/e2e-openshift/multi-cluster/05-assert.yaml new file mode 100644 index 0000000000..73befbcd29 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/05-assert.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 30 +commands: +- script: ./tests/e2e-openshift/multi-cluster/check_traces.sh diff --git a/tests/e2e-openshift/multi-cluster/check_traces.sh b/tests/e2e-openshift/multi-cluster/check_traces.sh new file mode 100755 index 0000000000..7fe6eea518 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/check_traces.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Define an array of service names +SERVICE_NAMES=("telemetrygen-http" "telemetrygen-grpc") + +# Get the Jaeger URL +JAEGER_URL=$(oc -n kuttl-multi-cluster-receive get route jaeger-allinone -o json | jq '.spec.host' -r) + +# Initialize a flag to check if any trace exists +trace_exists=false + +# Loop through each service name +for SERVICE_NAME in "${SERVICE_NAMES[@]}"; do + trace_count=$(curl -ksSL "https://$JAEGER_URL/api/traces?service=$SERVICE_NAME&limit=1" | jq -r '.data | length') + if [[ $trace_count -gt 0 ]]; then + echo "Traces for $SERVICE_NAME exist in Jaeger." + trace_exists=true + else + echo "Trace for $SERVICE_NAME does not exist in Jaeger." + fi +done + +# Fail the test step if no traces exist for any service name +if ! $trace_exists; then + exit 1 +fi diff --git a/tests/e2e-openshift/multi-cluster/create_otlp_sender.sh b/tests/e2e-openshift/multi-cluster/create_otlp_sender.sh new file mode 100755 index 0000000000..fadb2fc8a6 --- /dev/null +++ b/tests/e2e-openshift/multi-cluster/create_otlp_sender.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Get the HTTP and GRPC routes from OpenTelemetry receiver collector. +otlp_route_http=$(oc -n kuttl-multi-cluster-receive get route otlp-http-otlp-receiver-route -o json | jq '.spec.host' -r) +otlp_route_grpc=$(oc -n kuttl-multi-cluster-receive get route otlp-grpc-otlp-receiver-route -o json | jq '.spec.host' -r) + +# Define the collector content +collector_content=$(cat < "$openssl_config" +[req] +req_extensions = v3_req + +[v3_req] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = $hostname_domain +EOF + +# Generate private key for the server +openssl genpkey -algorithm RSA -out "$CERT_DIR/server.key" + +# Create CSR for the server with SANs +openssl req -new -key "$CERT_DIR/server.key" -out "$CERT_DIR/server.csr" -subj "$CERT_SUBJECT" -config "$openssl_config" + +# Generate self-signed certificate for the server with SANs +openssl x509 -req -days 365 -in "$CERT_DIR/server.csr" -signkey "$CERT_DIR/server.key" -out "$CERT_DIR/server.crt" -extensions v3_req -extfile "$openssl_config" + +# Generate a CA certificate (self-signed) +openssl req -new -x509 -days 365 -key "$CERT_DIR/server.key" -out "$CERT_DIR/ca.crt" -subj "$CERT_SUBJECT" + +echo "Certificates generated successfully in $CERT_DIR directory." + +# Delete any existing ConfigMaps +kubectl delete configmap -n kuttl-multi-cluster-send kuttl-certs +kubectl delete configmap -n kuttl-multi-cluster-receive kuttl-certs + +# Create a Kubernetes ConfigMap for the server certificate, private key, and CA certificate in kuttl-multi-cluster-send namespace +kubectl create configmap kuttl-certs -n kuttl-multi-cluster-send \ + --from-file=server.crt="$CERT_DIR/server.crt" \ + --from-file=server.key="$CERT_DIR/server.key" \ + --from-file=ca.crt="$CERT_DIR/ca.crt" + +# Create a Kubernetes ConfigMap for the server certificate, private key, and CA certificate in kuttl-multi-cluster-receive namespace +kubectl create configmap kuttl-certs -n kuttl-multi-cluster-receive \ + --from-file=server.crt="$CERT_DIR/server.crt" \ + --from-file=server.key="$CERT_DIR/server.key" \ + --from-file=ca.crt="$CERT_DIR/ca.crt" + +echo "ConfigMaps created successfully." diff --git a/tests/e2e-openshift/otlp-metrics-traces/00-assert.yaml b/tests/e2e-openshift/otlp-metrics-traces/00-assert.yaml new file mode 100644 index 0000000000..ef885ad07c --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/00-assert.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: kuttl-otlp-metrics + name: jaeger-allinone +status: + availableReplicas: 1 + readyReplicas: 1 + replicas: 1 diff --git a/tests/e2e-openshift/otlp-metrics-traces/00-install-jaeger.yaml b/tests/e2e-openshift/otlp-metrics-traces/00-install-jaeger.yaml new file mode 100644 index 0000000000..a941089d4b --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/00-install-jaeger.yaml @@ -0,0 +1,18 @@ +#Create the "kuttl-otlp-metrics" project to fulfill the requirement of specifying the Jaeger and OTEL collector endpoints. This project is essential due to KUTTL's current lack of support for templating. +#For this test case you'll need to install the Jaeger operator (OpenShift Distributed Tracing Platform in OpenShift) + +apiVersion: v1 +kind: Namespace +metadata: + name: kuttl-otlp-metrics + +--- +apiVersion: jaegertracing.io/v1 +kind: Jaeger +metadata: + name: jaeger-allinone + namespace: kuttl-otlp-metrics +spec: + strategy: allinone + ingress: + security: none diff --git a/tests/e2e-openshift/otlp-metrics-traces/01-assert.yaml b/tests/e2e-openshift/otlp-metrics-traces/01-assert.yaml new file mode 100644 index 0000000000..b7e3849892 --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/01-assert.yaml @@ -0,0 +1,4 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: ./tests/e2e-openshift/otlp-metrics-traces/check_user_workload_monitoring.sh diff --git a/tests/e2e-openshift/otlp-metrics-traces/01-workload-monitoring.yaml b/tests/e2e-openshift/otlp-metrics-traces/01-workload-monitoring.yaml new file mode 100644 index 0000000000..1d5c5e4b14 --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/01-workload-monitoring.yaml @@ -0,0 +1,11 @@ +# oc -n openshift-user-workload-monitoring get pod +# https://docs.openshift.com/container-platform/4.13/monitoring/enabling-monitoring-for-user-defined-projects.html#accessing-metrics-from-outside-cluster_enabling-monitoring-for-user-defined-projects + +apiVersion: v1 +kind: ConfigMap +metadata: + name: cluster-monitoring-config + namespace: openshift-monitoring +data: + config.yaml: | + enableUserWorkload: true diff --git a/tests/e2e-openshift/otlp-metrics-traces/02-assert.yaml b/tests/e2e-openshift/otlp-metrics-traces/02-assert.yaml new file mode 100644 index 0000000000..8315633e89 --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/02-assert.yaml @@ -0,0 +1,31 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: kuttl-otlp-metrics +status: + availableReplicas: 1 + readyReplicas: 1 + replicas: 1 + +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + app.kubernetes.io/instance: kuttl-otlp-metrics.cluster-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: cluster-collector-collector + name: cluster-collector-collector + namespace: kuttl-otlp-metrics +spec: + endpoints: + - port: monitoring + - port: prometheus + namespaceSelector: + matchNames: + - kuttl-otlp-metrics + selector: + matchLabels: + app.kubernetes.io/instance: kuttl-otlp-metrics.cluster-collector + app.kubernetes.io/managed-by: opentelemetry-operator diff --git a/tests/e2e-openshift/otlp-metrics-traces/02-otel-metrics-collector.yaml b/tests/e2e-openshift/otlp-metrics-traces/02-otel-metrics-collector.yaml new file mode 100644 index 0000000000..632b994457 --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/02-otel-metrics-collector.yaml @@ -0,0 +1,38 @@ +#https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/prometheusexporter/README.md + +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: cluster-collector + namespace: kuttl-otlp-metrics +spec: + mode: deployment + observability: + metrics: + enableMetrics: true + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + exporters: + otlp: + endpoint: jaeger-allinone-collector-headless.kuttl-otlp-metrics.svc:4317 + tls: + insecure: true + prometheus: + endpoint: 0.0.0.0:8889 + resource_to_telemetry_conversion: + enabled: true # by default resource attributes are dropped + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [otlp] + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus] diff --git a/tests/e2e-openshift/otlp-metrics-traces/03-assert.yaml b/tests/e2e-openshift/otlp-metrics-traces/03-assert.yaml new file mode 100644 index 0000000000..73bbb738a9 --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/03-assert.yaml @@ -0,0 +1,22 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: telemetrygen-traces +status: + active: 1 + ready: 1 + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: telemetrygen-metrics +status: + active: 1 + ready: 1 + +--- +apiVersion: v1 +kind: Pod +status: + phase: Running diff --git a/tests/e2e-openshift/otlp-metrics-traces/03-metrics-traces-gen.yaml b/tests/e2e-openshift/otlp-metrics-traces/03-metrics-traces-gen.yaml new file mode 100644 index 0000000000..441b8877aa --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/03-metrics-traces-gen.yaml @@ -0,0 +1,52 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: telemetrygen-traces +spec: + completions: 1 + parallelism: 1 + template: + metadata: + labels: + app: telemetrygen-traces + spec: + containers: + - name: telemetrygen-traces + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest + command: ["./telemetrygen"] + args: + - "--otlp-endpoint=cluster-collector-collector-headless.kuttl-otlp-metrics.svc:4317" + - "--otlp-insecure=true" + - "--rate=1" + - "--duration=30s" + - "--otlp-attributes=telemetrygen=\"traces\"" + - "--otlp-header=telemetrygen=\"traces\"" + - "traces" + restartPolicy: Never + +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: telemetrygen-metrics +spec: + completions: 1 + parallelism: 1 + template: + metadata: + labels: + app: telemetrygen-metrics + spec: + containers: + - name: telemetrygen-metrics + image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest + command: ["./telemetrygen"] + args: + - "--otlp-endpoint=cluster-collector-collector-headless.kuttl-otlp-metrics.svc:4317" + - "--otlp-insecure=true" + - "--duration=30s" + - "--rate=1" + - "--otlp-attributes=telemetrygen=\"metrics\"" + - "--otlp-header=telemetrygen=\"traces\"" + - "metrics" + restartPolicy: Never diff --git a/tests/e2e-openshift/otlp-metrics-traces/04-assert.yaml b/tests/e2e-openshift/otlp-metrics-traces/04-assert.yaml new file mode 100644 index 0000000000..9bec95f449 --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/04-assert.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 30 +commands: +- script: ./tests/e2e-openshift/otlp-metrics-traces/check_traces.sh diff --git a/tests/e2e-openshift/otlp-metrics-traces/05-assert.yaml b/tests/e2e-openshift/otlp-metrics-traces/05-assert.yaml new file mode 100644 index 0000000000..79570e05a4 --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/05-assert.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: ./tests/e2e-openshift/otlp-metrics-traces/check_metrics.sh diff --git a/tests/e2e-openshift/otlp-metrics-traces/check_metrics.sh b/tests/e2e-openshift/otlp-metrics-traces/check_metrics.sh new file mode 100755 index 0000000000..dd44adc871 --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/check_metrics.sh @@ -0,0 +1,15 @@ +#!/bin/bash +SECRET=$(oc get secret -n openshift-user-workload-monitoring | grep prometheus-user-workload-token | head -n 1 | awk '{print $1}') +TOKEN=$(echo $(oc get secret $SECRET -n openshift-user-workload-monitoring -o json | jq -r '.data.token') | base64 -d) +THANOS_QUERIER_HOST=$(oc get route thanos-querier -n openshift-monitoring -o json | jq -r '.spec.host') + +response=$(curl -k -H "Authorization: Bearer $TOKEN" -H "Content-type: application/json" "https://$THANOS_QUERIER_HOST/api/v1/query?query=gen") + +count=$(echo "$response" | jq -r '.data.result | length') + +if [[ $count -eq 0 ]]; then + echo "No telemetrygen metrics count with value present. Exiting with status 1." + exit 1 +else + echo "telemetrygen metrics with value is present." +fi diff --git a/tests/e2e-openshift/otlp-metrics-traces/check_traces.sh b/tests/e2e-openshift/otlp-metrics-traces/check_traces.sh new file mode 100755 index 0000000000..9761284aee --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/check_traces.sh @@ -0,0 +1,12 @@ +#!/bin/bash +JAEGER_URL=$(oc -n kuttl-otlp-metrics get route jaeger-allinone -o json | jq '.spec.host' -r) +SERVICE_NAME="telemetrygen" + +trace_exists=$(curl -ksSL "https://$JAEGER_URL/api/traces?service=$SERVICE_NAME&limit=1" | jq -r '.data | length') + +if [[ $trace_exists -gt 0 ]]; then + echo "Traces for $SERVICE_NAME exist in Jaeger." +else + echo "Trace for $SERVICE_NAME does not exist in Jaeger." + exit 1 # Fail the test step if the trace doesn't exist +fi diff --git a/tests/e2e-openshift/otlp-metrics-traces/check_user_workload_monitoring.sh b/tests/e2e-openshift/otlp-metrics-traces/check_user_workload_monitoring.sh new file mode 100755 index 0000000000..5ecaebe3df --- /dev/null +++ b/tests/e2e-openshift/otlp-metrics-traces/check_user_workload_monitoring.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +check_replicas() { + replicas=$(oc get $1 $2 -n openshift-user-workload-monitoring -o 'jsonpath={.status.availableReplicas} {.status.readyReplicas} {.status.replicas}') + for count in $replicas; do + if [[ $count =~ ^[0-9]+$ ]]; then + if ((count < 1)); then + echo "The number of replicas is 0 for $1 $2" + exit 1 + fi + else + echo "Error: Replica count is not a valid number for $1 $2" + exit 1 + fi + done +} + +check_replicas deployment prometheus-operator +check_replicas statefulset prometheus-user-workload +check_replicas statefulset thanos-ruler-user-workload diff --git a/tests/e2e-openshift/route/00-assert.yaml b/tests/e2e-openshift/route/00-assert.yaml new file mode 100644 index 0000000000..f0366fa187 --- /dev/null +++ b/tests/e2e-openshift/route/00-assert.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simplest-collector +status: + readyReplicas: 1 +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + annotations: + something.com: "true" + labels: + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otlp-grpc-simplest-route + name: otlp-grpc-simplest-route + ownerReferences: + - apiVersion: opentelemetry.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: OpenTelemetryCollector + name: simplest +spec: + port: + targetPort: otlp-grpc + to: + kind: Service + name: simplest-collector + wildcardPolicy: None +--- +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + annotations: + something.com: "true" + labels: + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: otlp-http-simplest-route + name: otlp-http-simplest-route + ownerReferences: + - apiVersion: opentelemetry.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: OpenTelemetryCollector + name: simplest +spec: + port: + targetPort: otlp-http + to: + kind: Service + name: simplest-collector + wildcardPolicy: None diff --git a/tests/e2e-openshift/route/00-install.yaml b/tests/e2e-openshift/route/00-install.yaml new file mode 100644 index 0000000000..1c060e5a07 --- /dev/null +++ b/tests/e2e-openshift/route/00-install.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest +spec: + mode: "deployment" + ingress: + type: route + annotations: + something.com: "true" + route: + termination: "insecure" + + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/tests/e2e-openshift/route/01-report-empty-otlphttp-spans.yaml b/tests/e2e-openshift/route/01-report-empty-otlphttp-spans.yaml new file mode 100644 index 0000000000..406d66cef2 --- /dev/null +++ b/tests/e2e-openshift/route/01-report-empty-otlphttp-spans.yaml @@ -0,0 +1,9 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + #!/bin/bash + set -ex + # Export empty payload and check of collector accepted it with 2xx status code + otlp_http_host=$(kubectl get route otlp-http-simplest-route -n $NAMESPACE -o jsonpath='{.spec.host}') + for i in {1..40}; do curl --fail -ivX POST http://${otlp_http_host}:80/v1/traces -H "Content-Type: application/json" -d '{}' && break || sleep 1; done diff --git a/tests/e2e-pdb/pdb/00-install.yaml b/tests/e2e-pdb/pdb/00-install.yaml new file mode 100644 index 0000000000..adc063327f --- /dev/null +++ b/tests/e2e-pdb/pdb/00-install.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: create-pdbs diff --git a/tests/e2e-pdb/pdb/01-assert.yaml b/tests/e2e-pdb/pdb/01-assert.yaml new file mode 100644 index 0000000000..997a671a51 --- /dev/null +++ b/tests/e2e-pdb/pdb/01-assert.yaml @@ -0,0 +1,63 @@ +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: max-integer-collector + namespace: create-pdbs +spec: + selector: + matchLabels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-pdbs.max-integer + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: max-integer-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + maxUnavailable: 1 +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: max-string-collector + namespace: create-pdbs +spec: + selector: + matchLabels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-pdbs.max-string + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: max-string-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + maxUnavailable: "10%" +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: min-integer-collector + namespace: create-pdbs +spec: + selector: + matchLabels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-pdbs.min-integer + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: min-integer-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + minAvailable: 1 +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: min-string-collector + namespace: create-pdbs +spec: + selector: + matchLabels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-pdbs.min-string + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: min-string-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: latest + minAvailable: "10%" diff --git a/tests/e2e-pdb/pdb/01-install.yaml b/tests/e2e-pdb/pdb/01-install.yaml new file mode 100644 index 0000000000..e73e2d3efe --- /dev/null +++ b/tests/e2e-pdb/pdb/01-install.yaml @@ -0,0 +1,141 @@ +# This creates four different deployments for checking different pdb options: +# * minAvailable integer +# * minAvailable string +# * maxUnavailable integer +# * maxUnavailable string +# +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: min-integer + namespace: create-pdbs +spec: + podDisruptionBudget: + minAvailable: 1 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: min-string + namespace: create-pdbs +spec: + podDisruptionBudget: + minAvailable: 10% + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: max-integer + namespace: create-pdbs +spec: + podDisruptionBudget: + maxUnavailable: 1 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: max-string + namespace: create-pdbs +spec: + podDisruptionBudget: + maxUnavailable: "10%" + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] diff --git a/tests/e2e-prometheuscr/create-pm-prometheus-exporters/00-install.yaml b/tests/e2e-prometheuscr/create-pm-prometheus-exporters/00-install.yaml new file mode 100644 index 0000000000..5440bee174 --- /dev/null +++ b/tests/e2e-prometheuscr/create-pm-prometheus-exporters/00-install.yaml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: create-pm-prometheus +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + namespace: create-pm-prometheus +spec: + mode: sidecar + observability: + metrics: + enableMetrics: true + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + prometheus/prod: + endpoint: 0.0.0.0:8884 + + prometheus/dev: + endpoint: 0.0.0.0:8885 + + service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus/dev, prometheus/prod] diff --git a/tests/e2e-prometheuscr/create-pm-prometheus-exporters/01-assert.yaml b/tests/e2e-prometheuscr/create-pm-prometheus-exporters/01-assert.yaml new file mode 100644 index 0000000000..d89c68dd59 --- /dev/null +++ b/tests/e2e-prometheuscr/create-pm-prometheus-exporters/01-assert.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + sidecar.opentelemetry.io/inject: "true" + labels: + app: pod-with-sidecar + namespace: create-pm-prometheus +spec: + containers: + - name: myapp + - name: otc-container + env: + - name: POD_NAME + - name: OTEL_CONFIG + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_POD_UID + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_RESOURCE_ATTRIBUTES +status: + phase: Running +--- +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + labels: + app.kubernetes.io/instance: create-pm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: simplest-collector + name: simplest-collector + namespace: create-pm-prometheus +spec: + jobLabel: "app.kubernetes.io/instance" + podMetricsEndpoints: + - port: monitoring + - port: prometheus-dev + - port: prometheus-prod + namespaceSelector: + matchNames: + - create-pm-prometheus + selector: + matchLabels: + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/instance: create-pm-prometheus.simplest diff --git a/tests/e2e-prometheuscr/create-pm-prometheus-exporters/01-install-app.yaml b/tests/e2e-prometheuscr/create-pm-prometheus-exporters/01-install-app.yaml new file mode 100644 index 0000000000..884f05d223 --- /dev/null +++ b/tests/e2e-prometheuscr/create-pm-prometheus-exporters/01-install-app.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app-with-sidecar + namespace: create-pm-prometheus +spec: + selector: + matchLabels: + app: pod-with-sidecar + replicas: 1 + template: + metadata: + labels: + app: pod-with-sidecar + annotations: + sidecar.opentelemetry.io/inject: "true" + spec: + containers: + - name: myapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/00-install.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/00-install.yaml new file mode 100644 index 0000000000..0121963c69 --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/00-install.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: create-sm-prometheus diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-assert.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-assert.yaml new file mode 100644 index 0000000000..0a3c07d13b --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-assert.yaml @@ -0,0 +1,65 @@ +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-dev + - port: prometheus-prod + namespaceSelector: + matchNames: + - create-sm-prometheus + selector: + matchLabels: + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/instance: create-sm-prometheus.simplest +--- +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector + namespace: create-sm-prometheus +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + - name: prometheus-dev + port: 8885 + protocol: TCP + targetPort: 8885 + - name: prometheus-prod + port: 8884 + protocol: TCP + targetPort: 8884 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/instance: create-sm-prometheus.simplest + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: simplest-collector-monitoring + app.kubernetes.io/part-of: opentelemetry + name: simplest-collector-monitoring + namespace: create-sm-prometheus +spec: + ports: + - name: monitoring + port: 8888 + protocol: TCP + targetPort: 8888 diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-install.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-install.yaml new file mode 100644 index 0000000000..b03318eb4c --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/01-install.yaml @@ -0,0 +1,29 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + namespace: create-sm-prometheus +spec: + observability: + metrics: + enableMetrics: true + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + prometheus/prod: + endpoint: 0.0.0.0:8884 + + prometheus/dev: + endpoint: 0.0.0.0:8885 + + service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus/dev, prometheus/prod] diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-assert.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-assert.yaml new file mode 100644 index 0000000000..b79840724e --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-assert.yaml @@ -0,0 +1,42 @@ +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 + app.kubernetes.io/instance: create-sm-prometheus.simplest +--- +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector + namespace: create-sm-prometheus +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + - name: prometheus-prod + port: 8884 + protocol: TCP + targetPort: 8884 \ No newline at end of file diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-install.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-install.yaml new file mode 100644 index 0000000000..e275da42eb --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/02-install.yaml @@ -0,0 +1,26 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + namespace: create-sm-prometheus +spec: + observability: + metrics: + enableMetrics: true + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + prometheus/prod: + endpoint: 0.0.0.0:8884 + + service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus/prod] diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/03-assert.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/03-assert.yaml new file mode 100644 index 0000000000..6590ac3935 --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/03-assert.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector + namespace: create-sm-prometheus +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + - name: prometheus-prod + port: 9091 + protocol: TCP + targetPort: 9091 diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/03-install.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/03-install.yaml new file mode 100644 index 0000000000..fe782b4d33 --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/03-install.yaml @@ -0,0 +1,26 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + namespace: create-sm-prometheus +spec: + observability: + metrics: + enableMetrics: true + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + prometheus/prod: + endpoint: 0.0.0.0:9091 + + service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus/prod] diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-error.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-error.yaml new file mode 100644 index 0000000000..61ad50e38b --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-error.yaml @@ -0,0 +1,19 @@ +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/04-install.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-install.yaml new file mode 100644 index 0000000000..5578a7da8e --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/04-install.yaml @@ -0,0 +1,26 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + namespace: create-sm-prometheus +spec: + observability: + metrics: + enableMetrics: false + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + prometheus/prod: + endpoint: 0.0.0.0:9091 + + service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus/prod] diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-assert.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-assert.yaml new file mode 100644 index 0000000000..f56ec033cd --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-assert.yaml @@ -0,0 +1,21 @@ +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-dev + - port: prometheus-prod + namespaceSelector: + matchNames: + - create-sm-prometheus + selector: + matchLabels: + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/instance: create-sm-prometheus.simplest diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-error.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-error.yaml new file mode 100644 index 0000000000..ecb59ba1fd --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-error.yaml @@ -0,0 +1,22 @@ +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-dev + - port: prometheus-prod + - port: prometheusremotewrite/prometheus + namespaceSelector: + matchNames: + - create-sm-prometheus + selector: + matchLabels: + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/instance: create-sm-prometheus.simplest diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-install.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-install.yaml new file mode 100644 index 0000000000..8e300294e5 --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/05-install.yaml @@ -0,0 +1,32 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + namespace: create-sm-prometheus +spec: + observability: + metrics: + enableMetrics: true + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + prometheus/prod: + endpoint: 0.0.0.0:8884 + + prometheus/dev: + endpoint: 0.0.0.0:8885 + + prometheusremotewrite/prometheus: + endpoint: http://prometheus-server.monitoring/api/v1/write + + service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus/dev, prometheus/prod, prometheusremotewrite/prometheus] diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-assert.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-assert.yaml new file mode 100644 index 0000000000..8df94a5984 --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-assert.yaml @@ -0,0 +1,20 @@ +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-dev + namespaceSelector: + matchNames: + - create-sm-prometheus + selector: + matchLabels: + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/instance: create-sm-prometheus.simplest diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-install.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-install.yaml new file mode 100644 index 0000000000..028458da18 --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/06-install.yaml @@ -0,0 +1,29 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest + namespace: create-sm-prometheus +spec: + observability: + metrics: + enableMetrics: true + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + prometheus/prod: + endpoint: 0.0.0.0:8884 + + prometheus/dev: + endpoint: 0.0.0.0:8885 + + service: + pipelines: + metrics: + receivers: [otlp] + processors: [] + exporters: [prometheus/dev] diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/07-delete.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/07-delete.yaml new file mode 100644 index 0000000000..76798588bb --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/07-delete.yaml @@ -0,0 +1,8 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +delete: + - apiVersion: opentelemetry.io/v1alpha1 + kind: OpenTelemetryCollector + metadata: + name: simplest + namespace: create-sm-prometheus \ No newline at end of file diff --git a/tests/e2e-prometheuscr/create-sm-prometheus-exporters/07-error.yaml b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/07-error.yaml new file mode 100644 index 0000000000..263dbb3c64 --- /dev/null +++ b/tests/e2e-prometheuscr/create-sm-prometheus-exporters/07-error.yaml @@ -0,0 +1,5 @@ +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: simplest-collector + namespace: create-sm-prometheus diff --git a/tests/e2e-upgrade/upgrade-test/00-assert.yaml b/tests/e2e-upgrade/upgrade-test/00-assert.yaml index 50736de4aa..d5c2c3825e 100644 --- a/tests/e2e-upgrade/upgrade-test/00-assert.yaml +++ b/tests/e2e-upgrade/upgrade-test/00-assert.yaml @@ -3,10 +3,11 @@ kind: Deployment metadata: name: simplest-collector annotations: - operatorVersion: "v0.49.0" + operatorVersion: "v0.86.0" spec: - selector: - matchLabels: - app.kubernetes.io/version: latest + template: + metadata: + labels: + app.kubernetes.io/version: latest status: readyReplicas: 1 diff --git a/tests/e2e-upgrade/upgrade-test/00-install.yaml b/tests/e2e-upgrade/upgrade-test/00-install.yaml index b0e18de3c6..9ae9054a40 100644 --- a/tests/e2e-upgrade/upgrade-test/00-install.yaml +++ b/tests/e2e-upgrade/upgrade-test/00-install.yaml @@ -3,7 +3,7 @@ kind: OpenTelemetryCollector metadata: name: simplest annotations: - operatorVersion: "v0.49.0" + operatorVersion: "v0.86.0" spec: replicas: 1 config: | @@ -18,11 +18,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger,otlp] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e-upgrade/upgrade-test/01-upgrade-operator.yaml b/tests/e2e-upgrade/upgrade-test/01-upgrade-operator.yaml index 346d4facde..248ffb6a9a 100644 --- a/tests/e2e-upgrade/upgrade-test/01-upgrade-operator.yaml +++ b/tests/e2e-upgrade/upgrade-test/01-upgrade-operator.yaml @@ -1,6 +1,4 @@ apiVersion: kuttl.dev/v1beta1 kind: TestStep commands: - - command: kubectl apply -f ../../_build/manifests/01-opentelemetry-operator.yaml - - command: kubectl rollout status -w deployment/opentelemetry-operator-controller-manager -n opentelemetry-operator-system - - command: sleep 60s + - script: cd ../../../ && make deploy VERSION=e2e diff --git a/tests/e2e-upgrade/upgrade-test/02-upgrade-collector.yaml b/tests/e2e-upgrade/upgrade-test/02-upgrade-collector.yaml index dceb4999cd..a2aefad52a 100644 --- a/tests/e2e-upgrade/upgrade-test/02-upgrade-collector.yaml +++ b/tests/e2e-upgrade/upgrade-test/02-upgrade-collector.yaml @@ -18,11 +18,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger,otlp] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.49.0.yaml b/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.49.0.yaml deleted file mode 100644 index 750fe9ca5e..0000000000 --- a/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.49.0.yaml +++ /dev/null @@ -1,2732 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - control-plane: controller-manager - name: opentelemetry-operator-system ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.8.0 - creationTimestamp: null - labels: - app.kubernetes.io/name: opentelemetry-operator - name: instrumentations.opentelemetry.io -spec: - group: opentelemetry.io - names: - kind: Instrumentation - listKind: InstrumentationList - plural: instrumentations - shortNames: - - otelinst - - otelinsts - singular: instrumentation - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .spec.exporter.endpoint - name: Endpoint - type: string - - jsonPath: .spec.sampler.type - name: Sampler - type: string - - jsonPath: .spec.sampler.argument - name: Sampler Arg - type: string - name: v1alpha1 - schema: - openAPIV3Schema: - description: Instrumentation is the spec for OpenTelemetry instrumentation. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: InstrumentationSpec defines the desired state of OpenTelemetry SDK and instrumentation. - properties: - env: - description: 'Env defines common env vars. There are four layers for env vars'' definitions and the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs'' vars`. If the former var had been defined, then the other vars would be ignored.' - items: - description: EnvVar represents an environment variable present in a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in the pod's namespace - properties: - key: - description: The key of the secret to select from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - exporter: - description: Exporter defines exporter configuration. - properties: - endpoint: - description: Endpoint is address of the collector with OTLP endpoint. - type: string - type: object - java: - description: Java defines configuration for java auto-instrumentation. - properties: - env: - description: 'Env defines java specific env vars. There are four layers for env vars'' definitions and the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs'' vars`. If the former var had been defined, then the other vars would be ignored.' - items: - description: EnvVar represents an environment variable present in a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in the pod's namespace - properties: - key: - description: The key of the secret to select from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - image: - description: Image is a container image with javaagent auto-instrumentation JAR. - type: string - type: object - nodejs: - description: NodeJS defines configuration for nodejs auto-instrumentation. - properties: - env: - description: 'Env defines nodejs specific env vars. There are four layers for env vars'' definitions and the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs'' vars`. If the former var had been defined, then the other vars would be ignored.' - items: - description: EnvVar represents an environment variable present in a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in the pod's namespace - properties: - key: - description: The key of the secret to select from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - image: - description: Image is a container image with NodeJS SDK and auto-instrumentation. - type: string - type: object - propagators: - description: Propagators defines inter-process context propagation configuration. - items: - description: Propagator represents the propagation type. - enum: - - tracecontext - - baggage - - b3 - - b3multi - - jaeger - - xray - - ottrace - - none - type: string - type: array - python: - description: Python defines configuration for python auto-instrumentation. - properties: - env: - description: 'Env defines python specific env vars. There are four layers for env vars'' definitions and the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs'' vars`. If the former var had been defined, then the other vars would be ignored.' - items: - description: EnvVar represents an environment variable present in a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in the pod's namespace - properties: - key: - description: The key of the secret to select from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - image: - description: Image is a container image with Python SDK and auto-instrumentation. - type: string - type: object - resource: - description: Resource defines the configuration for the resource attributes, as defined by the OpenTelemetry specification. - properties: - addK8sUIDAttributes: - description: AddK8sUIDAttributes defines whether K8s UID attributes should be collected (e.g. k8s.deployment.uid). - type: boolean - resourceAttributes: - additionalProperties: - type: string - description: 'Attributes defines attributes that are added to the resource. For example environment: dev' - type: object - type: object - sampler: - description: Sampler defines sampling configuration. - properties: - argument: - description: Argument defines sampler argument. The value depends on the sampler type. For instance for parentbased_traceidratio sampler type it is a number in range [0..1] e.g. 0.25. - type: string - type: - description: Type defines sampler type. The value can be for instance parentbased_always_on, parentbased_always_off, parentbased_traceidratio... - enum: - - always_on - - always_off - - traceidratio - - parentbased_always_on - - parentbased_always_off - - parentbased_traceidratio - - jaeger_remote - - xray - type: string - type: object - type: object - status: - description: InstrumentationStatus defines status of the instrumentation. - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: opentelemetry-operator-system/opentelemetry-operator-serving-cert - controller-gen.kubebuilder.io/version: v0.8.0 - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetrycollectors.opentelemetry.io -spec: - group: opentelemetry.io - names: - kind: OpenTelemetryCollector - listKind: OpenTelemetryCollectorList - plural: opentelemetrycollectors - shortNames: - - otelcol - - otelcols - singular: opentelemetrycollector - scope: Namespaced - versions: - - additionalPrinterColumns: - - description: Deployment Mode - jsonPath: .spec.mode - name: Mode - type: string - - description: OpenTelemetry Version - jsonPath: .status.version - name: Version - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: OpenTelemetryCollector is the Schema for the opentelemetrycollectors API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector. - properties: - args: - additionalProperties: - type: string - description: Args is the set of arguments to pass to the OpenTelemetry Collector binary - type: object - config: - description: Config is the raw JSON to be used as the collector's configuration. Refer to the OpenTelemetry Collector documentation for details. - type: string - env: - description: ENV vars to set on the OpenTelemetry Collector's Pods. These can then in certain cases be consumed in the config file for the Collector. - items: - description: EnvVar represents an environment variable present in a Container. - properties: - name: - description: Name of the environment variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment variable's value. Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its key must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.' - properties: - apiVersion: - description: Version of the schema the FieldPath is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of a secret in the pod's namespace - properties: - key: - description: The key of the secret to select from. Must be a valid secret key. - type: string - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate environment variables on the OpenTelemetry Collector's Pods. These can then in certain cases be consumed in the config file for the Collector. - items: - description: EnvFromSource represents the source of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap must be defined - type: boolean - type: object - prefix: - description: An optional identifier to prepend to each key in the ConfigMap. Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select from - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret must be defined - type: boolean - type: object - type: object - type: array - hostNetwork: - description: HostNetwork indicates if the pod should run in the host networking namespace. - type: boolean - image: - description: Image indicates the container image to use for the OpenTelemetry Collector. - type: string - imagePullPolicy: - description: ImagePullPolicy indicates the pull policy to be used for retrieving the container image (Always, Never, IfNotPresent) - type: string - maxReplicas: - description: MaxReplicas sets an upper bound to the autoscaling feature. If MaxReplicas is set autoscaling is enabled. - format: int32 - type: integer - mode: - description: Mode represents how the collector should be deployed (deployment, daemonset, statefulset or sidecar) - enum: - - daemonset - - deployment - - sidecar - - statefulset - type: string - nodeSelector: - additionalProperties: - type: string - description: NodeSelector to schedule OpenTelemetry Collector pods. This is only relevant to daemonset, statefulset, and deployment mode - type: object - podAnnotations: - additionalProperties: - type: string - description: PodAnnotations is the set of annotations that will be attached to Collector and Target Allocator pods. - type: object - podSecurityContext: - description: PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext. - properties: - fsGroup: - description: "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod: \n 1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw---- \n If unset, the Kubelet will not modify the ownership and permissions of any volume. Note that this field cannot be set when spec.os.name is windows." - format: int64 - type: integer - fsGroupChangePolicy: - description: 'fsGroupChangePolicy defines behavior of changing ownership and permission of the volume before being exposed inside Pod. This field will only apply to volume types which support fsGroup based ownership(and permissions). It will have no effect on ephemeral volume types such as: secret, configmaps and emptydir. Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. Note that this field cannot be set when spec.os.name is windows.' - type: string - runAsGroup: - description: The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container. Note that this field cannot be set when spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by the containers in this pod. Note that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile will be applied. Valid options are: \n Localhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied." - type: string - required: - - type - type: object - supplementalGroups: - description: A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container. Note that this field cannot be set when spec.os.name is windows. - items: - format: int64 - type: integer - type: array - sysctls: - description: Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported sysctls (by the container runtime) might fail to launch. Note that this field cannot be set when spec.os.name is windows. - items: - description: Sysctl defines a kernel parameter to be set - properties: - name: - description: Name of a property to set - type: string - value: - description: Value of a property to set - type: string - required: - - name - - value - type: object - type: array - windowsOptions: - description: The Windows specific settings applied to all containers. If unspecified, the options within a container's SecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should be run as a 'Host Process' container. This field is alpha-level and will only be honored by components that enable the WindowsHostProcessContainers feature flag. Setting this field without the feature flag will result in errors when validating the Pod. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - type: string - type: object - type: object - ports: - description: Ports allows a set of ports to be exposed by the underlying v1.Service. By default, the operator will attempt to infer the required ports by parsing the .Spec.Config property but this property can be used to open aditional ports that can't be inferred by the operator, like for custom receivers. - items: - description: ServicePort contains information on service's port. - properties: - appProtocol: - description: The application protocol for this port. This field follows standard Kubernetes label syntax. Un-prefixed names are reserved for IANA standard service names (as per RFC-6335 and http://www.iana.org/assignments/service-names). Non-standard protocols should use prefixed names such as mycompany.com/my-custom-protocol. - type: string - name: - description: The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service. - type: string - nodePort: - description: 'The port on each node on which this service is exposed when type is NodePort or LoadBalancer. Usually assigned by the system. If a value is specified, in-range, and not in use it will be used, otherwise the operation will fail. If not specified, a port will be allocated if this Service requires one. If this field is specified when creating a Service which does not need it, creation will fail. This field will be wiped when updating a Service to no longer need it (e.g. changing type from NodePort to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport' - format: int32 - type: integer - port: - description: The port that will be exposed by this service. - format: int32 - type: integer - protocol: - default: TCP - description: The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". Default is TCP. - type: string - targetPort: - anyOf: - - type: integer - - type: string - description: 'Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod''s container ports. If this is not specified, the value of the ''port'' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the ''port'' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service' - x-kubernetes-int-or-string: true - required: - - port - type: object - type: array - x-kubernetes-list-type: atomic - replicas: - description: Replicas is the number of pod instances for the underlying OpenTelemetry Collector - format: int32 - type: integer - resources: - description: Resources to set on the OpenTelemetry Collector pods. - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: SecurityContext will be set as the container security context. - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation controls whether a process can gain more privileges than its parent process. This bool directly controls if the no_new_privs flag will be set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged 2) has CAP_SYS_ADMIN Note that this field cannot be set when spec.os.name is windows.' - type: boolean - capabilities: - description: The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime. Note that this field cannot be set when spec.os.name is windows. - properties: - add: - description: Added capabilities - items: - description: Capability represent POSIX capabilities type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent POSIX capabilities type - type: string - type: array - type: object - privileged: - description: Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false. Note that this field cannot be set when spec.os.name is windows. - type: boolean - procMount: - description: procMount denotes the type of proc mount to use for the containers. The default is DefaultProcMount which uses the container runtime defaults for readonly paths and masked paths. This requires the ProcMountType feature flag to be enabled. Note that this field cannot be set when spec.os.name is windows. - type: string - readOnlyRootFilesystem: - description: Whether this container has a read-only root filesystem. Default is false. Note that this field cannot be set when spec.os.name is windows. - type: boolean - runAsGroup: - description: The GID to run the entrypoint of the container process. Uses runtime default if unset. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is windows. - properties: - level: - description: Level is SELinux level label that applies to the container. - type: string - role: - description: Role is a SELinux role label that applies to the container. - type: string - type: - description: Type is a SELinux type label that applies to the container. - type: string - user: - description: User is a SELinux user label that applies to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to use by this container. If seccomp options are provided at both the pod & container level, the container options override the pod options. Note that this field cannot be set when spec.os.name is windows. - properties: - localhostProfile: - description: localhostProfile indicates a profile defined in a file on the node should be used. The profile must be preconfigured on the node to work. Must be a descending path, relative to the kubelet's configured seccomp profile location. Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which kind of seccomp profile will be applied. Valid options are: \n Localhost - a profile defined in a file on the node should be used. RuntimeDefault - the container runtime default profile should be used. Unconfined - no profile should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings applied to all containers. If unspecified, the options from the PodSecurityContext will be used. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. Note that this field cannot be set when spec.os.name is linux. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName is the name of the GMSA credential spec to use. - type: string - hostProcess: - description: HostProcess determines if a container should be run as a 'Host Process' container. This field is alpha-level and will only be honored by components that enable the WindowsHostProcessContainers feature flag. Setting this field without the feature flag will result in errors when validating the Pod. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true. - type: boolean - runAsUserName: - description: The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. - type: string - type: object - type: object - serviceAccount: - description: ServiceAccount indicates the name of an existing service account to use with this instance. - type: string - targetAllocator: - description: TargetAllocator indicates a value which determines whether to spawn a target allocation resource or not. - properties: - enabled: - description: Enabled indicates whether to use a target allocation mechanism for Prometheus targets or not. - type: boolean - image: - description: Image indicates the container image to use for the OpenTelemetry TargetAllocator. - type: string - type: object - tolerations: - description: Toleration to schedule OpenTelemetry Collector pods. This is only relevant to daemonset, statefulset, and deployment mode - items: - description: The pod this Toleration is attached to tolerates any taint that matches the triple using the matching operator . - properties: - effect: - description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys. - type: string - operator: - description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string. - type: string - type: object - type: array - upgradeStrategy: - description: UpgradeStrategy represents how the operator will handle upgrades to the CR when a newer version of the operator is deployed - enum: - - automatic - - none - type: string - volumeClaimTemplates: - description: VolumeClaimTemplates will provide stable storage using PersistentVolumes. Only available when the mode=statefulset. - items: - description: PersistentVolumeClaim is a user's request for and claim to a persistent volume - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - description: 'Spec defines the desired characteristics of a volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - accessModes: - description: 'AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'This field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. If the AnyVolumeDataSource feature gate is enabled, this field will always have the same contents as the DataSourceRef field.' - properties: - apiGroup: - description: APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - dataSourceRef: - description: 'Specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any local object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef allows any non-core object, as well as PersistentVolumeClaim objects. * While DataSource ignores disallowed values (dropping them), DataSourceRef preserves all values, and generates an error if a disallowed value is specified. (Alpha) Using this field requires the AnyVolumeDataSource feature gate to be enabled.' - properties: - apiGroup: - description: APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - resources: - description: 'Resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: A label query over volumes to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - storageClassName: - description: 'Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. - type: string - volumeName: - description: VolumeName is the binding reference to the PersistentVolume backing this claim. - type: string - type: object - status: - description: 'Status represents the current information/status of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - accessModes: - description: 'AccessModes contains the actual access modes the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - allocatedResources: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: The storage resource within AllocatedResources tracks the capacity allocated to a PVC. It may be larger than the actual capacity when a volume expansion operation is requested. For storage quota, the larger value from allocatedResources and PVC.spec.resources is used. If allocatedResources is not set, PVC.spec.resources alone is used for quota calculation. If a volume expansion capacity request is lowered, allocatedResources is only lowered if there are no expansion operations in progress and if the actual volume capacity is equal or lower than the requested capacity. This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature. - type: object - capacity: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: Represents the actual resources of the underlying volume. - type: object - conditions: - description: Current Condition of persistent volume claim. If underlying persistent volume is being resized then the Condition will be set to 'ResizeStarted'. - items: - description: PersistentVolumeClaimCondition contails details about state of pvc - properties: - lastProbeTime: - description: Last time we probed the condition. - format: date-time - type: string - lastTransitionTime: - description: Last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Human-readable message indicating details about last transition. - type: string - reason: - description: Unique, this should be a short, machine understandable string that gives the reason for condition's last transition. If it reports "ResizeStarted" that means the underlying persistent volume is being resized. - type: string - status: - type: string - type: - description: PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type - type: string - required: - - status - - type - type: object - type: array - phase: - description: Phase represents the current phase of PersistentVolumeClaim. - type: string - resizeStatus: - description: ResizeStatus stores status of resize operation. ResizeStatus is not set by default but when expansion is complete resizeStatus is set to empty string by resize controller or kubelet. This is an alpha field and requires enabling RecoverVolumeExpansionFailure feature. - type: string - type: object - type: object - type: array - x-kubernetes-list-type: atomic - volumeMounts: - description: VolumeMounts represents the mount points to use in the underlying collector deployment(s) - items: - description: VolumeMount describes a mounting of a Volume within a container. - properties: - mountPath: - description: Path within the container at which the volume should be mounted. Must not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines how mounts are propagated from the host to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name of a Volume. - type: string - readOnly: - description: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root). - type: string - subPathExpr: - description: Expanded path within the volume from which the container's volume should be mounted. Behaves similarly to SubPath but environment variable references $(VAR_NAME) are expanded using the container's environment. Defaults to "" (volume's root). SubPathExpr and SubPath are mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - x-kubernetes-list-type: atomic - volumes: - description: Volumes represents which volumes to use in the underlying collector deployment(s). - items: - description: Volume represents a named volume in a pod that may be accessed by any container in the pod. - properties: - awsElasticBlockStore: - description: 'AWSElasticBlockStore represents an AWS Disk resource that is attached to a kubelet''s host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - properties: - fsType: - description: 'Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore TODO: how do we prevent errors in the filesystem from compromising the machine' - type: string - partition: - description: 'The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1". Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty).' - format: int32 - type: integer - readOnly: - description: 'Specify "true" to force and set the ReadOnly property in VolumeMounts to "true". If omitted, the default is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: boolean - volumeID: - description: 'Unique ID of the persistent disk resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' - type: string - required: - - volumeID - type: object - azureDisk: - description: AzureDisk represents an Azure Data Disk mount on the host and bind mount to the pod. - properties: - cachingMode: - description: 'Host Caching mode: None, Read Only, Read Write.' - type: string - diskName: - description: The Name of the data disk in the blob storage - type: string - diskURI: - description: The URI the data disk in the blob storage - type: string - fsType: - description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - kind: - description: 'Expected values Shared: multiple blob disks per storage account Dedicated: single blob disk per storage account Managed: azure managed data disk (only in managed availability set). defaults to shared' - type: string - readOnly: - description: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. - type: boolean - required: - - diskName - - diskURI - type: object - azureFile: - description: AzureFile represents an Azure File Service mount on the host and bind mount to the pod. - properties: - readOnly: - description: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. - type: boolean - secretName: - description: the name of secret that contains Azure Storage Account Name and Key - type: string - shareName: - description: Share Name - type: string - required: - - secretName - - shareName - type: object - cephfs: - description: CephFS represents a Ceph FS mount on the host that shares a pod's lifetime - properties: - monitors: - description: 'Required: Monitors is a collection of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - items: - type: string - type: array - path: - description: 'Optional: Used as the mounted root, rather than the full Ceph tree, default is /' - type: string - readOnly: - description: 'Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: boolean - secretFile: - description: 'Optional: SecretFile is the path to key ring for User, default is /etc/ceph/user.secret More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - secretRef: - description: 'Optional: SecretRef is reference to the authentication secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - user: - description: 'Optional: User is the rados user name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' - type: string - required: - - monitors - type: object - cinder: - description: 'Cinder represents a cinder volume attached and mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - properties: - fsType: - description: 'Filesystem type to mount. Must be a filesystem type supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - readOnly: - description: 'Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: boolean - secretRef: - description: 'Optional: points to a secret object containing parameters used to connect to OpenStack.' - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - volumeID: - description: 'volume id used to identify the volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' - type: string - required: - - volumeID - type: object - configMap: - description: ConfigMap represents a configMap that should populate this volume - properties: - defaultMode: - description: 'Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' - format: int32 - type: integer - items: - description: If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' - format: int32 - type: integer - path: - description: The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its keys must be defined - type: boolean - type: object - csi: - description: CSI (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature). - properties: - driver: - description: Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster. - type: string - fsType: - description: Filesystem type to mount. Ex. "ext4", "xfs", "ntfs". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply. - type: string - nodePublishSecretRef: - description: NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - readOnly: - description: Specifies a read-only configuration for the volume. Defaults to false (read/write). - type: boolean - volumeAttributes: - additionalProperties: - type: string - description: VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values. - type: object - required: - - driver - type: object - downwardAPI: - description: DownwardAPI represents downward API about the pod that should populate this volume - properties: - defaultMode: - description: 'Optional: mode bits to use on created files by default. Must be a Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' - format: int32 - type: integer - items: - description: Items is a list of downward API volume file - items: - description: DownwardAPIVolumeFile represents information to create the file containing the pod field - properties: - fieldRef: - description: 'Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.' - properties: - apiVersion: - description: Version of the schema the FieldPath is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' - format: int32 - type: integer - path: - description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - emptyDir: - description: 'EmptyDir represents a temporary directory that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - properties: - medium: - description: 'What type of storage medium should back this directory. The default is "" which means to use the node''s default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' - type: string - sizeLimit: - anyOf: - - type: integer - - type: string - description: 'Total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - type: object - ephemeral: - description: "Ephemeral represents a volume that is handled by a cluster storage driver. The volume's lifecycle is tied to the pod that defines it - it will be created before the pod starts, and deleted when the pod is removed. \n Use this if: a) the volume is only needed while the pod runs, b) features of normal volumes like restoring from snapshot or capacity tracking are needed, c) the storage driver is specified through a storage class, and d) the storage driver supports dynamic volume provisioning through a PersistentVolumeClaim (see EphemeralVolumeSource for more information on the connection between this volume type and PersistentVolumeClaim). \n Use PersistentVolumeClaim or one of the vendor-specific APIs for volumes that persist for longer than the lifecycle of an individual pod. \n Use CSI for light-weight local ephemeral volumes if the CSI driver is meant to be used that way - see the documentation of the driver for more information. \n A pod can use both types of ephemeral volumes and persistent volumes at the same time." - properties: - volumeClaimTemplate: - description: "Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e. the PVC will be deleted together with the pod. The name of the PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. Pod validation will reject the pod if the concatenated name is not valid for a PVC (for example, too long). \n An existing PVC with that name that is not owned by the pod will *not* be used for the pod to avoid using an unrelated volume by mistake. Starting the pod is then blocked until the unrelated PVC is removed. If such a pre-created PVC is meant to be used by the pod, the PVC has to updated with an owner reference to the pod once the pod exists. Normally this should not be necessary, but it may be useful when manually reconstructing a broken cluster. \n This field is read-only and no changes will be made by Kubernetes to the PVC after it has been created. \n Required, must not be nil." - properties: - metadata: - description: May contain labels and annotations that will be copied into the PVC when creating it. No other fields are allowed and will be rejected during validation. - properties: - annotations: - additionalProperties: - type: string - type: object - finalizers: - items: - type: string - type: array - labels: - additionalProperties: - type: string - type: object - name: - type: string - namespace: - type: string - type: object - spec: - description: The specification for the PersistentVolumeClaim. The entire content is copied unchanged into the PVC that gets created from this template. The same fields as in a PersistentVolumeClaim are also valid here. - properties: - accessModes: - description: 'AccessModes contains the desired access modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' - items: - type: string - type: array - dataSource: - description: 'This field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. If the AnyVolumeDataSource feature gate is enabled, this field will always have the same contents as the DataSourceRef field.' - properties: - apiGroup: - description: APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - dataSourceRef: - description: 'Specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any local object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef allows any non-core object, as well as PersistentVolumeClaim objects. * While DataSource ignores disallowed values (dropping them), DataSourceRef preserves all values, and generates an error if a disallowed value is specified. (Alpha) Using this field requires the AnyVolumeDataSource feature gate to be enabled.' - properties: - apiGroup: - description: APIGroup is the group for the resource being referenced. If APIGroup is not specified, the specified Kind must be in the core API group. For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - resources: - description: 'Resources represents the minimum resources the volume should have. If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements that are lower than previous value but must still be higher than capacity recorded in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - selector: - description: A label query over volumes to consider for binding. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - storageClassName: - description: 'Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' - type: string - volumeMode: - description: volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec. - type: string - volumeName: - description: VolumeName is the binding reference to the PersistentVolume backing this claim. - type: string - type: object - required: - - spec - type: object - type: object - fc: - description: FC represents a Fibre Channel resource that is attached to a kubelet's host machine and then exposed to the pod. - properties: - fsType: - description: 'Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. TODO: how do we prevent errors in the filesystem from compromising the machine' - type: string - lun: - description: 'Optional: FC target lun number' - format: int32 - type: integer - readOnly: - description: 'Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.' - type: boolean - targetWWNs: - description: 'Optional: FC target worldwide names (WWNs)' - items: - type: string - type: array - wwids: - description: 'Optional: FC volume world wide identifiers (wwids) Either wwids or combination of targetWWNs and lun must be set, but not both simultaneously.' - items: - type: string - type: array - type: object - flexVolume: - description: FlexVolume represents a generic volume resource that is provisioned/attached using an exec based plugin. - properties: - driver: - description: Driver is the name of the driver to use for this volume. - type: string - fsType: - description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". The default filesystem depends on FlexVolume script. - type: string - options: - additionalProperties: - type: string - description: 'Optional: Extra command options if any.' - type: object - readOnly: - description: 'Optional: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts.' - type: boolean - secretRef: - description: 'Optional: SecretRef is reference to the secret object containing sensitive information to pass to the plugin scripts. This may be empty if no secret object is specified. If the secret object contains more than one secret, all secrets are passed to the plugin scripts.' - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - required: - - driver - type: object - flocker: - description: Flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running - properties: - datasetName: - description: Name of the dataset stored as metadata -> name on the dataset for Flocker should be considered as deprecated - type: string - datasetUUID: - description: UUID of the dataset. This is unique identifier of a Flocker dataset - type: string - type: object - gcePersistentDisk: - description: 'GCEPersistentDisk represents a GCE Disk resource that is attached to a kubelet''s host machine and then exposed to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - properties: - fsType: - description: 'Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk TODO: how do we prevent errors in the filesystem from compromising the machine' - type: string - partition: - description: 'The partition in the volume that you want to mount. If omitted, the default is to mount by volume name. Examples: For volume /dev/sda1, you specify the partition as "1". Similarly, the volume partition for /dev/sda is "0" (or you can leave the property empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - format: int32 - type: integer - pdName: - description: 'Unique name of the PD resource in GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: string - readOnly: - description: 'ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' - type: boolean - required: - - pdName - type: object - gitRepo: - description: 'GitRepo represents a git repository at a particular revision. DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir into the Pod''s container.' - properties: - directory: - description: Target directory name. Must not contain or start with '..'. If '.' is supplied, the volume directory will be the git repository. Otherwise, if specified, the volume will contain the git repository in the subdirectory with the given name. - type: string - repository: - description: Repository URL - type: string - revision: - description: Commit hash for the specified revision. - type: string - required: - - repository - type: object - glusterfs: - description: 'Glusterfs represents a Glusterfs mount on the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' - properties: - endpoints: - description: 'EndpointsName is the endpoint name that details Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - path: - description: 'Path is the Glusterfs volume path. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: string - readOnly: - description: 'ReadOnly here will force the Glusterfs volume to be mounted with read-only permissions. Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' - type: boolean - required: - - endpoints - - path - type: object - hostPath: - description: 'HostPath represents a pre-existing file or directory on the host machine that is directly exposed to the container. This is generally used for system agents or other privileged things that are allowed to see the host machine. Most containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath --- TODO(jonesdl) We need to restrict who can use host directory mounts and who can/can not mount host directories as read/write.' - properties: - path: - description: 'Path of the directory on the host. If the path is a symlink, it will follow the link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - type: - description: 'Type for HostPath Volume Defaults to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' - type: string - required: - - path - type: object - iscsi: - description: 'ISCSI represents an ISCSI Disk resource that is attached to a kubelet''s host machine and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' - properties: - chapAuthDiscovery: - description: whether support iSCSI Discovery CHAP authentication - type: boolean - chapAuthSession: - description: whether support iSCSI Session CHAP authentication - type: boolean - fsType: - description: 'Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi TODO: how do we prevent errors in the filesystem from compromising the machine' - type: string - initiatorName: - description: Custom iSCSI Initiator Name. If initiatorName is specified with iscsiInterface simultaneously, new iSCSI interface : will be created for the connection. - type: string - iqn: - description: Target iSCSI Qualified Name. - type: string - iscsiInterface: - description: iSCSI Interface Name that uses an iSCSI transport. Defaults to 'default' (tcp). - type: string - lun: - description: iSCSI Target Lun number. - format: int32 - type: integer - portals: - description: iSCSI Target Portal List. The portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260). - items: - type: string - type: array - readOnly: - description: ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. - type: boolean - secretRef: - description: CHAP Secret for iSCSI target and initiator authentication - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - targetPortal: - description: iSCSI Target Portal. The Portal is either an IP or ip_addr:port if the port is other than default (typically TCP ports 860 and 3260). - type: string - required: - - iqn - - lun - - targetPortal - type: object - name: - description: 'Volume''s name. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' - type: string - nfs: - description: 'NFS represents an NFS mount on the host that shares a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - properties: - path: - description: 'Path that is exported by the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - readOnly: - description: 'ReadOnly here will force the NFS export to be mounted with read-only permissions. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: boolean - server: - description: 'Server is the hostname or IP address of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' - type: string - required: - - path - - server - type: object - persistentVolumeClaim: - description: 'PersistentVolumeClaimVolumeSource represents a reference to a PersistentVolumeClaim in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - properties: - claimName: - description: 'ClaimName is the name of a PersistentVolumeClaim in the same namespace as the pod using this volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' - type: string - readOnly: - description: Will force the ReadOnly setting in VolumeMounts. Default false. - type: boolean - required: - - claimName - type: object - photonPersistentDisk: - description: PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine - properties: - fsType: - description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - pdID: - description: ID that identifies Photon Controller persistent disk - type: string - required: - - pdID - type: object - portworxVolume: - description: PortworxVolume represents a portworx volume attached and mounted on kubelets host machine - properties: - fsType: - description: FSType represents the filesystem type to mount Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. - type: boolean - volumeID: - description: VolumeID uniquely identifies a Portworx volume - type: string - required: - - volumeID - type: object - projected: - description: Items for all in one resources secrets, configmaps, and downward API - properties: - defaultMode: - description: Mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set. - format: int32 - type: integer - sources: - description: list of volume projections - items: - description: Projection that may be projected along with other supported volume types - properties: - configMap: - description: information about the configMap data to project - properties: - items: - description: If unspecified, each key-value pair in the Data field of the referenced ConfigMap will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the ConfigMap, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' - format: int32 - type: integer - path: - description: The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the ConfigMap or its keys must be defined - type: boolean - type: object - downwardAPI: - description: information about the downwardAPI data to project - properties: - items: - description: Items is a list of DownwardAPIVolume file - items: - description: DownwardAPIVolumeFile represents information to create the file containing the pod field - properties: - fieldRef: - description: 'Required: Selects a field of the pod: only annotations, labels, name and namespace are supported.' - properties: - apiVersion: - description: Version of the schema the FieldPath is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the specified API version. - type: string - required: - - fieldPath - type: object - mode: - description: 'Optional: mode bits used to set permissions on this file, must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' - format: int32 - type: integer - path: - description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' - type: string - resourceFieldRef: - description: 'Selects a resource of the container: only resources limits and requests (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.' - properties: - containerName: - description: 'Container name: required for volumes, optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - required: - - path - type: object - type: array - type: object - secret: - description: information about the secret data to project - properties: - items: - description: If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' - format: int32 - type: integer - path: - description: The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the Secret or its key must be defined - type: boolean - type: object - serviceAccountToken: - description: information about the serviceAccountToken data to project - properties: - audience: - description: Audience is the intended audience of the token. A recipient of a token must identify itself with an identifier specified in the audience of the token, and otherwise should reject the token. The audience defaults to the identifier of the apiserver. - type: string - expirationSeconds: - description: ExpirationSeconds is the requested duration of validity of the service account token. As the token approaches expiration, the kubelet volume plugin will proactively rotate the service account token. The kubelet will start trying to rotate the token if the token is older than 80 percent of its time to live or if the token is older than 24 hours.Defaults to 1 hour and must be at least 10 minutes. - format: int64 - type: integer - path: - description: Path is the path relative to the mount point of the file to project the token into. - type: string - required: - - path - type: object - type: object - type: array - type: object - quobyte: - description: Quobyte represents a Quobyte mount on the host that shares a pod's lifetime - properties: - group: - description: Group to map volume access to Default is no group - type: string - readOnly: - description: ReadOnly here will force the Quobyte volume to be mounted with read-only permissions. Defaults to false. - type: boolean - registry: - description: Registry represents a single or multiple Quobyte Registry services specified as a string as host:port pair (multiple entries are separated with commas) which acts as the central registry for volumes - type: string - tenant: - description: Tenant owning the given Quobyte volume in the Backend Used with dynamically provisioned Quobyte volumes, value is set by the plugin - type: string - user: - description: User to map volume access to Defaults to serivceaccount user - type: string - volume: - description: Volume is a string that references an already created Quobyte volume by name. - type: string - required: - - registry - - volume - type: object - rbd: - description: 'RBD represents a Rados Block Device mount on the host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' - properties: - fsType: - description: 'Filesystem type of the volume that you want to mount. Tip: Ensure that the filesystem type is supported by the host operating system. Examples: "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd TODO: how do we prevent errors in the filesystem from compromising the machine' - type: string - image: - description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - keyring: - description: 'Keyring is the path to key ring for RBDUser. Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - monitors: - description: 'A collection of Ceph monitors. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - items: - type: string - type: array - pool: - description: 'The rados pool name. Default is rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - readOnly: - description: 'ReadOnly here will force the ReadOnly setting in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: boolean - secretRef: - description: 'SecretRef is name of the authentication secret for RBDUser. If provided overrides keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - user: - description: 'The rados user name. Default is admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' - type: string - required: - - image - - monitors - type: object - scaleIO: - description: ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes. - properties: - fsType: - description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Default is "xfs". - type: string - gateway: - description: The host address of the ScaleIO API Gateway. - type: string - protectionDomain: - description: The name of the ScaleIO Protection Domain for the configured storage. - type: string - readOnly: - description: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: SecretRef references to the secret for ScaleIO user and other sensitive information. If this is not provided, Login operation will fail. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - sslEnabled: - description: Flag to enable/disable SSL communication with Gateway, default false - type: boolean - storageMode: - description: Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned. Default is ThinProvisioned. - type: string - storagePool: - description: The ScaleIO Storage Pool associated with the protection domain. - type: string - system: - description: The name of the storage system as configured in ScaleIO. - type: string - volumeName: - description: The name of a volume already created in the ScaleIO system that is associated with this volume source. - type: string - required: - - gateway - - secretRef - - system - type: object - secret: - description: 'Secret represents a secret that should populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - properties: - defaultMode: - description: 'Optional: mode bits used to set permissions on created files by default. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. Defaults to 0644. Directories within the path are not affected by this setting. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' - format: int32 - type: integer - items: - description: If unspecified, each key-value pair in the Data field of the referenced Secret will be projected into the volume as a file whose name is the key and content is the value. If specified, the listed keys will be projected into the specified paths, and unlisted keys will not be present. If a key is specified which is not present in the Secret, the volume setup will error unless it is marked optional. Paths must be relative and may not contain the '..' path or start with '..'. - items: - description: Maps a string key to a path within a volume. - properties: - key: - description: The key to project. - type: string - mode: - description: 'Optional: mode bits used to set permissions on this file. Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. If not specified, the volume defaultMode will be used. This might be in conflict with other options that affect the file mode, like fsGroup, and the result can be other mode bits set.' - format: int32 - type: integer - path: - description: The relative path of the file to map the key to. May not be an absolute path. May not contain the path element '..'. May not start with the string '..'. - type: string - required: - - key - - path - type: object - type: array - optional: - description: Specify whether the Secret or its keys must be defined - type: boolean - secretName: - description: 'Name of the secret in the pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' - type: string - type: object - storageos: - description: StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes. - properties: - fsType: - description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - readOnly: - description: Defaults to false (read/write). ReadOnly here will force the ReadOnly setting in VolumeMounts. - type: boolean - secretRef: - description: SecretRef specifies the secret to use for obtaining the StorageOS API credentials. If not specified, default values will be attempted. - properties: - name: - description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names TODO: Add other useful fields. apiVersion, kind, uid?' - type: string - type: object - volumeName: - description: VolumeName is the human-readable name of the StorageOS volume. Volume names are only unique within a namespace. - type: string - volumeNamespace: - description: VolumeNamespace specifies the scope of the volume within StorageOS. If no namespace is specified then the Pod's namespace will be used. This allows the Kubernetes name scoping to be mirrored within StorageOS for tighter integration. Set VolumeName to any name to override the default behaviour. Set to "default" if you are not using namespaces within StorageOS. Namespaces that do not pre-exist within StorageOS will be created. - type: string - type: object - vsphereVolume: - description: VsphereVolume represents a vSphere volume attached and mounted on kubelets host machine - properties: - fsType: - description: Filesystem type to mount. Must be a filesystem type supported by the host operating system. Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. - type: string - storagePolicyID: - description: Storage Policy Based Management (SPBM) profile ID associated with the StoragePolicyName. - type: string - storagePolicyName: - description: Storage Policy Based Management (SPBM) profile name. - type: string - volumePath: - description: Path that identifies vSphere volume vmdk - type: string - required: - - volumePath - type: object - required: - - name - type: object - type: array - x-kubernetes-list-type: atomic - type: object - status: - description: OpenTelemetryCollectorStatus defines the observed state of OpenTelemetryCollector. - properties: - messages: - description: 'Messages about actions performed by the operator on this resource. Deprecated: use Kubernetes events instead.' - items: - type: string - type: array - x-kubernetes-list-type: atomic - replicas: - description: 'Replicas is currently not being set and might be removed in the next version. Deprecated: use "OpenTelemetryCollector.Status.Scale.Replicas" instead.' - format: int32 - type: integer - scale: - description: Scale is the OpenTelemetryCollector's scale subresource status. - properties: - replicas: - description: The total number non-terminated pods targeted by this OpenTelemetryCollector's deployment or statefulSet. - format: int32 - type: integer - selector: - description: The selector used to match the OpenTelemetryCollector's deployment or statefulSet pods. - type: string - type: object - version: - description: Version of the managed OpenTelemetry Collector (operand) - type: string - type: object - type: object - served: true - storage: true - subresources: - scale: - labelSelectorPath: .status.scale.selector - specReplicasPath: .spec.replicas - statusReplicasPath: .status.scale.replicas - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-controller-manager - namespace: opentelemetry-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-leader-election-role - namespace: opentelemetry-operator-system -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-manager-role -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - namespaces - verbs: - - list - - watch -- apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - "" - resources: - - services - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - daemonsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - deployments - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - apps - resources: - - replicasets - verbs: - - get - - list - - watch -- apiGroups: - - apps - resources: - - statefulsets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - autoscaling - resources: - - horizontalpodautoscalers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - get - - list - - update -- apiGroups: - - opentelemetry.io - resources: - - instrumentations - verbs: - - get - - list - - patch - - update - - watch -- apiGroups: - - opentelemetry.io - resources: - - opentelemetrycollectors - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - opentelemetry.io - resources: - - opentelemetrycollectors/finalizers - verbs: - - get - - patch - - update -- apiGroups: - - opentelemetry.io - resources: - - opentelemetrycollectors/status - verbs: - - get - - patch - - update ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-metrics-reader -rules: -- nonResourceURLs: - - /metrics - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-leader-election-rolebinding - namespace: opentelemetry-operator-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: opentelemetry-operator-leader-election-role -subjects: -- kind: ServiceAccount - name: opentelemetry-operator-controller-manager - namespace: opentelemetry-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: opentelemetry-operator-manager-role -subjects: -- kind: ServiceAccount - name: opentelemetry-operator-controller-manager - namespace: opentelemetry-operator-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: opentelemetry-operator-proxy-role -subjects: -- kind: ServiceAccount - name: opentelemetry-operator-controller-manager - namespace: opentelemetry-operator-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - control-plane: controller-manager - name: opentelemetry-operator-controller-manager-metrics-service - namespace: opentelemetry-operator-system -spec: - ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https - selector: - app.kubernetes.io/name: opentelemetry-operator - control-plane: controller-manager ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-webhook-service - namespace: opentelemetry-operator-system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - app.kubernetes.io/name: opentelemetry-operator - control-plane: controller-manager ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - control-plane: controller-manager - name: opentelemetry-operator-controller-manager - namespace: opentelemetry-operator-system -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: opentelemetry-operator - control-plane: controller-manager - template: - metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - control-plane: controller-manager - spec: - containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.11.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - args: - - --metrics-addr=127.0.0.1:8080 - - --enable-leader-election - image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.49.0 - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 200m - memory: 256Mi - requests: - cpu: 100m - memory: 64Mi - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - serviceAccountName: opentelemetry-operator-controller-manager - terminationGracePeriodSeconds: 10 - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: opentelemetry-operator-controller-manager-service-cert ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-serving-cert - namespace: opentelemetry-operator-system -spec: - dnsNames: - - opentelemetry-operator-webhook-service.opentelemetry-operator-system.svc - - opentelemetry-operator-webhook-service.opentelemetry-operator-system.svc.cluster.local - issuerRef: - kind: Issuer - name: opentelemetry-operator-selfsigned-issuer - secretName: opentelemetry-operator-controller-manager-service-cert - subject: - organizationalUnits: - - opentelemetry-operator ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-selfsigned-issuer - namespace: opentelemetry-operator-system -spec: - selfSigned: {} ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: opentelemetry-operator-system/opentelemetry-operator-serving-cert - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-mutating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: opentelemetry-operator-webhook-service - namespace: opentelemetry-operator-system - path: /mutate-opentelemetry-io-v1alpha1-instrumentation - failurePolicy: Fail - name: minstrumentation.kb.io - rules: - - apiGroups: - - opentelemetry.io - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - instrumentations - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: opentelemetry-operator-webhook-service - namespace: opentelemetry-operator-system - path: /mutate-opentelemetry-io-v1alpha1-opentelemetrycollector - failurePolicy: Fail - name: mopentelemetrycollector.kb.io - rules: - - apiGroups: - - opentelemetry.io - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - opentelemetrycollectors - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: opentelemetry-operator-webhook-service - namespace: opentelemetry-operator-system - path: /mutate-v1-pod - failurePolicy: Ignore - name: mpod.kb.io - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - - UPDATE - resources: - - pods - sideEffects: None ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - annotations: - cert-manager.io/inject-ca-from: opentelemetry-operator-system/opentelemetry-operator-serving-cert - labels: - app.kubernetes.io/name: opentelemetry-operator - name: opentelemetry-operator-validating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: opentelemetry-operator-webhook-service - namespace: opentelemetry-operator-system - path: /validate-opentelemetry-io-v1alpha1-instrumentation - failurePolicy: Fail - name: vinstrumentationcreateupdate.kb.io - rules: - - apiGroups: - - opentelemetry.io - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - instrumentations - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: opentelemetry-operator-webhook-service - namespace: opentelemetry-operator-system - path: /validate-opentelemetry-io-v1alpha1-instrumentation - failurePolicy: Ignore - name: vinstrumentationdelete.kb.io - rules: - - apiGroups: - - opentelemetry.io - apiVersions: - - v1alpha1 - operations: - - DELETE - resources: - - instrumentations - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: opentelemetry-operator-webhook-service - namespace: opentelemetry-operator-system - path: /validate-opentelemetry-io-v1alpha1-opentelemetrycollector - failurePolicy: Fail - name: vopentelemetrycollectorcreateupdate.kb.io - rules: - - apiGroups: - - opentelemetry.io - apiVersions: - - v1alpha1 - operations: - - CREATE - - UPDATE - resources: - - opentelemetrycollectors - sideEffects: None -- admissionReviewVersions: - - v1 - clientConfig: - service: - name: opentelemetry-operator-webhook-service - namespace: opentelemetry-operator-system - path: /validate-opentelemetry-io-v1alpha1-opentelemetrycollector - failurePolicy: Ignore - name: vopentelemetrycollectordelete.kb.io - rules: - - apiGroups: - - opentelemetry.io - apiVersions: - - v1alpha1 - operations: - - DELETE - resources: - - opentelemetrycollectors - sideEffects: None diff --git a/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.86.0.yaml b/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.86.0.yaml new file mode 100644 index 0000000000..82463fa4b9 --- /dev/null +++ b/tests/e2e-upgrade/upgrade-test/opentelemetry-operator-v0.86.0.yaml @@ -0,0 +1,8558 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager + name: opentelemetry-operator-system +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.12.0 + labels: + app.kubernetes.io/name: opentelemetry-operator + name: instrumentations.opentelemetry.io +spec: + group: opentelemetry.io + names: + kind: Instrumentation + listKind: InstrumentationList + plural: instrumentations + shortNames: + - otelinst + - otelinsts + singular: instrumentation + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .spec.exporter.endpoint + name: Endpoint + type: string + - jsonPath: .spec.sampler.type + name: Sampler + type: string + - jsonPath: .spec.sampler.argument + name: Sampler Arg + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Instrumentation is the spec for OpenTelemetry instrumentation. + properties: + apiVersion: + description: APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. + type: string + kind: + description: Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. + type: string + metadata: + type: object + spec: + description: InstrumentationSpec defines the desired state of OpenTelemetry + SDK and instrumentation. + properties: + apacheHttpd: + description: ApacheHttpd defines configuration for Apache HTTPD auto-instrumentation. + properties: + attrs: + description: 'Attrs defines Apache HTTPD agent specific attributes. + The precedence is: `agent default attributes` > `instrument + spec attributes` . Attributes are documented at https://github.' + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + configPath: + description: Location of Apache HTTPD server configuration. Needed + only if different from default "/usr/local/apache2/conf" + type: string + env: + description: Env defines Apache HTTPD specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Apache SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + version: + description: Apache HTTPD server version. One of 2.4 or 2.2. Default + is 2.4 + type: string + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 150Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + dotnet: + description: DotNet defines configuration for DotNet auto-instrumentation. + properties: + env: + description: Env defines DotNet specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with DotNet SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 150Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + env: + description: Env defines common env vars. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + exporter: + description: Exporter defines exporter configuration. + properties: + endpoint: + description: Endpoint is address of the collector with OTLP endpoint. + type: string + type: object + go: + description: Go defines configuration for Go auto-instrumentation. + properties: + env: + description: Env defines Go specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Go SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 150Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + java: + description: Java defines configuration for java auto-instrumentation. + properties: + env: + description: Env defines java specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with javaagent auto-instrumentation + JAR. + type: string + resources: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 150Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + nginx: + description: Nginx defines configuration for Nginx auto-instrumentation. + properties: + attrs: + description: 'Attrs defines Nginx agent specific attributes. The + precedence order is: `agent default attributes` > `instrument + spec attributes` . Attributes are documented at https://github.' + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + configFile: + description: Location of Nginx configuration file. Needed only + if different from default "/etx/nginx/nginx.conf" + type: string + env: + description: Env defines Nginx specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Nginx SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 150Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + nodejs: + description: NodeJS defines configuration for nodejs auto-instrumentation. + properties: + env: + description: Env defines nodejs specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with NodeJS SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 150Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + propagators: + description: Propagators defines inter-process context propagation + configuration. Values in this list will be set in the OTEL_PROPAGATORS + env var. Enum=tracecontext;baggage;b3;b3multi;jaeger;xray;ottrace;none + items: + description: Propagator represents the propagation type. + enum: + - tracecontext + - baggage + - b3 + - b3multi + - jaeger + - xray + - ottrace + - none + type: string + type: array + python: + description: Python defines configuration for python auto-instrumentation. + properties: + env: + description: Env defines python specific env vars. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + description: Image is a container image with Python SDK and auto-instrumentation. + type: string + resourceRequirements: + description: Resources describes the compute resource requirements. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + volumeLimitSize: + anyOf: + - type: integer + - type: string + description: VolumeSizeLimit defines size limit for volume used + for auto-instrumentation. The default size is 150Mi. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + resource: + description: Resource defines the configuration for the resource attributes, + as defined by the OpenTelemetry specification. + properties: + addK8sUIDAttributes: + description: AddK8sUIDAttributes defines whether K8s UID attributes + should be collected (e.g. k8s.deployment.uid). + type: boolean + resourceAttributes: + additionalProperties: + type: string + description: 'Attributes defines attributes that are added to + the resource. For example environment: dev' + type: object + type: object + sampler: + description: Sampler defines sampling configuration. + properties: + argument: + description: Argument defines sampler argument. The value depends + on the sampler type. For instance for parentbased_traceidratio + sampler type it is a number in range [0..1] e.g. 0.25. + type: string + type: + description: Type defines sampler type. The value will be set + in the OTEL_TRACES_SAMPLER env var. The value can be for instance + parentbased_always_on, parentbased_always_off, parentbased_traceidratio... + enum: + - always_on + - always_off + - traceidratio + - parentbased_always_on + - parentbased_always_off + - parentbased_traceidratio + - jaeger_remote + - xray + type: string + type: object + type: object + status: + description: InstrumentationStatus defines status of the instrumentation. + type: object + type: object + served: true + storage: true + subresources: + status: {} +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: opentelemetry-operator-system/opentelemetry-operator-serving-cert + controller-gen.kubebuilder.io/version: v0.12.0 + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetrycollectors.opentelemetry.io +spec: + group: opentelemetry.io + names: + kind: OpenTelemetryCollector + listKind: OpenTelemetryCollectorList + plural: opentelemetrycollectors + shortNames: + - otelcol + - otelcols + singular: opentelemetrycollector + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Deployment Mode + jsonPath: .spec.mode + name: Mode + type: string + - description: OpenTelemetry Version + jsonPath: .status.version + name: Version + type: string + - jsonPath: .status.scale.statusReplicas + name: Ready + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + - jsonPath: .status.image + name: Image + type: string + - description: Management State + jsonPath: .spec.managementState + name: Management + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: OpenTelemetryCollector is the Schema for the opentelemetrycollectors + API. + properties: + apiVersion: + description: APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. + type: string + kind: + description: Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. + type: string + metadata: + type: object + spec: + description: OpenTelemetryCollectorSpec defines the desired state of OpenTelemetryCollector. + properties: + additionalContainers: + description: AdditionalContainers allows injecting additional containers + into the Collector's pod definition. + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: Arguments to the entrypoint. The container image's + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container's environment. + items: + type: string + type: array + command: + description: Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's + environment. + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be + a C_IDENTIFIER. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + description: 'Container image name. More info: https://kubernetes.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not + specifying a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default "0.0.0. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: Number of port to expose on the pod's IP + address. This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If + specified, this must be a valid port number, 0 < x < + 65536. If HostNetwork is specified, this must match + ContainerPort. Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod + must have a unique name. Name for the port that can + be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe + fails. Cannot be updated. More info: https://kubernetes.' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + restartPolicy: + description: RestartPolicy defines the restart behavior of individual + containers in a pod. This field may only be set for init containers, + and the only allowed value is "Always". + type: string + securityContext: + description: SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. + properties: + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by + the container runtime. Note that this field cannot be + set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent to + root on the host. Defaults to false. Note that this field + cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to + use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root + filesystem. Default is false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a + non-root user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a + random SELinux context for each container. May also be + set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile + must be preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - + a profile defined in a file on the node should be + used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set + in PodSecurityContext. + type: string + type: object + type: object + startupProbe: + description: StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, reads + from stdin in the container will always result in EOF. Default + is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the + stdin channel after it has been opened by a single attach. + When stdin is true the stdin stream will remain open across + multiple attach sessions. + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the + container''s termination message will be written is mounted + into the container''s filesystem.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be + populated. File will use the contents of terminationMessagePath + to populate the container status message on both success and + failure. + type: string + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other + way around. When not set, MountPropagationNone is used. + This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + affinity: + description: If specified, indicates the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose ' + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-locate + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + items: + type: string + type: array + topologyKey: + description: 'This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose ' + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + args: + additionalProperties: + type: string + description: Args is the set of arguments to pass to the OpenTelemetry + Collector binary + type: object + autoscaler: + description: Autoscaler specifies the pod autoscaling configuration + to use for the OpenTelemetryCollector workload. + properties: + behavior: + description: HorizontalPodAutoscalerBehavior configures the scaling + behavior of the target in both Up and Down directions (scaleUp + and scaleDown fields respectively). + properties: + scaleDown: + description: scaleDown is scaling policy for scaling Down. + If not set, the default value is to allow to scale down + to minReplicas pods, with a 300 second stabilization window + (i.e. + properties: + policies: + description: policies is a list of potential scaling polices + which can be used during scaling. At least one policy + must be specified, otherwise the HPAScalingRules will + be discarded as invalid + items: + description: HPAScalingPolicy is a single policy which + must hold true for a specified past interval. + properties: + periodSeconds: + description: periodSeconds specifies the window + of time for which the policy should hold true. + PeriodSeconds must be greater than zero and less + than or equal to 1800 (30 min). + format: int32 + type: integer + type: + description: type is used to specify the scaling + policy. + type: string + value: + description: value contains the amount of change + which is permitted by the policy. It must be greater + than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + x-kubernetes-list-type: atomic + selectPolicy: + description: selectPolicy is used to specify which policy + should be used. If not set, the default value Max is + used. + type: string + stabilizationWindowSeconds: + description: stabilizationWindowSeconds is the number + of seconds for which past recommendations should be + considered while scaling up or scaling down. + format: int32 + type: integer + type: object + scaleUp: + description: scaleUp is scaling policy for scaling Up. + properties: + policies: + description: policies is a list of potential scaling polices + which can be used during scaling. At least one policy + must be specified, otherwise the HPAScalingRules will + be discarded as invalid + items: + description: HPAScalingPolicy is a single policy which + must hold true for a specified past interval. + properties: + periodSeconds: + description: periodSeconds specifies the window + of time for which the policy should hold true. + PeriodSeconds must be greater than zero and less + than or equal to 1800 (30 min). + format: int32 + type: integer + type: + description: type is used to specify the scaling + policy. + type: string + value: + description: value contains the amount of change + which is permitted by the policy. It must be greater + than zero + format: int32 + type: integer + required: + - periodSeconds + - type + - value + type: object + type: array + x-kubernetes-list-type: atomic + selectPolicy: + description: selectPolicy is used to specify which policy + should be used. If not set, the default value Max is + used. + type: string + stabilizationWindowSeconds: + description: stabilizationWindowSeconds is the number + of seconds for which past recommendations should be + considered while scaling up or scaling down. + format: int32 + type: integer + type: object + type: object + maxReplicas: + description: MaxReplicas sets an upper bound to the autoscaling + feature. If MaxReplicas is set autoscaling is enabled. + format: int32 + type: integer + metrics: + description: Metrics is meant to provide a customizable way to + configure HPA metrics. currently the only supported custom metrics + is type=Pod. + items: + description: MetricSpec defines a subset of metrics to be defined + for the HPA's metric array more metric type can be supported + as needed. See https://pkg.go.dev/k8s.io/api/autoscaling/v2#MetricSpec + for reference. + properties: + pods: + description: PodsMetricSource indicates how to scale on + a metric describing each pod in the current scale target + (for example, transactions-processed-per-second). + properties: + metric: + description: metric identifies the target metric by + name and selector + properties: + name: + description: name is the name of the given metric + type: string + selector: + description: selector is the string-encoded form + of a standard kubernetes label selector for the + given metric When set, it is passed as an additional + parameter to the metrics server for more specific + metrics scopi + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + required: + - name + type: object + target: + description: target specifies the target value for the + given metric + properties: + averageUtilization: + description: averageUtilization is the target value + of the average of the resource metric across all + relevant pods, represented as a percentage of + the requested value of the resource for the pods. + format: int32 + type: integer + averageValue: + anyOf: + - type: integer + - type: string + description: averageValue is the target value of + the average of the metric across all relevant + pods (as a quantity) + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: + description: type represents whether the metric + type is Utilization, Value, or AverageValue + type: string + value: + anyOf: + - type: integer + - type: string + description: value is the target value of the metric + (as a quantity). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + required: + - type + type: object + required: + - metric + - target + type: object + type: + description: MetricSourceType indicates the type of metric. + type: string + required: + - type + type: object + type: array + minReplicas: + description: MinReplicas sets a lower bound to the autoscaling + feature. Set this if your are using autoscaling. It must be + at least 1 + format: int32 + type: integer + targetCPUUtilization: + description: TargetCPUUtilization sets the target average CPU + used across all replicas. If average CPU exceeds this value, + the HPA will scale up. Defaults to 90 percent. + format: int32 + type: integer + targetMemoryUtilization: + description: TargetMemoryUtilization sets the target average memory + utilization across all replicas + format: int32 + type: integer + type: object + config: + description: Config is the raw JSON to be used as the collector's + configuration. Refer to the OpenTelemetry Collector documentation + for details. + type: string + configmaps: + description: ConfigMaps is a list of ConfigMaps in the same namespace + as the OpenTelemetryCollector object, which shall be mounted into + the Collector Pods. + items: + properties: + mountpath: + type: string + name: + description: Configmap defines name and path where the configMaps + should be mounted. + type: string + required: + - mountpath + - name + type: object + type: array + env: + description: ENV vars to set on the OpenTelemetry Collector's Pods. + These can then in certain cases be consumed in the config file for + the Collector. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables on + the OpenTelemetry Collector's Pods. These can then in certain cases + be consumed in the config file for the Collector. + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + hostNetwork: + description: HostNetwork indicates if the pod should run in the host + networking namespace. + type: boolean + image: + description: Image indicates the container image to use for the OpenTelemetry + Collector. + type: string + imagePullPolicy: + description: ImagePullPolicy indicates the pull policy to be used + for retrieving the container image (Always, Never, IfNotPresent) + type: string + ingress: + description: 'Ingress is used to specify how OpenTelemetry Collector + is exposed. This functionality is only available if one of the valid + modes is set. Valid modes are: deployment, daemonset and statefulset.' + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations to add to ingress. e.g. ''cert-manager.io/cluster-issuer: + "letsencrypt"''' + type: object + hostname: + description: Hostname by which the ingress proxy can be reached. + type: string + ingressClassName: + description: IngressClassName is the name of an IngressClass cluster + resource. Ingress controller implementations use this field + to know whether they should be serving this Ingress resource. + type: string + route: + description: Route is an OpenShift specific section that is only + considered when type "route" is used. + properties: + termination: + description: Termination indicates termination type. By default + "edge" is used. + enum: + - insecure + - edge + - passthrough + - reencrypt + type: string + type: object + ruleType: + description: RuleType defines how Ingress exposes collector receivers. + IngressRuleTypePath ("path") exposes each receiver port on a + unique path on single domain defined in Hostname. + enum: + - path + - subdomain + type: string + tls: + description: TLS configuration. + items: + description: IngressTLS describes the transport layer security + associated with an ingress. + properties: + hosts: + description: hosts is a list of hosts included in the TLS + certificate. The values in this list must match the name/s + used in the tlsSecret. + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: secretName is the name of the secret used to + terminate TLS traffic on port 443. Field is left optional + to allow TLS routing based on SNI hostname alone. + type: string + type: object + type: array + type: + description: 'Type default value is: "" Supported types are: ingress, + route' + enum: + - ingress + - route + type: string + type: object + initContainers: + description: InitContainers allows injecting initContainers to the + Collector's pod definition. + items: + description: A single application container that you want to run + within a pod. + properties: + args: + description: Arguments to the entrypoint. The container image's + CMD is used if this is not provided. Variable references $(VAR_NAME) + are expanded using the container's environment. + items: + type: string + type: array + command: + description: Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's + environment. + items: + type: string + type: array + env: + description: List of environment variables to set in the container. + Cannot be updated. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must be + a C_IDENTIFIER. + items: + description: EnvFromSource represents the source of a set + of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must be + defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each + key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + image: + description: 'Container image name. More info: https://kubernetes.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: PreStop is called immediately before a container + is terminated due to an API request or management event + such as liveness/startup probe failure, preemption, resource + contention, etc. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. More + info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. Not + specifying a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default "0.0.0. + items: + description: ContainerPort represents a network port in a + single container. + properties: + containerPort: + description: Number of port to expose on the pod's IP + address. This must be a valid port number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port to. + type: string + hostPort: + description: Number of port to expose on the host. If + specified, this must be a valid port number, 0 < x < + 65536. If HostNetwork is specified, this must match + ContainerPort. Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a pod + must have a unique name. Name for the port that can + be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe + fails. Cannot be updated. More info: https://kubernetes.' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resizePolicy: + description: Resources resize policy for the container. + items: + description: ContainerResizePolicy represents resource resize + policy for the container. + properties: + resourceName: + description: 'Name of the resource to which this resource + resize policy applies. Supported values: cpu, memory.' + type: string + restartPolicy: + description: Restart policy to apply when specified resource + is resized. If not specified, it defaults to NotRequired. + type: string + required: + - resourceName + - restartPolicy + type: object + type: array + x-kubernetes-list-type: atomic + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where this + field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + restartPolicy: + description: RestartPolicy defines the restart behavior of individual + containers in a pod. This field may only be set for init containers, + and the only allowed value is "Always". + type: string + securityContext: + description: SecurityContext defines the security options the + container should be run with. If set, the fields of SecurityContext + override the equivalent fields of PodSecurityContext. + properties: + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by + the container runtime. Note that this field cannot be + set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent to + root on the host. Defaults to false. Note that this field + cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to + use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root + filesystem. Default is false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a + non-root user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a + random SELinux context for each container. May also be + set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile + must be preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - + a profile defined in a file on the node should be + used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set + in PodSecurityContext. + type: string + type: object + type: object + startupProbe: + description: StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed until + this completes successfully. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for the + command is root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name. This will + be canonicalized upon output, so case-variant + names will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has + started before liveness probes are initiated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum value + is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on + the container. Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, reads + from stdin in the container will always result in EOF. Default + is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close the + stdin channel after it has been opened by a single attach. + When stdin is true the stdin stream will remain open across + multiple attach sessions. + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which the + container''s termination message will be written is mounted + into the container''s filesystem.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should be + populated. File will use the contents of terminationMessagePath + to populate the container status message on both success and + failure. + type: string + tty: + description: Whether this container should allocate a TTY for + itself, also requires 'stdin' to be true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices to be + used by the container. + items: + description: volumeDevice describes a mapping of a raw block + device within a container. + properties: + devicePath: + description: devicePath is the path inside of the container + that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the volume + should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are + propagated from the host to container and the other + way around. When not set, MountPropagationNone is used. + This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which + the container's volume should be mounted. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + lifecycle: + description: Actions that the management system should take in response + to container lifecycle events. Cannot be updated. + properties: + postStart: + description: PostStart is called immediately after a container + is created. If the handler fails, the container is terminated + and restarted according to its restart policy. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name. This will be + canonicalized upon output, so case-variant names + will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler + and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: PreStop is called immediately before a container + is terminated due to an API request or management event such + as liveness/startup probe failure, preemption, resource contention, + etc. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name. This will be + canonicalized upon output, so case-variant names + will be understood as the same header. + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported as a LifecycleHandler + and kept for the backward compatibility. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: Liveness config for the OpenTelemetry Collector except + the probe handler which is auto generated from the health extension + of the collector. + properties: + failureThreshold: + description: Minimum consecutive failures for the probe to be + considered failed after having succeeded. Defaults to 3. Minimum + value is 1. + format: int32 + type: integer + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. Defaults to 0 seconds. + Minimum value is 0. More info: https://kubernetes.' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. Default + to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to be + considered successful after having failed. Defaults to 1. Must + be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate + gracefully upon probe failure. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + managementState: + default: managed + description: ManagementState defines if the CR should be managed by + the operator or not. Default is managed. + enum: + - managed + - unmanaged + type: string + maxReplicas: + description: 'MaxReplicas sets an upper bound to the autoscaling feature. + If MaxReplicas is set autoscaling is enabled. Deprecated: use "OpenTelemetryCollector.Spec.Autoscaler.MaxReplicas" + instead.' + format: int32 + type: integer + minReplicas: + description: 'MinReplicas sets a lower bound to the autoscaling feature. Set + this if you are using autoscaling. It must be at least 1 Deprecated: + use "OpenTelemetryCollector.Spec.Autoscaler.MinReplicas" instead.' + format: int32 + type: integer + mode: + description: Mode represents how the collector should be deployed + (deployment, daemonset, statefulset or sidecar) + enum: + - daemonset + - deployment + - sidecar + - statefulset + type: string + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to schedule OpenTelemetry Collector pods. + This is only relevant to daemonset, statefulset, and deployment + mode + type: object + observability: + description: ObservabilitySpec defines how telemetry data gets handled. + properties: + metrics: + description: Metrics defines the metrics configuration for operands. + properties: + enableMetrics: + description: EnableMetrics specifies if ServiceMonitor should + be created for the OpenTelemetry Collector and Prometheus + Exporters. The operator.observability. + type: boolean + type: object + type: object + podAnnotations: + additionalProperties: + type: string + description: PodAnnotations is the set of annotations that will be + attached to Collector and Target Allocator pods. + type: object + podDisruptionBudget: + description: PodDisruptionBudget specifies the pod disruption budget + configuration to use for the OpenTelemetryCollector workload. + properties: + maxUnavailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at most "maxUnavailable" + pods selected by "selector" are unavailable after the eviction, + i.e. even in absence of the evicted pod. + x-kubernetes-int-or-string: true + minAvailable: + anyOf: + - type: integer + - type: string + description: An eviction is allowed if at least "minAvailable" + pods selected by "selector" will still be available after the + eviction, i.e. even in the absence of the evicted pod. + x-kubernetes-int-or-string: true + type: object + podSecurityContext: + description: PodSecurityContext configures the pod security context + for the opentelemetry-collector pod, when running as a deployment, + daemonset, or statefulset. + properties: + fsGroup: + description: "A special supplemental group that applies to all + containers in a pod. Some volume types allow the Kubelet to + change the ownership of that volume to be owned by the pod: + \n 1." + format: int64 + type: integer + fsGroupChangePolicy: + description: fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. + type: string + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in SecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in SecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers in this + pod. Note that this field cannot be set when spec.os.name is + windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process run + in each container, in addition to the container's primary GID, + the fsGroup (if specified), and group memberships defined in + the container image for th + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used for + the pod. Pods with unsupported sysctls (by the container runtime) + might fail to launch. Note that this field cannot be set when + spec.os. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext + will be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + ports: + description: Ports allows a set of ports to be exposed by the underlying + v1.Service. By default, the operator will attempt to infer the required + ports by parsing the .Spec. + items: + description: ServicePort contains information on service's port. + properties: + appProtocol: + description: The application protocol for this port. This is + used as a hint for implementations to offer richer behavior + for protocols that they understand. This field follows standard + Kubernetes label syntax. + type: string + name: + description: The name of this port within the service. This + must be a DNS_LABEL. All ports within a ServiceSpec must have + unique names. + type: string + nodePort: + description: The port on each node on which this service is + exposed when type is NodePort or LoadBalancer. Usually assigned + by the system. + format: int32 + type: integer + port: + description: The port that will be exposed by this service. + format: int32 + type: integer + protocol: + default: TCP + description: The IP protocol for this port. Supports "TCP", + "UDP", and "SCTP". Default is TCP. + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the pods + targeted by the service. Number must be in the range 1 to + 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: array + x-kubernetes-list-type: atomic + priorityClassName: + description: If specified, indicates the pod's priority. If not specified, + the pod priority will be default or zero if there is no default. + type: string + replicas: + description: Replicas is the number of pod instances for the underlying + OpenTelemetry Collector. Set this if your are not using autoscaling + format: int32 + type: integer + resources: + description: Resources to set on the OpenTelemetry Collector pods. + properties: + claims: + description: "Claims lists the names of resources, defined in + spec.resourceClaims, that are used by this container. \n This + is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims + of the Pod where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + securityContext: + description: SecurityContext configures the container security context + for the opentelemetry-collector container. + properties: + allowPrivilegeEscalation: + description: AllowPrivilegeEscalation controls whether a process + can gain more privileges than its parent process. This bool + directly controls if the no_new_privs flag will be set on the + container process. + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container + runtime. Note that this field cannot be set when spec.os.name + is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in privileged + containers are essentially equivalent to root on the host. Defaults + to false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use for + the containers. The default is DefaultProcMount which uses the + container runtime defaults for readonly paths and masked paths. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when spec.os.name + is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container process. + Uses runtime default if unset. May also be set in PodSecurityContext. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in PodSecurityContext. + properties: + level: + description: Level is SELinux level label that applies to + the container. + type: string + role: + description: Role is a SELinux role label that applies to + the container. + type: string + type: + description: Type is a SELinux type label that applies to + the container. + type: string + user: + description: User is a SELinux user label that applies to + the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. If + seccomp options are provided at both the pod & container level, + the container options override the pod options. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must be + preconfigured on the node to work. + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a profile + defined in a file on the node should be used." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will + be used. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named by + the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the GMSA + credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in PodSecurityContext. + type: string + type: object + type: object + serviceAccount: + description: ServiceAccount indicates the name of an existing service + account to use with this instance. When set, the operator will not + automatically create a ServiceAccount for the collector. + type: string + targetAllocator: + description: TargetAllocator indicates a value which determines whether + to spawn a target allocation resource or not. + properties: + allocationStrategy: + description: AllocationStrategy determines which strategy the + target allocator should use for allocation. The current options + are least-weighted and consistent-hashing. The default option + is least-weighted + enum: + - least-weighted + - consistent-hashing + type: string + enabled: + description: Enabled indicates whether to use a target allocation + mechanism for Prometheus targets or not. + type: boolean + env: + description: ENV vars to set on the OpenTelemetry TargetAllocator's + Pods. These can then in certain cases be consumed in the config + file for the TargetAllocator. + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in + the container and any service environment variables. + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, + `metadata.annotations['''']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + filterStrategy: + description: FilterStrategy determines how to filter targets before + allocating them among the collectors. The only current option + is relabel-config (drops targets based on prom relabel_config). + type: string + image: + description: Image indicates the container image to use for the + OpenTelemetry TargetAllocator. + type: string + nodeSelector: + additionalProperties: + type: string + description: NodeSelector to schedule OpenTelemetry TargetAllocator + pods. + type: object + prometheusCR: + description: PrometheusCR defines the configuration for the retrieval + of PrometheusOperator CRDs ( servicemonitor.monitoring.coreos.com/v1 + and podmonitor.monitoring.coreos.com/v1 ) retrieval. + properties: + enabled: + description: Enabled indicates whether to use a PrometheusOperator + custom resources as targets or not. + type: boolean + podMonitorSelector: + additionalProperties: + type: string + description: PodMonitors to be selected for target discovery. + This is a map of {key,value} pairs. Each {key,value} in + the map is going to exactly match a label in a PodMonitor's + meta labels. + type: object + scrapeInterval: + default: 30s + description: "Interval between consecutive scrapes. Equivalent + to the same setting on the Prometheus CRD. \n Default: \"30s\"" + format: duration + type: string + serviceMonitorSelector: + additionalProperties: + type: string + description: ServiceMonitors to be selected for target discovery. + This is a map of {key,value} pairs. Each {key,value} in + the map is going to exactly match a label in a ServiceMonitor's + meta labels. + type: object + type: object + replicas: + description: Replicas is the number of pod instances for the underlying + TargetAllocator. This should only be set to a value other than + 1 if a strategy that allows for high availability is chosen. + format: int32 + type: integer + resources: + description: Resources to set on the OpenTelemetryTargetAllocator + containers. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in + pod.spec.resourceClaims of the Pod where this field + is used. It makes that resource available inside a + container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of compute + resources required. + type: object + type: object + serviceAccount: + description: ServiceAccount indicates the name of an existing + service account to use with this instance. When set, the operator + will not automatically create a ServiceAccount for the TargetAllocator. + type: string + tolerations: + description: Toleration embedded kubernetes pod configuration + option, controls how pods can be scheduled with matching taints + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints embedded kubernetes pod + configuration option, controls how pods are spread across your + cluster among failure-domains such as regions, zones, nodes, + and other user-defined top + items: + description: TopologySpreadConstraint specifies how to spread + matching pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine + the number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: MatchLabelKeys is a set of pod label keys to + select the pods over which spreading will be calculated. + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: MaxSkew describes the degree to which pods + may be unevenly distributed. + format: int32 + type: integer + minDomains: + description: MinDomains indicates a minimum number of eligible + domains. + format: int32 + type: integer + nodeAffinityPolicy: + description: NodeAffinityPolicy indicates how we will treat + Pod's nodeAffinity/nodeSelector when calculating pod topology + spread skew. + type: string + nodeTaintsPolicy: + description: NodeTaintsPolicy indicates how we will treat + node taints when calculating pod topology spread skew. + type: string + topologyKey: + description: TopologyKey is the key of node labels. Nodes + that have a label with this key and identical values are + considered to be in the same topology. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with + a pod if it doesn't satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + type: object + terminationGracePeriodSeconds: + description: Duration in seconds the pod needs to terminate gracefully + upon probe failure. + format: int64 + type: integer + tolerations: + description: Toleration to schedule OpenTelemetry Collector pods. + This is only relevant to daemonset, statefulset, and deployment + mode + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match all + values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to Equal. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints embedded kubernetes pod configuration + option, controls how pods are spread across your cluster among failure-domains + such as regions, zones, nodes, and other user-defined top + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + type: object + type: object + x-kubernetes-map-type: atomic + matchLabelKeys: + description: MatchLabelKeys is a set of pod label keys to select + the pods over which spreading will be calculated. + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: MaxSkew describes the degree to which pods may + be unevenly distributed. + format: int32 + type: integer + minDomains: + description: MinDomains indicates a minimum number of eligible + domains. + format: int32 + type: integer + nodeAffinityPolicy: + description: NodeAffinityPolicy indicates how we will treat + Pod's nodeAffinity/nodeSelector when calculating pod topology + spread skew. + type: string + nodeTaintsPolicy: + description: NodeTaintsPolicy indicates how we will treat node + taints when calculating pod topology spread skew. + type: string + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. + type: string + whenUnsatisfiable: + description: WhenUnsatisfiable indicates how to deal with a + pod if it doesn't satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + upgradeStrategy: + description: UpgradeStrategy represents how the operator will handle + upgrades to the CR when a newer version of the operator is deployed + enum: + - automatic + - none + type: string + volumeClaimTemplates: + description: VolumeClaimTemplates will provide stable storage using + PersistentVolumes. Only available when the mode=statefulset. + items: + description: PersistentVolumeClaim is a user's request for and claim + to a persistent volume + properties: + apiVersion: + description: APIVersion defines the versioned schema of this + representation of an object. Servers should convert recognized + schemas to the latest internal value, and may reject unrecognized + values. + type: string + kind: + description: Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + description: 'spec defines the desired characteristics of a + volume requested by a pod author. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + accessModes: + description: 'accessModes contains the desired access modes + the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, the + specified Kind must be in the core API group. For + any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: dataSourceRef specifies the object from which + to populate the volume with data, if a non-empty volume + is desired. + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, the + specified Kind must be in the core API group. For + any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace is specified, + a gateway.networking.k8s. + type: string + required: + - kind + - name + type: object + resources: + description: resources represents the minimum resources + the volume should have. + properties: + claims: + description: "Claims lists the names of resources, defined + in spec.resourceClaims, that are used by this container. + \n This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate." + items: + description: ResourceClaim references one entry in + PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry + in pod.spec.resourceClaims of the Pod where + this field is used. It makes that resource available + inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of + compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum amount of + compute resources required. + type: object + type: object + selector: + description: selector is a label query over volumes to consider + for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists + or DoesNotExist, the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: 'storageClassName is the name of the StorageClass + required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume is required + by the claim. Value of Filesystem is implied when not + included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference to the + PersistentVolume backing this claim. + type: string + type: object + status: + description: 'status represents the current information/status + of a persistent volume claim. Read-only. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + accessModes: + description: 'accessModes contains the actual access modes + the volume backing the PVC has. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + allocatedResourceStatuses: + additionalProperties: + description: When a controller receives persistentvolume + claim update with ClaimResourceStatus for a resource + that it does not recognizes, then it should ignore that + update and let other controllers handle it. + type: string + description: allocatedResourceStatuses stores status of + resource being resized for the given PVC. Key names follow + standard Kubernetes label syntax. + type: object + x-kubernetes-map-type: granular + allocatedResources: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: allocatedResources tracks the resources allocated + to a PVC including its capacity. Key names follow standard + Kubernetes label syntax. + type: object + capacity: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: capacity represents the actual resources of + the underlying volume. + type: object + conditions: + description: conditions is the current Condition of persistent + volume claim. If underlying persistent volume is being + resized then the Condition will be set to 'ResizeStarted'. + items: + description: PersistentVolumeClaimCondition contains details + about state of pvc + properties: + lastProbeTime: + description: lastProbeTime is the time we probed the + condition. + format: date-time + type: string + lastTransitionTime: + description: lastTransitionTime is the time the condition + transitioned from one status to another. + format: date-time + type: string + message: + description: message is the human-readable message + indicating details about last transition. + type: string + reason: + description: reason is a unique, this should be a + short, machine understandable string that gives + the reason for condition's last transition. + type: string + status: + type: string + type: + description: PersistentVolumeClaimConditionType is + a valid value of PersistentVolumeClaimCondition.Type + type: string + required: + - status + - type + type: object + type: array + phase: + description: phase represents the current phase of PersistentVolumeClaim. + type: string + type: object + type: object + type: array + x-kubernetes-list-type: atomic + volumeMounts: + description: VolumeMounts represents the mount points to use in the + underlying collector deployment(s) + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: Path within the container at which the volume should + be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are propagated + from the host to container and the other way around. When + not set, MountPropagationNone is used. This field is beta + in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which the + container's volume should be mounted. + type: string + required: + - mountPath + - name + type: object + type: array + x-kubernetes-list-type: atomic + volumes: + description: Volumes represents which volumes to use in the underlying + collector deployment(s). + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'awsElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs".' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1".' + format: int32 + type: integer + readOnly: + description: 'readOnly value true will force the readOnly + setting in VolumeMounts. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'volumeID is unique ID of the persistent disk + resource in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: azureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'cachingMode is the Host Caching mode: None, + Read Only, Read Write.' + type: string + diskName: + description: diskName is the Name of the data disk in the + blob storage + type: string + diskURI: + description: diskURI is the URI of data disk in the blob + storage + type: string + fsType: + description: fsType is Filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + kind: + description: 'kind expected values are Shared: multiple + blob disks per storage account Dedicated: single blob + disk per storage account Managed: azure managed data + disk (only in managed availability set).' + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: azureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: secretName is the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: shareName is the azure share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: cephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'monitors is Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'path is Optional: Used as the mounted root, + rather than the full Ceph tree, default is /' + type: string + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'secretFile is Optional: SecretFile is the + path to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'secretRef is Optional: SecretRef is reference + to the authentication secret for User, default is empty. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is optional: User is the rados user name, + default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Examples: "ext4", "xfs", "ntfs". Implicitly inferred to + be "ext4" if unspecified.' + type: string + readOnly: + description: 'readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'secretRef is optional: points to a secret + object containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + description: 'volumeID used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: configMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'defaultMode is optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + items: + description: items if unspecified, each key-value pair in + the Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and content + is the value. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: optional specify whether the ConfigMap or its + keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: csi (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated + CSI driver which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: nodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: readOnly specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: volumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: downwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a Optional: mode bits used to set + permissions on created files by default.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used to set permissions + on this file, must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'emptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: medium represents what type of storage medium + should back this directory. The default is "" which means + to use the node's default medium. Must be an empty string + (default) or Memory. + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: sizeLimit is the total amount of local storage + required for this EmptyDir volume. The size limit is also + applicable for memory medium. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: ephemeral represents a volume that is handled by + a cluster storage driver. + properties: + volumeClaimTemplate: + description: Will be used to create a stand-alone PVC to + provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. + properties: + metadata: + description: May contain labels and annotations that + will be copied into the PVC when creating it. No other + fields are allowed and will be rejected during validation. + properties: + annotations: + additionalProperties: + type: string + type: object + finalizers: + items: + type: string + type: array + labels: + additionalProperties: + type: string + type: object + name: + type: string + namespace: + type: string + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the PVC + that gets created from this template. + properties: + accessModes: + description: 'accessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'dataSource field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + description: dataSourceRef specifies the object + from which to populate the volume with data, if + a non-empty volume is desired. + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + namespace: + description: Namespace is the namespace of resource + being referenced Note that when a namespace + is specified, a gateway.networking.k8s. + type: string + required: + - kind + - name + type: object + resources: + description: resources represents the minimum resources + the volume should have. + properties: + claims: + description: "Claims lists the names of resources, + defined in spec.resourceClaims, that are used + by this container. \n This is an alpha field + and requires enabling the DynamicResourceAllocation + feature gate." + items: + description: ResourceClaim references one + entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name + of one entry in pod.spec.resourceClaims + of the Pod where this field is used. + It makes that resource available inside + a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Requests describes the minimum + amount of compute resources required. + type: object + type: object + selector: + description: selector is a label query over volumes + to consider for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + description: 'storageClassName is the name of the + StorageClass required by the claim. More info: + https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume + is required by the claim. Value of Filesystem + is implied when not included in claim spec. + type: string + volumeName: + description: volumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: fc represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + lun: + description: 'lun is Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'readOnly is Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'targetWWNs is Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'wwids Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: flexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: driver is the name of the driver to use for + this volume. + type: string + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". The default filesystem depends + on FlexVolume script. + type: string + options: + additionalProperties: + type: string + description: 'options is Optional: this field holds extra + command options if any.' + type: object + readOnly: + description: 'readOnly is Optional: defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'secretRef is Optional: secretRef is reference + to the secret object containing sensitive information + to pass to the plugin scripts. This may be empty if no + secret object is specified.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + description: flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: datasetName is Name of the dataset stored as + metadata -> name on the dataset for Flocker should be + considered as deprecated + type: string + datasetUUID: + description: datasetUUID is the UUID of the dataset. This + is unique identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'gcePersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.' + properties: + fsType: + description: 'fsType is filesystem type of the volume that + you want to mount. Tip: Ensure that the filesystem type + is supported by the host operating system. Examples: "ext4", + "xfs", "ntfs".' + type: string + partition: + description: 'partition is the partition in the volume that + you want to mount. If omitted, the default is to mount + by volume name. Examples: For volume /dev/sda1, you specify + the partition as "1".' + format: int32 + type: integer + pdName: + description: 'pdName is unique name of the PD resource in + GCE. Used to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'gitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated.' + properties: + directory: + description: directory is the target directory name. Must + not contain or start with '..'. If '.' is supplied, the + volume directory will be the git repository. + type: string + repository: + description: repository is the URL + type: string + revision: + description: revision is the commit hash for the specified + revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'endpoints is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'readOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: hostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + properties: + path: + description: 'path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'iscsi represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to + the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: chapAuthDiscovery defines whether support iSCSI + Discovery CHAP authentication + type: boolean + chapAuthSession: + description: chapAuthSession defines whether support iSCSI + Session CHAP authentication + type: boolean + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs".' + type: string + initiatorName: + description: initiatorName is the custom iSCSI Initiator + Name. + type: string + iqn: + description: iqn is the target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iscsiInterface is the interface Name that uses + an iSCSI transport. Defaults to 'default' (tcp). + type: string + lun: + description: lun represents iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: portals is the iSCSI Target Portal List. The + portal is either an IP or ip_addr:port if the port is + other than default (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: secretRef is the CHAP Secret for iSCSI target + and initiator authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + description: targetPortal is iSCSI Target Portal. The Portal + is either an IP or ip_addr:port if the port is other than + default (typically TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'name of the volume. Must be a DNS_LABEL and unique + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'nfs represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'readOnly here will force the NFS export to + be mounted with read-only permissions. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'persistentVolumeClaimVolumeSource represents a + reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.' + properties: + claimName: + description: 'claimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: readOnly Will force the ReadOnly setting in + VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: photonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + pdID: + description: pdID is the ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: portworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: volumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: projected items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: defaultMode are the mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. + format: int32 + type: integer + sources: + description: sources is the list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu + and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + description: secret information about the secret data + to project + properties: + items: + description: items if unspecified, each key-value + pair in the Data field of the referenced Secret + will be projected into the volume as a file + whose name is the key and content is the value. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits + used to set permissions on this file. + Must be an octal value between 0000 and + 0777 or a decimal value between 0 and + 511.' + format: int32 + type: integer + path: + description: path is the relative path of + the file to map the key to. May not be + an absolute path. May not contain the + path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: optional field specify whether the + Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about + the serviceAccountToken data to project + properties: + audience: + description: audience is the intended audience + of the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. + type: string + expirationSeconds: + description: expirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, the + kubelet volume plugin will proactively rotate + the service account token. + format: int64 + type: integer + path: + description: path is the path relative to the + mount point of the file to project the token + into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: group to map volume access to Default is no + group + type: string + readOnly: + description: readOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults to + false. + type: boolean + registry: + description: registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: user to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'rbd represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'fsType is the filesystem type of the volume + that you want to mount. Tip: Ensure that the filesystem + type is supported by the host operating system. Examples: + "ext4", "xfs", "ntfs".' + type: string + image: + description: 'image is the rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'monitors is a collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'pool is the rados pool name. Default is rbd. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'readOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'secretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is + nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + user: + description: 'user is the rados user name. Default is admin. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: scaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: gateway is the host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: protectionDomain is the name of the ScaleIO + Protection Domain for the configured storage. + type: string + readOnly: + description: readOnly Defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + description: sslEnabled Flag enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: storageMode indicates whether the storage for + a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: storagePool is the ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: system is the name of the storage system as + configured in ScaleIO. + type: string + volumeName: + description: volumeName is the name of a volume already + created in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'defaultMode is Optional: mode bits used to + set permissions on created files by default. Must be an + octal value between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + items: + description: items If unspecified, each key-value pair in + the Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and content + is the value. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: 'mode is Optional: mode bits used to + set permissions on this file. Must be an octal value + between 0000 and 0777 or a decimal value between + 0 and 511.' + format: int32 + type: integer + path: + description: path is the relative path of the file + to map the key to. May not be an absolute path. + May not contain the path element '..'. May not start + with the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: optional field specify whether the Secret or + its keys must be defined + type: boolean + secretName: + description: 'secretName is the name of the secret in the + pod''s namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: storageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: fsType is the filesystem type to mount. Must + be a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: readOnly defaults to false (read/write). ReadOnly + here will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: secretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + description: volumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within + a namespace. + type: string + volumeNamespace: + description: volumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. + type: string + type: object + vsphereVolume: + description: vsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: fsType is filesystem type to mount. Must be + a filesystem type supported by the host operating system. + Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + storagePolicyID: + description: storagePolicyID is the storage Policy Based + Management (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: storagePolicyName is the storage Policy Based + Management (SPBM) profile name. + type: string + volumePath: + description: volumePath is the path that identifies vSphere + volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + x-kubernetes-list-type: atomic + type: object + status: + description: OpenTelemetryCollectorStatus defines the observed state of + OpenTelemetryCollector. + properties: + image: + description: Image indicates the container image to use for the OpenTelemetry + Collector. + type: string + messages: + description: 'Messages about actions performed by the operator on + this resource. Deprecated: use Kubernetes events instead.' + items: + type: string + type: array + x-kubernetes-list-type: atomic + replicas: + description: 'Replicas is currently not being set and might be removed + in the next version. Deprecated: use "OpenTelemetryCollector.Status.Scale.Replicas" + instead.' + format: int32 + type: integer + scale: + description: Scale is the OpenTelemetryCollector's scale subresource + status. + properties: + replicas: + description: The total number non-terminated pods targeted by + this OpenTelemetryCollector's deployment or statefulSet. + format: int32 + type: integer + selector: + description: The selector used to match the OpenTelemetryCollector's + deployment or statefulSet pods. + type: string + statusReplicas: + description: StatusReplicas is the number of pods targeted by + this OpenTelemetryCollector's with a Ready Condition / Total + number of non-terminated pods targeted by this OpenTelemetryCollector's + (their labels matc + type: string + type: object + version: + description: Version of the managed OpenTelemetry Collector (operand) + type: string + type: object + type: object + served: true + storage: true + subresources: + scale: + labelSelectorPath: .status.scale.selector + specReplicasPath: .spec.replicas + statusReplicasPath: .status.scale.replicas + status: {} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-controller-manager + namespace: opentelemetry-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-leader-election-role + namespace: opentelemetry-operator-system +rules: + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get + - update + - patch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-manager-role +rules: + - apiGroups: + - "" + resources: + - configmaps + - serviceaccounts + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - "" + resources: + - namespaces + verbs: + - list + - watch + - apiGroups: + - apps + resources: + - daemonsets + - deployments + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - apps + resources: + - replicasets + verbs: + - get + - list + - watch + - apiGroups: + - autoscaling + resources: + - horizontalpodautoscalers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - get + - list + - update + - apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - opentelemetry.io + resources: + - instrumentations + verbs: + - get + - list + - patch + - update + - watch + - apiGroups: + - opentelemetry.io + resources: + - opentelemetrycollectors + verbs: + - get + - list + - patch + - update + - watch + - apiGroups: + - opentelemetry.io + resources: + - opentelemetrycollectors/finalizers + verbs: + - get + - patch + - update + - apiGroups: + - opentelemetry.io + resources: + - opentelemetrycollectors/status + verbs: + - get + - patch + - update + - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - route.openshift.io + resources: + - routes + - routes/custom-host + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-metrics-reader +rules: + - nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-proxy-role +rules: + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-leader-election-rolebinding + namespace: opentelemetry-operator-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: opentelemetry-operator-leader-election-role +subjects: + - kind: ServiceAccount + name: opentelemetry-operator-controller-manager + namespace: opentelemetry-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: opentelemetry-operator-manager-role +subjects: + - kind: ServiceAccount + name: opentelemetry-operator-controller-manager + namespace: opentelemetry-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: opentelemetry-operator-proxy-role +subjects: + - kind: ServiceAccount + name: opentelemetry-operator-controller-manager + namespace: opentelemetry-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager + name: opentelemetry-operator-controller-manager-metrics-service + namespace: opentelemetry-operator-system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: https + selector: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-webhook-service + namespace: opentelemetry-operator-system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager + name: opentelemetry-operator-controller-manager + namespace: opentelemetry-operator-system +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager + template: + metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + control-plane: controller-manager + spec: + containers: + - args: + - --metrics-addr=127.0.0.1:8080 + - --enable-leader-election + - --zap-log-level=info + - --zap-time-encoding=rfc3339nano + - --feature-gates=+operator.autoinstrumentation.go,+operator.autoinstrumentation.nginx + image: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator:0.86.0 + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + requests: + cpu: 100m + memory: 64Mi + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + serviceAccountName: opentelemetry-operator-controller-manager + terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: opentelemetry-operator-controller-manager-service-cert +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-serving-cert + namespace: opentelemetry-operator-system +spec: + dnsNames: + - opentelemetry-operator-webhook-service.opentelemetry-operator-system.svc + - opentelemetry-operator-webhook-service.opentelemetry-operator-system.svc.cluster.local + issuerRef: + kind: Issuer + name: opentelemetry-operator-selfsigned-issuer + secretName: opentelemetry-operator-controller-manager-service-cert + subject: + organizationalUnits: + - opentelemetry-operator +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-selfsigned-issuer + namespace: opentelemetry-operator-system +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: opentelemetry-operator-system/opentelemetry-operator-serving-cert + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-mutating-webhook-configuration +webhooks: + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: opentelemetry-operator-webhook-service + namespace: opentelemetry-operator-system + path: /mutate-opentelemetry-io-v1alpha1-instrumentation + failurePolicy: Fail + name: minstrumentation.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - instrumentations + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: opentelemetry-operator-webhook-service + namespace: opentelemetry-operator-system + path: /mutate-opentelemetry-io-v1alpha1-opentelemetrycollector + failurePolicy: Fail + name: mopentelemetrycollector.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - opentelemetrycollectors + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: opentelemetry-operator-webhook-service + namespace: opentelemetry-operator-system + path: /mutate-v1-pod + failurePolicy: Ignore + name: mpod.kb.io + rules: + - apiGroups: + - "" + apiVersions: + - v1 + operations: + - CREATE + - UPDATE + resources: + - pods + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: opentelemetry-operator-system/opentelemetry-operator-serving-cert + labels: + app.kubernetes.io/name: opentelemetry-operator + name: opentelemetry-operator-validating-webhook-configuration +webhooks: + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: opentelemetry-operator-webhook-service + namespace: opentelemetry-operator-system + path: /validate-opentelemetry-io-v1alpha1-instrumentation + failurePolicy: Fail + name: vinstrumentationcreateupdate.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - instrumentations + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: opentelemetry-operator-webhook-service + namespace: opentelemetry-operator-system + path: /validate-opentelemetry-io-v1alpha1-instrumentation + failurePolicy: Ignore + name: vinstrumentationdelete.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - instrumentations + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: opentelemetry-operator-webhook-service + namespace: opentelemetry-operator-system + path: /validate-opentelemetry-io-v1alpha1-opentelemetrycollector + failurePolicy: Fail + name: vopentelemetrycollectorcreateupdate.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - opentelemetrycollectors + sideEffects: None + - admissionReviewVersions: + - v1 + clientConfig: + service: + name: opentelemetry-operator-webhook-service + namespace: opentelemetry-operator-system + path: /validate-opentelemetry-io-v1alpha1-opentelemetrycollector + failurePolicy: Ignore + name: vopentelemetrycollectordelete.kb.io + rules: + - apiGroups: + - opentelemetry.io + apiVersions: + - v1alpha1 + operations: + - DELETE + resources: + - opentelemetrycollectors + sideEffects: None diff --git a/tests/e2e/autoscale/00-assert.yaml b/tests/e2e/autoscale/00-assert.yaml deleted file mode 100644 index 4039af39fb..0000000000 --- a/tests/e2e/autoscale/00-assert.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: simplest-collector -status: - readyReplicas: 1 ---- -apiVersion: autoscaling/v1 -kind: HorizontalPodAutoscaler -metadata: - name: simplest-collector -spec: - minReplicas: 1 - maxReplicas: 2 -# This is not neccesarily exact. We really just want to wait until this is no longer -status: - currentCPUUtilizationPercentage: 20 ---- -apiVersion: autoscaling/v2beta2 -kind: HorizontalPodAutoscaler -metadata: - name: simplest-set-utilization-collector -spec: - minReplicas: 1 - maxReplicas: 2 - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: 50 \ No newline at end of file diff --git a/tests/e2e/autoscale/01-assert.yaml b/tests/e2e/autoscale/01-assert.yaml deleted file mode 100644 index 2c2eec0238..0000000000 --- a/tests/e2e/autoscale/01-assert.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: tracegen -status: - conditions: - - status: "True" - type: Complete - ---- -apiVersion: opentelemetry.io/v1alpha1 -kind: OpenTelemetryCollector - -metadata: - name: simplest -status: - scale: - replicas: 2 diff --git a/tests/e2e/autoscale/01-install.yaml b/tests/e2e/autoscale/01-install.yaml deleted file mode 100644 index eac4408366..0000000000 --- a/tests/e2e/autoscale/01-install.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: batch/v1 -kind: Job -metadata: - name: tracegen -spec: - template: - spec: - containers: - - name: tracegen - image: ghcr.io/open-telemetry/opentelemetry-collector-contrib/tracegen:latest - command: - - "./tracegen" - args: - - -otlp-endpoint=simplest-collector-headless:4317 - - -otlp-insecure - - -duration=1m - - -workers=20 - restartPolicy: Never - backoffLimit: 4 diff --git a/tests/e2e/daemonset-features/00-add-scc.yaml b/tests/e2e/daemonset-features/00-add-scc.yaml new file mode 100644 index 0000000000..63d417ea96 --- /dev/null +++ b/tests/e2e/daemonset-features/00-add-scc.yaml @@ -0,0 +1,4 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: ./add-scc-openshift.sh diff --git a/tests/e2e/daemonset-features/00-install.yaml b/tests/e2e/daemonset-features/01-install.yaml similarity index 78% rename from tests/e2e/daemonset-features/00-install.yaml rename to tests/e2e/daemonset-features/01-install.yaml index 3dde940cf4..c452864d7a 100644 --- a/tests/e2e/daemonset-features/00-install.yaml +++ b/tests/e2e/daemonset-features/01-install.yaml @@ -1,9 +1,9 @@ apiVersion: opentelemetry.io/v1alpha1 kind: OpenTelemetryCollector metadata: - name: daemonset + name: daemonset spec: - mode: daemonset + mode: daemonset hostNetwork: true config: | receivers: @@ -12,10 +12,10 @@ spec: grpc: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/daemonset-features/00-assert.yaml b/tests/e2e/daemonset-features/02-assert.yaml similarity index 90% rename from tests/e2e/daemonset-features/00-assert.yaml rename to tests/e2e/daemonset-features/02-assert.yaml index d862c1f6a2..03798dbd6d 100644 --- a/tests/e2e/daemonset-features/00-assert.yaml +++ b/tests/e2e/daemonset-features/02-assert.yaml @@ -10,5 +10,3 @@ spec: - args: - --config=/conf/collector.yaml name: otc-container -status: - numberReady: 1 diff --git a/tests/e2e/daemonset-features/03-add-sa-collector.yaml b/tests/e2e/daemonset-features/03-add-sa-collector.yaml new file mode 100644 index 0000000000..1da56ca212 --- /dev/null +++ b/tests/e2e/daemonset-features/03-add-sa-collector.yaml @@ -0,0 +1,4 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: ./add-sa-collector.sh diff --git a/tests/e2e/daemonset-features/03-assert.yaml b/tests/e2e/daemonset-features/03-assert.yaml new file mode 100644 index 0000000000..102ea5139b --- /dev/null +++ b/tests/e2e/daemonset-features/03-assert.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Pod +metadata: + labels: + app.kubernetes.io/name: daemonset-collector + app.kubernetes.io/part-of: opentelemetry +status: + phase: Running diff --git a/tests/e2e/daemonset-features/add-sa-collector.sh b/tests/e2e/daemonset-features/add-sa-collector.sh new file mode 100755 index 0000000000..cdd05f78fa --- /dev/null +++ b/tests/e2e/daemonset-features/add-sa-collector.sh @@ -0,0 +1,5 @@ +#!/bin/bash +if [[ "$(kubectl api-resources --api-group=operator.openshift.io -o name)" ]]; then + echo "Adding service account to the OpenTelemetry Collector" + kubectl patch otelcol daemonset --type=merge -p '{"spec":{"serviceAccount":"otel-collector-daemonset"}}' -n $NAMESPACE +fi diff --git a/tests/e2e/daemonset-features/add-scc-openshift.sh b/tests/e2e/daemonset-features/add-scc-openshift.sh new file mode 100755 index 0000000000..7412a51d12 --- /dev/null +++ b/tests/e2e/daemonset-features/add-scc-openshift.sh @@ -0,0 +1,10 @@ +#!/bin/bash +if [[ "$(kubectl api-resources --api-group=operator.openshift.io -o name)" ]]; then + echo "Running the test against an OpenShift Cluster" + echo "Creating an Service Account" + echo "Creating a Security Context Constrain" + echo "Setting the Service Account for the Daemonset" + echo "Adding the new policy to the Service Account" + kubectl apply -f scc.yaml -n $NAMESPACE + oc adm policy add-scc-to-user -z otel-collector-daemonset daemonset-with-hostport -n $NAMESPACE +fi diff --git a/tests/e2e/daemonset-features/scc.yaml b/tests/e2e/daemonset-features/scc.yaml new file mode 100644 index 0000000000..20e211716f --- /dev/null +++ b/tests/e2e/daemonset-features/scc.yaml @@ -0,0 +1,22 @@ +kind: SecurityContextConstraints +apiVersion: security.openshift.io/v1 +metadata: + name: daemonset-with-hostport + annotations: + kubernetes.io/description: 'Allows DaemonSets to bind to a well-known host port' +runAsUser: + type: RunAsAny +seLinuxContext: + type: RunAsAny +allowHostPorts: true +allowHostDirVolumePlugin: false +allowHostIPC: false +allowHostNetwork: true +allowHostPID: false +allowPrivilegedContainer: false +readOnlyRootFilesystem: false +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: otel-collector-daemonset diff --git a/tests/e2e/ingress-subdomains/00-assert.yaml b/tests/e2e/ingress-subdomains/00-assert.yaml new file mode 100644 index 0000000000..d92353b3d6 --- /dev/null +++ b/tests/e2e/ingress-subdomains/00-assert.yaml @@ -0,0 +1,45 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simplest-collector +status: + readyReplicas: 1 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + annotations: + something.com: "true" + labels: + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: simplest-ingress + name: simplest-ingress + ownerReferences: + - apiVersion: opentelemetry.io/v1alpha1 + blockOwnerDeletion: true + controller: true + kind: OpenTelemetryCollector + name: simplest +spec: + rules: + - host: otlp-grpc.test.otel + http: + paths: + - backend: + service: + name: simplest-collector + port: + name: otlp-grpc + path: / + pathType: Prefix + - host: otlp-http.test.otel + http: + paths: + - backend: + service: + name: simplest-collector + port: + name: otlp-http + path: / + pathType: Prefix diff --git a/tests/e2e/ingress-subdomains/00-install.yaml b/tests/e2e/ingress-subdomains/00-install.yaml new file mode 100644 index 0000000000..3f2cff5686 --- /dev/null +++ b/tests/e2e/ingress-subdomains/00-install.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest +spec: + mode: "deployment" + ingress: + type: ingress + ruleType: subdomain + hostname: "test.otel" + annotations: + something.com: "true" + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/tests/e2e/ingress-subdomains/01-report-empty-otlphttp-spans.yaml b/tests/e2e/ingress-subdomains/01-report-empty-otlphttp-spans.yaml new file mode 100644 index 0000000000..b20290f8a7 --- /dev/null +++ b/tests/e2e/ingress-subdomains/01-report-empty-otlphttp-spans.yaml @@ -0,0 +1,8 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: | + #!/bin/bash + set -ex + # Export empty payload and check of collector accepted it with 2xx status code + for i in {1..40}; do curl --fail -ivX POST --resolve 'otlp-http.test.otel:80:127.0.0.1' http://otlp-http.test.otel:80/v1/traces -H "Content-Type: application/json" -d '{}' && break || sleep 1; done diff --git a/tests/e2e/ingress/00-assert.yaml b/tests/e2e/ingress/00-assert.yaml index 7d98dc4c40..5604513d10 100644 --- a/tests/e2e/ingress/00-assert.yaml +++ b/tests/e2e/ingress/00-assert.yaml @@ -3,6 +3,8 @@ apiVersion: apps/v1 kind: Deployment metadata: name: simplest-collector +status: + readyReplicas: 1 --- apiVersion: networking.k8s.io/v1 kind: Ingress @@ -38,10 +40,3 @@ spec: name: otlp-http path: /otlp-http pathType: Prefix - - backend: - service: - name: simplest-collector - port: - name: otlp-http-legac - path: /otlp-http-legacy - pathType: Prefix diff --git a/tests/e2e/ingress/00-install.yaml b/tests/e2e/ingress/00-install.yaml index 9da41295dc..a94b434a6c 100644 --- a/tests/e2e/ingress/00-install.yaml +++ b/tests/e2e/ingress/00-install.yaml @@ -18,11 +18,11 @@ spec: http: exporters: - logging: + debug: service: pipelines: traces: receivers: [otlp] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/instrumentation-python-multicontainer/00-install-collector.yaml b/tests/e2e/instrumentation-python-multicontainer/00-install-collector.yaml deleted file mode 100644 index f8e1e98e07..0000000000 --- a/tests/e2e/instrumentation-python-multicontainer/00-install-collector.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: opentelemetry.io/v1alpha1 -kind: OpenTelemetryCollector -metadata: - name: sidecar -spec: - mode: sidecar - config: | - receivers: - otlp: - protocols: - grpc: - http: - processors: - - exporters: - logging: - - service: - pipelines: - traces: - receivers: [otlp] - processors: [] - exporters: [logging] diff --git a/tests/e2e/instrumentation-python/00-install-collector.yaml b/tests/e2e/instrumentation-python/00-install-collector.yaml deleted file mode 100644 index f8e1e98e07..0000000000 --- a/tests/e2e/instrumentation-python/00-install-collector.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: opentelemetry.io/v1alpha1 -kind: OpenTelemetryCollector -metadata: - name: sidecar -spec: - mode: sidecar - config: | - receivers: - otlp: - protocols: - grpc: - http: - processors: - - exporters: - logging: - - service: - pipelines: - traces: - receivers: [otlp] - processors: [] - exporters: [logging] diff --git a/tests/e2e/instrumentation-sdk/00-install-collector.yaml b/tests/e2e/instrumentation-sdk/00-install-collector.yaml deleted file mode 100644 index f8e1e98e07..0000000000 --- a/tests/e2e/instrumentation-sdk/00-install-collector.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: opentelemetry.io/v1alpha1 -kind: OpenTelemetryCollector -metadata: - name: sidecar -spec: - mode: sidecar - config: | - receivers: - otlp: - protocols: - grpc: - http: - processors: - - exporters: - logging: - - service: - pipelines: - traces: - receivers: [otlp] - processors: [] - exporters: [logging] diff --git a/tests/e2e/managed-reconcile/00-assert.yaml b/tests/e2e/managed-reconcile/00-assert.yaml new file mode 100644 index 0000000000..2c6158305b --- /dev/null +++ b/tests/e2e/managed-reconcile/00-assert.yaml @@ -0,0 +1,48 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simplest-collector +status: + readyReplicas: 1 +spec: + template: + spec: + serviceAccountName: simplest-collector + +--- + +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector-headless +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 diff --git a/tests/e2e/managed-reconcile/00-install.yaml b/tests/e2e/managed-reconcile/00-install.yaml new file mode 100644 index 0000000000..c96dfca740 --- /dev/null +++ b/tests/e2e/managed-reconcile/00-install.yaml @@ -0,0 +1,22 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest +spec: + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] \ No newline at end of file diff --git a/tests/e2e/managed-reconcile/01-assert.yaml b/tests/e2e/managed-reconcile/01-assert.yaml new file mode 100644 index 0000000000..3cfdb7f49b --- /dev/null +++ b/tests/e2e/managed-reconcile/01-assert.yaml @@ -0,0 +1,72 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simplest-collector +status: + readyReplicas: 1 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector-headless +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + +--- + +apiVersion: v1 +kind: ConfigMap +metadata: + name: simplest-collector +data: + collector.yaml: | + receivers: + jaeger: + protocols: + grpc: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [jaeger, otlp] + processors: [] + exporters: [debug] diff --git a/tests/e2e/managed-reconcile/01-disable-reconciliation.yaml b/tests/e2e/managed-reconcile/01-disable-reconciliation.yaml new file mode 100644 index 0000000000..f837bce41c --- /dev/null +++ b/tests/e2e/managed-reconcile/01-disable-reconciliation.yaml @@ -0,0 +1,51 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest +spec: + managementState: unmanaged + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] + +--- +# change config map in Unmanaged mode and it should not get overridden as reconciliation is disabled +apiVersion: v1 +kind: ConfigMap +metadata: + name: simplest-collector +data: + collector.yaml: | + receivers: + jaeger: + protocols: + grpc: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [jaeger, otlp] + processors: [] + exporters: [debug] diff --git a/tests/e2e/managed-reconcile/02-assert.yaml b/tests/e2e/managed-reconcile/02-assert.yaml new file mode 100644 index 0000000000..f1fe0ee158 --- /dev/null +++ b/tests/e2e/managed-reconcile/02-assert.yaml @@ -0,0 +1,73 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simplest-collector +status: + readyReplicas: 1 +spec: + template: + spec: + serviceAccountName: simplest-collector + +--- + +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector-headless +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector +spec: + ports: + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + +--- + +apiVersion: v1 +kind: ConfigMap +metadata: + name: simplest-collector +data: + collector.yaml: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/tests/e2e/managed-reconcile/02-enable-reconciliation.yaml b/tests/e2e/managed-reconcile/02-enable-reconciliation.yaml new file mode 100644 index 0000000000..3740997bf4 --- /dev/null +++ b/tests/e2e/managed-reconcile/02-enable-reconciliation.yaml @@ -0,0 +1,23 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest +spec: + managementState: managed + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [debug] diff --git a/tests/e2e/multiple-configmaps/00-assert.yaml b/tests/e2e/multiple-configmaps/00-assert.yaml new file mode 100644 index 0000000000..14c929470a --- /dev/null +++ b/tests/e2e/multiple-configmaps/00-assert.yaml @@ -0,0 +1,51 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mount-test1 +data: + export.ini: | + - EXPORT=true +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mount-test2 +data: + otel.ini: | + - OTEL_ENDPOINT=localhost +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simplest-with-configmaps-collector +spec: + template: + spec: + volumes: + - name: otc-internal + configMap: + name: simplest-with-configmaps-collector + items: + - key: collector.yaml + path: collector.yaml + defaultMode: 420 + - name: configmap-mount-test1 + configMap: + name: mount-test1 + defaultMode: 420 + - name: configmap-mount-test2 + configMap: + name: mount-test2 + defaultMode: 420 + containers: + - name: otc-container + volumeMounts: + - name: otc-internal + mountPath: /conf + - name: configmap-mount-test1 + mountPath: /var/conf/configmap-mount-test1 + - name: configmap-mount-test2 + mountPath: /var/conf/dir/configmap-mount-test2 +status: + readyReplicas: 1 diff --git a/tests/e2e/multiple-configmaps/00-install.yaml b/tests/e2e/multiple-configmaps/00-install.yaml new file mode 100644 index 0000000000..0dc18245fd --- /dev/null +++ b/tests/e2e/multiple-configmaps/00-install.yaml @@ -0,0 +1,43 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mount-test1 +data: + export.ini: | + - EXPORT=true +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mount-test2 +data: + otel.ini: | + - OTEL_ENDPOINT=localhost +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest-with-configmaps +spec: + mode: "deployment" + configmaps: + - name: mount-test1 + mountpath: / + - name: mount-test2 + mountpath: /dir + config: | + receivers: + otlp: + protocols: + grpc: + http: + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] diff --git a/tests/e2e/prometheus-config-validation/00-assert.yaml b/tests/e2e/prometheus-config-validation/00-assert.yaml new file mode 100644 index 0000000000..3f4f177968 --- /dev/null +++ b/tests/e2e/prometheus-config-validation/00-assert.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: promreceiver-allocatorconfig-collector +status: + replicas: 1 + readyReplicas: 1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: promreceiver-allocatorconfig-targetallocator +status: + replicas: 1 + readyReplicas: 1 +--- +# Print TA and operator logs if test fails +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +collectors: + - selector: app.kubernetes.io/component=opentelemetry-targetallocator + - selector: app.kubernetes.io/component=opentelemetry-collector + - selector: app.kubernetes.io/name=opentelemetry-operator + namespace: opentelemetry-operator-system + container: manager diff --git a/tests/e2e/prometheus-config-validation/00-promreceiver-allocatorconfig.yaml b/tests/e2e/prometheus-config-validation/00-promreceiver-allocatorconfig.yaml new file mode 100644 index 0000000000..b248e0f30e --- /dev/null +++ b/tests/e2e/prometheus-config-validation/00-promreceiver-allocatorconfig.yaml @@ -0,0 +1,69 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ta +automountServiceAccountToken: true +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: pod-view +rules: +- apiGroups: [""] + resources: [ "pods" ] + verbs: [ "get", "list", "watch"] +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl -n $NAMESPACE create rolebinding default-view-$NAMESPACE --role=pod-view --serviceaccount=$NAMESPACE:ta +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: promreceiver-allocatorconfig +spec: + mode: statefulset + targetAllocator: + enabled: true + image: "local/opentelemetry-operator-targetallocator:e2e" + serviceAccount: ta + prometheusCR: + enabled: true + + config: | + receivers: + jaeger: + protocols: + grpc: + + # Collect own metrics + prometheus: + config: + scrape_configs: + - job_name: 'otel-collector' + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888' ] + target_allocator: + endpoint: http://promreceiver-allocatorconfig-targetallocator + interval: 30s + collector_id: ${POD_NAME} + + processors: + + exporters: + debug: + extensions: + health_check: + service: + telemetry: + metrics: + address: :8888 + extensions: + - health_check + pipelines: + traces: + receivers: [jaeger] + processors: [] + exporters: [debug] diff --git a/tests/e2e/prometheus-config-validation/01-assert.yaml b/tests/e2e/prometheus-config-validation/01-assert.yaml new file mode 100644 index 0000000000..07d50c63c4 --- /dev/null +++ b/tests/e2e/prometheus-config-validation/01-assert.yaml @@ -0,0 +1,28 @@ +## Uncomment this after https://github.com/open-telemetry/opentelemetry-operator/issues/958 is resolved +## Unfortunately kuttl doesn't have a way to skip tests + +# apiVersion: apps/v1 +# kind: StatefulSet +# metadata: +# name: labeldrop-collector +# status: +# replicas: 1 +# readyReplicas: 1 +# --- +# apiVersion: apps/v1 +# kind: Deployment +# metadata: +# name: labeldrop-targetallocator +# status: +# replicas: 1 +# readyReplicas: 1 +# --- +# # Print TA and operator logs if test fails +# apiVersion: kuttl.dev/v1beta1 +# kind: TestAssert +# collectors: +# - selector: app.kubernetes.io/component=opentelemetry-targetallocator +# - selector: app.kubernetes.io/component=opentelemetry-collector +# - selector: app.kubernetes.io/name=opentelemetry-operator +# namespace: opentelemetry-operator-system +# container: manager diff --git a/tests/e2e/prometheus-config-validation/01-promreceiver-labeldrop.yaml b/tests/e2e/prometheus-config-validation/01-promreceiver-labeldrop.yaml new file mode 100644 index 0000000000..45c9c3a2a1 --- /dev/null +++ b/tests/e2e/prometheus-config-validation/01-promreceiver-labeldrop.yaml @@ -0,0 +1,49 @@ +# Uncomment this after https://github.com/open-telemetry/opentelemetry-operator/issues/958 is resolved +# Unfortunately kuttl doesn't have a way to skip tests + +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: labeldrop +spec: + mode: statefulset + targetAllocator: + enabled: true + image: "local/opentelemetry-operator-targetallocator:e2e" + serviceAccount: ta + prometheusCR: + enabled: true + + config: | + receivers: + jaeger: + protocols: + grpc: + + # Collect own metrics + prometheus: + config: + scrape_configs: + - job_name: 'otel-collector' + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888' ] + metric_relabel_configs: + - action: labeldrop + regex: (id|name) + + target_allocator: + endpoint: http://labeldrop-targetallocator + interval: 30s + collector_id: ${POD_NAME} + + processors: + + exporters: + debug: + service: + pipelines: + traces: + receivers: [jaeger] + processors: [] + exporters: [debug] diff --git a/tests/e2e/prometheus-config-validation/02-assert.yaml b/tests/e2e/prometheus-config-validation/02-assert.yaml new file mode 100644 index 0000000000..33727878de --- /dev/null +++ b/tests/e2e/prometheus-config-validation/02-assert.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: promreceiver-allocatorconfig-extra-collector +status: + replicas: 1 + readyReplicas: 1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: promreceiver-allocatorconfig-extra-targetallocator +status: + replicas: 1 + readyReplicas: 1 +--- +# Print TA and operator logs if test fails +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +collectors: + - selector: app.kubernetes.io/component=opentelemetry-targetallocator + - selector: app.kubernetes.io/component=opentelemetry-collector + - selector: app.kubernetes.io/name=opentelemetry-operator + namespace: opentelemetry-operator-system + container: manager diff --git a/tests/e2e/prometheus-config-validation/02-promreceiver-allocatorconfig-extra.yaml b/tests/e2e/prometheus-config-validation/02-promreceiver-allocatorconfig-extra.yaml new file mode 100644 index 0000000000..9fe535501e --- /dev/null +++ b/tests/e2e/prometheus-config-validation/02-promreceiver-allocatorconfig-extra.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: promreceiver-allocatorconfig-extra +spec: + mode: statefulset + targetAllocator: + enabled: true + image: "local/opentelemetry-operator-targetallocator:e2e" + serviceAccount: ta + prometheusCR: + enabled: true + + config: | + receivers: + jaeger: + protocols: + grpc: + + # Collect own metrics + prometheus: + config: + scrape_configs: + - job_name: 'otel-collector' + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888' ] + target_allocator: + endpoint: http://promreceiver-allocatorconfig-extra-targetallocator + interval: 30s + collector_id: ${POD_NAME} + http_sd_config: + refresh_interval: 10s + + processors: + + exporters: + debug: + extensions: + health_check: + service: + telemetry: + metrics: + address: :8888 + extensions: + - health_check + pipelines: + traces: + receivers: [jaeger] + processors: [] + exporters: [debug] + diff --git a/tests/e2e/prometheus-config-validation/03-assert.yaml b/tests/e2e/prometheus-config-validation/03-assert.yaml new file mode 100644 index 0000000000..b9c6d5d5eb --- /dev/null +++ b/tests/e2e/prometheus-config-validation/03-assert.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: promreceiver-nopromconfig-collector +status: + replicas: 1 + readyReplicas: 1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: promreceiver-nopromconfig-targetallocator +status: + replicas: 1 + readyReplicas: 1 +--- +# Print TA and operator logs if test fails +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +collectors: + - selector: app.kubernetes.io/component=opentelemetry-targetallocator + - selector: app.kubernetes.io/component=opentelemetry-collector + - selector: app.kubernetes.io/name=opentelemetry-operator + namespace: opentelemetry-operator-system + container: manager diff --git a/tests/e2e/prometheus-config-validation/03-promreceiver-nopromconfig.yaml b/tests/e2e/prometheus-config-validation/03-promreceiver-nopromconfig.yaml new file mode 100644 index 0000000000..980f3b85de --- /dev/null +++ b/tests/e2e/prometheus-config-validation/03-promreceiver-nopromconfig.yaml @@ -0,0 +1,48 @@ +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: promreceiver-nopromconfig +spec: + mode: statefulset + targetAllocator: + enabled: true + image: "local/opentelemetry-operator-targetallocator:e2e" + serviceAccount: ta + prometheusCR: + enabled: true + + config: | + receivers: + jaeger: + protocols: + grpc: + + prometheus: + config: + scrape_configs: [] + target_allocator: + endpoint: http://promreceiver-nopromconfig-targetallocator + interval: 30s + collector_id: ${POD_NAME} + http_sd_config: + refresh_interval: 10s + + processors: + + exporters: + debug: + extensions: + health_check: + service: + telemetry: + metrics: + address: :8888 + extensions: + - health_check + pipelines: + traces: + receivers: [jaeger] + processors: [] + exporters: [debug] + diff --git a/tests/e2e/smoke-daemonset/00-assert.yaml b/tests/e2e/smoke-daemonset/00-assert.yaml new file mode 100644 index 0000000000..7dff619578 --- /dev/null +++ b/tests/e2e/smoke-daemonset/00-assert.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: daemonset-test-collector +spec: + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 +status: + numberMisscheduled: 0 + +--- +# This KUTTL assert uses the check-daemonset.sh script to ensure the number of ready pods in a daemonset matches the desired count, retrying until successful or a timeout occurs. The script is needed as the number of Kubernetes cluster nodes can vary and we cannot statically set desiredNumberScheduled and numberReady in the assert for daemonset status. + +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +commands: +- script: ./tests/e2e/smoke-daemonset/check-daemonset.sh diff --git a/tests/e2e/smoke-daemonset/00-install.yaml b/tests/e2e/smoke-daemonset/00-install.yaml new file mode 100644 index 0000000000..aa49f7f538 --- /dev/null +++ b/tests/e2e/smoke-daemonset/00-install.yaml @@ -0,0 +1,24 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: daemonset-test +spec: + mode: daemonset + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + config: | + receivers: + jaeger: + protocols: + grpc: + processors: + exporters: + debug: + service: + pipelines: + traces: + receivers: [jaeger] + processors: [] + exporters: [debug] diff --git a/tests/e2e/smoke-daemonset/check-daemonset.sh b/tests/e2e/smoke-daemonset/check-daemonset.sh new file mode 100755 index 0000000000..4e6c2d8062 --- /dev/null +++ b/tests/e2e/smoke-daemonset/check-daemonset.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Name of the daemonset to check +DAEMONSET_NAME="daemonset-test-collector" + +# Get the desired and ready pod counts for the daemonset +read DESIRED READY <<< $(kubectl get daemonset -n $NAMESPACE $DAEMONSET_NAME -o custom-columns=:status.desiredNumberScheduled,:status.numberReady --no-headers) + +# Check if the desired count matches the ready count +if [ "$DESIRED" -eq "$READY" ]; then + echo "Desired count ($DESIRED) matches the ready count ($READY) for $DAEMONSET_NAME." +else + echo "Desired count ($DESIRED) does not match the ready count ($READY) for $DAEMONSET_NAME." + exit 1 +fi diff --git a/tests/e2e/smoke-init-containers/00-assert.yaml b/tests/e2e/smoke-init-containers/00-assert.yaml new file mode 100644 index 0000000000..3f00937815 --- /dev/null +++ b/tests/e2e/smoke-init-containers/00-assert.yaml @@ -0,0 +1,59 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: simplest-collector +spec: + template: + spec: + initContainers: + - name: init-test-echo +status: + readyReplicas: 1 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector-headless +spec: + ports: + - appProtocol: grpc + name: jaeger-grpc + port: 14250 + protocol: TCP + targetPort: 14250 + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 + +--- + +apiVersion: v1 +kind: Service +metadata: + name: simplest-collector +spec: + ports: + - appProtocol: grpc + name: jaeger-grpc + port: 14250 + protocol: TCP + targetPort: 14250 + - appProtocol: grpc + name: otlp-grpc + port: 4317 + protocol: TCP + targetPort: 4317 + - appProtocol: http + name: otlp-http + port: 4318 + protocol: TCP + targetPort: 4318 diff --git a/tests/e2e/smoke-init-containers/00-install.yaml b/tests/e2e/smoke-init-containers/00-install.yaml new file mode 100644 index 0000000000..a0eb85cdea --- /dev/null +++ b/tests/e2e/smoke-init-containers/00-install.yaml @@ -0,0 +1,29 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: simplest +spec: + initContainers: + - name: init-test-echo + image: alpine + command: ['sh', '-c', 'sleep 5 && echo "this is a test"'] + config: | + receivers: + jaeger: + protocols: + grpc: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [jaeger,otlp] + processors: [] + exporters: [debug] diff --git a/tests/e2e/smoke-pod-annotations/00-install.yaml b/tests/e2e/smoke-pod-annotations/00-install.yaml index cce1909fa0..d1e17a83b3 100644 --- a/tests/e2e/smoke-pod-annotations/00-install.yaml +++ b/tests/e2e/smoke-pod-annotations/00-install.yaml @@ -16,11 +16,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/smoke-pod-labels/00-assert.yaml b/tests/e2e/smoke-pod-labels/00-assert.yaml new file mode 100644 index 0000000000..8bb507544b --- /dev/null +++ b/tests/e2e/smoke-pod-labels/00-assert.yaml @@ -0,0 +1,12 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: testlabel-collector + labels: + app.kubernetes.io/component: opentelemetry-collector + app.kubernetes.io/managed-by: opentelemetry-operator + app.kubernetes.io/name: testlabel-collector + app.kubernetes.io/part-of: opentelemetry + app.kubernetes.io/version: a0c6dea261b3d794971c23c5665173a39f0ce2aa09b9d88b753bd46776d7b05 +status: + readyReplicas: 1 diff --git a/tests/e2e/smoke-pod-labels/00-install.yaml b/tests/e2e/smoke-pod-labels/00-install.yaml new file mode 100644 index 0000000000..00843cf434 --- /dev/null +++ b/tests/e2e/smoke-pod-labels/00-install.yaml @@ -0,0 +1,30 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: testlabel +spec: + image: otel/opentelemetry-collector-contrib@sha256:a0c6dea261b3d794971c23c5665173a39f0ce2aa09b9d88b753bd46776d7b05b + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + memory_limiter: + check_interval: 1s + limit_percentage: 75 + spike_limit_percentage: 15 + batch: + send_batch_size: 10000 + timeout: 10s + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] diff --git a/tests/e2e/smoke-restarting-deployment/00-install.yaml b/tests/e2e/smoke-restarting-deployment/00-install.yaml index 7ff13711ee..705047cffd 100644 --- a/tests/e2e/smoke-restarting-deployment/00-install.yaml +++ b/tests/e2e/smoke-restarting-deployment/00-install.yaml @@ -10,10 +10,10 @@ spec: grpc: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/smoke-restarting-deployment/01-install-second-config.yaml b/tests/e2e/smoke-restarting-deployment/01-install-second-config.yaml index 78f90878bb..13856cf51e 100644 --- a/tests/e2e/smoke-restarting-deployment/01-install-second-config.yaml +++ b/tests/e2e/smoke-restarting-deployment/01-install-second-config.yaml @@ -13,11 +13,11 @@ spec: grpc: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger, otlp] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/smoke-sidecar-other-namespace/00-install.yaml b/tests/e2e/smoke-sidecar-other-namespace/00-install.yaml new file mode 100644 index 0000000000..125b485dbf --- /dev/null +++ b/tests/e2e/smoke-sidecar-other-namespace/00-install.yaml @@ -0,0 +1,29 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: kuttl-otel-sidecar-other-namespace +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar-for-my-app + namespace: kuttl-otel-sidecar-other-namespace +spec: + mode: sidecar + config: | + receivers: + jaeger: + protocols: + grpc: + processors: + + exporters: + debug: + + service: + pipelines: + traces: + receivers: [jaeger] + processors: [] + exporters: [debug] diff --git a/tests/e2e/smoke-sidecar-other-namespace/01-assert.yaml b/tests/e2e/smoke-sidecar-other-namespace/01-assert.yaml new file mode 100644 index 0000000000..af10887f64 --- /dev/null +++ b/tests/e2e/smoke-sidecar-other-namespace/01-assert.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Pod +metadata: + namespace: kuttl-otel-sidecar-other-namespace + annotations: + sidecar.opentelemetry.io/inject: "kuttl-otel-sidecar-other-namespace/sidecar-for-my-app" + labels: + app: my-pod-with-sidecar +spec: + containers: + - name: myapp + - name: otc-container + env: + - name: POD_NAME + - name: OTEL_CONFIG + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_POD_UID + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_RESOURCE_ATTRIBUTES +status: + phase: Running diff --git a/tests/e2e/smoke-sidecar-other-namespace/01-install-app.yaml b/tests/e2e/smoke-sidecar-other-namespace/01-install-app.yaml new file mode 100644 index 0000000000..8f5bfd137f --- /dev/null +++ b/tests/e2e/smoke-sidecar-other-namespace/01-install-app.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment-with-sidecar + namespace: kuttl-otel-sidecar-other-namespace +spec: + selector: + matchLabels: + app: my-pod-with-sidecar + replicas: 1 + template: + metadata: + labels: + app: my-pod-with-sidecar + annotations: + sidecar.opentelemetry.io/inject: "kuttl-otel-sidecar-other-namespace/sidecar-for-my-app" + spec: + containers: + - name: myapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main diff --git a/tests/e2e/smoke-sidecar/00-install.yaml b/tests/e2e/smoke-sidecar/00-install.yaml index 1d498c6268..ad33d3353e 100644 --- a/tests/e2e/smoke-sidecar/00-install.yaml +++ b/tests/e2e/smoke-sidecar/00-install.yaml @@ -12,11 +12,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/smoke-sidecar/01-assert.yaml b/tests/e2e/smoke-sidecar/01-assert.yaml index 358711a288..b4bd1b9603 100644 --- a/tests/e2e/smoke-sidecar/01-assert.yaml +++ b/tests/e2e/smoke-sidecar/01-assert.yaml @@ -11,6 +11,7 @@ spec: - name: otc-container env: - name: POD_NAME + - name: OTEL_CONFIG - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME - name: OTEL_RESOURCE_ATTRIBUTES_POD_UID - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME diff --git a/tests/e2e/smoke-sidecar/01-install-app.yaml b/tests/e2e/smoke-sidecar/01-install-app.yaml index 8a7396ef6d..2044f5d0d5 100644 --- a/tests/e2e/smoke-sidecar/01-install-app.yaml +++ b/tests/e2e/smoke-sidecar/01-install-app.yaml @@ -16,4 +16,4 @@ spec: spec: containers: - name: myapp - image: k8s.gcr.io/echoserver:1.4 + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main diff --git a/tests/e2e/smoke-simplest/00-assert.yaml b/tests/e2e/smoke-simplest/00-assert.yaml index f19dd651af..17365cdacd 100644 --- a/tests/e2e/smoke-simplest/00-assert.yaml +++ b/tests/e2e/smoke-simplest/00-assert.yaml @@ -28,11 +28,6 @@ spec: port: 4318 protocol: TCP targetPort: 4318 - - appProtocol: http - name: otlp-http-legacy - port: 55681 - protocol: TCP - targetPort: 4318 --- @@ -57,8 +52,3 @@ spec: port: 4318 protocol: TCP targetPort: 4318 - - appProtocol: http - name: otlp-http-legacy - port: 55681 - protocol: TCP - targetPort: 4318 diff --git a/tests/e2e/smoke-simplest/00-install.yaml b/tests/e2e/smoke-simplest/00-install.yaml index eb4cc2b39e..0fb69308c7 100644 --- a/tests/e2e/smoke-simplest/00-install.yaml +++ b/tests/e2e/smoke-simplest/00-install.yaml @@ -15,11 +15,11 @@ spec: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger,otlp] processors: [] - exporters: [logging] \ No newline at end of file + exporters: [debug] \ No newline at end of file diff --git a/tests/e2e/smoke-statefulset/00-install.yaml b/tests/e2e/smoke-statefulset/00-install.yaml index 99f1f3b592..86b39af574 100644 --- a/tests/e2e/smoke-statefulset/00-install.yaml +++ b/tests/e2e/smoke-statefulset/00-install.yaml @@ -11,10 +11,10 @@ spec: grpc: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/smoke-targetallocator/00-assert.yaml b/tests/e2e/smoke-targetallocator/00-assert.yaml index c180946c0e..f46af1dd15 100644 --- a/tests/e2e/smoke-targetallocator/00-assert.yaml +++ b/tests/e2e/smoke-targetallocator/00-assert.yaml @@ -13,6 +13,7 @@ metadata: status: replicas: 1 readyReplicas: 1 + observedGeneration: 1 --- apiVersion: v1 kind: ConfigMap @@ -20,9 +21,32 @@ metadata: name: stateful-targetallocator --- apiVersion: v1 -kind: ServiceAccount +kind: ConfigMap metadata: - name: stateful-targetallocator + name: stateful-collector +data: + collector.yaml: | + exporters: + debug: null + processors: null + receivers: + jaeger: + protocols: + grpc: null + prometheus: + config: {} + target_allocator: + collector_id: ${POD_NAME} + endpoint: http://stateful-targetallocator:80 + interval: 30s + service: + pipelines: + traces: + exporters: + - debug + processors: [] + receivers: + - jaeger --- # Print TA pod logs if test fails apiVersion: kuttl.dev/v1beta1 diff --git a/tests/e2e/smoke-targetallocator/00-install.yaml b/tests/e2e/smoke-targetallocator/00-install.yaml index 0bbdc5ce1a..e61d014c5f 100644 --- a/tests/e2e/smoke-targetallocator/00-install.yaml +++ b/tests/e2e/smoke-targetallocator/00-install.yaml @@ -28,6 +28,8 @@ spec: enabled: true serviceAccount: ta image: "local/opentelemetry-operator-targetallocator:e2e" + prometheusCR: + enabled: true config: | receivers: jaeger: @@ -42,14 +44,14 @@ spec: scrape_interval: 10s static_configs: - targets: [ '0.0.0.0:8888' ] - + processors: - + exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/smoke-targetallocator/01-assert.yaml b/tests/e2e/smoke-targetallocator/01-assert.yaml new file mode 100644 index 0000000000..e420b03487 --- /dev/null +++ b/tests/e2e/smoke-targetallocator/01-assert.yaml @@ -0,0 +1,8 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: stateful-targetallocator +status: + replicas: 1 + readyReplicas: 1 + observedGeneration: 2 diff --git a/tests/e2e/smoke-targetallocator/01-change-ta-config.yaml b/tests/e2e/smoke-targetallocator/01-change-ta-config.yaml new file mode 100644 index 0000000000..2ded7614a6 --- /dev/null +++ b/tests/e2e/smoke-targetallocator/01-change-ta-config.yaml @@ -0,0 +1,39 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: stateful +spec: + mode: statefulset + targetAllocator: + enabled: true + serviceAccount: ta + image: "local/opentelemetry-operator-targetallocator:e2e" + prometheusCR: + enabled: true + serviceMonitorSelector: + key: value # this is just an arbitrary change to the target allocator config + config: | + receivers: + jaeger: + protocols: + grpc: + + # Collect own metrics + prometheus: + config: + scrape_configs: + - job_name: 'otel-collector' + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888' ] + + processors: + + exporters: + debug: + service: + pipelines: + traces: + receivers: [jaeger] + processors: [] + exporters: [debug] diff --git a/tests/e2e/statefulset-features/00-install.yaml b/tests/e2e/statefulset-features/00-install.yaml index 695d3a378c..d144a8ec83 100644 --- a/tests/e2e/statefulset-features/00-install.yaml +++ b/tests/e2e/statefulset-features/00-install.yaml @@ -25,10 +25,10 @@ spec: grpc: processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/statefulset-features/01-assert.yaml b/tests/e2e/statefulset-features/01-assert.yaml new file mode 100644 index 0000000000..fdccb0fadd --- /dev/null +++ b/tests/e2e/statefulset-features/01-assert.yaml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: stateful-collector +spec: + podManagementPolicy: Parallel + template: + spec: + containers: + - args: + - --config=/conf/collector.yaml + name: otc-container + volumeMounts: + - mountPath: /conf + name: otc-internal + - mountPath: /usr/share/testvolume + name: testvolume + volumes: + - configMap: + items: + - key: collector.yaml + path: collector.yaml + name: stateful-collector + name: otc-internal + - emptyDir: {} + name: testvolume + volumeClaimTemplates: + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: testvolume + spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1Gi + volumeMode: Filesystem +status: + replicas: 3 + readyReplicas: 3 diff --git a/tests/e2e/statefulset-features/01-update-volume-claim-templates.yaml b/tests/e2e/statefulset-features/01-update-volume-claim-templates.yaml new file mode 100644 index 0000000000..bed1ca77c3 --- /dev/null +++ b/tests/e2e/statefulset-features/01-update-volume-claim-templates.yaml @@ -0,0 +1,34 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: stateful +spec: + mode: statefulset + replicas: 3 + volumes: + - name: testvolume + volumeMounts: + - name: testvolume + mountPath: /usr/share/testvolume + volumeClaimTemplates: + - metadata: + name: testvolume + spec: + accessModes: [ "ReadWriteMany" ] # change accessMode to trigger statefulset recreation. + resources: + requests: + storage: 1Gi + config: | + receivers: + jaeger: + protocols: + grpc: + processors: + exporters: + debug: + service: + pipelines: + traces: + receivers: [jaeger] + processors: [] + exporters: [debug] diff --git a/tests/e2e/targetallocator-features/00-assert.yaml b/tests/e2e/targetallocator-features/00-assert.yaml index fa49547bb6..dc002c7676 100644 --- a/tests/e2e/targetallocator-features/00-assert.yaml +++ b/tests/e2e/targetallocator-features/00-assert.yaml @@ -47,11 +47,24 @@ metadata: spec: template: spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: ingress-ready + operator: In + values: + - "true" + securityContext: + runAsUser: 1000 containers: - name: ta-container args: - --enable-prometheus-cr-watcher env: + - name: TEST_ENV + value: "test" - name: OTELCOL_NAMESPACE valueFrom: fieldRef: @@ -59,6 +72,20 @@ spec: volumeMounts: - mountPath: /conf name: ta-internal + readinessProbe: + httpGet: + path: /readyz + successThreshold: 1 + failureThreshold: 3 + timeoutSeconds: 1 + periodSeconds: 10 + livenessProbe: + httpGet: + path: /livez + successThreshold: 1 + failureThreshold: 3 + timeoutSeconds: 1 + periodSeconds: 10 volumes: - configMap: items: diff --git a/tests/e2e/targetallocator-features/00-install.yaml b/tests/e2e/targetallocator-features/00-install.yaml index 40849c7e3f..b62935db60 100644 --- a/tests/e2e/targetallocator-features/00-install.yaml +++ b/tests/e2e/targetallocator-features/00-install.yaml @@ -12,6 +12,9 @@ rules: - apiGroups: [""] resources: [ "pods" ] verbs: [ "get", "list", "watch"] +- apiGroups: ["monitoring.coreos.com"] + resources: ["servicemonitors"] + verbs: ["get", "list", "watch"] --- apiVersion: kuttl.dev/v1beta1 kind: TestStep @@ -41,8 +44,22 @@ spec: enabled: true image: "local/opentelemetry-operator-targetallocator:e2e" serviceAccount: ta + securityContext: + runAsUser: 1000 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: ingress-ready + operator: In + values: + - "true" prometheusCR: enabled: true + env: + - name: TEST_ENV + value: "test" config: | receivers: jaeger: @@ -53,18 +70,27 @@ spec: prometheus: config: scrape_configs: - - job_name: 'otel-collector' - scrape_interval: 10s - static_configs: - - targets: [ '0.0.0.0:8888' ] + - job_name: 'otel-collector' + scrape_interval: 10s + static_configs: + - targets: [ '0.0.0.0:8888' ] + relabel_configs: + - regex: __meta_kubernetes_node_label_(.+) + action: labelmap + replacement: $$1 + - regex: test_.* + action: labeldrop + - regex: 'metrica_*|metricb.*' + action: labelkeep + replacement: $$1 processors: exporters: - logging: + debug: service: pipelines: traces: receivers: [jaeger] processors: [] - exporters: [logging] + exporters: [debug] diff --git a/tests/e2e/targetallocator-features/01-assert.yaml b/tests/e2e/targetallocator-features/01-assert.yaml new file mode 100644 index 0000000000..7e1a837f98 --- /dev/null +++ b/tests/e2e/targetallocator-features/01-assert.yaml @@ -0,0 +1,14 @@ +# waiting for liveness probe to work +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 5 +--- +apiVersion: v1 +kind: Pod +metadata: + labels: + app.kubernetes.io/component: opentelemetry-targetallocator +status: + containerStatuses: + - name: ta-container + restartCount: 0 diff --git a/tests/e2e/targetallocator-features/01-liveness.yaml b/tests/e2e/targetallocator-features/01-liveness.yaml new file mode 100644 index 0000000000..db15c603f3 --- /dev/null +++ b/tests/e2e/targetallocator-features/01-liveness.yaml @@ -0,0 +1,5 @@ +# waiting for liveness probe to work +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: +- command: sleep 35 diff --git a/tests/e2e/targetallocator-prometheuscr/00-assert.yaml b/tests/e2e/targetallocator-prometheuscr/00-assert.yaml new file mode 100644 index 0000000000..cd45341931 --- /dev/null +++ b/tests/e2e/targetallocator-prometheuscr/00-assert.yaml @@ -0,0 +1,53 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: prometheus-cr-collector +status: + replicas: 1 + readyReplicas: 1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: prometheus-cr-targetallocator +status: + replicas: 1 + readyReplicas: 1 + observedGeneration: 1 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: prometheus-cr-targetallocator +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: prometheus-cr-collector +data: + collector.yaml: | + exporters: + prometheus: + endpoint: 0.0.0.0:9090 + processors: null + receivers: + prometheus: + config: {} + target_allocator: + collector_id: ${POD_NAME} + endpoint: http://prometheus-cr-targetallocator:80 + interval: 30s + service: + pipelines: + metrics: + exporters: + - prometheus + processors: [] + receivers: + - prometheus +--- +# Print TA pod logs if test fails +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +collectors: + - selector: app.kubernetes.io/managed-by=opentelemetry-operator diff --git a/tests/e2e/targetallocator-prometheuscr/00-install.yaml b/tests/e2e/targetallocator-prometheuscr/00-install.yaml new file mode 100644 index 0000000000..148763a3f6 --- /dev/null +++ b/tests/e2e/targetallocator-prometheuscr/00-install.yaml @@ -0,0 +1,137 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: ta +automountServiceAccountToken: true +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: collector +automountServiceAccountToken: true +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: ta +rules: +- apiGroups: [""] + resources: + - pods + - nodes + - services + - endpoints + - configmaps + - secrets + - namespaces + verbs: + - get + - watch + - list +- apiGroups: ["apps"] + resources: + - statefulsets + - services + - endpoints + verbs: + - get + - watch + - list +- apiGroups: ["discovery.k8s.io"] + resources: + - endpointslices + verbs: + - get + - watch + - list +- apiGroups: ["networking.k8s.io"] + resources: + - ingresses + verbs: + - get + - watch + - list +- apiGroups: ["monitoring.coreos.com"] + resources: + - servicemonitors + - podmonitors + verbs: + - get + - watch + - list +- nonResourceURLs: ["/metrics"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: collector +rules: +- apiGroups: [""] + resources: + - pods + - nodes + - nodes/metrics + - services + - endpoints + verbs: + - get + - watch + - list +- apiGroups: ["networking.k8s.io"] + resources: + - ingresses + verbs: + - get + - watch + - list +- nonResourceURLs: ["/metrics", "/metrics/cadvisor"] + verbs: ["get"] +--- +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - command: kubectl create clusterrolebinding ta-$NAMESPACE --clusterrole=ta --serviceaccount=$NAMESPACE:ta + - command: kubectl create clusterrolebinding collector-$NAMESPACE --clusterrole=collector --serviceaccount=$NAMESPACE:collector +--- +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: prometheus-cr +spec: + mode: statefulset + serviceAccount: collector + targetAllocator: + enabled: true + serviceAccount: ta + image: "local/opentelemetry-operator-targetallocator:e2e" + prometheusCR: + enabled: true + config: | + receivers: + prometheus: + config: + scrape_configs: [] + + processors: + + exporters: + prometheus: + endpoint: 0.0.0.0:9090 + service: + pipelines: + metrics: + receivers: [prometheus] + processors: [] + exporters: [prometheus] +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: prometheus-cr +spec: + endpoints: + - port: monitoring + selector: + matchLabels: + app.kubernetes.io/managed-by: opentelemetry-operator diff --git a/tests/e2e/targetallocator-prometheuscr/01-assert.yaml b/tests/e2e/targetallocator-prometheuscr/01-assert.yaml new file mode 100644 index 0000000000..b3b95bf022 --- /dev/null +++ b/tests/e2e/targetallocator-prometheuscr/01-assert.yaml @@ -0,0 +1,20 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: check-metrics +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: check-ta-jobs +status: + succeeded: 1 +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: check-ta-scrape-configs +status: + succeeded: 1 \ No newline at end of file diff --git a/tests/e2e/targetallocator-prometheuscr/01-install.yaml b/tests/e2e/targetallocator-prometheuscr/01-install.yaml new file mode 100644 index 0000000000..9c8e8d2ec4 --- /dev/null +++ b/tests/e2e/targetallocator-prometheuscr/01-install.yaml @@ -0,0 +1,48 @@ +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: check-metrics +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: check-metrics + image: curlimages/curl + args: + - /bin/sh + - -c + - curl -s http://prometheus-cr-collector:9090/metrics | grep "otelcol_exporter_sent_metric_points_total{" +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: check-ta-jobs +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: check-metrics + image: curlimages/curl + args: + - /bin/sh + - -c + - curl -s http://prometheus-cr-targetallocator/scrape_configs | grep "prometheus-cr" +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: check-ta-scrape-configs +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: check-metrics + image: curlimages/curl + args: + - /bin/sh + - -c + - curl -s http://prometheus-cr-targetallocator/jobs | grep "prometheus-cr" \ No newline at end of file diff --git a/tests/instrumentation-e2e-apps/apache-httpd/Dockerfile b/tests/instrumentation-e2e-apps/apache-httpd/Dockerfile new file mode 100644 index 0000000000..ee13146083 --- /dev/null +++ b/tests/instrumentation-e2e-apps/apache-httpd/Dockerfile @@ -0,0 +1,5 @@ + +FROM httpd:2.4 + +RUN sed -i "s#Listen 80#Listen 8080#g" /usr/local/apache2/conf/httpd.conf +RUN chmod 777 -R /usr/local/apache2/ \ No newline at end of file diff --git a/tests/instrumentation-e2e-apps/dotnet/Dockerfile b/tests/instrumentation-e2e-apps/dotnet/Dockerfile new file mode 100644 index 0000000000..c7f2eb8330 --- /dev/null +++ b/tests/instrumentation-e2e-apps/dotnet/Dockerfile @@ -0,0 +1,18 @@ +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:7.0.400-bookworm-slim-amd64 AS build +ARG TARGETARCH +WORKDIR /source + +RUN dotnet new webapp -o MyWebApp --no-https -f net7.0 + +WORKDIR /source/MyWebApp +RUN dotnet publish -a $TARGETARCH -o /app + +FROM mcr.microsoft.com/dotnet/aspnet:7.0.10-bullseye-slim + +ENV DOTNET_ROLL_FORWARD=Major +ENV DOTNET_ROLL_FORWARD_PRE_RELEASE=1 + +WORKDIR /app +COPY --from=build /app/ . + +ENTRYPOINT ["./MyWebApp"] \ No newline at end of file diff --git a/tests/instrumentation-e2e-apps/golang/Dockerfile b/tests/instrumentation-e2e-apps/golang/Dockerfile new file mode 100644 index 0000000000..fd80c06f1b --- /dev/null +++ b/tests/instrumentation-e2e-apps/golang/Dockerfile @@ -0,0 +1,8 @@ +FROM golang:1.21-alpine as builder + +COPY main.go . +RUN CGO_ENABLED=0 GOOS=linux GO111MODULE=on go build -o app main.go + +FROM scratch +COPY --from=builder /go/app . +ENTRYPOINT ["./app"] diff --git a/tests/instrumentation-e2e-apps/golang/main.go b/tests/instrumentation-e2e-apps/golang/main.go new file mode 100644 index 0000000000..ad36410372 --- /dev/null +++ b/tests/instrumentation-e2e-apps/golang/main.go @@ -0,0 +1,37 @@ +// 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 main + +import ( + "fmt" + "net/http" + "time" +) + +func main() { + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Println("Hi") + }) + + server := &http.Server{ + Addr: ":8080", + ReadHeaderTimeout: 3 * time.Second, + } + + err := server.ListenAndServe() + if err != nil { + panic(err) + } +} diff --git a/tests/instrumentation-e2e-apps/java/DemoApplication.java b/tests/instrumentation-e2e-apps/java/DemoApplication.java new file mode 100644 index 0000000000..ca32ea8cc1 --- /dev/null +++ b/tests/instrumentation-e2e-apps/java/DemoApplication.java @@ -0,0 +1,23 @@ +package com.example.app; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + + +@RestController +@SpringBootApplication +public class DemoApplication { + + @RequestMapping("/") + String home() { + return "Hello World!"; + } + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} \ No newline at end of file diff --git a/tests/instrumentation-e2e-apps/java/Dockerfile b/tests/instrumentation-e2e-apps/java/Dockerfile new file mode 100644 index 0000000000..4e06136ea7 --- /dev/null +++ b/tests/instrumentation-e2e-apps/java/Dockerfile @@ -0,0 +1,23 @@ +FROM --platform=$BUILDPLATFORM eclipse-temurin:17-jdk-focal as builder + +RUN apt update +RUN apt install zip unzip -y + +RUN mkdir /app + +# In some archs, there could be probems setting the executable permissions +RUN mkdir /app/.gradle +RUN chmod -R +x /root/ + +RUN curl -s "https://get.sdkman.io" | bash +RUN bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && sdk install springboot && spring init /app" + +WORKDIR /app +COPY DemoApplication.java /app/src/main/java/com/example/app/ +COPY build.gradle . +RUN ./gradlew bootJar --no-daemon + +FROM eclipse-temurin:17.0.8.1_1-jre + +COPY --from=builder /app/build/libs/app-0.0.1-SNAPSHOT.jar . +ENTRYPOINT ["java", "-jar", "app-0.0.1-SNAPSHOT.jar"] diff --git a/tests/instrumentation-e2e-apps/java/build.gradle b/tests/instrumentation-e2e-apps/java/build.gradle new file mode 100644 index 0000000000..99bd136931 --- /dev/null +++ b/tests/instrumentation-e2e-apps/java/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.1.3' + id 'io.spring.dependency-management' version '1.1.3' +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '17' +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.boot:spring-boot-starter-web-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/tests/instrumentation-e2e-apps/nodejs/Dockerfile b/tests/instrumentation-e2e-apps/nodejs/Dockerfile new file mode 100644 index 0000000000..7d624b85e0 --- /dev/null +++ b/tests/instrumentation-e2e-apps/nodejs/Dockerfile @@ -0,0 +1,5 @@ +FROM node:20-alpine3.17 +RUN npm install express -g --verbose +COPY index.js . + +ENTRYPOINT ["node", "index.js"] \ No newline at end of file diff --git a/tests/instrumentation-e2e-apps/nodejs/index.js b/tests/instrumentation-e2e-apps/nodejs/index.js new file mode 100644 index 0000000000..17e8f5ee50 --- /dev/null +++ b/tests/instrumentation-e2e-apps/nodejs/index.js @@ -0,0 +1,11 @@ +const express = require('express') +const app = express() +const port = 3000 + +app.get('/', (req, res) => { + res.send('Hello World!') +}) + +app.listen(port, () => { + console.log("Hi") +}) diff --git a/tests/instrumentation-e2e-apps/python/Dockerfile b/tests/instrumentation-e2e-apps/python/Dockerfile new file mode 100644 index 0000000000..e6558779b8 --- /dev/null +++ b/tests/instrumentation-e2e-apps/python/Dockerfile @@ -0,0 +1,7 @@ +FROM python:alpine3.18 + +COPY requirements.txt . +RUN pip install -r requirements.txt +COPY app.py . + +ENTRYPOINT ["flask", "run", "-p", "8080"] diff --git a/tests/instrumentation-e2e-apps/python/app.py b/tests/instrumentation-e2e-apps/python/app.py new file mode 100644 index 0000000000..32a35d6938 --- /dev/null +++ b/tests/instrumentation-e2e-apps/python/app.py @@ -0,0 +1,10 @@ +from flask import Flask + +app = Flask(__name__) + +@app.route('/') +def index(): + return "Hi" + +if __name__ == "__main__": + app.run(host='0.0.0.0') diff --git a/tests/instrumentation-e2e-apps/python/requirements.txt b/tests/instrumentation-e2e-apps/python/requirements.txt new file mode 100644 index 0000000000..cc35792f29 --- /dev/null +++ b/tests/instrumentation-e2e-apps/python/requirements.txt @@ -0,0 +1 @@ +Flask==2.3.3 \ No newline at end of file diff --git a/versions.txt b/versions.txt index 1f281296be..17d7d500cb 100644 --- a/versions.txt +++ b/versions.txt @@ -2,26 +2,40 @@ # 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.61.0 +opentelemetry-collector=0.90.1 # Represents the current release of the OpenTelemetry Operator. -operator=0.61.0 +operator=0.90.0 # Represents the current release of the Target Allocator. -targetallocator=0.60.0 +targetallocator=0.90.0 + +# Represents the current release of the Operator OpAMP Bridge. +operator-opamp-bridge=0.90.0 # Represents the current release of Java instrumentation. # Should match autoinstrumentation/java/version.txt -autoinstrumentation-java=1.11.1 +autoinstrumentation-java=1.32.0 # Represents the current release of NodeJS instrumentation. # Should match value in autoinstrumentation/nodejs/package.json -autoinstrumentation-nodejs=0.31.0 +autoinstrumentation-nodejs=0.44.0 # Represents the current release of Python instrumentation. # Should match value in autoinstrumentation/python/requirements.txt -autoinstrumentation-python=0.34b0 +autoinstrumentation-python=0.41b0 # Represents the current release of DotNet instrumentation. # Should match autoinstrumentation/dotnet/version.txt -autoinstrumentation-dotnet=0.3.1-beta.1 +autoinstrumentation-dotnet=1.2.0 + +# Represents the current release of Go instrumentation. +autoinstrumentation-go=v0.8.0-alpha + +# Represents the current release of Apache HTTPD instrumentation. +# Should match autoinstrumentation/apache-httpd/version.txt +autoinstrumentation-apache-httpd=1.0.3 + +# Represents the current release of Apache Nginx instrumentation. +# Should match autoinstrumentation/apache-httpd/version.txt +autoinstrumentation-nginx=1.0.3