From ee45bc3333b0c9935456c08275b9c0caaf1d8830 Mon Sep 17 00:00:00 2001 From: Maksym Pavlenko Date: Mon, 21 Sep 2026 13:36:20 -0700 Subject: [PATCH] shim: use a stable OpenTelemetry service name Use containerd-shim-runc-v2 as the service name and report the supplied shim ID and build version through service.instance.id and service.version. Preserve inherited resource attributes while overriding the shim identity fields. Test resource decoding and inherited attribute precedence with the OpenTelemetry SDK. Signed-off-by: Maksym Pavlenko --- .../manager/manager_linux.go | 17 +++- .../manager/manager_linux_test.go | 84 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 cmd/containerd-shim-runc-v2/manager/manager_linux_test.go diff --git a/cmd/containerd-shim-runc-v2/manager/manager_linux.go b/cmd/containerd-shim-runc-v2/manager/manager_linux.go index 4e2f1c1195..ee7e281308 100644 --- a/cmd/containerd-shim-runc-v2/manager/manager_linux.go +++ b/cmd/containerd-shim-runc-v2/manager/manager_linux.go @@ -24,10 +24,12 @@ import ( "fmt" "io" "net" + "net/url" "os" "os/exec" "path/filepath" goruntime "runtime" + "strings" "syscall" "time" @@ -105,7 +107,20 @@ func newCommand(ctx context.Context, id, containerdAddress, containerdTTRPCAddre cmd := exec.Command(self, args...) cmd.Dir = cwd cmd.Env = append(os.Environ(), "GOMAXPROCS=4") - cmd.Env = append(cmd.Env, "OTEL_SERVICE_NAME=containerd-shim-"+id) + // Preserve inherited resource attributes, but override the daemon's identity. + // The OpenTelemetry SDK uses the last value for duplicate resource keys. + var attrs []string + if inherited := strings.TrimSpace(os.Getenv("OTEL_RESOURCE_ATTRIBUTES")); inherited != "" { + attrs = append(attrs, inherited) + } + attrs = append(attrs, + "service.instance.id="+url.PathEscape(id), + "service.version="+url.PathEscape(version.Version), + ) + cmd.Env = append(cmd.Env, + "OTEL_SERVICE_NAME=containerd-shim-runc-v2", + "OTEL_RESOURCE_ATTRIBUTES="+strings.Join(attrs, ","), + ) cmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: true, diff --git a/cmd/containerd-shim-runc-v2/manager/manager_linux_test.go b/cmd/containerd-shim-runc-v2/manager/manager_linux_test.go new file mode 100644 index 0000000000..49e07ad301 --- /dev/null +++ b/cmd/containerd-shim-runc-v2/manager/manager_linux_test.go @@ -0,0 +1,84 @@ +/* + Copyright The containerd 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 manager + +import ( + "context" + "strings" + "testing" + + "github.com/containerd/containerd/v2/pkg/namespaces" + "github.com/containerd/containerd/v2/version" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/resource" +) + +func TestNewCommandTracingResource(t *testing.T) { + for _, tc := range []struct { + name string + inherited string + attributes map[string]string + }{ + {name: "empty"}, + {name: "whitespace", inherited: " "}, + { + name: "inherited", + inherited: "service.name=containerd,service.instance.id=daemon,service.version=old,service.namespace=runtime,host.name=node,custom.value=a%2Cb%3Dc%25", + attributes: map[string]string{ + "service.namespace": "runtime", + "host.name": "node", + "custom.value": "a,b=c%", + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Setenv("OTEL_SERVICE_NAME", "containerd") + t.Setenv("OTEL_RESOURCE_ATTRIBUTES", tc.inherited) + ctx := namespaces.WithNamespace(context.Background(), "test-ns") + for _, id := range []string{"test-shim", "another-shim"} { + cmd, err := newCommand(ctx, id, "/containerd.sock", "/containerd.sock.ttrpc", false) + require.NoError(t, err) + // Environ applies exec.Cmd's duplicate-variable handling. Read the + // resulting resource with the SDK without using its cached default. + for _, entry := range cmd.Environ() { + key, value, _ := strings.Cut(entry, "=") + if key == "OTEL_SERVICE_NAME" || key == "OTEL_RESOURCE_ATTRIBUTES" { + t.Setenv(key, value) + } + } + res, err := resource.New(ctx, resource.WithFromEnv()) + require.NoError(t, err) + attrs := res.Set() + for key, expected := range map[string]string{ + "service.name": "containerd-shim-runc-v2", + "service.instance.id": id, + "service.version": version.Version, + } { + value, ok := attrs.Value(attribute.Key(key)) + require.True(t, ok, key) + require.Equal(t, expected, value.AsString(), key) + } + for key, expected := range tc.attributes { + value, ok := attrs.Value(attribute.Key(key)) + require.True(t, ok, key) + require.Equal(t, expected, value.AsString(), key) + } + } + }) + } +}