Skip to content

Commit

Permalink
Support DotNet auto-instrumentation (#976)
Browse files Browse the repository at this point in the history
* Adds support for dotnet auto-instrumentation

* changes default image for testing

* adds dotnet auto-instrumentation to the ldflags in makefile

* changes in Makefile

* fixes issue in makefile

* fixes issue in dockerfile

* passes dotnet sample app image to e2e test

* fixes version in the dot net autoinstrumentation version file

* adds testing changes for dot-net-autoinstrumentation

* fixes annotation

* downloads dot-net release from github

* removes git push condition check

* removes git push condition check

* changes repo to logicmonitor

* adds dontnet auto-instrumentation related details in readme

* adds docker image layer caching in github action

* triggers action on pull request

* adds e2e test cases for dotnet instrumentation

* fixes version for dotnet instrumentation

* version changes

* restores clusterversion file

* restores changes in Makefile

* minor change

* Adds configuration details to dockerfile

* updates version of dotnet auto-instrumentation

* changes env var name from OTEL_DOTNET_TRACER_INSTRUMENTATIONS to OTEL_DOTNET_AUTO_TRACES_ENABLED_INSTRUMENTATIONS

* removes java related env variables

* fixes e2e test cases

* fixes e2e test case

* deletes .DS_Store file

* sets OTEL_DOTNET_AUTO_TRACES_ENABLED_INSTRUMENTATIONS to default value

* fixes e2e test case

* fixes e2e test case
  • Loading branch information
avadhut123pisal authored Jul 26, 2022
1 parent b18ddf1 commit 7ce297d
Show file tree
Hide file tree
Showing 35 changed files with 1,741 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish-autoinstrumentation-dotnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ ARG TARGETALLOCATOR_VERSION
ARG AUTO_INSTRUMENTATION_JAVA_VERSION
ARG AUTO_INSTRUMENTATION_NODEJS_VERSION
ARG AUTO_INSTRUMENTATION_PYTHON_VERSION
ARG AUTO_INSTRUMENTATION_DOTNET_VERSION

# Build
RUN CGO_ENABLED=0 GOOS=linux GO111MODULE=on go build -ldflags="-X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -X ${VERSION_PKG}.otelCol=${OTELCOL_VERSION} -X ${VERSION_PKG}.targetAllocator=${TARGETALLOCATOR_VERSION} -X ${VERSION_PKG}.autoInstrumentationJava=${AUTO_INSTRUMENTATION_JAVA_VERSION} -X ${VERSION_PKG}.autoInstrumentationNodeJS=${AUTO_INSTRUMENTATION_NODEJS_VERSION} -X ${VERSION_PKG}.autoInstrumentationPython=${AUTO_INSTRUMENTATION_PYTHON_VERSION}" -a -o manager main.go
RUN CGO_ENABLED=0 GOOS=linux GO111MODULE=on go build -ldflags="-X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -X ${VERSION_PKG}.otelCol=${OTELCOL_VERSION} -X ${VERSION_PKG}.targetAllocator=${TARGETALLOCATOR_VERSION} -X ${VERSION_PKG}.autoInstrumentationJava=${AUTO_INSTRUMENTATION_JAVA_VERSION} -X ${VERSION_PKG}.autoInstrumentationNodeJS=${AUTO_INSTRUMENTATION_NODEJS_VERSION} -X ${VERSION_PKG}.autoInstrumentationPython=${AUTO_INSTRUMENTATION_PYTHON_VERSION} -X ${VERSION_PKG}.autoInstrumentationDotNet=${AUTO_INSTRUMENTATION_DOTNET_VERSION}" -a -o manager main.go

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ TARGETALLOCATOR_VERSION ?= "$(shell grep -v '\#' versions.txt | grep targetalloc
AUTO_INSTRUMENTATION_JAVA_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-java | awk -F= '{print $$2}')"
AUTO_INSTRUMENTATION_NODEJS_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-nodejs | awk -F= '{print $$2}')"
AUTO_INSTRUMENTATION_PYTHON_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-python | awk -F= '{print $$2}')"
LD_FLAGS ?= "-X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -X ${VERSION_PKG}.otelCol=${OTELCOL_VERSION} -X ${VERSION_PKG}.targetAllocator=${TARGETALLOCATOR_VERSION} -X ${VERSION_PKG}.autoInstrumentationJava=${AUTO_INSTRUMENTATION_JAVA_VERSION} -X ${VERSION_PKG}.autoInstrumentationNodeJS=${AUTO_INSTRUMENTATION_NODEJS_VERSION} -X ${VERSION_PKG}.autoInstrumentationPython=${AUTO_INSTRUMENTATION_PYTHON_VERSION}"
AUTO_INSTRUMENTATION_DOTNET_VERSION ?= "$(shell grep -v '\#' versions.txt | grep autoinstrumentation-dotnet | awk -F= '{print $$2}')"
LD_FLAGS ?= "-X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -X ${VERSION_PKG}.otelCol=${OTELCOL_VERSION} -X ${VERSION_PKG}.targetAllocator=${TARGETALLOCATOR_VERSION} -X ${VERSION_PKG}.autoInstrumentationJava=${AUTO_INSTRUMENTATION_JAVA_VERSION} -X ${VERSION_PKG}.autoInstrumentationNodeJS=${AUTO_INSTRUMENTATION_NODEJS_VERSION} -X ${VERSION_PKG}.autoInstrumentationPython=${AUTO_INSTRUMENTATION_PYTHON_VERSION} -X ${VERSION_PKG}.autoInstrumentationDotNet=${AUTO_INSTRUMENTATION_DOTNET_VERSION}"

