otel: capture whether process was invoked from a terminal

This commit adds a "terminal" attribute to `BaseMetricAttributes`
that allows us to discern whether an invocation was from an interactive
terminal or not.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
This commit is contained in:
Laura Brehm
2024-04-02 11:05:20 +01:00
parent 155dc5e4e4
commit ee1b2836af
4 changed files with 119 additions and 14 deletions

View File

@ -1,9 +1,16 @@
package command
import (
"bytes"
"context"
"io"
"reflect"
"strings"
"testing"
"github.com/docker/cli/cli/streams"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/attribute"
"gotest.tools/v3/assert"
)
@ -92,3 +99,91 @@ func TestGetCommandName(t *testing.T) {
})
}
}
func TestStdioAttributes(t *testing.T) {
outBuffer := new(bytes.Buffer)
errBuffer := new(bytes.Buffer)
t.Parallel()
for _, tc := range []struct {
test string
stdinTty bool
stdoutTty bool
// TODO(laurazard): test stderr
expected []attribute.KeyValue
}{
{
test: "",
expected: []attribute.KeyValue{
attribute.Bool("command.stdin.isatty", false),
attribute.Bool("command.stdout.isatty", false),
attribute.Bool("command.stderr.isatty", false),
},
},
{
test: "",
stdinTty: true,
stdoutTty: true,
expected: []attribute.KeyValue{
attribute.Bool("command.stdin.isatty", true),
attribute.Bool("command.stdout.isatty", true),
attribute.Bool("command.stderr.isatty", false),
},
},
} {
tc := tc
t.Run(tc.test, func(t *testing.T) {
t.Parallel()
cli := &DockerCli{
in: streams.NewIn(io.NopCloser(strings.NewReader(""))),
out: streams.NewOut(outBuffer),
err: errBuffer,
}
cli.In().SetIsTerminal(tc.stdinTty)
cli.Out().SetIsTerminal(tc.stdoutTty)
actual := stdioAttributes(cli)
assert.Check(t, reflect.DeepEqual(actual, tc.expected))
})
}
}
func TestAttributesFromError(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
testName string
err error
expected []attribute.KeyValue
}{
{
testName: "no error",
err: nil,
expected: []attribute.KeyValue{
attribute.String("command.status.code", "0"),
},
},
{
testName: "non-0 exit code",
err: statusError{StatusCode: 127},
expected: []attribute.KeyValue{
attribute.String("command.error.type", "generic"),
attribute.String("command.status.code", "127"),
},
},
{
testName: "canceled",
err: context.Canceled,
expected: []attribute.KeyValue{
attribute.String("command.error.type", "canceled"),
attribute.String("command.status.code", "1"),
},
},
} {
tc := tc
t.Run(tc.testName, func(t *testing.T) {
t.Parallel()
actual := attributesFromError(tc.err)
assert.Check(t, reflect.DeepEqual(actual, tc.expected))
})
}
}