Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject otelcol sidecar into any namespace #1395

Merged
merged 8 commits into from
Feb 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .chloggen/inject-any-namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. operator, target allocator, github action)
component: operator

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Support sidecar injecton into any namespace.

# One or more tracking issues related to the change
issues: [199]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ The `CustomResource` for the `OpenTelemetryCollector` exposes a property named `

#### Sidecar injection

A sidecar with the OpenTelemetry Collector can be injected into pod-based workloads by setting the pod annotation `sidecar.opentelemetry.io/inject` to either `"true"`, or to the name of a concrete `OpenTelemetryCollector` from the same namespace, like in the following example:
A sidecar with the OpenTelemetry Collector can be injected into pod-based workloads by setting the pod annotation `sidecar.opentelemetry.io/inject` to either `"true"`, or to the name of a concrete `OpenTelemetryCollector`, like in the following example:

```yaml
kubectl apply -f - <<EOF
Expand Down Expand Up @@ -139,6 +139,13 @@ The annotation value can come either from the namespace, or from the pod. The mo
* the pod annotation is used when it's set to a concrete instance name or to `"false"`
* namespace annotation is used when the pod annotation is either absent or set to `"true"`, and the namespace is set to a concrete instance or to `"false"`

The possible values for the annotation can be:

* "true" - inject `OpenTelemetryCollector` resource from the namespace.
* "sidecar-for-my-app" - name of `OpenTelemetryCollector` CR instance in the current namespace.
* "my-other-namespace/my-instrumentation" - name and namespace of `OpenTelemetryCollector` CR instance in another namespace.
* "false" - do not inject

When using a pod-based workload, such as `Deployment` or `Statefulset`, make sure to add the annotation to the `PodTemplate` part. Like:

