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

Expose the Prometheus exporter port when specified in the config #1953

Merged
merged 12 commits into from
Aug 1, 2023
16 changes: 16 additions & 0 deletions .chloggen/1689-expose-prometheusexporter-pod-when-enabled.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: Expose the Prometheus exporter port in the OpenTelemetry Collector container when it is used in the configuration.

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

# (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:
59 changes: 59 additions & 0 deletions pkg/collector/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ package collector

import (
"fmt"
"net"
"sort"
"strconv"
"strings"

"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -183,9 +186,65 @@ func getConfigContainerPorts(logger logr.Logger, cfg string) map[string]corev1.C
ContainerPort: metricsPort,
Protocol: corev1.ProtocolTCP,
}

promExporterPorts, errs := getPrometheusExporterPorts(c)
for _, err := range errs {
logger.V(2).Info("There was a problem getting the prometheus exporter port: %s", err)
}
for _, promPort := range promExporterPorts {
ports[promPort.Name] = promPort
}

return ports
}

func getPrometheusExporterPort(exporterConfig map[interface{}]interface{}) (int32, error) {
var promPort int32 = 0
if endpoint, ok := exporterConfig["endpoint"]; ok {
_, port, err := net.SplitHostPort(endpoint.(string))
if err != nil {
return 0, err
}
i64, err := strconv.ParseInt(port, 10, 32)
if err != nil {
return 0, err
}
promPort = int32(i64)
}
return promPort, nil
}

func getPrometheusExporterPorts(c map[interface{}]interface{}) ([]corev1.ContainerPort, []error) {
errors := make([]error, 0)
ports := make([]corev1.ContainerPort, 0)
if exporters, ok := c["exporters"]; ok && exporters != nil {
for e, exporterConfig := range exporters.(map[interface{}]interface{}) {
exporterName := e.(string)
if strings.Contains(exporterName, "prometheus") {
containerPort, err := getPrometheusExporterPort(exporterConfig.(map[interface{}]interface{}))
if err != nil {
errors = append(errors,
fmt.Errorf(
"there was a problem getting the port. Exporter %s",
exporterName,
),
)
}
ports = append(ports,
corev1.ContainerPort{
Name: naming.PortName(exporterName),
ContainerPort: containerPort,
Protocol: corev1.ProtocolTCP,
},
)
}
}
} else {
errors = append(errors, fmt.Errorf("no exporters specified in the configuration"))
}
return ports, errors
}

func portMapToList(portMap map[string]corev1.ContainerPort) []corev1.ContainerPort {
ports := make([]corev1.ContainerPort, 0, len(portMap))
for _, p := range portMap {
Expand Down
37 changes: 37 additions & 0 deletions pkg/collector/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,43 @@ service:
},
},
},
{
description: "prometheus exporter",
specConfig: `exporters:
prometheus:
endpoint: "0.0.0.0:9090"`,
specPorts: []corev1.ServicePort{},
expectedPorts: []corev1.ContainerPort{
metricContainerPort,
{
Name: "prometheus",
ContainerPort: 9090,
Protocol: corev1.ProtocolTCP,
},
},
},
{
description: "multiple prometheus exporters",
specConfig: `exporters:
prometheus/prod:
endpoint: "0.0.0.0:9090"
prometheus/dev:
endpoint: "0.0.0.0:9091"`,
specPorts: []corev1.ServicePort{},
expectedPorts: []corev1.ContainerPort{
metricContainerPort,
{
Name: "prometheus-prod",
ContainerPort: 9090,
Protocol: corev1.ProtocolTCP,
},
{
Name: "prometheus-dev",
ContainerPort: 9091,
Protocol: corev1.ProtocolTCP,
},
},
},
}

for _, testCase := range tests {
Expand Down
40 changes: 40 additions & 0 deletions pkg/naming/ports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package naming

import (
"strings"
"unicode/utf8"
)

// PortName returns a dns-safe string for the given name.
// Any char that is not [a-z0-9] is replaced by "-" or "a".
iblancasa marked this conversation as resolved.
Show resolved Hide resolved
// Replacement character "a" is used only at the beginning or at the end of the name.
// The function does not change length of the string.
func PortName(name string) string {
var d []rune

for i, x := range strings.ToLower(name) {
if regex.Match([]byte(string(x))) {
d = append(d, x)
} else if i == 0 || i == utf8.RuneCountInString(name)-1 {
d = append(d, 'a')
} else {
d = append(d, '-')
}
}

return string(d)
}
43 changes: 43 additions & 0 deletions pkg/naming/ports_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package naming

import (
"regexp"
"testing"

"github.com/stretchr/testify/assert"
)

func TestPortName(t *testing.T) {
var tests = []struct {
in string
out string
}{
{"simplest", "simplest"},
{"prometheus/dev", "prometheus-dev"},
{"prometheus.dev", "prometheus-dev"},
{"prometheus-", "prometheusa"},
{"-prometheus", "aprometheus"},
}
rule, err := regexp.Compile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`)
assert.NoError(t, err)

for _, tt := range tests {
assert.Equal(t, tt.out, PortName(tt.in))
matched := rule.Match([]byte(tt.out))
assert.True(t, matched, "%v is not a valid name", tt.out)
}
}
Loading