Skip to content

Commit 8384910

Browse files
committed
add featuregate for k8s 1.28 native sidecar container
Signed-off-by: Benedikt Bongartz <[email protected]>
1 parent 60d37e1 commit 8384910

File tree

4 files changed

+172
-1
lines changed

4 files changed

+172
-1
lines changed

.chloggen/native_sidecar.yaml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
2+
change_type: enhancement
3+
4+
# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
5+
component: pkg/sidecar
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: Add native sidecar injection behind a feature gate which is disabled by default.
9+
10+
# One or more tracking issues related to the change
11+
issues: [2376]
12+
13+
# (Optional) One or more lines of additional information to render under the primary note.
14+
# These lines will be padded with 2 spaces and then inserted directly into the document.
15+
# Use pipe (|) for multiline entries.
16+
subtext:

pkg/featuregate/featuregate.go

+13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ const (
2525
)
2626

2727
var (
28+
// EnableNativeSidecarContainers is the feature gate that controls whether a
29+
// sidecar should be injected as a native sidecar or the classic way.
30+
// Native sidecar containers have been available since kubernetes v1.28 in
31+
// alpha and v1.29 in beta.
32+
// It needs to be enabled with +featureGate=SidecarContainers.
33+
// See:
34+
// https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/#feature-gates-for-alpha-or-beta-features
35+
EnableNativeSidecarContainers = featuregate.GlobalRegistry().MustRegister(
36+
"operator.sidecarcontainers.native",
37+
featuregate.StageAlpha,
38+
featuregate.WithRegisterDescription("controls whether the operator supports sidecar containers as init containers"),
39+
featuregate.WithRegisterFromVersion("v0.98.0"),
40+
)
2841
EnableJavaAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister(
2942
"operator.autoinstrumentation.java",
3043
featuregate.StageBeta,

pkg/sidecar/pod.go

+28-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/open-telemetry/opentelemetry-operator/internal/config"
2626
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector"
2727
"github.com/open-telemetry/opentelemetry-operator/internal/naming"
28+
"github.com/open-telemetry/opentelemetry-operator/pkg/featuregate"
2829
)
2930

3031
const (
@@ -47,7 +48,15 @@ func add(cfg config.Config, logger logr.Logger, otelcol v1beta1.OpenTelemetryCol
4748
container.Env = append(container.Env, attributes...)
4849
}
4950
pod.Spec.InitContainers = append(pod.Spec.InitContainers, otelcol.Spec.InitContainers...)
50-
pod.Spec.Containers = append(pod.Spec.Containers, container)
51+
52+
if featuregate.EnableNativeSidecarContainers.IsEnabled() {
53+
policy := corev1.ContainerRestartPolicyAlways
54+
container.RestartPolicy = &policy
55+
// TODO(frzifus): Add StartupProbe
56+
pod.Spec.InitContainers = append(pod.Spec.InitContainers, container)
57+
} else {
58+
pod.Spec.Containers = append(pod.Spec.Containers, container)
59+
}
5160
pod.Spec.Volumes = append(pod.Spec.Volumes, otelcol.Spec.Volumes...)
5261

5362
if pod.Labels == nil {
@@ -71,6 +80,17 @@ func remove(pod corev1.Pod) (corev1.Pod, error) {
7180
}
7281
}
7382
pod.Spec.Containers = containers
83+
84+
// NOTE: we also remove init containers (native sidecars) since k8s 1.28.
85+
// This should have no side effects.
86+
var initContainers []corev1.Container
87+
for _, initContainer := range pod.Spec.InitContainers {
88+
if initContainer.Name != naming.Container() {
89+
initContainers = append(initContainers, initContainer)
90+
}
91+
}
92+
pod.Spec.InitContainers = initContainers
93+
7494
return pod, nil
7595
}
7696

@@ -81,5 +101,12 @@ func existsIn(pod corev1.Pod) bool {
81101
return true
82102
}
83103
}
104+
// NOTE: we also check init containers (native sidecars) since k8s 1.28.
105+
// This should have no side effects.
106+
for _, container := range pod.Spec.InitContainers {
107+
if container.Name == naming.Container() {
108+
return true
109+
}
110+
}
84111
return false
85112
}

pkg/sidecar/pod_test.go

+115
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,114 @@ import (
1919

2020
"github.com/stretchr/testify/assert"
2121
"github.com/stretchr/testify/require"
22+
colfeaturegate "go.opentelemetry.io/collector/featuregate"
2223
corev1 "k8s.io/api/core/v1"
2324
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2425
logf "sigs.k8s.io/controller-runtime/pkg/log"
2526

2627
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
2728
"github.com/open-telemetry/opentelemetry-operator/internal/config"
2829
"github.com/open-telemetry/opentelemetry-operator/internal/naming"
30+
"github.com/open-telemetry/opentelemetry-operator/pkg/featuregate"
2931
)
3032