# Image URL to use all building/pushing image targets
IMG_PREFIX ?= ghcr.io/${USER}/opentelemetry-operator
Expand Down Expand Up @@ -174,7 +175,7 @@ set-test-image-vars:
# Build the container image, used only for local dev purposes
.PHONY: container
container:
docker build -t ${IMG} --build-arg VERSION_PKG=${VERSION_PKG} --build-arg VERSION=${VERSION} --build-arg VERSION_DATE=${VERSION_DATE} --build-arg OTELCOL_VERSION=${OTELCOL_VERSION} --build-arg TARGETALLOCATOR_VERSION=${TARGETALLOCATOR_VERSION} --build-arg AUTO_INSTRUMENTATION_JAVA_VERSION=${AUTO_INSTRUMENTATION_JAVA_VERSION} --build-arg AUTO_INSTRUMENTATION_NODEJS_VERSION=${AUTO_INSTRUMENTATION_NODEJS_VERSION} --build-arg AUTO_INSTRUMENTATION_PYTHON_VERSION=${AUTO_INSTRUMENTATION_PYTHON_VERSION} .
docker build -t ${IMG} --build-arg VERSION_PKG=${VERSION_PKG} --build-arg VERSION=${VERSION} --build-arg VERSION_DATE=${VERSION_DATE} --build-arg OTELCOL_VERSION=${OTELCOL_VERSION} --build-arg TARGETALLOCATOR_VERSION=${TARGETALLOCATOR_VERSION} --build-arg AUTO_INSTRUMENTATION_JAVA_VERSION=${AUTO_INSTRUMENTATION_JAVA_VERSION} --build-arg AUTO_INSTRUMENTATION_NODEJS_VERSION=${AUTO_INSTRUMENTATION_NODEJS_VERSION} --build-arg AUTO_INSTRUMENTATION_PYTHON_VERSION=${AUTO_INSTRUMENTATION_PYTHON_VERSION} --build-arg AUTO_INSTRUMENTATION_DOTNET_VERSION=${AUTO_INSTRUMENTATION_DOTNET_VERSION} .

# Push the container image, used only for local dev purposes
.PHONY: container-push
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ When using sidecar mode the OpenTelemetry collector container will have the envi

### OpenTelemetry auto-instrumentation injection

The operator can inject and configure OpenTelemetry auto-instrumentation libraries. Currently Java, NodeJS and Python are supported.
The operator can inject and configure OpenTelemetry auto-instrumentation libraries. Currently DotNet, Java, NodeJS and Python are supported.

To use auto-instrumentation, configure an `Instrumentation` resource with the configuration for the SDK and instrumentation.

