Skip to content

Commit 7437bb8

Browse files
Merge branch 'main' into chore/improving-set-targets
2 parents bc95872 + e979bb0 commit 7437bb8

38 files changed

+494
-246
lines changed

.chloggen/revert-3379-otel-configmap.yaml .chloggen/fix-metrics-service-address-env-var.yaml

+5-6
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@
22
change_type: bug_fix
33

44
# 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: auto-instrumentation
5+
component: operator
66

77
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8-
note: Reverts PR 3379 which inadvertently broke users setting JAVA_TOOL_OPTIONS
8+
note: Fix the admission webhook to when metrics service address host uses env var expansion
99

1010
# One or more tracking issues related to the change
11-
issues: [3463]
11+
issues: [3513]
1212

1313
# (Optional) One or more lines of additional information to render under the primary note.
1414
# These lines will be padded with 2 spaces and then inserted directly into the document.
1515
# Use pipe (|) for multiline entries.
1616
subtext: |
17-
Reverts a previous PR which was causing JAVA_TOOL_OPTIONS to not be overriden when
18-
set by users. This was resulting in application crashloopbackoffs for users relying
19-
on java autoinstrumentation.
17+
This should allow the metrics service address to have the host portion expanded from an environment variable,
18+
like `$(env:POD_IP)` instead of using `0.0.0.0`, which is the [recommended by the Collector](https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/security-best-practices.md#safeguards-against-denial-of-service-attacks).

.chloggen/fix-prometheus-rule-file.yaml

-16
This file was deleted.

.chloggen/scrape_config_probe.yaml

-16
This file was deleted.

.chloggen/service-extension.yaml

-16
This file was deleted.

.github/workflows/publish-autoinstrumentation-java.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jobs:
3737
ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java
3838
tags: |
3939
type=match,pattern=v(.*),group=1,value=v${{ env.VERSION }}
40+
type=semver,pattern={{major}},value=v${{ env.VERSION }}
4041
4142
- name: Set up QEMU
4243
uses: docker/setup-qemu-action@v3

CHANGELOG.md

+137-53
Large diffs are not rendered by default.

Makefile

+9-4
Original file line numberDiff line numberDiff line change
@@ -491,11 +491,16 @@ CHLOGGEN ?= $(LOCALBIN)/chloggen
491491
GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint
492492
CHAINSAW ?= $(LOCALBIN)/chainsaw
493493

494-
KUSTOMIZE_VERSION ?= v5.0.3
495-
CONTROLLER_TOOLS_VERSION ?= v0.16.1
494+
# renovate: datasource=go depName=sigs.k8s.io/kustomize/kustomize/v5
495+
KUSTOMIZE_VERSION ?= v5.5.0
496+
# renovate: datasource=go depName=sigs.k8s.io/controller-tools/cmd/controller-gen
497+
CONTROLLER_TOOLS_VERSION ?= v0.16.5
498+
# renovate: datasource=go depName=github.com/golangci/golangci-lint/cmd/golangci-lint
496499
GOLANGCI_LINT_VERSION ?= v1.57.2
497-
KIND_VERSION ?= v0.20.0
498-
CHAINSAW_VERSION ?= v0.2.8
500+
# renovate: datasource=go depName=sigs.k8s.io/kind
501+
KIND_VERSION ?= v0.25.0
502+
# renovate: datasource=go depName=github.com/kyverno/chainsaw
503+
CHAINSAW_VERSION ?= v0.2.12
499504

500505
.PHONY: install-tools
501506
install-tools: kustomize golangci-lint kind controller-gen envtest crdoc kind operator-sdk chainsaw

README.md

+48-8
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,16 @@ This will create an OpenTelemetry Collector instance named `simplest`, exposing
7272

7373
The `config` node holds the `YAML` that should be passed down as-is to the underlying OpenTelemetry Collector instances. Refer to the [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector) documentation for a reference of the possible entries.
7474

75-
> 🚨 **NOTE:** At this point, the Operator does _not_ validate the contents of the configuration file: if the configuration is invalid, the instance will still be created but the underlying OpenTelemetry Collector might crash.
75+
> 🚨 **NOTE:** At this point, the Operator does _not_ validate the whole contents of the configuration file: if the configuration is invalid, the instance might still be created but the underlying OpenTelemetry Collector might crash.
7676
7777
> 🚨 **Note:** For private GKE clusters, you will need to either add a firewall rule that allows master nodes access to port `9443/tcp` on worker nodes, or change the existing rule that allows access to port `80/tcp`, `443/tcp` and `10254/tcp` to also allow access to port `9443/tcp`. More information can be found in the [Official GCP Documentation](https://cloud.google.com/load-balancing/docs/tcp/setting-up-tcp#config-hc-firewall). See the [GKE documentation](https://cloud.google.com/kubernetes-engine/docs/how-to/private-clusters#add_firewall_rules) on adding rules and the [Kubernetes issue](https://github.com/kubernetes/kubernetes/issues/79739) for more detail.
7878
79-
The Operator does examine the configuration file to discover configured receivers and their ports. If it finds receivers with ports, it creates a pair of kubernetes services, one headless, exposing those ports within the cluster. The headless service contains a `service.beta.openshift.io/serving-cert-secret-name` annotation that will cause OpenShift to create a secret containing a certificate and key. This secret can be mounted as a volume and the certificate and key used in those receivers' TLS configurations.
79+
The Operator does examine the configuration file for a few purposes:
8080

81+
- To discover configured receivers and their ports. If it finds receivers with ports, it creates a pair of kubernetes services, one headless, exposing those ports within the cluster. If the port is using environment variable expansion or cannot be parsed, an error will be returned. The headless service contains a `service.beta.openshift.io/serving-cert-secret-name` annotation that will cause OpenShift to create a secret containing a certificate and key. This secret can be mounted as a volume and the certificate and key used in those receivers' TLS configurations.
82+
83+
- To check if Collector observability is enabled (controlled by `spec.observability.metrics.enableMetrics`). In this case, a Service and ServiceMonitor/PodMonitor are created for the Collector instance. As a consequence, if the metrics service address contains an invalid port or uses environment variable expansion for the port, an error will be returned. A workaround for the environment variable case is to set `enableMetrics` to `false` and manually create the previously mentioned objects with the correct port if you need them.
84+
8185
### Upgrades
8286

8387
As noted above, the OpenTelemetry Collector format is continuing to evolve. However, a best-effort attempt is made to upgrade all managed `OpenTelemetryCollector` resources.
@@ -534,9 +538,10 @@ apiVersion: opentelemetry.io/v1alpha1
534538
kind: Instrumentation
535539
metadata:
536540
name: my-instrumentation
537-
apache:
541+
spec:
542+
apacheHttpd:
538543
image: your-customized-auto-instrumentation-image:apache-httpd
539-
version: 2.2
544+
version: "2.2"
540545
configPath: /your-custom-config-path
541546
attrs:
542547
- name: ApacheModuleOtelMaxQueueSize
@@ -556,6 +561,7 @@ apiVersion: opentelemetry.io/v1alpha1
556561
kind: Instrumentation
557562
metadata:
558563
name: my-instrumentation
564+
spec:
559565
nginx:
560566
image: your-customized-auto-instrumentation-image:nginx # if custom instrumentation image is needed
561567
configFile: /my/custom-dir/custom-nginx.conf
@@ -725,14 +731,16 @@ EOF
725731

726732
### Configure resource attributes with annotations
727733

728-
This example shows a pod configuration with OpenTelemetry annotations using the `resource.opentelemetry.io/` prefix. These annotations can be used to add resource attributes to data produced by OpenTelemetry instrumentation.
734+
This example shows a pod configuration with OpenTelemetry annotations using the `resource.opentelemetry.io/` prefix.
735+
These annotations can be used to add resource attributes to data produced by OpenTelemetry instrumentation.
729736

730737
```yaml
731738
apiVersion: v1
732739
kind: Pod
733740
metadata:
734741
name: example-pod
735742
annotations:
743+
# this is just an example, you can create any resource attributes you need
736744
resource.opentelemetry.io/service.name: "my-service"
737745
resource.opentelemetry.io/service.version: "1.0.0"
738746
resource.opentelemetry.io/environment: "production"
@@ -750,7 +758,6 @@ The following labels are supported:
750758
- `app.kubernetes.io/name` becomes `service.name`
751759
- `app.kubernetes.io/version` becomes `service.version`
752760
- `app.kubernetes.io/part-of` becomes `service.namespace`
753-
- `app.kubernetes.io/instance` becomes `service.instance.id`
754761

755762
```yaml
756763
apiVersion: v1
@@ -761,7 +768,6 @@ metadata:
761768
app.kubernetes.io/name: "my-service"
762769
app.kubernetes.io/version: "1.0.0"
763770
app.kubernetes.io/part-of: "shop"
764-
app.kubernetes.io/instance: "my-service-123"
765771
spec:
766772
containers:
767773
- name: main-container
@@ -794,6 +800,40 @@ The priority for setting resource attributes is as follows (first found wins):
794800
This priority is applied for each resource attribute separately, so it is possible to set some attributes via
795801
annotations and others via labels.
796802

803+
### How resource attributes are calculated from the pod's metadata
804+
805+
The following resource attributes are calculated from the pod's metadata.
806+
807+
#### How `service.name` is calculated
808+
809+
Choose the first value found:
810+
811+
- `pod.annotation[resource.opentelemetry.io/service.name]`
812+
- `if (config[useLabelsForResourceAttributes]) pod.label[app.kubernetes.io/name]`
813+
- `k8s.depleyment.name`
814+
- `k8s.replicaset.name`
815+
- `k8s.statefulset.name`
816+
- `k8s.daemonset.name`
817+
- `k8s.cronjob.name`
818+
- `k8s.job.name`
819+
- `k8s.pod.name`
820+
- `k8s.container.name`
821+
822+
#### How `service.version` is calculated
823+
824+
Choose the first value found:
825+
826+
- `pod.annotation[resource.opentelemetry.io/service.version]`
827+
- `if (cfg[useLabelsForResourceAttributes]) pod.label[app.kubernetes.io/version]`
828+
- `if (contains(container docker image tag, '/') == false) container docker image tag`
829+
830+
#### How `service.instance.id` is calculated
831+
832+
Choose the first value found:
833+
834+
- `pod.annotation[resource.opentelemetry.io/service.instance.id]`
835+
- `concat([k8s.namespace.name, k8s.pod.name, k8s.container.name], '.')`
836+
797837
## Contributing and Developing
798838

799839
Please see [CONTRIBUTING.md](CONTRIBUTING.md).
@@ -802,7 +842,6 @@ In addition to the [core responsibilities](https://github.com/open-telemetry/com
802842

803843
Approvers ([@open-telemetry/operator-approvers](https://github.com/orgs/open-telemetry/teams/operator-approvers)):
804844

805-
- [Benedikt Bongartz](https://github.com/frzifus), Red Hat
806845
- [Tyler Helmuth](https://github.com/TylerHelmuth), Honeycomb
807846
- [Yuri Oliveira Sa](https://github.com/yuriolisa), Red Hat
808847
- [Israel Blancas](https://github.com/iblancasa), Red Hat
@@ -818,6 +857,7 @@ Emeritus Approvers:
818857

819858
Maintainers ([@open-telemetry/operator-maintainers](https://github.com/orgs/open-telemetry/teams/operator-maintainers)):
820859

860+
- [Benedikt Bongartz](https://github.com/frzifus), Red Hat
821861
- [Jacob Aronoff](https://github.com/jaronoff97), Lightstep
822862
- [Mikołaj Świątek](https://github.com/swiatekm), Elastic
823863
- [Pavol Loffay](https://github.com/pavolloffay), Red Hat

RELEASE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ The operator should be released within a week after the [OpenTelemetry collector
4444

4545
| Version | Release manager |
4646
|----------|-----------------|
47-
| v0.115.0 | @TylerHelmuth |
4847
| v0.116.0 | @jaronoff97 |
4948
| v0.117.0 | @iblancasa |
5049
| v0.118.0 | @frzifus |
5150
| v0.119.0 | @yuriolisa |
5251
| v0.120.0 | @pavolloffay |
5352
| v0.121.0 | @swiatekm |
53+
| v0.122.0 | @TylerHelmuth |

apis/v1alpha1/instrumentation_types.go

-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,6 @@ type Defaults struct {
152152
// - `app.kubernetes.io/name` becomes `service.name`
153153
// - `app.kubernetes.io/version` becomes `service.version`
154154
// - `app.kubernetes.io/part-of` becomes `service.namespace`
155-
// - `app.kubernetes.io/instance` becomes `service.instance.id`
156155
UseLabelsForResourceAttributes bool `json:"useLabelsForResourceAttributes,omitempty"`
157156
}
158157

apis/v1beta1/collector_webhook_test.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ func TestCollectorDefaultingWebhook(t *testing.T) {
555555
ctx := context.Background()
556556
err := cvw.Default(ctx, &test.otelcol)
557557
if test.expected.Spec.Config.Service.Telemetry == nil {
558-
assert.NoError(t, test.expected.Spec.Config.Service.ApplyDefaults(), "could not apply defaults")
558+
assert.NoError(t, test.expected.Spec.Config.Service.ApplyDefaults(logr.Discard()), "could not apply defaults")
559559
}
560560
assert.NoError(t, err)
561561
assert.Equal(t, test.expected, test.otelcol)
@@ -588,7 +588,17 @@ func TestOTELColValidatingWebhook(t *testing.T) {
588588
five := int32(5)
589589
maxInt := int32(math.MaxInt32)
590590

591-
cfg := v1beta1.Config{}
591+
cfg := v1beta1.Config{
592+
Service: v1beta1.Service{
593+
Telemetry: &v1beta1.AnyConfig{
594+
Object: map[string]interface{}{
595+
"metrics": map[string]interface{}{
596+
"address": "${env:POD_ID}:8888",
597+
},
598+
},
599+
},
600+
},
601+
}
592602
err := yaml.Unmarshal([]byte(cfgYaml), &cfg)
593603
require.NoError(t, err)
594604

apis/v1beta1/config.go

+46-23
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import (
1818
"bytes"
1919
"encoding/json"
2020
"fmt"
21-
"net"
2221
"reflect"
22+
"regexp"
2323
"sort"
2424
"strconv"
2525
"strings"
@@ -269,7 +269,7 @@ func (c *Config) getEnvironmentVariablesForComponentKinds(logger logr.Logger, co
269269

270270
// applyDefaultForComponentKinds applies defaults to the endpoints for the given ComponentKind(s).
271271
func (c *Config) applyDefaultForComponentKinds(logger logr.Logger, componentKinds ...ComponentKind) error {
272-
if err := c.Service.ApplyDefaults(); err != nil {
272+
if err := c.Service.ApplyDefaults(logger); err != nil {
273273
return err
274274
}
275275
enabledComponents := c.GetEnabledComponents()
@@ -427,37 +427,60 @@ type Service struct {
427427
Pipelines map[string]*Pipeline `json:"pipelines" yaml:"pipelines"`
428428
}
429429

430-
// MetricsEndpoint gets the port number and host address for the metrics endpoint from the collector config if it has been set.
431-
func (s *Service) MetricsEndpoint() (string, int32, error) {
432-
defaultAddr := "0.0.0.0"
433-
if s.GetTelemetry() == nil {
434-
// telemetry isn't set, use the default
435-
return defaultAddr, 8888, nil
436-
}
437-
host, port, netErr := net.SplitHostPort(s.GetTelemetry().Metrics.Address)
438-
if netErr != nil && strings.Contains(netErr.Error(), "missing port in address") {
439-
return defaultAddr, 8888, nil
440-
} else if netErr != nil {
441-
return "", 0, netErr
442-
}
443-
i64, err := strconv.ParseInt(port, 10, 32)
430+
const (
431+
defaultServicePort int32 = 8888
432+
defaultServiceHost = "0.0.0.0"
433+
)
434+
435+
// MetricsEndpoint attempts gets the host and port number from the host address without doing any validation regarding the
436+
// address itself.
437+
// It works even before env var expansion happens, when a simple `net.SplitHostPort` would fail because of the extra colon
438+
// from the env var, i.e. the address looks like "${env:POD_IP}:4317", "${env:POD_IP}", or "${POD_IP}".
439+
// In cases which the port itself is a variable, i.e. "${env:POD_IP}:${env:PORT}", this returns an error. This happens
440+
// because the port is used to generate Service objects and mappings.
441+
func (s *Service) MetricsEndpoint(logger logr.Logger) (string, int32, error) {
442+
telemetry := s.GetTelemetry()
443+
if telemetry == nil || telemetry.Metrics.Address == "" {
444+
return defaultServiceHost, defaultServicePort, nil
445+
}
446+
447+
// The regex below matches on strings that end with a colon followed by the environment variable expansion syntax.
448+
// So it should match on strings ending with: ":${env:POD_IP}" or ":${POD_IP}".
449+
const portEnvVarRegex = `:\${[env:]?.*}$`
450+
isPortEnvVar := regexp.MustCompile(portEnvVarRegex).MatchString(telemetry.Metrics.Address)
451+
if isPortEnvVar {
452+
errMsg := fmt.Sprintf("couldn't determine metrics port from configuration: %s",
453+
telemetry.Metrics.Address)
454+
logger.Info(errMsg)
455+
return "", 0, fmt.Errorf(errMsg)
456+
}
457+
458+
// The regex below matches on strings that end with a colon followed by 1 or more numbers (representing the port).
459+
const explicitPortRegex = `:(\d+$)`
460+
explicitPortMatches := regexp.MustCompile(explicitPortRegex).FindStringSubmatch(telemetry.Metrics.Address)
461+
if len(explicitPortMatches) <= 1 {
462+
return telemetry.Metrics.Address, defaultServicePort, nil
463+
}
464+
465+
port, err := strconv.ParseInt(explicitPortMatches[1], 10, 32)
444466
if err != nil {
467+
errMsg := fmt.Sprintf("couldn't determine metrics port from configuration: %s",
468+
telemetry.Metrics.Address)
469+
logger.Info(errMsg, "error", err)
445470
return "", 0, err
446471
}
447472

448-
if host == "" {
449-
host = defaultAddr
450-
}
451-
452-
return host, int32(i64), nil
473+
host, _, _ := strings.Cut(telemetry.Metrics.Address, explicitPortMatches[0])
474+
return host, int32(port), nil
453475
}
454476

455477
// ApplyDefaults inserts configuration defaults if it has not been set.
456-
func (s *Service) ApplyDefaults() error {
457-
telemetryAddr, telemetryPort, err := s.MetricsEndpoint()
478+
func (s *Service) ApplyDefaults(logger logr.Logger) error {
479+
telemetryAddr, telemetryPort, err := s.MetricsEndpoint(logger)
458480
if err != nil {
459481
return err
460482
}
483+
461484
tm := &AnyConfig{
462485
Object: map[string]interface{}{
463486
"metrics": map[string]interface{}{

0 commit comments

Comments
 (0)