3133
var logger = logf.Log.WithName("unit-tests")
3234

35+
func sidecarFeatureGate(t *testing.T) {
36+
originalVal := featuregate.EnableNativeSidecarContainers.IsEnabled()
37+
t.Logf("original is: %+v", originalVal)
38+
require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNativeSidecarContainers.ID(), true))
39+
t.Cleanup(func() {
40+
require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableNativeSidecarContainers.ID(), originalVal))
41+
})
42+
}
43+
44+
func TestAddNativeSidecar(t *testing.T) {
45+
sidecarFeatureGate(t)
46+
// prepare
47+
pod := corev1.Pod{
48+
Spec: corev1.PodSpec{
49+
Containers: []corev1.Container{
50+
{Name: "my-app"},
51+
},
52+
InitContainers: []corev1.Container{
53+
{
54+
Name: "my-init",
55+
},
56+
},
57+
// cross-test: the pod has a volume already, make sure we don't remove it
58+
Volumes: []corev1.Volume{{}},
59+
},
60+
}
61+
62+
otelcol := v1beta1.OpenTelemetryCollector{
63+
ObjectMeta: metav1.ObjectMeta{
64+
Name: "otelcol-native-sidecar",
65+
Namespace: "some-app",
66+
},
67+
Spec: v1beta1.OpenTelemetryCollectorSpec{
68+
Mode: v1beta1.ModeSidecar,
69+
OpenTelemetryCommonFields: v1beta1.OpenTelemetryCommonFields{
70+
Ports: []corev1.ServicePort{
71+
{
72+
Name: "metrics",
73+
Port: 8888,
74+
Protocol: corev1.ProtocolTCP,
75+
},
76+
},
77+
InitContainers: []corev1.Container{
78+
{
79+
Name: "test",
80+
},
81+
},
82+
},
83+
},
84+
}
85+
86+
otelcolYaml, err := otelcol.Spec.Config.Yaml()
87+
require.NoError(t, err)
88+
cfg := config.New(config.WithCollectorImage("some-default-image"))
89+
90+
// test
91+
changed, err := add(cfg, logger, otelcol, pod, nil)
92+
93+
// verify
94+
assert.NoError(t, err)
95+
require.Len(t, changed.Spec.Containers, 1)
96+
require.Len(t, changed.Spec.InitContainers, 3)
97+
require.Len(t, changed.Spec.Volumes, 1)
98+
assert.Equal(t, "some-app.otelcol-native-sidecar",
99+
changed.Labels["sidecar.opentelemetry.io/injected"])
100+
expectedPolicy := corev1.ContainerRestartPolicyAlways
101+
assert.Equal(t, corev1.Container{
102+
Name: "otc-container",
103+
Image: "some-default-image",
104+
Args: []string{"--config=env:OTEL_CONFIG"},
105+
RestartPolicy: &expectedPolicy,
106+
Env: []corev1.EnvVar{
107+
{
108+
Name: "POD_NAME",
109+
ValueFrom: &corev1.EnvVarSource{
110+
FieldRef: &corev1.ObjectFieldSelector{
111+
FieldPath: "metadata.name",
112+
},
113+
},
114+
},
115+
{
116+
Name: "OTEL_CONFIG",
117+
Value: string(otelcolYaml),
118+
},
119+
},
120+
Ports: []corev1.ContainerPort{
121+
{
122+
Name: "metrics",
123+
ContainerPort: 8888,
124+
Protocol: corev1.ProtocolTCP,
125+
},
126+
},
127+
}, changed.Spec.InitContainers[2])
128+
}
129+
33130
func TestAddSidecarWhenNoSidecarExists(t *testing.T) {
34131
// prepare
35132
pod := corev1.Pod{
@@ -144,6 +241,11 @@ func TestRemoveSidecar(t *testing.T) {
144241
{Name: naming.Container()},
145242
{Name: naming.Container()}, // two sidecars! should remove both
146243
},
244+
InitContainers: []corev1.Container{
245+
{Name: "something"},
246+
{Name: naming.Container()}, // NOTE: native sidecar since k8s 1.28.
247+
{Name: naming.Container()}, // two sidecars! should remove both
248+
},
147249
},
148250
}
149251

@@ -190,6 +292,19 @@ func TestExistsIn(t *testing.T) {
190292
},
191293
true},
192294

295+
{"does-have-native-sidecar",
296+
corev1.Pod{
297+
Spec: corev1.PodSpec{
298+
Containers: []corev1.Container{
299+
{Name: "my-app"},
300+
},
301+
InitContainers: []corev1.Container{
302+
{Name: naming.Container()},
303+
},
304+
},
305+
},
306+
true},
307+
193308
{"does-not-have-sidecar",
194309
corev1.Pod{
195310
Spec: corev1.PodSpec{

0 commit comments

Comments
 (0)