Expand Down Expand Up @@ -210,12 +210,20 @@ Python:
instrumentation.opentelemetry.io/inject-python: "true"
```

DotNet:
```bash
instrumentation.opentelemetry.io/inject-dotnet: "true"
```

The possible values for the annotation can be
* `"true"` - inject and `Instrumentation` resource from the namespace.
* `"my-instrumentation"` - name of `Instrumentation` CR instance in the current namespace.
* `"my-other-namespace/my-instrumentation"` - name and namespace of `Instrumentation` CR instance in another namespace.
* `"false"` - do not inject


>**Note:** For `DotNet` auto-instrumentation, by default, operator sets the `OTEL_DOTNET_AUTO_TRACES_ENABLED_INSTRUMENTATIONS` environment variable which specifies the list of traces source instrumentations you want to enable. The value that is set by default by the operator is all available instrumentations supported by the `openTelemery-dotnet-instrumentation` release consumed in the image, i.e. `AspNet,HttpClient,SqlClient`. This value can be overriden by configuring the environment variable explicitely.
#### Multi-container pods

If nothing else is specified, instrumentation is performed on the first container available in the pod spec.
Expand Down Expand Up @@ -271,6 +279,8 @@ spec:
image: your-customized-auto-instrumentation-image:nodejs
python:
image: your-customized-auto-instrumentation-image:python
dotnet:
image: your-customized-auto-instrumentation-image:dotnet
```

The Dockerfiles for auto-instrumentation can be found in [autoinstrumentation directory](./autoinstrumentation).
Expand Down
16 changes: 16 additions & 0 deletions apis/v1alpha1/instrumentation_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ type InstrumentationSpec struct {
// Python defines configuration for python auto-instrumentation.
// +optional
Python Python `json:"python,omitempty"`

// DotNet defines configuration for DotNet auto-instrumentation.
// +optional
DotNet DotNet `json:"dotnet,omitempty"`
}

// Resource defines the configuration for the resource attributes, as defined by the OpenTelemetry specification.
Expand Down Expand Up @@ -129,6 +133,18 @@ type Python struct {
Env []corev1.EnvVar `json:"env,omitempty"`
}

type DotNet struct {
// Image is a container image with DotNet SDK and auto-instrumentation.
// +optional
Image string `json:"image,omitempty"`

// Env defines DotNet specific env vars. There are four layers for env vars' definitions and
// the precedence order is: `original container env vars` > `language specific env vars` > `common env vars` > `instrument spec configs' vars`.
// If the former var had been defined, then the other vars would be ignored.
// +optional
Env []corev1.EnvVar `json:"env,omitempty"`
}

// InstrumentationStatus defines status of the instrumentation.
type InstrumentationStatus struct {
}
Expand Down
37 changes: 32 additions & 5 deletions apis/v1alpha1/instrumentation_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ import (
)

const (
AnnotationDefaultAutoInstrumentationJava = "instrumentation.opentelemetry.io/default-auto-instrumentation-java-image"
AnnotationDefaultAutoInstrumentationNodeJS = "instrumentation.opentelemetry.io/default-auto-instrumentation-nodejs-image"
AnnotationDefaultAutoInstrumentationPython = "instrumentation.opentelemetry.io/default-auto-instrumentation-python-image"
envPrefix = "OTEL_"
envSplunkPrefix = "SPLUNK_"
AnnotationDefaultAutoInstrumentationJava = "instrumentation.opentelemetry.io/default-auto-instrumentation-java-image"
AnnotationDefaultAutoInstrumentationNodeJS = "instrumentation.opentelemetry.io/default-auto-instrumentation-nodejs-image"
AnnotationDefaultAutoInstrumentationPython = "instrumentation.opentelemetry.io/default-auto-instrumentation-python-image"
AnnotationDefaultAutoInstrumentationDotNet = "instrumentation.opentelemetry.io/default-auto-instrumentation-dotnet-image"
envPrefix = "OTEL_"
envSplunkPrefix = "SPLUNK_"
envOtelDotnetAutoTracesEnabledInstrumentations = "OTEL_DOTNET_AUTO_TRACES_ENABLED_INSTRUMENTATIONS"
defaultEnabledTracesInstrumentations = "AspNet,HttpClient,SqlClient"
)

