diff --git a/README.md b/README.md index ea84b7c..bf3de5b 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ This tool is able to override the following attributes: | Service Name | --service-name | `junit2otlp` | Overrides OpenTelemetry's service name. If the `OTEL_SERVICE_NAME` environment variable is set, it will take precedence over any other value. | | Service Version | --service-version | Empty | Overrides OpenTelemetry's service version. If the `OTEL_SERVICE_VERSION` environment variable is set, it will take precedence over any other value. | | Trace Name | --trace-name | `junit2otlp` | Overrides OpenTelemetry's trace name. | +| Properties Allowed | --properties-allowed | All | Comma separated list of properties to be allowed in the jUnit report. | For using this tool in a distributed tracing scenario, where there is a parent trace in which the test reports traces should be attached, it's important to set the `TRACEPARENT` environment variable, so that the traces and spans generated by this tool are located under the right parent trace. Please read more on this [here](https://github.com/open-telemetry/opentelemetry-specification/issues/740). diff --git a/go.mod b/go.mod index 4b92754..efbd4d6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.23 require ( github.com/go-git/go-git/v5 v5.4.2 - github.com/joshdk/go-junit v0.0.0-20210226021600-6145f504ca0d + github.com/joshdk/go-junit v1.0.0 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.14.0 @@ -24,6 +24,7 @@ require ( github.com/Microsoft/hcsshim v0.9.4 // indirect github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect + github.com/avast/retry-go v3.0.0+incompatible // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/containerd/cgroups v1.0.4 // indirect github.com/containerd/containerd v1.6.8 // indirect diff --git a/go.sum b/go.sum index fa836d6..573aa4e 100644 --- a/go.sum +++ b/go.sum @@ -115,6 +115,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -600,6 +602,8 @@ github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUB github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v0.0.0-20210226021600-6145f504ca0d h1:lcSbmPJf3b19MTZtGDLI6Y2Jnk3VBDT8UG/8IVCEMxA= github.com/joshdk/go-junit v0.0.0-20210226021600-6145f504ca0d/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= +github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= +github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= 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= diff --git a/main.go b/main.go index 8f34197..da8e7b1 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,8 @@ import ( "log" "os" "runtime" + "slices" + "strings" "time" "github.com/joshdk/go-junit" @@ -27,20 +29,33 @@ var repositoryPathFlag string var serviceNameFlag string var serviceVersionFlag string var traceNameFlag string +var propertiesAllowedString string + +const propertiesAllowAll = "all" var runtimeAttributes []attribute.KeyValue +var propsAllowed []string func init() { flag.StringVar(&repositoryPathFlag, "repository-path", getDefaultwd(), "Path to the SCM repository to be read") flag.StringVar(&serviceNameFlag, "service-name", "", "OpenTelemetry Service Name to be used when sending traces and metrics for the jUnit report") flag.StringVar(&serviceVersionFlag, "service-version", "", "OpenTelemetry Service Version to be used when sending traces and metrics for the jUnit report") flag.StringVar(&traceNameFlag, "trace-name", Junit2otlp, "OpenTelemetry Trace Name to be used when sending traces and metrics for the jUnit report") + flag.StringVar(&propertiesAllowedString, "properties-allowed", propertiesAllowAll, "Comma separated list of properties to be allowed in the jUnit report") - // initialise runtime keys + // initialize runtime keys runtimeAttributes = []attribute.KeyValue{ semconv.HostArchKey.String(runtime.GOARCH), semconv.OSNameKey.String(runtime.GOOS), } + + propsAllowed = []string{} + if propertiesAllowedString != "" { + allowed := strings.Split(propertiesAllowedString, ",") + for _, prop := range allowed { + propsAllowed = append(propsAllowed, strings.TrimSpace(prop)) + } + } } func createIntCounter(meter metric.Meter, name string, description string) metric.Int64Counter { @@ -193,6 +208,13 @@ func initTracerProvider(ctx context.Context, res *resource.Resource) (*sdktrace. func propsToLabels(props map[string]string) []attribute.KeyValue { attributes := []attribute.KeyValue{} for k, v := range props { + // if propertiesAllowedString is not "all" (default) and the key is not in the + // allowed list, skip it + if propertiesAllowedString != propertiesAllowAll && + len(propsAllowed) > 0 && !slices.Contains(propsAllowed, k) { + continue + } + attributes = append(attributes, attribute.Key(k).String(v)) } @@ -212,6 +234,10 @@ func (pr *PipeReader) Read() ([]byte, error) { var buf []byte scanner := bufio.NewScanner(os.Stdin) + // 64KB initial buffer, 1MB max buffer size + // was seeing large failure messages causing parsing to fail + scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024) + for scanner.Scan() { buf = append(buf, scanner.Bytes()...) } diff --git a/main_test.go b/main_test.go index f51de8c..4ebfc46 100644 --- a/main_test.go +++ b/main_test.go @@ -9,8 +9,9 @@ import ( "path" "strings" "testing" - "time" + "github.com/avast/retry-go" + "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" "github.com/testcontainers/testcontainers-go" ) @@ -212,9 +213,17 @@ func Test_Main_SampleXML(t *testing.T) { t.Error(err) } - collectorPort, err := otelCollector.MappedPort(ctx, "4317/tcp") + var collectorPort nat.Port + err = retry.Do(func() error { + collectorPort, err = otelCollector.MappedPort(ctx, "4317/tcp") + if err != nil { + return err + } + + return nil + }) if err != nil { - t.Errorf("could not get mapped port for otel-collector: %v", err) + t.Errorf("could not get the collector port: %s", err) } os.Setenv(exporterEndpointKey, "http://localhost:"+collectorPort.Port()) @@ -251,25 +260,31 @@ func Test_Main_SampleXML(t *testing.T) { t.Error() } - // TODO: retry until the file is written by the otel-exporter - time.Sleep(time.Second * 30) - - // assert using the generated file - jsonBytes, err := os.ReadFile(reportFilePath) - if err != nil { - t.Error(err) - } - // merge both JSON files // 1. get the spans and metrics JSONs, they are separated by \n // 2. remote white spaces // 3. unmarshal each resource separately // 4. assign each resource to the test report struct - content := string(jsonBytes) - jsons := strings.Split(strings.TrimSpace(content), "\n") - if len(jsons) != 2 { - t.Errorf("expected 2 JSONs, got %d - %s", len(jsons), jsons) + var jsons []string + err = retry.Do(func() error { + // assert using the generated file + jsonBytes, err := os.ReadFile(reportFilePath) + if err != nil { + t.Error(err) + } + + content := string(jsonBytes) + + jsons = strings.Split(strings.TrimSpace(content), "\n") + if len(jsons) != 2 { + return fmt.Errorf("expected 2 JSONs, got %d - %s", len(jsons), jsons) + } + + return nil + }) + if err != nil { + t.Errorf("error while waiting for collector output: %s", err) } jsonSpans := ""