From 82b4585deb9088431ba993b89f42383d6fe7a45a Mon Sep 17 00:00:00 2001
From: Pavol Loffay
Date: Tue, 5 Mar 2024 16:45:08 +0100
Subject: [PATCH 1/3] Add conversion from v1beta1 to v1alpha1 (#2706)
* Add conversion from v1beta1 to v1alpha1
Signed-off-by: Pavol Loffay
* Fix
Signed-off-by: Pavol Loffay
* Fix
Signed-off-by: Pavol Loffay
* Fix
Signed-off-by: Pavol Loffay
---------
Signed-off-by: Pavol Loffay
---
apis/v1alpha1/convert.go | 184 ++++++++++++++++++++-
apis/v1alpha1/convert_test.go | 297 ++++++++++++++++++++++++++++------
2 files changed, 432 insertions(+), 49 deletions(-)
diff --git a/apis/v1alpha1/convert.go b/apis/v1alpha1/convert.go
index 6428f12619..ea4d6913c3 100644
--- a/apis/v1alpha1/convert.go
+++ b/apis/v1alpha1/convert.go
@@ -35,7 +35,7 @@ func Tov1beta1(in OpenTelemetryCollector) (v1beta1.OpenTelemetryCollector, error
StatusReplicas: in.Status.Scale.StatusReplicas,
},
Version: in.Status.Version,
- Image: in.Spec.Image,
+ Image: in.Status.Image,
},
}
@@ -108,7 +108,7 @@ func Tov1beta1(in OpenTelemetryCollector) (v1beta1.OpenTelemetryCollector, error
out.Spec.OpenTelemetryCommonFields.InitContainers = copy.Spec.InitContainers
out.Spec.OpenTelemetryCommonFields.AdditionalContainers = copy.Spec.AdditionalContainers
- out.Spec.TargetAllocator = TargetAllocatorEmbedded(copy.Spec.TargetAllocator)
+ out.Spec.TargetAllocator = tov1beta1TA(copy.Spec.TargetAllocator)
out.Spec.Mode = v1beta1.Mode(copy.Spec.Mode)
out.Spec.UpgradeStrategy = v1beta1.UpgradeStrategy(copy.Spec.UpgradeStrategy)
@@ -132,6 +132,7 @@ func Tov1beta1(in OpenTelemetryCollector) (v1beta1.OpenTelemetryCollector, error
}
out.Spec.Observability.Metrics.EnableMetrics = copy.Spec.Observability.Metrics.EnableMetrics
+ out.Spec.Observability.Metrics.DisablePrometheusAnnotations = copy.Spec.Observability.Metrics.DisablePrometheusAnnotations
for _, cm := range copy.Spec.ConfigMaps {
out.Spec.ConfigMaps = append(out.Spec.ConfigMaps, v1beta1.ConfigMapsSpec{
@@ -146,7 +147,7 @@ func Tov1beta1(in OpenTelemetryCollector) (v1beta1.OpenTelemetryCollector, error
return out, nil
}
-func TargetAllocatorEmbedded(in OpenTelemetryTargetAllocator) v1beta1.TargetAllocatorEmbedded {
+func tov1beta1TA(in OpenTelemetryTargetAllocator) v1beta1.TargetAllocatorEmbedded {
out := v1beta1.TargetAllocatorEmbedded{}
out.Replicas = in.Replicas
out.NodeSelector = in.NodeSelector
@@ -184,3 +185,180 @@ func TargetAllocatorEmbedded(in OpenTelemetryTargetAllocator) v1beta1.TargetAllo
}
return out
}
+
+func tov1alpha1(in v1beta1.OpenTelemetryCollector) (*OpenTelemetryCollector, error) {
+ copy := in.DeepCopy()
+ configYaml, err := in.Spec.Config.Yaml()
+ if err != nil {
+ return nil, err
+ }
+
+ return &OpenTelemetryCollector{
+ ObjectMeta: copy.ObjectMeta,
+ Status: OpenTelemetryCollectorStatus{
+ Scale: ScaleSubresourceStatus{
+ Selector: in.Status.Scale.Selector,
+ Replicas: in.Status.Scale.Replicas,
+ StatusReplicas: in.Status.Scale.StatusReplicas,
+ },
+ Version: in.Status.Version,
+ Image: in.Status.Image,
+ },
+
+ Spec: OpenTelemetryCollectorSpec{
+ ManagementState: ManagementStateType(copy.Spec.ManagementState),
+ Resources: copy.Spec.Resources,
+ NodeSelector: copy.Spec.NodeSelector,
+ Args: copy.Spec.Args,
+ Replicas: copy.Spec.Replicas,
+ Autoscaler: tov1alpha1Autoscaler(copy.Spec.Autoscaler),
+ PodDisruptionBudget: tov1alpha1PodDisruptionBudget(copy.Spec.PodDisruptionBudget),
+ SecurityContext: copy.Spec.SecurityContext,
+ PodSecurityContext: copy.Spec.PodSecurityContext,
+ PodAnnotations: copy.Spec.PodAnnotations,
+ TargetAllocator: tov1alpha1TA(in.Spec.TargetAllocator),
+ Mode: Mode(copy.Spec.Mode),
+ ServiceAccount: copy.Spec.ServiceAccount,
+ Image: copy.Spec.Image,
+ UpgradeStrategy: UpgradeStrategy(copy.Spec.UpgradeStrategy),
+ ImagePullPolicy: copy.Spec.ImagePullPolicy,
+ Config: configYaml,
+ VolumeMounts: copy.Spec.VolumeMounts,
+ Ports: copy.Spec.Ports,
+ Env: copy.Spec.Env,
+ EnvFrom: copy.Spec.EnvFrom,
+ VolumeClaimTemplates: copy.Spec.VolumeClaimTemplates,
+ Tolerations: copy.Spec.Tolerations,
+ Volumes: copy.Spec.Volumes,
+ Ingress: Ingress{
+ Type: IngressType(copy.Spec.Ingress.Type),
+ RuleType: IngressRuleType(copy.Spec.Ingress.RuleType),
+ Hostname: copy.Spec.Ingress.Hostname,
+ Annotations: copy.Spec.Ingress.Annotations,
+ TLS: copy.Spec.Ingress.TLS,
+ IngressClassName: copy.Spec.Ingress.IngressClassName,
+ Route: OpenShiftRoute{
+ Termination: TLSRouteTerminationType(copy.Spec.Ingress.Route.Termination),
+ },
+ },
+ HostNetwork: copy.Spec.HostNetwork,
+ ShareProcessNamespace: copy.Spec.ShareProcessNamespace,
+ PriorityClassName: copy.Spec.PriorityClassName,
+ Affinity: copy.Spec.Affinity,
+ Lifecycle: copy.Spec.Lifecycle,
+ TerminationGracePeriodSeconds: copy.Spec.TerminationGracePeriodSeconds,
+ LivenessProbe: tov1alpha1Probe(copy.Spec.LivenessProbe),
+ InitContainers: copy.Spec.InitContainers,
+ AdditionalContainers: copy.Spec.AdditionalContainers,
+ Observability: ObservabilitySpec{
+ Metrics: MetricsConfigSpec{
+ EnableMetrics: copy.Spec.Observability.Metrics.EnableMetrics,
+ DisablePrometheusAnnotations: copy.Spec.Observability.Metrics.DisablePrometheusAnnotations,
+ },
+ },
+ TopologySpreadConstraints: copy.Spec.TopologySpreadConstraints,
+ ConfigMaps: tov1alpha1ConfigMaps(copy.Spec.ConfigMaps),
+ UpdateStrategy: copy.Spec.DaemonSetUpdateStrategy,
+ DeploymentUpdateStrategy: copy.Spec.DeploymentUpdateStrategy,
+ },
+ }, nil
+}
+
+func tov1alpha1PodDisruptionBudget(in *v1beta1.PodDisruptionBudgetSpec) *PodDisruptionBudgetSpec {
+ if in == nil {
+ return nil
+ }
+ return &PodDisruptionBudgetSpec{
+ MinAvailable: in.MinAvailable,
+ MaxUnavailable: in.MaxUnavailable,
+ }
+}
+
+func tov1alpha1Probe(in *v1beta1.Probe) *Probe {
+ if in == nil {
+ return nil
+ }
+ return &Probe{
+ InitialDelaySeconds: in.InitialDelaySeconds,
+ TimeoutSeconds: in.TimeoutSeconds,
+ PeriodSeconds: in.PeriodSeconds,
+ SuccessThreshold: in.SuccessThreshold,
+ FailureThreshold: in.FailureThreshold,
+ TerminationGracePeriodSeconds: in.TerminationGracePeriodSeconds,
+ }
+}
+
+func tov1alpha1Autoscaler(in *v1beta1.AutoscalerSpec) *AutoscalerSpec {
+ if in == nil {
+ return nil
+ }
+
+ var metrics []MetricSpec
+ for _, m := range in.Metrics {
+ metrics = append(metrics, MetricSpec{
+ Type: m.Type,
+ Pods: m.Pods,
+ })
+ }
+
+ return &AutoscalerSpec{
+ MinReplicas: in.MinReplicas,
+ MaxReplicas: in.MaxReplicas,
+ Behavior: in.Behavior,
+ Metrics: metrics,
+ TargetCPUUtilization: in.TargetCPUUtilization,
+ TargetMemoryUtilization: in.TargetMemoryUtilization,
+ }
+}
+
+func tov1alpha1ConfigMaps(in []v1beta1.ConfigMapsSpec) []ConfigMapsSpec {
+ var mapsSpecs []ConfigMapsSpec
+ for _, m := range in {
+ mapsSpecs = append(mapsSpecs, ConfigMapsSpec{
+ Name: m.Name,
+ MountPath: m.MountPath,
+ })
+ }
+ return mapsSpecs
+}
+
+func tov1alpha1TA(in v1beta1.TargetAllocatorEmbedded) OpenTelemetryTargetAllocator {
+ var podMonitorSelector map[string]string
+ if in.PrometheusCR.PodMonitorSelector != nil {
+ podMonitorSelector = in.PrometheusCR.PodMonitorSelector.MatchLabels
+ }
+ var serviceMonitorSelector map[string]string
+ if in.PrometheusCR.ServiceMonitorSelector != nil {
+ serviceMonitorSelector = in.PrometheusCR.ServiceMonitorSelector.MatchLabels
+ }
+
+ return OpenTelemetryTargetAllocator{
+ Replicas: in.Replicas,
+ NodeSelector: in.NodeSelector,
+ Resources: in.Resources,
+ AllocationStrategy: OpenTelemetryTargetAllocatorAllocationStrategy(in.AllocationStrategy),
+ FilterStrategy: string(in.FilterStrategy),
+ ServiceAccount: in.ServiceAccount,
+ Image: in.Image,
+ Enabled: in.Enabled,
+ Affinity: in.Affinity,
+ PrometheusCR: OpenTelemetryTargetAllocatorPrometheusCR{
+ Enabled: in.PrometheusCR.Enabled,
+ ScrapeInterval: in.PrometheusCR.ScrapeInterval,
+ PodMonitorSelector: podMonitorSelector,
+ ServiceMonitorSelector: serviceMonitorSelector,
+ },
+ SecurityContext: in.SecurityContext,
+ PodSecurityContext: in.PodSecurityContext,
+ TopologySpreadConstraints: in.TopologySpreadConstraints,
+ Tolerations: in.Tolerations,
+ Env: in.Env,
+ Observability: ObservabilitySpec{
+ Metrics: MetricsConfigSpec{
+ EnableMetrics: in.Observability.Metrics.EnableMetrics,
+ DisablePrometheusAnnotations: in.Observability.Metrics.DisablePrometheusAnnotations,
+ },
+ },
+ PodDisruptionBudget: tov1alpha1PodDisruptionBudget(in.PodDisruptionBudget),
+ }
+}
diff --git a/apis/v1alpha1/convert_test.go b/apis/v1alpha1/convert_test.go
index a63a32f79a..ad248d278b 100644
--- a/apis/v1alpha1/convert_test.go
+++ b/apis/v1alpha1/convert_test.go
@@ -19,8 +19,12 @@ import (
"time"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
+ appsv1 "k8s.io/api/apps/v1"
+ autoscalingv2 "k8s.io/api/autoscaling/v2"
v1 "k8s.io/api/core/v1"
+ networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
@@ -28,9 +32,7 @@ import (
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
)
-func Test_V1Alpha1to2(t *testing.T) {
- t.Run("valid config", func(t *testing.T) {
- config := `---
+const collectorCfg = `---
receivers:
otlp:
protocols:
@@ -48,9 +50,12 @@ service:
processors: [resourcedetection]
exporters: [otlp]
`
+
+func Test_tov1beta1_config(t *testing.T) {
+ t.Run("valid config", func(t *testing.T) {
cfgV1 := OpenTelemetryCollector{
Spec: OpenTelemetryCollectorSpec{
- Config: config,
+ Config: collectorCfg,
Args: map[string]string{
"test": "something",
},
@@ -64,7 +69,7 @@ service:
yamlCfg, err := yaml.Marshal(&cfgV2.Spec.Config)
assert.Nil(t, err)
- assert.YAMLEq(t, config, string(yamlCfg))
+ assert.YAMLEq(t, collectorCfg, string(yamlCfg))
})
t.Run("invalid config", func(t *testing.T) {
config := `!!!`
@@ -79,13 +84,252 @@ service:
})
}
-func Test_TargetAllocator(t *testing.T) {
+func Test_tov1alpha1_config(t *testing.T) {
+ cfg := v1beta1.Config{}
+ err := yaml.Unmarshal([]byte(collectorCfg), &cfg)
+ require.NoError(t, err)
+
+ beta1Col := v1beta1.OpenTelemetryCollector{
+ Spec: v1beta1.OpenTelemetryCollectorSpec{
+ Config: cfg,
+ },
+ }
+ alpha1Col, err := tov1alpha1(beta1Col)
+ require.NoError(t, err)
+ assert.YAMLEq(t, collectorCfg, alpha1Col.Spec.Config)
+}
+
+func Test_tov1beta1AndBack(t *testing.T) {
+ one := int32(1)
+ two := int64(2)
+ intstrAAA := intstr.FromString("aaa")
+ boolTrue := true
+ ingressClass := "someClass"
+ colalpha1 := &OpenTelemetryCollector{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "otel",
+ Namespace: "observability",
+ Labels: map[string]string{"foo": "bar"},
+ Annotations: map[string]string{"bax": "foo"},
+ },
+ Spec: OpenTelemetryCollectorSpec{
+ ManagementState: ManagementStateManaged,
+ Resources: v1.ResourceRequirements{
+ Limits: v1.ResourceList{
+ v1.ResourceCPU: resource.MustParse("500m"),
+ v1.ResourceMemory: resource.MustParse("128Mi"),
+ },
+ Requests: v1.ResourceList{
+ v1.ResourceCPU: resource.MustParse("500m"),
+ v1.ResourceMemory: resource.MustParse("128Mi"),
+ },
+ },
+ NodeSelector: map[string]string{"aaa": "ccc"},
+ Args: map[string]string{"foo": "bar"},
+ Replicas: &one,
+ Autoscaler: &AutoscalerSpec{
+ MinReplicas: &one,
+ MaxReplicas: &one,
+ Behavior: &autoscalingv2.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscalingv2.HPAScalingRules{
+ Policies: []autoscalingv2.HPAScalingPolicy{
+ {
+ Type: "aaa",
+ Value: 2,
+ PeriodSeconds: 4,
+ },
+ },
+ },
+ },
+ Metrics: []MetricSpec{
+ {
+ Type: autoscalingv2.ContainerResourceMetricSourceType,
+ Pods: &autoscalingv2.PodsMetricSource{
+ Metric: autoscalingv2.MetricIdentifier{
+ Name: "rrrrt",
+ },
+ },
+ },
+ },
+ TargetCPUUtilization: &one,
+ TargetMemoryUtilization: &one,
+ },
+ PodDisruptionBudget: &PodDisruptionBudgetSpec{
+ MinAvailable: &intstrAAA,
+ MaxUnavailable: &intstrAAA,
+ },
+ SecurityContext: &v1.SecurityContext{
+ RunAsUser: &two,
+ },
+ PodSecurityContext: &v1.PodSecurityContext{
+ RunAsNonRoot: &boolTrue,
+ },
+ PodAnnotations: map[string]string{"foo": "bar"},
+ TargetAllocator: createTA(),
+ Mode: ModeDeployment,
+ ServiceAccount: "foo",
+ Image: "baz/bar:1.0",
+ UpgradeStrategy: UpgradeStrategyAutomatic,
+ ImagePullPolicy: v1.PullAlways,
+ Config: collectorCfg,
+ VolumeMounts: []v1.VolumeMount{
+ {
+ Name: "aaa",
+ },
+ },
+ Ports: []v1.ServicePort{
+ {
+ Name: "otlp",
+ },
+ },
+ Env: []v1.EnvVar{
+ {
+ Name: "foo",
+ Value: "bar",
+ ValueFrom: &v1.EnvVarSource{
+ ResourceFieldRef: &v1.ResourceFieldSelector{
+ ContainerName: "bbb",
+ Resource: "aaa",
+ Divisor: resource.Quantity{},
+ },
+ },
+ },
+ },
+ EnvFrom: []v1.EnvFromSource{
+ {
+ Prefix: "aa",
+ ConfigMapRef: &v1.ConfigMapEnvSource{
+ LocalObjectReference: v1.LocalObjectReference{
+ Name: "bbb",
+ },
+ },
+ },
+ },
+ VolumeClaimTemplates: []v1.PersistentVolumeClaim{
+ {
+ TypeMeta: metav1.TypeMeta{},
+ ObjectMeta: metav1.ObjectMeta{},
+ Spec: v1.PersistentVolumeClaimSpec{
+ VolumeName: "aaaa",
+ },
+ },
+ },
+ Tolerations: []v1.Toleration{
+ {
+ Key: "11",
+ Operator: "33",
+ Value: "44",
+ Effect: "55",
+ },
+ },
+ Volumes: []v1.Volume{
+ {
+ Name: "cfg",
+ VolumeSource: v1.VolumeSource{},
+ },
+ },
+ Ingress: Ingress{
+ Type: IngressTypeRoute,
+ RuleType: IngressRuleTypePath,
+ Hostname: "foo.com",
+ Annotations: map[string]string{"aa": "bb"},
+ TLS: []networkingv1.IngressTLS{
+ {
+ Hosts: []string{"foo"},
+ SecretName: "bar",
+ },
+ },
+ IngressClassName: &ingressClass,
+ Route: OpenShiftRoute{
+ Termination: TLSRouteTerminationTypeEdge,
+ },
+ },
+ HostNetwork: true,
+ ShareProcessNamespace: true,
+ PriorityClassName: "foobar",
+ Affinity: &v1.Affinity{
+ NodeAffinity: &v1.NodeAffinity{
+ PreferredDuringSchedulingIgnoredDuringExecution: []v1.PreferredSchedulingTerm{{
+ Weight: 444,
+ }},
+ },
+ },
+ Lifecycle: &v1.Lifecycle{
+ PostStart: &v1.LifecycleHandler{
+ Exec: &v1.ExecAction{
+ Command: []string{"/bin"},
+ },
+ },
+ },
+ TerminationGracePeriodSeconds: &two,
+ LivenessProbe: &Probe{
+ PeriodSeconds: &one,
+ },
+ InitContainers: []v1.Container{
+ {
+ Name: "init",
+ },
+ },
+ AdditionalContainers: []v1.Container{
+ {
+ Name: "some",
+ },
+ },
+ Observability: ObservabilitySpec{
+ Metrics: MetricsConfigSpec{
+ EnableMetrics: true,
+ DisablePrometheusAnnotations: true,
+ },
+ },
+ TopologySpreadConstraints: []v1.TopologySpreadConstraint{
+ {
+ TopologyKey: "key",
+ },
+ },
+ ConfigMaps: []ConfigMapsSpec{
+ {
+ Name: "aaa",
+ MountPath: "bbb",
+ },
+ },
+ UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
+ Type: appsv1.RollingUpdateDaemonSetStrategyType,
+ },
+ DeploymentUpdateStrategy: appsv1.DeploymentStrategy{
+ Type: appsv1.RecreateDeploymentStrategyType,
+ },
+ },
+ Status: OpenTelemetryCollectorStatus{
+ Scale: ScaleSubresourceStatus{
+ Selector: "bar",
+ Replicas: 1,
+ StatusReplicas: "foo",
+ },
+ Version: "1.0",
+ Image: "foo/bar:1.0",
+ },
+ }
+
+ colbeta1, err := Tov1beta1(*colalpha1)
+ require.NoError(t, err)
+ colalpha1Converted, err := tov1alpha1(colbeta1)
+ require.NoError(t, err)
+
+ assert.YAMLEq(t, colalpha1.Spec.Config, colalpha1Converted.Spec.Config)
+
+ // empty the config to enable assertion on the entire objects
+ colalpha1.Spec.Config = ""
+ colalpha1Converted.Spec.Config = ""
+ assert.Equal(t, colalpha1, colalpha1Converted)
+}
+
+func createTA() OpenTelemetryTargetAllocator {
replicas := int32(2)
runAsNonRoot := true
privileged := true
runAsUser := int64(1337)
runasGroup := int64(1338)
- input := OpenTelemetryTargetAllocator{
+ return OpenTelemetryTargetAllocator{
Replicas: &replicas,
NodeSelector: map[string]string{"key": "value"},
Resources: v1.ResourceRequirements{
@@ -176,43 +420,4 @@ func Test_TargetAllocator(t *testing.T) {
},
},
}
-
- expected := v1beta1.TargetAllocatorEmbedded{
- Replicas: input.Replicas,
- NodeSelector: input.NodeSelector,
- Resources: input.Resources,
- AllocationStrategy: v1beta1.TargetAllocatorAllocationStrategyConsistentHashing,
- FilterStrategy: v1beta1.TargetAllocatorFilterStrategyRelabelConfig,
- ServiceAccount: input.ServiceAccount,
- Image: input.Image,
- Enabled: input.Enabled,
- Affinity: input.Affinity,
- PrometheusCR: v1beta1.TargetAllocatorPrometheusCR{
- Enabled: input.PrometheusCR.Enabled,
- ScrapeInterval: input.PrometheusCR.ScrapeInterval,
- PodMonitorSelector: &metav1.LabelSelector{
- MatchLabels: input.PrometheusCR.PodMonitorSelector,
- },
- ServiceMonitorSelector: &metav1.LabelSelector{
- MatchLabels: input.PrometheusCR.ServiceMonitorSelector,
- },
- },
- SecurityContext: input.SecurityContext,
- PodSecurityContext: input.PodSecurityContext,
- TopologySpreadConstraints: input.TopologySpreadConstraints,
- Tolerations: input.Tolerations,
- Env: input.Env,
- Observability: v1beta1.ObservabilitySpec{
- Metrics: v1beta1.MetricsConfigSpec{
- EnableMetrics: input.Observability.Metrics.EnableMetrics,
- DisablePrometheusAnnotations: input.Observability.Metrics.DisablePrometheusAnnotations,
- },
- },
- PodDisruptionBudget: &v1beta1.PodDisruptionBudgetSpec{
- MinAvailable: input.PodDisruptionBudget.MinAvailable,
- MaxUnavailable: input.PodDisruptionBudget.MaxUnavailable,
- },
- }
-
- assert.Equal(t, expected, TargetAllocatorEmbedded(input))
}
From 830a0f74dff5bca7323bb329d8c6798cbf9cbf66 Mon Sep 17 00:00:00 2001
From: Luiz Lelis
Date: Tue, 5 Mar 2024 12:55:21 -0300
Subject: [PATCH 2/3] chore: update `dotnet` instr. to command-line (#2690)
* chore: update `dotnet` instr. to command-line
* fix: `.NET` pkg instrumentation upgrade
* fix: remove `debug` logs
* fix: pod mutator tests for `.NET` instrumentations
* fix: using capital N for `DotNet`
---
.../dotnet-instrumentation-command-flag.yaml | 16 ++++++++
internal/config/main.go | 8 ++++
internal/config/options.go | 6 +++
main.go | 6 ++-
pkg/constants/env.go | 1 +
pkg/featuregate/featuregate.go | 6 ---
pkg/instrumentation/podmutator.go | 2 +-
pkg/instrumentation/podmutator_test.go | 38 ++++++++++---------
pkg/instrumentation/upgrade/upgrade.go | 14 +++----
pkg/instrumentation/upgrade/upgrade_test.go | 2 +
10 files changed, 66 insertions(+), 33 deletions(-)
create mode 100644 .chloggen/dotnet-instrumentation-command-flag.yaml
diff --git a/.chloggen/dotnet-instrumentation-command-flag.yaml b/.chloggen/dotnet-instrumentation-command-flag.yaml
new file mode 100644
index 0000000000..b9e20c6e08
--- /dev/null
+++ b/.chloggen/dotnet-instrumentation-command-flag.yaml
@@ -0,0 +1,16 @@
+# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
+change_type: breaking
+
+# 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: change dotnet instrumentation feature gate into command line flag --enable-dotnet-instrumentation
+
+# One or more tracking issues related to the change
+issues: [2582, 2671]
+
+# (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:
diff --git a/internal/config/main.go b/internal/config/main.go
index b336ed4bce..7454200ab0 100644
--- a/internal/config/main.go
+++ b/internal/config/main.go
@@ -45,6 +45,7 @@ type Config struct {
createRBACPermissions bool
enableMultiInstrumentation bool
enableApacheHttpdInstrumentation bool
+ enableDotNetInstrumentation bool
autoInstrumentationDotNetImage string
autoInstrumentationGoImage string
autoInstrumentationApacheHttpdImage string
@@ -79,6 +80,7 @@ func New(opts ...Option) Config {
createRBACPermissions: o.createRBACPermissions,
enableMultiInstrumentation: o.enableMultiInstrumentation,
enableApacheHttpdInstrumentation: o.enableApacheHttpdInstrumentation,
+ enableDotNetInstrumentation: o.enableDotNetInstrumentation,
targetAllocatorImage: o.targetAllocatorImage,
operatorOpAMPBridgeImage: o.operatorOpAMPBridgeImage,
targetAllocatorConfigMapEntry: o.targetAllocatorConfigMapEntry,
@@ -118,10 +120,16 @@ func (c *Config) EnableMultiInstrumentation() bool {
return c.enableMultiInstrumentation
}
+// EnableApacheHttpdAutoInstrumentation is true when the operator supports ApacheHttpd auto instrumentation.
func (c *Config) EnableApacheHttpdAutoInstrumentation() bool {
return c.enableApacheHttpdInstrumentation
}
+// EnableDotNetAutoInstrumentation is true when the operator supports dotnet auto instrumentation.
+func (c *Config) EnableDotNetAutoInstrumentation() bool {
+ return c.enableDotNetInstrumentation
+}
+
// CollectorConfigMapEntry represents the configuration file name for the collector. Immutable.
func (c *Config) CollectorConfigMapEntry() string {
return c.collectorConfigMapEntry
diff --git a/internal/config/options.go b/internal/config/options.go
index a87385d1f4..e698c84a5b 100644
--- a/internal/config/options.go
+++ b/internal/config/options.go
@@ -44,6 +44,7 @@ type options struct {
createRBACPermissions bool
enableMultiInstrumentation bool
enableApacheHttpdInstrumentation bool
+ enableDotNetInstrumentation bool
targetAllocatorConfigMapEntry string
operatorOpAMPBridgeConfigMapEntry string
targetAllocatorImage string
@@ -92,6 +93,11 @@ func WithEnableApacheHttpdInstrumentation(s bool) Option {
o.enableApacheHttpdInstrumentation = s
}
}
+func WithEnableDotNetInstrumentation(s bool) Option {
+ return func(o *options) {
+ o.enableDotNetInstrumentation = s
+ }
+}
func WithTargetAllocatorConfigMapEntry(s string) Option {
return func(o *options) {
o.targetAllocatorConfigMapEntry = s
diff --git a/main.go b/main.go
index 1711f4810e..2f0d0d9b61 100644
--- a/main.go
+++ b/main.go
@@ -109,6 +109,7 @@ func main() {
createRBACPermissions bool
enableMultiInstrumentation bool
enableApacheHttpdInstrumentation bool
+ enableDotNetInstrumentation bool
collectorImage string
targetAllocatorImage string
operatorOpAMPBridgeImage string
@@ -132,7 +133,8 @@ func main() {
"Enabling this will ensure there is only one active controller manager.")
pflag.BoolVar(&createRBACPermissions, "create-rbac-permissions", false, "Automatically create RBAC permissions needed by the processors")
pflag.BoolVar(&enableMultiInstrumentation, "enable-multi-instrumentation", false, "Controls whether the operator supports multi instrumentation")
- pflag.BoolVar(&enableApacheHttpdInstrumentation, constants.FlagApacheHttpd, true, "controls whether the operator supports Apache HTTPD auto-instrumentation")
+ pflag.BoolVar(&enableApacheHttpdInstrumentation, constants.FlagApacheHttpd, true, "Controls whether the operator supports Apache HTTPD auto-instrumentation")
+ pflag.BoolVar(&enableDotNetInstrumentation, constants.FlagDotNet, true, "Controls whether the operator supports dotnet auto-instrumentation")
stringFlagOrEnv(&collectorImage, "collector-image", "RELATED_IMAGE_COLLECTOR", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:%s", v.OpenTelemetryCollector), "The default OpenTelemetry collector image. This image is used when no image is specified in the CustomResource.")
stringFlagOrEnv(&targetAllocatorImage, "target-allocator-image", "RELATED_IMAGE_TARGET_ALLOCATOR", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/target-allocator:%s", v.TargetAllocator), "The default OpenTelemetry target allocator image. This image is used when no image is specified in the CustomResource.")
stringFlagOrEnv(&operatorOpAMPBridgeImage, "operator-opamp-bridge-image", "RELATED_IMAGE_OPERATOR_OPAMP_BRIDGE", fmt.Sprintf("ghcr.io/open-telemetry/opentelemetry-operator/operator-opamp-bridge:%s", v.OperatorOpAMPBridge), "The default OpenTelemetry Operator OpAMP Bridge image. This image is used when no image is specified in the CustomResource.")
@@ -172,6 +174,7 @@ func main() {
"labels-filter", labelsFilter,
"enable-multi-instrumentation", enableMultiInstrumentation,
"enable-apache-httpd-instrumentation", enableApacheHttpdInstrumentation,
+ "enable-dotnet-instrumentation", enableDotNetInstrumentation,
)
restConfig := ctrl.GetConfigOrDie()
@@ -190,6 +193,7 @@ func main() {
config.WithCreateRBACPermissions(createRBACPermissions),
config.WithEnableMultiInstrumentation(enableMultiInstrumentation),
config.WithEnableApacheHttpdInstrumentation(enableApacheHttpdInstrumentation),
+ config.WithEnableDotNetInstrumentation(enableDotNetInstrumentation),
config.WithTargetAllocatorImage(targetAllocatorImage),
config.WithOperatorOpAMPBridgeImage(operatorOpAMPBridgeImage),
config.WithAutoInstrumentationJavaImage(autoInstrumentationJava),
diff --git a/pkg/constants/env.go b/pkg/constants/env.go
index 510adef6cb..64efe59aa2 100644
--- a/pkg/constants/env.go
+++ b/pkg/constants/env.go
@@ -36,4 +36,5 @@ const (
EnvNodeName = "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME"
FlagApacheHttpd = "enable-apache-httpd-instrumentation"
+ FlagDotNet = "enable-dotnet-instrumentation"
)
diff --git a/pkg/featuregate/featuregate.go b/pkg/featuregate/featuregate.go
index f12fb985b6..1269188c4f 100644
--- a/pkg/featuregate/featuregate.go
+++ b/pkg/featuregate/featuregate.go
@@ -25,12 +25,6 @@ const (
)
var (
- EnableDotnetAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister(
- "operator.autoinstrumentation.dotnet",
- featuregate.StageBeta,
- featuregate.WithRegisterDescription("controls whether the operator supports .NET auto-instrumentation"),
- featuregate.WithRegisterFromVersion("v0.76.1"),
- )
EnablePythonAutoInstrumentationSupport = featuregate.GlobalRegistry().MustRegister(
"operator.autoinstrumentation.python",
featuregate.StageBeta,
diff --git a/pkg/instrumentation/podmutator.go b/pkg/instrumentation/podmutator.go
index 28da01ac6a..004e8239dd 100644
--- a/pkg/instrumentation/podmutator.go
+++ b/pkg/instrumentation/podmutator.go
@@ -265,7 +265,7 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c
logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod")
return pod, err
}
- if featuregate.EnableDotnetAutoInstrumentationSupport.IsEnabled() || inst == nil {
+ if pm.config.EnableDotNetAutoInstrumentation() || inst == nil {
insts.DotNet.Instrumentation = inst
insts.DotNet.AdditionalAnnotations = map[string]string{annotationDotNetRuntime: annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationDotNetRuntime)}
} else {
diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go
index ffe5327a03..4fa270d8dc 100644
--- a/pkg/instrumentation/podmutator_test.go
+++ b/pkg/instrumentation/podmutator_test.go
@@ -1811,6 +1811,7 @@ func TestMutatePod(t *testing.T) {
},
},
},
+ config: config.New(config.WithEnableDotNetInstrumentation(true)),
},
{
name: "dotnet injection, by namespace annotations",
@@ -1990,6 +1991,7 @@ func TestMutatePod(t *testing.T) {
},
},
},
+ config: config.New(config.WithEnableDotNetInstrumentation(true)),
},
{
name: "dotnet injection multiple containers, true",
@@ -2265,6 +2267,7 @@ func TestMutatePod(t *testing.T) {
},
},
},
+ config: config.New(config.WithEnableDotNetInstrumentation(true)),
},
{
name: "dotnet injection feature gate disabled",
@@ -2343,13 +2346,7 @@ func TestMutatePod(t *testing.T) {
},
},
},
- setFeatureGates: func(t *testing.T) {
- originalVal := featuregate.EnableDotnetAutoInstrumentationSupport.IsEnabled()
- require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableDotnetAutoInstrumentationSupport.ID(), false))
- t.Cleanup(func() {
- require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableDotnetAutoInstrumentationSupport.ID(), originalVal))
- })
- },
+ config: config.New(config.WithEnableDotNetInstrumentation(false)),
},
{
name: "go injection, true",
@@ -3845,7 +3842,10 @@ func TestMutatePod(t *testing.T) {
},
},
},
- config: config.New(config.WithEnableMultiInstrumentation(true)),
+ config: config.New(
+ config.WithEnableMultiInstrumentation(true),
+ config.WithEnableDotNetInstrumentation(true),
+ ),
},
{
name: "multi instrumentation for multiple containers feature gate enabled, container-names not used",
@@ -4503,7 +4503,10 @@ func TestMutatePod(t *testing.T) {
},
},
},
- config: config.New(config.WithEnableMultiInstrumentation(true)),
+ config: config.New(
+ config.WithEnableMultiInstrumentation(true),
+ config.WithEnableDotNetInstrumentation(true),
+ ),
},
{
name: "multi instrumentation for multiple containers feature gate disabled, multiple instrumentation annotations set",
@@ -4980,7 +4983,10 @@ func TestMutatePod(t *testing.T) {
},
},
},
- config: config.New(config.WithEnableMultiInstrumentation(true)),
+ config: config.New(
+ config.WithEnableMultiInstrumentation(true),
+ config.WithEnableDotNetInstrumentation(true),
+ ),
},
{
name: "multi instrumentation feature gate disabled, instrumentation feature gate disabled and annotation set, multiple specific containers set",
@@ -5080,14 +5086,10 @@ func TestMutatePod(t *testing.T) {
},
},
},
- config: config.New(config.WithEnableMultiInstrumentation(true)),
- setFeatureGates: func(t *testing.T) {
- originalValDotNetInstr := featuregate.EnableDotnetAutoInstrumentationSupport.IsEnabled()
- require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableDotnetAutoInstrumentationSupport.ID(), false))
- t.Cleanup(func() {
- require.NoError(t, colfeaturegate.GlobalRegistry().Set(featuregate.EnableDotnetAutoInstrumentationSupport.ID(), originalValDotNetInstr))
- })
- },
+ config: config.New(
+ config.WithEnableMultiInstrumentation(true),
+ config.WithEnableDotNetInstrumentation(false),
+ ),
},
}
diff --git a/pkg/instrumentation/upgrade/upgrade.go b/pkg/instrumentation/upgrade/upgrade.go
index 0788ac3537..831271c2a8 100644
--- a/pkg/instrumentation/upgrade/upgrade.go
+++ b/pkg/instrumentation/upgrade/upgrade.go
@@ -35,7 +35,6 @@ var (
constants.AnnotationDefaultAutoInstrumentationJava: featuregate.EnableJavaAutoInstrumentationSupport,
constants.AnnotationDefaultAutoInstrumentationNodeJS: featuregate.EnableNodeJSAutoInstrumentationSupport,
constants.AnnotationDefaultAutoInstrumentationPython: featuregate.EnablePythonAutoInstrumentationSupport,
- constants.AnnotationDefaultAutoInstrumentationDotNet: featuregate.EnableDotnetAutoInstrumentationSupport,
constants.AnnotationDefaultAutoInstrumentationGo: featuregate.EnableGoAutoInstrumentationSupport,
constants.AnnotationDefaultAutoInstrumentationNginx: featuregate.EnableNginxAutoInstrumentationSupport,
}
@@ -62,7 +61,8 @@ type InstrumentationUpgrade struct {
func NewInstrumentationUpgrade(client client.Client, logger logr.Logger, recorder record.EventRecorder, cfg config.Config) *InstrumentationUpgrade {
defaultAnnotationToConfig := map[string]autoInstConfig{
- constants.AnnotationDefaultAutoInstrumentationApacheHttpd: autoInstConfig{constants.FlagApacheHttpd, cfg.EnableApacheHttpdAutoInstrumentation()},
+ constants.AnnotationDefaultAutoInstrumentationApacheHttpd: {constants.FlagApacheHttpd, cfg.EnableApacheHttpdAutoInstrumentation()},
+ constants.AnnotationDefaultAutoInstrumentationDotNet: {constants.FlagDotNet, cfg.EnableDotNetAutoInstrumentation()},
}
return &InstrumentationUpgrade{
@@ -127,6 +127,11 @@ func (u *InstrumentationUpgrade) upgrade(_ context.Context, inst v1alpha1.Instru
upgraded.Spec.ApacheHttpd.Image = u.DefaultAutoInstApacheHttpd
upgraded.Annotations[annotation] = u.DefaultAutoInstApacheHttpd
}
+ case constants.AnnotationDefaultAutoInstrumentationDotNet:
+ if inst.Spec.DotNet.Image == autoInst {
+ upgraded.Spec.DotNet.Image = u.DefaultAutoInstDotNet
+ upgraded.Annotations[annotation] = u.DefaultAutoInstDotNet
+ }
}
} else {
u.Logger.Error(nil, "autoinstrumentation not enabled for this language", "flag", config.id)
@@ -155,11 +160,6 @@ func (u *InstrumentationUpgrade) upgrade(_ context.Context, inst v1alpha1.Instru
upgraded.Spec.Python.Image = u.DefaultAutoInstPython
upgraded.Annotations[annotation] = u.DefaultAutoInstPython
}
- case constants.AnnotationDefaultAutoInstrumentationDotNet:
- if inst.Spec.DotNet.Image == autoInst {
- upgraded.Spec.DotNet.Image = u.DefaultAutoInstDotNet
- upgraded.Annotations[annotation] = u.DefaultAutoInstDotNet
- }
case constants.AnnotationDefaultAutoInstrumentationGo:
if inst.Spec.Go.Image == autoInst {
upgraded.Spec.Go.Image = u.DefaultAutoInstGo
diff --git a/pkg/instrumentation/upgrade/upgrade_test.go b/pkg/instrumentation/upgrade/upgrade_test.go
index 7a7ea8dc4b..9e9aa7547f 100644
--- a/pkg/instrumentation/upgrade/upgrade_test.go
+++ b/pkg/instrumentation/upgrade/upgrade_test.go
@@ -79,6 +79,7 @@ func TestUpgrade(t *testing.T) {
config.WithAutoInstrumentationApacheHttpdImage("apache-httpd:1"),
config.WithAutoInstrumentationNginxImage("nginx:1"),
config.WithEnableApacheHttpdInstrumentation(true),
+ config.WithEnableDotNetInstrumentation(true),
),
).Default(context.Background(), inst)
assert.Nil(t, err)
@@ -101,6 +102,7 @@ func TestUpgrade(t *testing.T) {
config.WithAutoInstrumentationApacheHttpdImage("apache-httpd:2"),
config.WithAutoInstrumentationNginxImage("nginx:2"),
config.WithEnableApacheHttpdInstrumentation(true),
+ config.WithEnableDotNetInstrumentation(true),
)
up := NewInstrumentationUpgrade(k8sClient, ctrl.Log.WithName("instrumentation-upgrade"), &record.FakeRecorder{}, cfg)
From 1432f77c3b724a6853090e4ed212b3896420e0b4 Mon Sep 17 00:00:00 2001
From: Ishwar Kanse
Date: Tue, 5 Mar 2024 21:26:27 +0530
Subject: [PATCH 3/3] Fix multi instrumentation test asserts (#2709)
---
.../01-assert.yaml | 334 ++++++++++++------
.../01-install-app.yaml | 31 ++
.../chainsaw-test.yaml | 22 ++
.../01-assert.yaml | 38 +-
.../01-install-app.yaml | 16 +-
.../chainsaw-test.yaml | 22 ++
.../01-assert.yaml | 85 +++--
.../01-install-app.yaml | 15 +
.../chainsaw-test.yaml | 22 ++
9 files changed, 433 insertions(+), 152 deletions(-)
diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml
index 136b7abaf9..bced4be0f5 100644
--- a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml
+++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-assert.yaml
@@ -2,128 +2,232 @@ apiVersion: v1
kind: Pod
metadata:
annotations:
- instrumentation.opentelemetry.io/container-names: "shouldnt-be-instrumented"
- instrumentation.opentelemetry.io/dotnet-container-names: "dotnetapp"
+ instrumentation.opentelemetry.io/container-names: shouldnt-be-instrumented
+ instrumentation.opentelemetry.io/dotnet-container-names: dotnetapp
instrumentation.opentelemetry.io/inject-dotnet: "true"
instrumentation.opentelemetry.io/inject-java: "true"
instrumentation.opentelemetry.io/inject-nodejs: "true"
instrumentation.opentelemetry.io/inject-python: "true"
- instrumentation.opentelemetry.io/java-container-names: "javaapp"
- instrumentation.opentelemetry.io/nodejs-container-names: "nodejsapp"
- instrumentation.opentelemetry.io/python-container-names: "pythonapp"
+ instrumentation.opentelemetry.io/java-container-names: javaapp
+ instrumentation.opentelemetry.io/nodejs-container-names: nodejsapp
+ instrumentation.opentelemetry.io/python-container-names: pythonapp
sidecar.opentelemetry.io/inject: "true"
labels:
app: pod-with-multi-instrumentation
spec:
containers:
- - name: dotnetapp
- env:
- - name: OTEL_SERVICE_NAME
- value: dotnetapp
- - name: CORECLR_ENABLE_PROFILING
- value: "1"
- - name: CORECLR_PROFILER
- value: "{918728DD-259F-4A6A-AC2B-B85E1B658318}"
- - name: CORECLR_PROFILER_PATH
- value: /otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so
- - name: DOTNET_STARTUP_HOOKS
- value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll
- - name: DOTNET_ADDITIONAL_DEPS
- value: /otel-auto-instrumentation-dotnet/AdditionalDeps
- - name: OTEL_DOTNET_AUTO_HOME
- value: /otel-auto-instrumentation-dotnet
- - name: DOTNET_SHARED_STORE
- value: /otel-auto-instrumentation-dotnet/store
- - name: OTEL_TRACES_SAMPLER
- value: parentbased_traceidratio
- - name: OTEL_TRACES_SAMPLER_ARG
- value: "0.85"
- - name: OTEL_EXPORTER_OTLP_ENDPOINT
- value: http://localhost:4317
- - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
- - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
- - name: OTEL_PROPAGATORS
- value: jaeger,b3
- - name: OTEL_RESOURCE_ATTRIBUTES
- volumeMounts:
- - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
- - mountPath: /otel-auto-instrumentation-dotnet
- name: opentelemetry-auto-instrumentation-dotnet
- - name: javaapp
- env:
- - name: OTEL_SERVICE_NAME
- value: javaapp
- - name: JAVA_TOOL_OPTIONS
- value: " -javaagent:/otel-auto-instrumentation-java/javaagent.jar"
- - name: OTEL_TRACES_SAMPLER
- value: parentbased_traceidratio
- - name: OTEL_TRACES_SAMPLER_ARG
- value: "0.85"
- - name: OTEL_EXPORTER_OTLP_ENDPOINT
- value: http://localhost:4317
- - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
- - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
- - name: OTEL_PROPAGATORS
- value: jaeger,b3
- - name: OTEL_RESOURCE_ATTRIBUTES
- volumeMounts:
- - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
- - mountPath: /otel-auto-instrumentation-java
- name: opentelemetry-auto-instrumentation-java
- - name: nodejsapp
- env:
- - name: OTEL_SERVICE_NAME
- value: nodejsapp
- - name: NODE_OPTIONS
- value: ' --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js'
- - name: OTEL_TRACES_SAMPLER
- value: parentbased_traceidratio
- - name: OTEL_TRACES_SAMPLER_ARG
- value: "0.85"
- - name: OTEL_EXPORTER_OTLP_ENDPOINT
- value: http://localhost:4317
- - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
- - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
- - name: OTEL_PROPAGATORS
- value: jaeger,b3
- - name: OTEL_RESOURCE_ATTRIBUTES
- volumeMounts:
- - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
- - mountPath: /otel-auto-instrumentation-nodejs
- name: opentelemetry-auto-instrumentation-nodejs
- - name: pythonapp
- env:
- - name: OTEL_SERVICE_NAME
- value: pythonapp
- - name: OTEL_EXPORTER_OTLP_ENDPOINT
- value: http://localhost:4318
- - name: PYTHONPATH
- value: /otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation-python
- - name: OTEL_TRACES_EXPORTER
- value: otlp
- - name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
- value: http/protobuf
- - name: OTEL_METRICS_EXPORTER
- value: otlp
- - name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL
- value: http/protobuf
- - name: OTEL_TRACES_SAMPLER
- value: parentbased_traceidratio
- - name: OTEL_TRACES_SAMPLER_ARG
- value: "0.85"
- - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
- - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
- - name: OTEL_PROPAGATORS
- value: jaeger,b3
- - name: OTEL_RESOURCE_ATTRIBUTES
- volumeMounts:
- - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
- - mountPath: /otel-auto-instrumentation-python
- name: opentelemetry-auto-instrumentation-python
- - name: shouldnt-be-instrumented
- env:
- - name: TEST
- value: test
- - name: otc-container
+ - env:
+ - name: ASPNETCORE_URLS
+ value: http://+:8083
+ - name: OTEL_SERVICE_NAME
+ value: dotnetapp
+ - name: CORECLR_ENABLE_PROFILING
+ value: "1"
+ - name: CORECLR_PROFILER
+ value: '{918728DD-259F-4A6A-AC2B-B85E1B658318}'
+ - name: CORECLR_PROFILER_PATH
+ value: /otel-auto-instrumentation-dotnet/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so
+ - name: DOTNET_STARTUP_HOOKS
+ value: /otel-auto-instrumentation-dotnet/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll
+ - name: DOTNET_ADDITIONAL_DEPS
+ value: /otel-auto-instrumentation-dotnet/AdditionalDeps
+ - name: OTEL_DOTNET_AUTO_HOME
+ value: /otel-auto-instrumentation-dotnet
+ - name: DOTNET_SHARED_STORE
+ value: /otel-auto-instrumentation-dotnet/store
+ - name: OTEL_TRACES_SAMPLER
+ value: parentbased_traceidratio
+ - name: OTEL_TRACES_SAMPLER_ARG
+ value: "0.85"
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://localhost:4317
+ - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: spec.nodeName
+ - name: OTEL_PROPAGATORS
+ value: jaeger,b3
+ - name: OTEL_RESOURCE_ATTRIBUTES
+ name: dotnetapp
+ volumeMounts:
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - mountPath: /otel-auto-instrumentation-dotnet
+ name: opentelemetry-auto-instrumentation-dotnet
+ - env:
+ - name: OTEL_SERVICE_NAME
+ value: javaapp
+ - name: JAVA_TOOL_OPTIONS
+ value: ' -javaagent:/otel-auto-instrumentation-java/javaagent.jar'
+ - name: OTEL_TRACES_SAMPLER
+ value: parentbased_traceidratio
+ - name: OTEL_TRACES_SAMPLER_ARG
+ value: "0.85"
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://localhost:4317
+ - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: spec.nodeName
+ - name: OTEL_PROPAGATORS
+ value: jaeger,b3
+ - name: OTEL_RESOURCE_ATTRIBUTES
+ name: javaapp
+ volumeMounts:
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - mountPath: /otel-auto-instrumentation-java
+ name: opentelemetry-auto-instrumentation-java
+ - env:
+ - name: NODE_PATH
+ value: /usr/local/lib/node_modules
+ - name: OTEL_SERVICE_NAME
+ value: nodejsapp
+ - name: NODE_OPTIONS
+ value: ' --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js'
+ - name: OTEL_TRACES_SAMPLER
+ value: parentbased_traceidratio
+ - name: OTEL_TRACES_SAMPLER_ARG
+ value: "0.85"
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://localhost:4317
+ - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: spec.nodeName
+ - name: OTEL_PROPAGATORS
+ value: jaeger,b3
+ - name: OTEL_RESOURCE_ATTRIBUTES
+ name: nodejsapp
+ volumeMounts:
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - mountPath: /otel-auto-instrumentation-nodejs
+ name: opentelemetry-auto-instrumentation-nodejs
+ - command:
+ - flask
+ - run
+ - -p
+ - "8087"
+ env:
+ - name: OTEL_SERVICE_NAME
+ value: pythonapp
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://localhost:4318
+ - name: PYTHONPATH
+ value: /otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation-python
+ - name: OTEL_TRACES_EXPORTER
+ value: otlp
+ - name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
+ value: http/protobuf
+ - name: OTEL_METRICS_EXPORTER
+ value: otlp
+ - name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL
+ value: http/protobuf
+ - name: OTEL_TRACES_SAMPLER
+ value: parentbased_traceidratio
+ - name: OTEL_TRACES_SAMPLER_ARG
+ value: "0.85"
+ - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: spec.nodeName
+ - name: OTEL_PROPAGATORS
+ value: jaeger,b3
+ - name: OTEL_RESOURCE_ATTRIBUTES
+ name: pythonapp
+ volumeMounts:
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - mountPath: /otel-auto-instrumentation-python
+ name: opentelemetry-auto-instrumentation-python
+ - env:
+ - name: TEST
+ value: test
+ name: shouldnt-be-instrumented
+ volumeMounts:
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - args:
+ - --config=env:OTEL_CONFIG
+ name: otc-container
+ initContainers:
+ - name: opentelemetry-auto-instrumentation-java
+ volumeMounts:
+ - mountPath: /otel-auto-instrumentation-java
+ name: opentelemetry-auto-instrumentation-java
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - name: opentelemetry-auto-instrumentation-nodejs
+ volumeMounts:
+ - mountPath: /otel-auto-instrumentation-nodejs
+ name: opentelemetry-auto-instrumentation-nodejs
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - name: opentelemetry-auto-instrumentation-python
+ volumeMounts:
+ - mountPath: /otel-auto-instrumentation-python
+ name: opentelemetry-auto-instrumentation-python
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - name: opentelemetry-auto-instrumentation-dotnet
+ volumeMounts:
+ - mountPath: /otel-auto-instrumentation-dotnet
+ name: opentelemetry-auto-instrumentation-dotnet
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
status:
- phase: Running
+ containerStatuses:
+ - name: dotnetapp
+ ready: true
+ started: true
+ - name: javaapp
+ ready: true
+ started: true
+ - name: nodejsapp
+ ready: true
+ started: true
+ - name: otc-container
+ ready: true
+ started: true
+ - name: pythonapp
+ ready: true
+ started: true
+ - name: shouldnt-be-instrumented
+ ready: true
+ started: true
+ initContainerStatuses:
+ - name: opentelemetry-auto-instrumentation-java
+ ready: true
+ - name: opentelemetry-auto-instrumentation-nodejs
+ ready: true
+ - name: opentelemetry-auto-instrumentation-python
+ ready: true
+ - name: opentelemetry-auto-instrumentation-dotnet
+ ready: true
+ started: false
+ phase: Running
\ No newline at end of file
diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml
index 652eb0a348..32f0b4658c 100644
--- a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml
+++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/01-install-app.yaml
@@ -23,17 +23,48 @@ spec:
instrumentation.opentelemetry.io/python-container-names: "pythonapp"
sidecar.opentelemetry.io/inject: "true"
spec:
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 3000
+ fsGroup: 3000
containers:
- name: dotnetapp
image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-dotnet:main
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
+ env:
+ - name: ASPNETCORE_URLS
+ value: "http://+:8083"
- name: javaapp
image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-java:main
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
- name: nodejsapp
image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
+ env:
+ - name: NODE_PATH
+ value: /usr/local/lib/node_modules
- name: pythonapp
image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main
+ command: ["flask", "run", "-p", "8087"]
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
- name: shouldnt-be-instrumented
image: rabbitmq:3
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
env:
- name: TEST
value: test
diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/chainsaw-test.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/chainsaw-test.yaml
index e5303719f6..ec40d8f6e1 100755
--- a/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/chainsaw-test.yaml
+++ b/tests/e2e-multi-instrumentation/instrumentation-multi-multicontainer/chainsaw-test.yaml
@@ -6,6 +6,25 @@ metadata:
name: instrumentation-multi-multicontainer
spec:
steps:
+ - name: step-00
+ try:
+ # In OpenShift, when a namespace is created, all necessary SCC annotations are automatically added. However, if a namespace is created using a resource file with only selected SCCs, the other auto-added SCCs are not included. Therefore, the UID-range and supplemental groups SCC annotations must be set after the namespace is created.
+ - command:
+ entrypoint: kubectl
+ args:
+ - annotate
+ - namespace
+ - ${NAMESPACE}
+ - openshift.io/sa.scc.uid-range=1000/1000
+ - --overwrite
+ - command:
+ entrypoint: kubectl
+ args:
+ - annotate
+ - namespace
+ - ${NAMESPACE}
+ - openshift.io/sa.scc.supplemental-groups=3000/3000
+ - --overwrite
- name: step-00
try:
- apply:
@@ -18,3 +37,6 @@ spec:
file: 01-install-app.yaml
- assert:
file: 01-assert.yaml
+ catch:
+ - podLogs:
+ selector: app=pod-with-multi-instrumentation
diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-assert.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-assert.yaml
index f1af37c665..fbadd4f5af 100644
--- a/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-assert.yaml
+++ b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-assert.yaml
@@ -9,14 +9,34 @@ metadata:
app: pod-multi-instr-no-containers
spec:
containers:
- - name: nodejsapp
- env:
- - name: TEST
- value: test
- - name: pythonapp
- env:
- - name: TEST
- value: test
- - name: otc-container
+ - env:
+ - name: TEST
+ value: test
+ - name: NODE_PATH
+ value: /usr/local/lib/node_modules
+ name: nodejsapp
+ volumeMounts:
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - env:
+ - name: TEST
+ value: test
+ name: pythonapp
+ volumeMounts:
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - args:
+ - --config=env:OTEL_CONFIG
+ name: otc-container
status:
+ containerStatuses:
+ - name: nodejsapp
+ ready: true
+ started: true
+ - name: otc-container
+ ready: true
+ started: true
+ - name: pythonapp
+ ready: true
+ started: true
phase: Running
diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-install-app.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-install-app.yaml
index 9921c8791c..5fcca25549 100644
--- a/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-install-app.yaml
+++ b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/01-install-app.yaml
@@ -16,14 +16,28 @@ spec:
instrumentation.opentelemetry.io/inject-python: "true"
sidecar.opentelemetry.io/inject: "true"
spec:
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 3000
+ fsGroup: 3000
containers:
- name: nodejsapp
image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
env:
- name: TEST
value: test
+ - name: NODE_PATH
+ value: /usr/local/lib/node_modules
- name: pythonapp
image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
env:
- name: TEST
- value: test
+ value: test
\ No newline at end of file
diff --git a/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/chainsaw-test.yaml b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/chainsaw-test.yaml
index c79b2a8815..326cdea01f 100755
--- a/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/chainsaw-test.yaml
+++ b/tests/e2e-multi-instrumentation/instrumentation-multi-no-containers/chainsaw-test.yaml
@@ -6,6 +6,25 @@ metadata:
name: instrumentation-multi-no-containers
spec:
steps:
+ - name: step-00
+ try:
+ # In OpenShift, when a namespace is created, all necessary SCC annotations are automatically added. However, if a namespace is created using a resource file with only selected SCCs, the other auto-added SCCs are not included. Therefore, the UID-range and supplemental groups SCC annotations must be set after the namespace is created.
+ - command:
+ entrypoint: kubectl
+ args:
+ - annotate
+ - namespace
+ - ${NAMESPACE}
+ - openshift.io/sa.scc.uid-range=1000/1000
+ - --overwrite
+ - command:
+ entrypoint: kubectl
+ args:
+ - annotate
+ - namespace
+ - ${NAMESPACE}
+ - openshift.io/sa.scc.supplemental-groups=3000/3000
+ - --overwrite
- name: step-00
try:
- apply:
@@ -18,3 +37,6 @@ spec:
file: 01-install-app.yaml
- assert:
file: 01-assert.yaml
+ catch:
+ - podLogs:
+ selector: app=pod-multi-instr-no-containers
diff --git a/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-assert.yaml b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-assert.yaml
index bec9919253..07b8002d38 100644
--- a/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-assert.yaml
+++ b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-assert.yaml
@@ -8,31 +8,62 @@ metadata:
app: pod-single-instr-first-container
spec:
containers:
- - name: nodejsapp
- env:
- - name: OTEL_SERVICE_NAME
- value: nodejsapp
- - name: NODE_OPTIONS
- value: ' --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js'
- - name: OTEL_TRACES_SAMPLER
- value: parentbased_traceidratio
- - name: OTEL_TRACES_SAMPLER_ARG
- value: "0.85"
- - name: OTEL_EXPORTER_OTLP_ENDPOINT
- value: http://localhost:4317
- - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
- - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
- - name: OTEL_PROPAGATORS
- value: jaeger,b3
- - name: OTEL_RESOURCE_ATTRIBUTES
- volumeMounts:
- - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
- - mountPath: /otel-auto-instrumentation-nodejs
- name: opentelemetry-auto-instrumentation-nodejs
- - name: pythonapp
- env:
- - name: TEST
- value: test
- - name: otc-container
+ - env:
+ - name: NODE_PATH
+ value: /usr/local/lib/node_modules
+ - name: OTEL_SERVICE_NAME
+ value: nodejsapp
+ - name: NODE_OPTIONS
+ value: ' --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js'
+ - name: OTEL_TRACES_SAMPLER
+ value: parentbased_traceidratio
+ - name: OTEL_TRACES_SAMPLER_ARG
+ value: "0.85"
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
+ value: http://localhost:4317
+ - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: spec.nodeName
+ - name: OTEL_PROPAGATORS
+ value: jaeger,b3
+ - name: OTEL_RESOURCE_ATTRIBUTES
+ name: nodejsapp
+ volumeMounts:
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - mountPath: /otel-auto-instrumentation-nodejs
+ name: opentelemetry-auto-instrumentation-nodejs
+ - env:
+ - name: TEST
+ value: test
+ name: pythonapp
+ volumeMounts:
+ - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+ readOnly: true
+ - args:
+ - --config=env:OTEL_CONFIG
+ name: otc-container
+ initContainers:
+ - name: opentelemetry-auto-instrumentation-nodejs
status:
- phase: Running
+ containerStatuses:
+ - name: nodejsapp
+ ready: true
+ started: true
+ - name: otc-container
+ ready: true
+ started: true
+ - name: pythonapp
+ ready: true
+ started: true
+ initContainerStatuses:
+ - name: opentelemetry-auto-instrumentation-nodejs
+ ready: true
+ phase: Running
\ No newline at end of file
diff --git a/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-install-app.yaml b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-install-app.yaml
index e33830e2e7..353bff51f1 100644
--- a/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-install-app.yaml
+++ b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/01-install-app.yaml
@@ -15,11 +15,26 @@ spec:
instrumentation.opentelemetry.io/inject-nodejs: "true"
sidecar.opentelemetry.io/inject: "true"
spec:
+ securityContext:
+ runAsUser: 1000
+ runAsGroup: 3000
+ fsGroup: 3000
containers:
- name: nodejsapp
image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
+ env:
+ - name: NODE_PATH
+ value: /usr/local/lib/node_modules
- name: pythonapp
image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-python:main
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop: ["ALL"]
env:
- name: TEST
value: test
diff --git a/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/chainsaw-test.yaml b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/chainsaw-test.yaml
index e9d5115609..56a75baca7 100755
--- a/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/chainsaw-test.yaml
+++ b/tests/e2e-multi-instrumentation/instrumentation-single-instr-first-container/chainsaw-test.yaml
@@ -6,6 +6,25 @@ metadata:
name: instrumentation-single-instr-first-container
spec:
steps:
+ - name: step-00
+ try:
+ # In OpenShift, when a namespace is created, all necessary SCC annotations are automatically added. However, if a namespace is created using a resource file with only selected SCCs, the other auto-added SCCs are not included. Therefore, the UID-range and supplemental groups SCC annotations must be set after the namespace is created.
+ - command:
+ entrypoint: kubectl
+ args:
+ - annotate
+ - namespace
+ - ${NAMESPACE}
+ - openshift.io/sa.scc.uid-range=1000/1000
+ - --overwrite
+ - command:
+ entrypoint: kubectl
+ args:
+ - annotate
+ - namespace
+ - ${NAMESPACE}
+ - openshift.io/sa.scc.supplemental-groups=3000/3000
+ - --overwrite
- name: step-00
try:
- apply:
@@ -18,3 +37,6 @@ spec:
file: 01-install-app.yaml
- assert:
file: 01-assert.yaml
+ catch:
+ - podLogs:
+ selector: app=pod-single-instr-first-container