Skip to content

Commit

Permalink
Choose target container injection with annotation (#689)
Browse files Browse the repository at this point in the history
Choose container injection by annotation
  • Loading branch information
fscellos authored May 13, 2022
1 parent a02d99c commit b4bd108
Show file tree
Hide file tree
Showing 33 changed files with 875 additions and 84 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,4 +324,4 @@ Thanks to all the people who already contributed!
[godoc-img]: https://godoc.org/github.com/open-telemetry/opentelemetry-operator?status.svg
[godoc]: https://godoc.org/github.com/open-telemetry/opentelemetry-operator/pkg/apis/opentelemetry/v1alpha1#OpenTelemetryCollector
[contributors]: https://github.com/open-telemetry/opentelemetry-operator/graphs/contributors
[contributors-img]: https://contributors-img.web.app/image?repo=open-telemetry/opentelemetry-operator
[contributors-img]: https://contributors-img.web.app/image?repo=open-telemetry/opentelemetry-operator
2 changes: 1 addition & 1 deletion config/manager/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
resources:
- manager.yaml
- manager.yaml
2 changes: 1 addition & 1 deletion kuttl-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ commands:
- command: sleep 5s
testDirs:
- ./tests/e2e/
timeout: 150
timeout: 150
7 changes: 4 additions & 3 deletions pkg/instrumentation/annotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ import (
const (
// annotationInjectJava indicates whether java auto-instrumentation should be injected or not.
// Possible values are "true", "false" or "<Instrumentation>" name.
annotationInjectJava = "instrumentation.opentelemetry.io/inject-java"
annotationInjectNodeJS = "instrumentation.opentelemetry.io/inject-nodejs"
annotationInjectPython = "instrumentation.opentelemetry.io/inject-python"
annotationInjectJava = "instrumentation.opentelemetry.io/inject-java"
annotationInjectNodeJS = "instrumentation.opentelemetry.io/inject-nodejs"
annotationInjectPython = "instrumentation.opentelemetry.io/inject-python"
annotationInjectContainerName = "instrumentation.opentelemetry.io/container-names"
)

// annotationValue returns the effective annotationInjectJava value, based on the annotations from the pod and namespace.
Expand Down
27 changes: 27 additions & 0 deletions pkg/instrumentation/helper.go
Original file line number Diff line number Diff line change
@@ -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 instrumentation

import corev1 "k8s.io/api/core/v1"

// Calculate if we already inject InitContainers.
func IsInitContainerMissing(pod corev1.Pod) bool {
for _, initContainer := range pod.Spec.InitContainers {
if initContainer.Name == initContainerName {
return false
}
}
return true
}
74 changes: 74 additions & 0 deletions pkg/instrumentation/helper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// 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 instrumentation

import (
"testing"

"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
)

func TestInitContainerMissing(t *testing.T) {
tests := []struct {
name string
pod corev1.Pod
expected bool
}{
{
name: "InitContainer_Already_Inject",
pod: corev1.Pod{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "istio-init",
},
{
Name: initContainerName,
},
},
},
},
expected: false,
},
{
name: "InitContainer_Absent_1",
pod: corev1.Pod{
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "istio-init",
},
},
},
},
expected: true,
},
{
name: "InitContainer_Absent_2",
pod: corev1.Pod{
Spec: corev1.PodSpec{},
},
expected: true,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := IsInitContainerMissing(test.pod)
assert.Equal(t, test.expected, result)
})
}
}
35 changes: 19 additions & 16 deletions pkg/instrumentation/javaagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ const (
javaJVMArgument = " -javaagent:/otel-auto-instrumentation/javaagent.jar"
)

func injectJavaagent(logger logr.Logger, javaSpec v1alpha1.Java, pod corev1.Pod) corev1.Pod {
func injectJavaagent(logger logr.Logger, javaSpec v1alpha1.Java, pod corev1.Pod, index int) corev1.Pod {
// caller checks if there is at least one container
container := &pod.Spec.Containers[0]
container := &pod.Spec.Containers[index]

// inject env vars
for _, env := range javaSpec.Env {
Expand Down Expand Up @@ -57,21 +57,24 @@ func injectJavaagent(logger logr.Logger, javaSpec v1alpha1.Java, pod corev1.Pod)
MountPath: "/otel-auto-instrumentation",
})

pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
}})
// We just inject Volumes and init containers for the first processed container
if IsInitContainerMissing(pod) {
pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
}})

pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{
Name: initContainerName,
Image: javaSpec.Image,
Command: []string{"cp", "/javaagent.jar", "/otel-auto-instrumentation/javaagent.jar"},
VolumeMounts: []corev1.VolumeMount{{
Name: volumeName,
MountPath: "/otel-auto-instrumentation",
}},
})
pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{
Name: initContainerName,
Image: javaSpec.Image,
Command: []string{"cp", "/javaagent.jar", "/otel-auto-instrumentation/javaagent.jar"},
VolumeMounts: []corev1.VolumeMount{{
Name: volumeName,
MountPath: "/otel-auto-instrumentation",
}},
})
}

