Files
substrate/cmd/atecontroller/main_test.go
T
Krisztian F c155efd1ac feat(otel): onboard atecontroller to the OTLP path (#754)
atecontroller had no OTel at all. Dev-mode zap logger, and its
controller-runtime metrics were only available on a :8080 that we don't
scrape.

After this PR, logs go through the shared slog handler (plus a
`--log-level` flag to match the other binaries), and
controller-runtime's Prometheus registry is bridged onto the OTLP reader
so the reconcile/workqueue metrics actually reach the collector.
Filtering these out is a pipeline responsibility. Also added otelgrpc to
the ateapi client, which was untraced.

This unblocks #564 the workperpool metrics, cc @Angelawork, @JeffLuoo:
there's a working `MeterProvider` to use for
`ate.workerpool.desired_workers`/`ready_workers`.

Couple of things to mention for review:

-`InitMetricsPushOnly`, not `InitMetrics`, even though we do serve
:8080. That port is controller-runtime's own private registry, not the
global one `serverboot.metricsMux` serves, so a pull reader there would
collect into something we never expose.
- Bridge is pinned to v0.68.0 to match otelgrpc. Wanted to go to
v0.70.0, but that requires otel/sdk/metric 1.45.0 and pulls the whole
SDK up with it (406 vendor files instead of 81). Happy to do that bump
separately.
- zap/zapr fall out of go.mod since atecontroller was the last importer.
- I included an OTel collector image bump from the early 2024 (!) one to
latest, which was breaking exposing native histograms

- [x] Tests pass
- [x] Appropriate changes to documentation are included in the PR
2026-08-06 10:25:03 -04:00

81 lines
2.5 KiB
Go

// Copyright 2026 Google LLC
//
// 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"
"log/slog"
"slices"
"strings"
"testing"
prombridge "go.opentelemetry.io/contrib/bridges/prometheus"
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
)
func TestNewControllerRuntimeLoggerVerbosity(t *testing.T) {
const probe = "verbosity-probe"
tests := []struct {
name string
level slog.Level
verbosity int
wantLog bool
}{
{name: "info keeps V(0)", level: slog.LevelInfo, verbosity: 0, wantLog: true},
{name: "info drops V(1)", level: slog.LevelInfo, verbosity: 1, wantLog: false},
{name: "debug keeps V(1)", level: slog.LevelDebug, verbosity: 1, wantLog: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var buf strings.Builder
log := newControllerRuntimeLogger(slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: tt.level}))
log.V(tt.verbosity).Info(probe)
if got := strings.Contains(buf.String(), probe); got != tt.wantLog {
t.Errorf("logged = %v, want %v (output %q)", got, tt.wantLog, buf.String())
}
})
}
}
// The e2e read-back asserts atecontroller reaches the collector, which holds only
// because the bridged registry already has series before any manager starts: the
// controller_runtime_* vectors are empty until controllers register, so the Go and
// process collectors are what make the first push non-empty.
func TestBridgedRegistryProducesBeforeManagerStart(t *testing.T) {
t.Parallel()
produced, err := prombridge.NewMetricProducer(prombridge.WithGatherer(ctrlmetrics.Registry)).
Produce(context.Background())
if err != nil {
t.Fatalf("Produce: %v", err)
}
var names []string
for _, sm := range produced {
for _, m := range sm.Metrics {
names = append(names, m.Name)
}
}
if len(names) == 0 {
t.Fatal("bridging controller-runtime's registry produced no metrics")
}
if !slices.ContainsFunc(names, func(n string) bool { return strings.HasPrefix(n, "go_") }) {
t.Errorf("no go_* family in %v", names)
}
}