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

Add RBAC Support for loadbalancingExporter #3104

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions controllers/reconcile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
autoRBAC "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/rbac"
"github.com/open-telemetry/opentelemetry-operator/internal/config"
"github.com/open-telemetry/opentelemetry-operator/internal/manifests"
_ "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/processor"
ta "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters"
"github.com/open-telemetry/opentelemetry-operator/internal/naming"
)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ require (
github.com/prometheus/client_golang v1.19.1
github.com/prometheus/common v0.55.0
github.com/prometheus/prometheus v0.53.0
github.com/samber/lo v1.44.0
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,8 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samber/lo v1.44.0 h1:5il56KxRE+GHsm1IR+sZ/6J42NODigFiqCWpSc2dybA=
github.com/samber/lo v1.44.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27 h1:yGAraK1uUjlhSXgNMIy8o/J4LFNcy7yeipBqt9N9mVg=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
Expand Down
17 changes: 17 additions & 0 deletions internal/manifests/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type ManifestFactory[T client.Object, Params any] func(params Params) (T, error)
type SimpleManifestFactory[T client.Object, Params any] func(params Params) T
type K8sManifestFactory[Params any] ManifestFactory[client.Object, Params]

type MultiManifestFactory[T client.Object, Params any] func(params Params) ([]T, error)
type K8sMultiManifestFactory[Params any] func(params Params) ([]client.Object, error)

func FactoryWithoutError[T client.Object, Params any](f SimpleManifestFactory[T, Params]) K8sManifestFactory[Params] {
return func(params Params) (client.Object, error) {
return f(params), nil
Expand All @@ -38,6 +41,20 @@ func Factory[T client.Object, Params any](f ManifestFactory[T, Params]) K8sManif
}
}

func MultiFactory[T client.Object, Params any](f MultiManifestFactory[T, Params]) K8sMultiManifestFactory[Params] {
return func(params Params) ([]client.Object, error) {
objs, err := f(params)
if err != nil {
return nil, err
}
var clientObjs []client.Object
for _, obj := range objs {
clientObjs = append(clientObjs, obj)
}
return clientObjs, nil
}
}

// ObjectIsNotNil ensures that we only create an object IFF it isn't nil,
// and it's concrete type isn't nil either. This works around the Go type system
// by using reflection to verify its concrete type isn't nil.
Expand Down
26 changes: 7 additions & 19 deletions internal/manifests/collector/adapters/config_to_ports.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,8 @@ import (
receiverParser "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/receiver"
)

type ComponentType int

const (
ComponentTypeReceiver ComponentType = iota
ComponentTypeExporter
ComponentTypeProcessor
)

func (c ComponentType) String() string {
return [...]string{"receiver", "exporter", "processor"}[c]
}

// ConfigToComponentPorts converts the incoming configuration object into a set of service ports required by the exporters.
func ConfigToComponentPorts(logger logr.Logger, cType ComponentType, config map[interface{}]interface{}) ([]corev1.ServicePort, error) {
func ConfigToComponentPorts(logger logr.Logger, cType parser.ComponentType, config map[interface{}]interface{}) ([]corev1.ServicePort, error) {
// now, we gather which ports we might need to open
// for that, we get all the exporters and check their `endpoint` properties,
// extracting the port from it. The port name has to be a "DNS_LABEL", so, we try to make it follow the pattern:
Expand Down Expand Up @@ -87,12 +75,12 @@ func ConfigToComponentPorts(logger logr.Logger, cType ComponentType, config map[
var cmptParser parser.ComponentPortParser
var err error
switch cType {
case ComponentTypeExporter:
case parser.ComponentTypeExporter:
cmptParser, err = exporterParser.For(logger, cmptName, exporter)
case ComponentTypeReceiver:
case parser.ComponentTypeReceiver:
cmptParser, err = receiverParser.For(logger, cmptName, exporter)
case ComponentTypeProcessor:
logger.V(4).Info("processors don't provide a way to enable associated ports", "name", key)
case parser.ComponentTypeProcessor, parser.ComponentTypeConnector:
logger.V(4).Info("processors and connectors don't provide a way to enable associated ports", "name", key)
}

if err != nil {
Expand All @@ -119,13 +107,13 @@ func ConfigToComponentPorts(logger logr.Logger, cType ComponentType, config map[
}

func ConfigToPorts(logger logr.Logger, config map[interface{}]interface{}) ([]corev1.ServicePort, error) {
ports, err := ConfigToComponentPorts(logger, ComponentTypeReceiver, config)
ports, err := ConfigToComponentPorts(logger, parser.ComponentTypeReceiver, config)
if err != nil {
logger.Error(err, "there was a problem while getting the ports from the receivers")
return nil, err
}

exporterPorts, err := ConfigToComponentPorts(logger, ComponentTypeExporter, config)
exporterPorts, err := ConfigToComponentPorts(logger, parser.ComponentTypeExporter, config)
if err != nil {
logger.Error(err, "there was a problem while getting the ports from the exporters")
return nil, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func TestExtractPortsFromConfig(t *testing.T) {
require.NotEmpty(t, config)

// test
ports, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeReceiver, config)
ports, err := adapters.ConfigToComponentPorts(logger, parser.ComponentTypeReceiver, config)
assert.NoError(t, err)
assert.Len(t, ports, 10)

Expand Down Expand Up @@ -133,7 +133,7 @@ func TestNoPortsParsed(t *testing.T) {
require.NoError(t, err)

// test
ports, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeReceiver, config)
ports, err := adapters.ConfigToComponentPorts(logger, parser.ComponentTypeReceiver, config)

// verify
assert.Nil(t, ports)
Expand Down Expand Up @@ -162,7 +162,7 @@ func TestInvalidReceivers(t *testing.T) {
require.NoError(t, err)

// test
ports, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeReceiver, config)
ports, err := adapters.ConfigToComponentPorts(logger, parser.ComponentTypeReceiver, config)

// verify
assert.NoError(t, err)
Expand Down Expand Up @@ -198,7 +198,7 @@ func TestParserFailed(t *testing.T) {
}

// test
ports, err := adapters.ConfigToComponentPorts(logger, adapters.ComponentTypeReceiver, config)
ports, err := adapters.ConfigToComponentPorts(logger, parser.ComponentTypeReceiver, config)

// verify
assert.Len(t, ports, 0)
Expand Down
51 changes: 32 additions & 19 deletions internal/manifests/collector/adapters/config_to_rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,48 +15,61 @@
package adapters

import (
"fmt"

"github.com/go-logr/logr"
rbacv1 "k8s.io/api/rbac/v1"

"github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/processor"
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/authz"
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser"
)

// ConfigToRBAC parses the OpenTelemetry Collector configuration and checks what RBAC resources are needed to be created.
func ConfigToRBAC(logger logr.Logger, config map[interface{}]interface{}) []rbacv1.PolicyRule {
var policyRules []rbacv1.PolicyRule
processorsRaw, ok := config["processors"]
func ConfigToRBAC(logger logr.Logger, config map[any]any) (policyRules []authz.DynamicRolePolicy) {
componentTypes := []parser.ComponentType{
parser.ComponentTypeProcessor,
parser.ComponentTypeExporter,
}

for _, cType := range componentTypes {
policyRules = append(policyRules, configToRBACForComponentType(logger, config, cType)...)
}
return
}

func configToRBACForComponentType(logger logr.Logger, config map[any]any, cType parser.ComponentType) []authz.DynamicRolePolicy {
var policyRules []authz.DynamicRolePolicy
componentsRaw, ok := config[cType.Plural()]
if !ok {
logger.V(2).Info("no processors available as part of the configuration")
logger.V(2).Info(fmt.Sprintf("no %s available as part of the configuration", cType.String()))
return policyRules
}

processors, ok := processorsRaw.(map[interface{}]interface{})
components, ok := componentsRaw.(map[any]any)
if !ok {
logger.V(2).Info("processors doesn't contain valid components")
logger.V(2).Info(fmt.Sprintf("%s doesn't contain valid components", cType.String()))
return policyRules
}

enabledProcessors := getEnabledComponents(config, ComponentTypeProcessor)
enabledComponents := getEnabledComponents(config, cType)

for key, val := range processors {
if !enabledProcessors[key] {
for key, val := range components {
if !enabledComponents[key] {
continue
}

processorCfg, ok := val.(map[interface{}]interface{})
componentCfg, ok := val.(map[any]any)
if !ok {
logger.V(2).Info("processor doesn't seem to be a map of properties", "processor", key)
processorCfg = map[interface{}]interface{}{}
logger.V(2).Info(fmt.Sprintf("%s doesn't seem to be a map of properties", cType.String()), "component", key)
componentCfg = map[any]any{}
}

processorName := key.(string)
processorParser, err := processor.For(logger, processorName, processorCfg)
componentName := key.(string)
componentParser, err := parser.AuthzFor(logger, cType, componentName, componentCfg)
if err != nil {
logger.V(2).Info("no parser found for", "processor", processorName)
logger.V(2).Info(fmt.Sprintf("no parser found for %s", cType.String()), "component", componentName)
continue
}

policyRules = append(policyRules, processorParser.GetRBACRules()...)
policyRules = append(policyRules, componentParser.GetRBACRules()...)
}

return policyRules
Expand Down
38 changes: 23 additions & 15 deletions internal/manifests/collector/adapters/config_to_rbac_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,25 @@ import (
"github.com/stretchr/testify/require"
rbacv1 "k8s.io/api/rbac/v1"
logf "sigs.k8s.io/controller-runtime/pkg/log"

"github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/authz"
_ "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/exporter"
_ "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser/processor"
)

func TestConfigRBAC(t *testing.T) {
tests := []struct {
desc string
config string
expectedRules []rbacv1.PolicyRule
expectedRules []authz.DynamicRolePolicy
}{
{
desc: "No processors",
config: `processors:
service:
traces:
processors:`,
expectedRules: ([]rbacv1.PolicyRule)(nil),
expectedRules: ([]authz.DynamicRolePolicy)(nil),
},
{
desc: "processors no rbac",
Expand All @@ -45,7 +49,7 @@ service:
pipelines:
traces:
processors: [batch]`,
expectedRules: ([]rbacv1.PolicyRule)(nil),
expectedRules: ([]authz.DynamicRolePolicy)(nil),
},
{
desc: "resourcedetection-processor k8s",
Expand All @@ -56,12 +60,14 @@ service:
pipelines:
traces:
processors: [resourcedetection]`,
expectedRules: []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"nodes"},
Verbs: []string{"get", "list"},
},
expectedRules: []authz.DynamicRolePolicy{{
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"nodes"},
Verbs: []string{"get", "list"},
},
}},
},
},
{
Expand All @@ -73,12 +79,14 @@ service:
pipelines:
traces:
processors: [resourcedetection]`,
expectedRules: []rbacv1.PolicyRule{
{
APIGroups: []string{"config.openshift.io"},
Resources: []string{"infrastructures", "infrastructures/status"},
Verbs: []string{"get", "watch", "list"},
},
expectedRules: []authz.DynamicRolePolicy{{
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{"config.openshift.io"},
Resources: []string{"infrastructures", "infrastructures/status"},
Verbs: []string{"get", "watch", "list"},
},
}},
},
},
}
Expand Down
16 changes: 9 additions & 7 deletions internal/manifests/collector/adapters/config_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,19 @@

package adapters

import "fmt"
import (
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser"
)

// Following Otel Doc: Configuring a receiver does not enable it. The receivers are enabled via pipelines within the service section.
// getEnabledComponents returns all enabled components as a true flag set. If it can't find any receiver, it will return a nil interface.
func getEnabledComponents(config map[interface{}]interface{}, componentType ComponentType) map[interface{}]bool {
componentTypePlural := fmt.Sprintf("%ss", componentType.String())
func getEnabledComponents(config map[any]any, componentType parser.ComponentType) map[interface{}]bool {
componentTypePlural := componentType.Plural()
cfgComponents, ok := config[componentTypePlural]
if !ok {
return nil
}
components, ok := cfgComponents.(map[interface{}]interface{})
components, ok := cfgComponents.(map[any]any)
if !ok {
return nil
}
Expand All @@ -41,12 +43,12 @@ func getEnabledComponents(config map[interface{}]interface{}, componentType Comp
availableComponents[componentID] = false
}

cfgService, withService := config["service"].(map[interface{}]interface{})
cfgService, withService := config["service"].(map[any]any)
if !withService {
return nil
}

pipeline, withPipeline := cfgService["pipelines"].(map[interface{}]interface{})
pipeline, withPipeline := cfgService["pipelines"].(map[any]any)
if !withPipeline {
return nil
}
Expand All @@ -71,7 +73,7 @@ func getEnabledComponents(config map[interface{}]interface{}, componentType Comp
}
//Condition will get information if there are multiple configured pipelines.
if len(pipelineV) > 0 {
pipelineDesc, ok := pipelineCfg.(map[interface{}]interface{})
pipelineDesc, ok := pipelineCfg.(map[any]any)
if !ok {
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
"testing"

"github.com/stretchr/testify/require"

"github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/parser"
)

func TestConfigValidate(t *testing.T) {
Expand Down Expand Up @@ -57,7 +59,7 @@ service:
require.NotEmpty(t, config)

// test
check := getEnabledComponents(config, ComponentTypeReceiver)
check := getEnabledComponents(config, parser.ComponentTypeReceiver)
require.NotEmpty(t, check)
}

Expand Down Expand Up @@ -98,6 +100,6 @@ service:
require.NotEmpty(t, config)

// test
check := getEnabledComponents(config, ComponentTypeReceiver)
check := getEnabledComponents(config, parser.ComponentTypeReceiver)
require.Empty(t, check)
}
Loading
Loading