return pod
}
2 changes: 1 addition & 1 deletion pkg/instrumentation/javaagent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func TestInjectJavaagent(t *testing.T) {

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
pod := injectJavaagent(logr.Discard(), test.Java, test.pod)
pod := injectJavaagent(logr.Discard(), test.Java, test.pod, 0)
assert.Equal(t, test.expected, pod)
})
}
Expand Down
35 changes: 19 additions & 16 deletions pkg/instrumentation/nodejs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ const (
nodeRequireArgument = " --require /otel-auto-instrumentation/autoinstrumentation.js"
)

func injectNodeJSSDK(logger logr.Logger, nodeJSSpec v1alpha1.NodeJS, pod corev1.Pod) corev1.Pod {
func injectNodeJSSDK(logger logr.Logger, nodeJSSpec v1alpha1.NodeJS, pod corev1.Pod, index int) corev1.Pod {
// caller checks if there is at least one container
container := &pod.Spec.Containers[0]
container := &pod.Spec.Containers[index]

// inject env vars
for _, env := range nodeJSSpec.Env {
Expand Down Expand Up @@ -57,21 +57,24 @@ func injectNodeJSSDK(logger logr.Logger, nodeJSSpec v1alpha1.NodeJS, pod corev1.
MountPath: "/otel-auto-instrumentation",
})

pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
}})
// We just inject Volumes and init containers for the first processed container
if IsInitContainerMissing(pod) {
pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
}})

pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{
Name: initContainerName,
Image: nodeJSSpec.Image,
Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"},
VolumeMounts: []corev1.VolumeMount{{
Name: volumeName,
MountPath: "/otel-auto-instrumentation",
}},
})
pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{
Name: initContainerName,
Image: nodeJSSpec.Image,
Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"},
VolumeMounts: []corev1.VolumeMount{{
Name: volumeName,
MountPath: "/otel-auto-instrumentation",
}},
})
}

return pod
}
2 changes: 1 addition & 1 deletion pkg/instrumentation/nodejs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func TestInjectNodeJSSDK(t *testing.T) {

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
pod := injectNodeJSSDK(logr.Discard(), test.NodeJS, test.pod)
pod := injectNodeJSSDK(logr.Discard(), test.NodeJS, test.pod, 0)
assert.Equal(t, test.expected, pod)
})
}
Expand Down
10 changes: 9 additions & 1 deletion pkg/instrumentation/podmutator.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,17 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c
return pod, nil
}

// We retrieve the annotation for podname
var targetContainers = annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInjectContainerName)

// once it's been determined that instrumentation is desired, none exists yet, and we know which instance it should talk to,
// we should inject the instrumentation.
return pm.sdkInjector.inject(ctx, insts, ns, pod), nil
modifiedPod := pod
for _, currentContainer := range strings.Split(targetContainers, ",") {
modifiedPod = pm.sdkInjector.inject(ctx, insts, ns, modifiedPod, strings.TrimSpace(currentContainer))
}

return modifiedPod, nil
}

func (pm *instPodMutator) getInstrumentationInstance(ctx context.Context, ns corev1.Namespace, pod corev1.Pod, instAnnotation string) (*v1alpha1.Instrumentation, error) {
Expand Down
35 changes: 19 additions & 16 deletions pkg/instrumentation/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ const (
pythonPathSuffix = "/otel-auto-instrumentation"
)

func injectPythonSDK(logger logr.Logger, pythonSpec v1alpha1.Python, pod corev1.Pod) corev1.Pod {
func injectPythonSDK(logger logr.Logger, pythonSpec v1alpha1.Python, pod corev1.Pod, index int) corev1.Pod {
// caller checks if there is at least one container
container := &pod.Spec.Containers[0]
container := &pod.Spec.Containers[index]

// inject env vars
for _, env := range pythonSpec.Env {
Expand Down Expand Up @@ -71,21 +71,24 @@ func injectPythonSDK(logger logr.Logger, pythonSpec v1alpha1.Python, pod corev1.
MountPath: "/otel-auto-instrumentation",
})

pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
}})
// We just inject Volumes and init containers for the first processed container
if IsInitContainerMissing(pod) {
pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
}})

pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{
Name: initContainerName,
Image: pythonSpec.Image,
Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"},
VolumeMounts: []corev1.VolumeMount{{
Name: volumeName,
MountPath: "/otel-auto-instrumentation",
}},
})
pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{
Name: initContainerName,
Image: pythonSpec.Image,
Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"},
VolumeMounts: []corev1.VolumeMount{{
Name: volumeName,
MountPath: "/otel-auto-instrumentation",
}},
})
}

return pod
}
2 changes: 1 addition & 1 deletion pkg/instrumentation/python_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ func TestInjectPythonSDK(t *testing.T) {

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
pod := injectPythonSDK(logr.Discard(), test.Python, test.pod)
pod := injectPythonSDK(logr.Discard(), test.Python, test.pod, 0)
assert.Equal(t, test.expected, pod)
})
}
Expand Down
Loading

0 comments on commit b4bd108

Please sign in to comment.