```yaml
Expand Down
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
3 changes: 1 addition & 2 deletions internal/webhookhandler/webhookhandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,10 @@ func TestShouldInjectSidecar(t *testing.T) {
// verify
assert.True(t, res.Allowed)
assert.Nil(t, res.AdmissionResponse.Result)
assert.Len(t, res.Patches, 3)
assert.Len(t, res.Patches, 2)

expectedMap := map[string]bool{
"/metadata/labels": false, // add a new label
"/spec/volumes": false, // add a new volume with the configmap
"/spec/containers": false, // replace the containers, adding one new container
}
for _, patch := range res.Patches {
Expand Down
25 changes: 13 additions & 12 deletions pkg/collector/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import (
const maxPortLen = 15

// Container builds a container for the given collector.
func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector) corev1.Container {
func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector, addConfig bool) corev1.Container {
image := otelcol.Spec.Image
if len(image) == 0 {
image = cfg.CollectorImage()
Expand All @@ -52,28 +52,29 @@ func Container(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelem
}
}

var volumeMounts []corev1.VolumeMount
argsMap := otelcol.Spec.Args
if argsMap == nil {
argsMap = map[string]string{}
}

if _, exists := argsMap["config"]; exists {
logger.Info("the 'config' flag isn't allowed and is being ignored")
if addConfig {
if _, exists := argsMap["config"]; exists {
logger.Info("the 'config' flag isn't allowed and is being ignored")
}
// this effectively overrides any 'config' entry that might exist in the CR
argsMap["config"] = fmt.Sprintf("/conf/%s", cfg.CollectorConfigMapEntry())
volumeMounts = append(volumeMounts,
corev1.VolumeMount{
Name: naming.ConfigMapVolume(),
MountPath: "/conf",
})
}

// this effectively overrides any 'config' entry that might exist in the CR
argsMap["config"] = fmt.Sprintf("/conf/%s", cfg.CollectorConfigMapEntry())

var args []string
for k, v := range argsMap {
args = append(args, fmt.Sprintf("--%s=%s", k, v))
}

volumeMounts := []corev1.VolumeMount{{
Name: naming.ConfigMapVolume(),
MountPath: "/conf",
}}

if len(otelcol.Spec.VolumeMounts) > 0 {
volumeMounts = append(volumeMounts, otelcol.Spec.VolumeMounts...)
}
Expand Down
30 changes: 15 additions & 15 deletions pkg/collector/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestContainerNewDefault(t *testing.T) {
cfg := config.New(config.WithCollectorImage("default-image"))

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Equal(t, "default-image", c.Image)
Expand All @@ -58,7 +58,7 @@ func TestContainerWithImageOverridden(t *testing.T) {
cfg := config.New(config.WithCollectorImage("default-image"))

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Equal(t, "overridden-image", c.Image)
Expand Down Expand Up @@ -191,7 +191,7 @@ service:
cfg := config.New(config.WithCollectorImage("default-image"))

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.ElementsMatch(t, testCase.expectedPorts, c.Ports)
Expand All @@ -212,7 +212,7 @@ func TestContainerConfigFlagIsIgnored(t *testing.T) {
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Len(t, c.Args, 2)
Expand All @@ -232,7 +232,7 @@ func TestContainerCustomVolumes(t *testing.T) {
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Len(t, c.VolumeMounts, 2)
Expand All @@ -241,7 +241,7 @@ func TestContainerCustomVolumes(t *testing.T) {

func TestContainerCustomSecurityContext(t *testing.T) {
// default config without security context
c1 := Container(config.New(), logger, v1alpha1.OpenTelemetryCollector{Spec: v1alpha1.OpenTelemetryCollectorSpec{}})
c1 := Container(config.New(), logger, v1alpha1.OpenTelemetryCollector{Spec: v1alpha1.OpenTelemetryCollectorSpec{}}, true)

// verify
assert.Nil(t, c1.SecurityContext)
Expand All @@ -258,7 +258,7 @@ func TestContainerCustomSecurityContext(t *testing.T) {
RunAsUser: &uid,
},
},
})
}, true)

// verify
assert.NotNil(t, c2.SecurityContext)
Expand All @@ -281,7 +281,7 @@ func TestContainerEnvVarsOverridden(t *testing.T) {
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Len(t, c.Env, 2)
Expand All @@ -297,7 +297,7 @@ func TestContainerDefaultEnvVars(t *testing.T) {
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Len(t, c.Env, 1)
Expand All @@ -323,7 +323,7 @@ func TestContainerResourceRequirements(t *testing.T) {
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Equal(t, resource.MustParse("100m"), *c.Resources.Limits.Cpu())
Expand All @@ -340,7 +340,7 @@ func TestContainerDefaultResourceRequirements(t *testing.T) {
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Empty(t, c.Resources)
Expand All @@ -359,7 +359,7 @@ func TestContainerArgs(t *testing.T) {
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Contains(t, c.Args, "--metrics-level=detailed")
Expand All @@ -376,7 +376,7 @@ func TestContainerImagePullPolicy(t *testing.T) {
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Equal(t, c.ImagePullPolicy, corev1.PullIfNotPresent)
Expand Down Expand Up @@ -409,7 +409,7 @@ func TestContainerEnvFrom(t *testing.T) {
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Contains(t, c.EnvFrom, envFrom1)
Expand All @@ -429,7 +429,7 @@ service:
cfg := config.New()

// test
c := Container(cfg, logger, otelcol)
c := Container(cfg, logger, otelcol, true)

// verify
assert.Equal(t, "/", c.LivenessProbe.HTTPGet.Path)
Expand Down
2 changes: 1 addition & 1 deletion pkg/collector/daemonset.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func DaemonSet(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelem
},
Spec: corev1.PodSpec{
ServiceAccountName: ServiceAccountName(otelcol),
Containers: []corev1.Container{Container(cfg, logger, otelcol)},
Containers: []corev1.Container{Container(cfg, logger, otelcol, true)},
Volumes: Volumes(cfg, otelcol),
Tolerations: otelcol.Spec.Tolerations,
NodeSelector: otelcol.Spec.NodeSelector,
Expand Down
2 changes: 1 addition & 1 deletion pkg/collector/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func Deployment(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTele
},
Spec: corev1.PodSpec{
ServiceAccountName: ServiceAccountName(otelcol),
Containers: []corev1.Container{Container(cfg, logger, otelcol)},
Containers: []corev1.Container{Container(cfg, logger, otelcol, true)},
Volumes: Volumes(cfg, otelcol),
DNSPolicy: getDNSPolicy(otelcol),
HostNetwork: otelcol.Spec.HostNetwork,
Expand Down
13 changes: 7 additions & 6 deletions pkg/collector/reconcile/config_replace.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
_ "github.com/prometheus/prometheus/discovery/install" // Package install has the side-effect of registering all builtin.
"gopkg.in/yaml.v2"

"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
"github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters"
"github.com/open-telemetry/opentelemetry-operator/pkg/naming"
ta "github.com/open-telemetry/opentelemetry-operator/pkg/targetallocator/adapters"
Expand All @@ -34,16 +35,16 @@ type Config struct {
PromConfig *promconfig.Config `yaml:"config"`
}

func ReplaceConfig(params Params) (string, error) {
if !params.Instance.Spec.TargetAllocator.Enabled {
return params.Instance.Spec.Config, nil
func ReplaceConfig(instance v1alpha1.OpenTelemetryCollector) (string, error) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the API bc the function uses only the otelcol instance

if !instance.Spec.TargetAllocator.Enabled {
return instance.Spec.Config, nil
}
config, getStringErr := adapters.ConfigFromString(params.Instance.Spec.Config)
config, getStringErr := adapters.ConfigFromString(instance.Spec.Config)
if getStringErr != nil {
return "", getStringErr
}

promCfgMap, getCfgPromErr := ta.ConfigToPromConfig(params.Instance.Spec.Config)
promCfgMap, getCfgPromErr := ta.ConfigToPromConfig(instance.Spec.Config)
if getCfgPromErr != nil {
return "", getCfgPromErr
}
Expand All @@ -65,7 +66,7 @@ func ReplaceConfig(params Params) (string, error) {
escapedJob := url.QueryEscape(cfg.PromConfig.ScrapeConfigs[i].JobName)
cfg.PromConfig.ScrapeConfigs[i].ServiceDiscoveryConfigs = discovery.Configs{
&http.SDConfig{
URL: fmt.Sprintf("http://%s:80/jobs/%s/targets?collector_id=$POD_NAME", naming.TAService(params.Instance), escapedJob),
URL: fmt.Sprintf("http://%s:80/jobs/%s/targets?collector_id=$POD_NAME", naming.TAService(instance), escapedJob),
},
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/collector/reconcile/config_replace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestPrometheusParser(t *testing.T) {
assert.NoError(t, err)

t.Run("should update config with http_sd_config", func(t *testing.T) {
actualConfig, err := ReplaceConfig(param)
actualConfig, err := ReplaceConfig(param.Instance)
assert.NoError(t, err)

// prepare
Expand Down Expand Up @@ -63,7 +63,7 @@ func TestPrometheusParser(t *testing.T) {

t.Run("should not update config with http_sd_config", func(t *testing.T) {
param.Instance.Spec.TargetAllocator.Enabled = false
actualConfig, err := ReplaceConfig(param)
actualConfig, err := ReplaceConfig(param.Instance)
assert.NoError(t, err)

// prepare
Expand Down
2 changes: 1 addition & 1 deletion pkg/collector/reconcile/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func desiredConfigMap(_ context.Context, params Params) corev1.ConfigMap {
} else {
labels["app.kubernetes.io/version"] = "latest"
}
config, err := ReplaceConfig(params)
config, err := ReplaceConfig(params.Instance)
if err != nil {
params.Log.V(2).Info("failed to update prometheus config to use sharded targets: ", err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/collector/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func StatefulSet(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTel
},
Spec: corev1.PodSpec{
ServiceAccountName: ServiceAccountName(otelcol),
Containers: []corev1.Container{Container(cfg, logger, otelcol)},
Containers: []corev1.Container{Container(cfg, logger, otelcol, true)},
Volumes: Volumes(cfg, otelcol),
DNSPolicy: getDNSPolicy(otelcol),
HostNetwork: otelcol.Spec.HostNetwork,
Expand Down
18 changes: 13 additions & 5 deletions pkg/sidecar/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,31 @@ import (
"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
"github.com/open-telemetry/opentelemetry-operator/internal/config"
"github.com/open-telemetry/opentelemetry-operator/pkg/collector"
"github.com/open-telemetry/opentelemetry-operator/pkg/collector/reconcile"
"github.com/open-telemetry/opentelemetry-operator/pkg/naming"
)

const (
label = "sidecar.opentelemetry.io/injected"
label = "sidecar.opentelemetry.io/injected"
confEnvVar = "OTEL_CONFIG"
)

// add a new sidecar container to the given pod, based on the given OpenTelemetryCollector.
func add(cfg config.Config, logger logr.Logger, otelcol v1alpha1.OpenTelemetryCollector, pod corev1.Pod, attributes []corev1.EnvVar) (corev1.Pod, error) {
// add the container
volumes := collector.Volumes(cfg, otelcol)
container := collector.Container(cfg, logger, otelcol)
otelColCfg, err := reconcile.ReplaceConfig(otelcol)
if err != nil {
return pod, err
}

container := collector.Container(cfg, logger, otelcol, false)
container.Args = append(container.Args, fmt.Sprintf("--config=env:%s", confEnvVar))

container.Env = append(container.Env, corev1.EnvVar{Name: confEnvVar, Value: otelColCfg})
if !hasResourceAttributeEnvVar(container.Env) {
container.Env = append(container.Env, attributes...)
}
pod.Spec.Containers = append(pod.Spec.Containers, container)
pod.Spec.Volumes = append(pod.Spec.Volumes, volumes...)
pod.Spec.Volumes = append(pod.Spec.Volumes, otelcol.Spec.Volumes...)

if pod.Labels == nil {
pod.Labels = map[string]string{}
Expand Down
Loading