Skip to content

Commit

Permalink
Still provide an owner even when /etc/passwd doesn't have the UID (#16)
Browse files Browse the repository at this point in the history
Especially for using in Docker where USER is otherwise unaccompanied by a `mkuser` or `useradd` or similar RUN call.
  • Loading branch information
greed42 authored Jan 16, 2023
1 parent 83d36de commit f05ed8c
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 1 deletion.
16 changes: 15 additions & 1 deletion pkg/tracing/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,21 @@ func NewProviderFromEnv(ctx context.Context, resourceOptions ...resource.Option)
resourceOptions,
// Allow environment variables to override constant attributes provided by the caller.
resource.WithFromEnv(),
resource.WithProcess(),
// We need to replace the default implementation of WithProcessOwner, as it can fail
// if there is no passwd file (for example in Docker containers without /etc/passwd
// but with CGO enabled). However, there is no straight-forward way to produce a new
// With... grouping, due to package visibility rules. (And there is no way, also due
// to visibility, to replace the default process owner provider.) So we have to enumerate
// _all_ the calls WithProcess is equivalent to, so we can change the process owner
// implementation.
resource.WithProcessPID(),
resource.WithProcessExecutableName(),
resource.WithProcessExecutablePath(),
resource.WithProcessCommandArgs(),
WithSafeProcessOwner(),
resource.WithProcessRuntimeName(),
resource.WithProcessRuntimeVersion(),
resource.WithProcessRuntimeDescription(),
)...,
)
if err != nil {
Expand Down
46 changes: 46 additions & 0 deletions pkg/tracing/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package tracing

import (
"context"
"fmt"
"os"
"os/user"

"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
)

type safeProcessOwnerProvider struct{}

var _ resource.Detector = safeProcessOwnerProvider{}

func (safeProcessOwnerProvider) Detect(context.Context) (*resource.Resource, error) {
// Re-implement the username logic of
// https://cs.opensource.google/go/go/+/refs/tags/go1.19.5:src/os/user/lookup_stubs.go
// so we can use the same rules in CGO mode as well.
username := os.Getenv("USER")
if u, err := user.Current(); err == nil {
username = u.Username
}

// Instead of returning an empty user, just convert the ID number to a string
// and use that -- so we always provide some sort of user. (Like the 'id' program
// would do.) This will allow us tostill provide some form of owner attribute
// regardless of any mismatch amongst the Docker USER parameter, the
// /etc/passwd file, the Kubernetes runAsUser setting...
if username == "" {
// ...but on Windows id will be -1: ignore all negatives.
if id := os.Getuid(); id >= 0 {
username = fmt.Sprintf("%d", id)
} else {
// Just give up.
return resource.Empty(), nil
}
}

return resource.NewWithAttributes(semconv.SchemaURL, semconv.ProcessOwnerKey.String(username)), nil
}

func WithSafeProcessOwner() resource.Option {
return resource.WithDetectors(safeProcessOwnerProvider{})
}

0 comments on commit f05ed8c

Please sign in to comment.