diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index bfee4c0b6..1e832d264 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,7 +6,7 @@ on: pull_request: jobs: - generate: + generate-and-test: runs-on: ubuntu-latest steps: - name: Checkout Repo @@ -24,6 +24,9 @@ jobs: - name: verify output run: | git diff --exit-code || (echo 'generated diff, please run "make generate"' && exit 1) + - name: Run unit tests + run: | + make test docker-build: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/e2e/k8s/sample-job.yml b/.github/workflows/e2e/k8s/sample-job.yml index 99a94ebfb..6414ef806 100644 --- a/.github/workflows/e2e/k8s/sample-job.yml +++ b/.github/workflows/e2e/k8s/sample-job.yml @@ -30,7 +30,7 @@ spec: - name: OTEL_GO_AUTO_TARGET_EXE value: /sample-app/main - name: OTEL_EXPORTER_OTLP_ENDPOINT - value: "test-opentelemetry-collector:4317" + value: "http://test-opentelemetry-collector:4317" - name: OTEL_SERVICE_NAME value: "sample-app" - name: OTEL_PROPAGATORS diff --git a/Makefile b/Makefile index 191a2e048..f97439274 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,15 @@ $(TOOLS)/go-licenses: PACKAGE=github.com/google/go-licenses .PHONY: tools tools: $(GOLICENSES) +ALL_GO_MODS := $(shell find . -type f -name 'go.mod' ! -path '$(TOOLS_MOD_DIR)/*' ! -path './LICENSES/*' | sort) +GO_MODS_TO_TEST := $(ALL_GO_MODS:%=test/%) + +.PHONY: test +test: $(GO_MODS_TO_TEST) +test/%: GO_MOD=$* +test/%: + cd $(shell dirname $(GO_MOD)) && go test -v ./... + .PHONY: generate generate: export CFLAGS := $(BPF_INCLUDE) generate: diff --git a/docs/getting-started/emojivoto-instrumented.yaml b/docs/getting-started/emojivoto-instrumented.yaml index f19a50b58..d59c7ea6b 100644 --- a/docs/getting-started/emojivoto-instrumented.yaml +++ b/docs/getting-started/emojivoto-instrumented.yaml @@ -61,7 +61,7 @@ spec: - name: OTEL_GO_AUTO_TARGET_EXE value: /usr/local/bin/emojivoto-emoji-svc - name: OTEL_EXPORTER_OTLP_ENDPOINT - value: "jaeger:4317" + value: "http://jaeger:4317" - name: OTEL_SERVICE_NAME value: "emojivoto-emoji" securityContext: @@ -143,7 +143,7 @@ spec: - name: OTEL_GO_AUTO_TARGET_EXE value: /usr/local/bin/emojivoto-voting-svc - name: OTEL_EXPORTER_OTLP_ENDPOINT - value: "jaeger:4317" + value: "http://jaeger:4317" - name: OTEL_SERVICE_NAME value: "emojivoto-voting" securityContext: @@ -227,7 +227,7 @@ spec: - name: OTEL_GO_AUTO_TARGET_EXE value: /usr/local/bin/emojivoto-web - name: OTEL_EXPORTER_OTLP_ENDPOINT - value: "jaeger:4317" + value: "http://jaeger:4317" - name: OTEL_SERVICE_NAME value: "emojivoto-web" securityContext: @@ -244,4 +244,4 @@ spec: emptyDir: {} - name: kernel-debug hostPath: - path: /sys/kernel/debug \ No newline at end of file + path: /sys/kernel/debug diff --git a/go.mod b/go.mod index dc840c67c..1066cf238 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,9 @@ require ( github.com/hashicorp/go-version v1.6.0 github.com/pkg/errors v0.9.1 github.com/prometheus/procfs v0.9.0 + github.com/stretchr/testify v1.8.2 go.opentelemetry.io/otel v1.14.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 go.opentelemetry.io/otel/sdk v1.14.0 go.opentelemetry.io/otel/trace v1.14.0 @@ -17,15 +19,17 @@ require ( golang.org/x/arch v0.3.0 golang.org/x/sys v0.7.0 google.golang.org/grpc v1.54.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/cenkalti/backoff/v4 v4.2.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect diff --git a/go.sum b/go.sum index ba0e6f6ab..56f922ee8 100644 --- a/go.sum +++ b/go.sum @@ -161,11 +161,16 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -454,6 +459,7 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -462,6 +468,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/opentelemetry/controller.go b/pkg/opentelemetry/controller.go index e196fb0d2..d0d1dbfb9 100644 --- a/pkg/opentelemetry/controller.go +++ b/pkg/opentelemetry/controller.go @@ -19,11 +19,14 @@ import ( "fmt" "os" "runtime" + "strings" "time" "github.com/prometheus/procfs" + "go.opentelemetry.io/auto" "go.opentelemetry.io/auto/pkg/instrumentors/events" "go.opentelemetry.io/auto/pkg/log" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" @@ -34,10 +37,20 @@ import ( ) const ( - otelEndpointEnvVar = "OTEL_EXPORTER_OTLP_ENDPOINT" otelServiceNameEnvVar = "OTEL_SERVICE_NAME" ) +var ( + // Controller-local reference to the auto-instrumentation release version. + releaseVersion = auto.Version() + // Start of this auto-instrumentation's exporter User-Agent header, e.g. ""OTel-Go-Auto-Instrumentation/1.2.3" + baseUserAgent = fmt.Sprintf("OTel-Go-Auto-Instrumentation/%s", releaseVersion) + // Information about the runtime environment for inclusion in User-Agent, e.g. "go/1.18.2 (linux/amd64)" + runtimeInfo = fmt.Sprintf("%s (%s/%s)", strings.Replace(runtime.Version(), "go", "go/", 1), runtime.GOOS, runtime.GOARCH) + // Combined User-Agent identifying this auto-instrumentation and its runtime environment, see RFC7231 for format considerations. + autoinstUserAgent = fmt.Sprintf("%s %s", baseUserAgent, runtimeInfo) +) + type Controller struct { tracerProvider trace.TracerProvider tracersMap map[string]trace.Tracer @@ -83,11 +96,6 @@ func (c *Controller) convertTime(t int64) time.Time { } func NewController() (*Controller, error) { - endpoint, exists := os.LookupEnv(otelEndpointEnvVar) - if !exists { - return nil, fmt.Errorf("%s env var must be set", otelEndpointEnvVar) - } - serviceName, exists := os.LookupEnv(otelServiceNameEnvVar) if !exists { return nil, fmt.Errorf("%s env var must be set", otelServiceNameEnvVar) @@ -98,26 +106,20 @@ func NewController() (*Controller, error) { resource.WithAttributes( semconv.ServiceNameKey.String(serviceName), semconv.TelemetrySDKLanguageGo, + semconv.TelemetryAutoVersionKey.String(releaseVersion), ), ) if err != nil { return nil, err } - log.Logger.V(0).Info("Establishing connection to OpenTelemetry collector ...") - timeoutContext, cancel := context.WithTimeout(ctx, time.Second*10) - defer cancel() - conn, err := grpc.DialContext(timeoutContext, endpoint, grpc.WithInsecure(), grpc.WithBlock()) - if err != nil { - log.Logger.Error(err, "unable to connect to OpenTelemetry collector", "addr", endpoint) - return nil, err - } - - traceExporter, err := otlptracegrpc.New(ctx, - otlptracegrpc.WithGRPCConn(conn), + log.Logger.V(0).Info("Establishing connection to OTLP receiver ...") + otlpTraceClient := otlptracegrpc.NewClient( + otlptracegrpc.WithDialOption(grpc.WithUserAgent(autoinstUserAgent)), ) - + traceExporter, err := otlptrace.New(ctx, otlpTraceClient) if err != nil { + log.Logger.Error(err, "unable to connect to OTLP endpoint") return nil, err } diff --git a/pkg/opentelemetry/controller_test.go b/pkg/opentelemetry/controller_test.go new file mode 100644 index 000000000..e13b0d9c8 --- /dev/null +++ b/pkg/opentelemetry/controller_test.go @@ -0,0 +1,27 @@ +// Copyright The OpenTelemetry 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 opentelemetry + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/auto" +) + +func TestUserAgent(t *testing.T) { + assert.Contains(t, autoinstUserAgent, fmt.Sprintf("OTel-Go-Auto-Instrumentation/%s", auto.Version())) +} diff --git a/test/e2e/gorillamux/traces.json b/test/e2e/gorillamux/traces.json index 0b0617cff..ba6adfbc6 100644 --- a/test/e2e/gorillamux/traces.json +++ b/test/e2e/gorillamux/traces.json @@ -9,6 +9,12 @@ "stringValue": "sample-app" } }, + { + "key": "telemetry.auto.version", + "value": { + "stringValue": "v0.1.0-alpha" + } + }, { "key": "telemetry.sdk.language", "value": { diff --git a/test/e2e/nethttp/traces.json b/test/e2e/nethttp/traces.json index ad20ef277..a11b52ea0 100644 --- a/test/e2e/nethttp/traces.json +++ b/test/e2e/nethttp/traces.json @@ -9,6 +9,12 @@ "stringValue": "sample-app" } }, + { + "key": "telemetry.auto.version", + "value": { + "stringValue": "v0.1.0-alpha" + } + }, { "key": "telemetry.sdk.language", "value": { diff --git a/version.go b/version.go new file mode 100644 index 000000000..5596caae1 --- /dev/null +++ b/version.go @@ -0,0 +1,20 @@ +// Copyright The OpenTelemetry 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 auto + +// Version is the current release version of OpenTelemetry Go auto-instrumentation in use. +func Version() string { + return "v0.1.0-alpha" +} diff --git a/version_test.go b/version_test.go new file mode 100644 index 000000000..903f700ea --- /dev/null +++ b/version_test.go @@ -0,0 +1,54 @@ +// Copyright The OpenTelemetry 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 auto + +import ( + "io/ioutil" + "regexp" + "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" +) + +// regex taken from https://github.com/Masterminds/semver/tree/v3.1.1 +var versionRegex = regexp.MustCompile(`^v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` + + `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` + + `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?$`) + +func TestVersionSemver(t *testing.T) { + v := Version() + assert.NotNil(t, versionRegex.FindStringSubmatch(v), "version is not semver: %s", v) +} + +func TestVersionMatchesYaml(t *testing.T) { + versionYaml, err := ioutil.ReadFile("versions.yaml") + if err != nil { + t.Fatalf("Couldn't read versions.yaml file: %e", err) + return + } + + var versionInfo map[string]interface{} + + err = yaml.Unmarshal(versionYaml, &versionInfo) + if err != nil { + t.Fatalf("Couldn't parse version.yaml: %e", err) + return + } + + // incredibad, but it's where the intended version is declared at the moment + expectedVersion := versionInfo["module-sets"].(map[string]interface{})["alpha"].(map[string]interface{})["version"] + assert.Equal(t, expectedVersion, Version(), "Build version should match versions.yaml.") +}