// log is for logging in this package.
Expand Down Expand Up @@ -72,6 +75,18 @@ func (r *Instrumentation) Default() {
r.Spec.Python.Image = val
}
}
if r.Spec.DotNet.Image == "" {
if val, ok := r.Annotations[AnnotationDefaultAutoInstrumentationDotNet]; ok {
r.Spec.DotNet.Image = val
}
}
// by default set all the available instrumentations for dotnet unless the env is already set
if !r.isEnvVarSet(envOtelDotnetAutoTracesEnabledInstrumentations) {
r.Spec.DotNet.Env = append(r.Spec.DotNet.Env, corev1.EnvVar{
Name: envOtelDotnetAutoTracesEnabledInstrumentations,
Value: defaultEnabledTracesInstrumentations,
})
}
}

// +kubebuilder:webhook:verbs=create;update,path=/validate-opentelemetry-io-v1alpha1-instrumentation,mutating=false,failurePolicy=fail,groups=opentelemetry.io,resources=instrumentations,versions=v1alpha1,name=vinstrumentationcreateupdate.kb.io,sideEffects=none,admissionReviewVersions=v1
Expand Down Expand Up @@ -125,6 +140,9 @@ func (in *Instrumentation) validate() error {
if err := in.validateEnv(in.Spec.Python.Env); err != nil {
return err
}
if err := in.validateEnv(in.Spec.DotNet.Env); err != nil {
return err
}

return nil
}
Expand All @@ -137,3 +155,12 @@ func (in *Instrumentation) validateEnv(envs []corev1.EnvVar) error {
}
return nil
}

func (in *Instrumentation) isEnvVarSet(name string) bool {
for _, env := range in.Spec.DotNet.Env {
if env.Name == name {
return true
}
}
return false
}
34 changes: 34 additions & 0 deletions apis/v1alpha1/instrumentation_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"testing"

"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand All @@ -28,13 +29,46 @@ func TestInstrumentationDefaultingWebhook(t *testing.T) {
AnnotationDefaultAutoInstrumentationJava: "java-img:1",
AnnotationDefaultAutoInstrumentationNodeJS: "nodejs-img:1",
AnnotationDefaultAutoInstrumentationPython: "python-img:1",
AnnotationDefaultAutoInstrumentationDotNet: "dotnet-img:1",
},
},
}
inst.Default()
assert.Equal(t, "java-img:1", inst.Spec.Java.Image)
assert.Equal(t, "nodejs-img:1", inst.Spec.NodeJS.Image)
assert.Equal(t, "python-img:1", inst.Spec.Python.Image)
assert.Equal(t, "dotnet-img:1", inst.Spec.DotNet.Image)
assert.Equal(t, true, inst.isEnvVarSet(envOtelDotnetAutoTracesEnabledInstrumentations))
}

func TestInstrumentationDefaultingWebhookOtelDotNetTracesEnabledInstruEnvSet(t *testing.T) {
inst := &Instrumentation{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
AnnotationDefaultAutoInstrumentationJava: "java-img:1",
AnnotationDefaultAutoInstrumentationNodeJS: "nodejs-img:1",
AnnotationDefaultAutoInstrumentationPython: "python-img:1",
AnnotationDefaultAutoInstrumentationDotNet: "dotnet-img:1",
},
},
Spec: InstrumentationSpec{
DotNet: DotNet{
Env: []v1.EnvVar{
{
Name: envOtelDotnetAutoTracesEnabledInstrumentations,
Value: "AspNet,HttpClient",
},
},
},
},
}
inst.Default()
for _, env := range inst.Spec.DotNet.Env {
if env.Name == envOtelDotnetAutoTracesEnabledInstrumentations {
assert.Equal(t, "AspNet,HttpClient", env.Value)
break
}
}
}

func TestInstrumentationValidatingWebhook(t *testing.T) {
Expand Down
23 changes: 23 additions & 0 deletions apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 7ce297d

Please sign in to comment.