From 89a094c5fef518bf130e09080c118b0233e9694f Mon Sep 17 00:00:00 2001 From: Periklis Tsirakidis Date: Tue, 20 Jun 2023 19:40:16 +0200 Subject: [PATCH] operator: Add rules labels filters for openshift application tenant (#9600) Co-authored-by: Mohamed-Amine Bouqsimi --- operator/CHANGELOG.md | 1 + operator/internal/manifests/build.go | 1 - operator/internal/manifests/gateway.go | 7 + .../internal/manifests/gateway_tenants.go | 11 + operator/internal/manifests/gateway_test.go | 1 + .../manifests/openshift/alertingrule.go | 27 +++ .../manifests/openshift/alertingrule_test.go | 195 ++++++++++++++++++ .../internal/manifests/openshift/configure.go | 24 +++ operator/internal/manifests/rules_config.go | 18 ++ 9 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 operator/internal/manifests/openshift/alertingrule.go create mode 100644 operator/internal/manifests/openshift/alertingrule_test.go diff --git a/operator/CHANGELOG.md b/operator/CHANGELOG.md index 56288f273e..6ffa62f9dd 100644 --- a/operator/CHANGELOG.md +++ b/operator/CHANGELOG.md @@ -1,5 +1,6 @@ ## Main +- [9600](https://github.com/grafana/loki/pull/9600) **periklis**: Add rules labels filters for openshift-logging application tenant - [9735](https://github.com/grafana/loki/pull/9735) **JoaoBraveCoding** Adjust 1x.extra-small resources according to findings - [9689](https://github.com/grafana/loki/pull/9689) **xperimental**: Fix availability of demo LokiStack size - [9630](https://github.com/grafana/loki/pull/9630) **jpinsonneau**: Expose per_stream_rate_limit & burst diff --git a/operator/internal/manifests/build.go b/operator/internal/manifests/build.go index 64be16bf15..880f6653ba 100644 --- a/operator/internal/manifests/build.go +++ b/operator/internal/manifests/build.go @@ -61,7 +61,6 @@ func BuildAll(opts Options) ([]client.Object, error) { res = append(res, BuildLokiGossipRingService(opts.Name)) if opts.Stack.Rules != nil && opts.Stack.Rules.Enabled { - rulesCMShards, err := RulesConfigMapShards(&opts) if err != nil { return nil, err diff --git a/operator/internal/manifests/gateway.go b/operator/internal/manifests/gateway.go index 9f4e05b938..7cb8e79299 100644 --- a/operator/internal/manifests/gateway.go +++ b/operator/internal/manifests/gateway.go @@ -56,6 +56,13 @@ func BuildGateway(opts Options) ([]client.Object, error) { if err := configureGatewayRulesAPI(&dpl.Spec.Template.Spec, opts.Name, opts.Namespace); err != nil { return nil, err } + + if opts.Stack.Tenants != nil { + mode := opts.Stack.Tenants.Mode + if err := configureGatewayDeploymentRulesAPIForMode(dpl, mode); err != nil { + return nil, err + } + } } if opts.Gates.HTTPEncryption { diff --git a/operator/internal/manifests/gateway_tenants.go b/operator/internal/manifests/gateway_tenants.go index 25805690ca..904871e13d 100644 --- a/operator/internal/manifests/gateway_tenants.go +++ b/operator/internal/manifests/gateway_tenants.go @@ -74,6 +74,17 @@ func configureGatewayDeploymentForMode(d *appsv1.Deployment, mode lokiv1.ModeTyp return nil } +func configureGatewayDeploymentRulesAPIForMode(d *appsv1.Deployment, mode lokiv1.ModeType) error { + switch mode { + case lokiv1.Static, lokiv1.Dynamic, lokiv1.OpenshiftNetwork: + return nil // nothing to configure + case lokiv1.OpenshiftLogging: + return openshift.ConfigureGatewayDeploymentRulesAPI(d, gatewayContainerName) + } + + return nil +} + func configureGatewayServiceForMode(s *corev1.ServiceSpec, mode lokiv1.ModeType) error { switch mode { case lokiv1.Static, lokiv1.Dynamic: diff --git a/operator/internal/manifests/gateway_test.go b/operator/internal/manifests/gateway_test.go index 6517e4c1a7..95af2966f6 100644 --- a/operator/internal/manifests/gateway_test.go +++ b/operator/internal/manifests/gateway_test.go @@ -690,6 +690,7 @@ func TestBuildGateway_WithRulesEnabled(t *testing.T) { wantArgs: []string{ "--logs.rules.endpoint=https://abcd-ruler-http.efgh.svc.cluster.local:3100", "--logs.rules.read-only=true", + "--logs.rules.label-filters=application:kubernetes_namespace_name", }, }, { diff --git a/operator/internal/manifests/openshift/alertingrule.go b/operator/internal/manifests/openshift/alertingrule.go new file mode 100644 index 0000000000..22923ed154 --- /dev/null +++ b/operator/internal/manifests/openshift/alertingrule.go @@ -0,0 +1,27 @@ +package openshift + +import lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + +func AlertingRuleTenantLabels(ar *lokiv1.AlertingRule) { + switch ar.Spec.TenantID { + case tenantApplication: + for groupIdx, group := range ar.Spec.Groups { + group := group + for ruleIdx, rule := range group.Rules { + rule := rule + if rule.Labels == nil { + rule.Labels = map[string]string{} + } + rule.Labels[opaDefaultLabelMatcher] = ar.Namespace + group.Rules[ruleIdx] = rule + } + ar.Spec.Groups[groupIdx] = group + } + case tenantInfrastructure, tenantAudit: + // Do nothing + case tenantNetwork: + // Do nothing + default: + // Do nothing + } +} diff --git a/operator/internal/manifests/openshift/alertingrule_test.go b/operator/internal/manifests/openshift/alertingrule_test.go new file mode 100644 index 0000000000..66a08bdfe8 --- /dev/null +++ b/operator/internal/manifests/openshift/alertingrule_test.go @@ -0,0 +1,195 @@ +package openshift + +import ( + "testing" + + lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestAlertingRuleTenantLabels(t *testing.T) { + tt := []struct { + rule *lokiv1.AlertingRule + want *lokiv1.AlertingRule + }{ + { + rule: &lokiv1.AlertingRule{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test-ns", + }, + Spec: lokiv1.AlertingRuleSpec{ + TenantID: tenantApplication, + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + }, + }, + }, + }, + }, + }, + want: &lokiv1.AlertingRule{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "test-ns", + }, + Spec: lokiv1.AlertingRuleSpec{ + TenantID: tenantApplication, + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + Labels: map[string]string{ + opaDefaultLabelMatcher: "test-ns", + }, + }, + }, + }, + }, + }, + }, + }, + { + rule: &lokiv1.AlertingRule{ + Spec: lokiv1.AlertingRuleSpec{ + TenantID: tenantInfrastructure, + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + }, + }, + }, + }, + }, + }, + want: &lokiv1.AlertingRule{ + Spec: lokiv1.AlertingRuleSpec{ + TenantID: tenantInfrastructure, + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + }, + }, + }, + }, + }, + }, + }, + { + rule: &lokiv1.AlertingRule{ + Spec: lokiv1.AlertingRuleSpec{ + TenantID: tenantAudit, + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + }, + }, + }, + }, + }, + }, + want: &lokiv1.AlertingRule{ + Spec: lokiv1.AlertingRuleSpec{ + TenantID: tenantAudit, + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + }, + }, + }, + }, + }, + }, + }, + { + rule: &lokiv1.AlertingRule{ + Spec: lokiv1.AlertingRuleSpec{ + TenantID: tenantNetwork, + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + }, + }, + }, + }, + }, + }, + want: &lokiv1.AlertingRule{ + Spec: lokiv1.AlertingRuleSpec{ + TenantID: tenantNetwork, + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + }, + }, + }, + }, + }, + }, + }, + { + rule: &lokiv1.AlertingRule{ + Spec: lokiv1.AlertingRuleSpec{ + TenantID: "unknown", + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + }, + }, + }, + }, + }, + }, + want: &lokiv1.AlertingRule{ + Spec: lokiv1.AlertingRuleSpec{ + TenantID: "unknown", + Groups: []*lokiv1.AlertingRuleGroup{ + { + Name: "test-group", + Rules: []*lokiv1.AlertingRuleGroupSpec{ + { + Alert: "alert", + }, + }, + }, + }, + }, + }, + }, + } + for _, tc := range tt { + tc := tc + t.Run(tc.rule.Spec.TenantID, func(t *testing.T) { + t.Parallel() + AlertingRuleTenantLabels(tc.rule) + + require.Equal(t, tc.want, tc.rule) + }) + } +} diff --git a/operator/internal/manifests/openshift/configure.go b/operator/internal/manifests/openshift/configure.go index 1f3db9a9b2..29680f73d3 100644 --- a/operator/internal/manifests/openshift/configure.go +++ b/operator/internal/manifests/openshift/configure.go @@ -76,6 +76,30 @@ func ConfigureGatewayDeployment( return nil } +// ConfigureGatewayDeploymentRulesAPI merges CLI argument to the gateway container +// that allow only Rules API access with a valid namespace input for the tenant application. +func ConfigureGatewayDeploymentRulesAPI(d *appsv1.Deployment, containerName string) error { + var gwIndex int + for i, c := range d.Spec.Template.Spec.Containers { + if c.Name == containerName { + gwIndex = i + break + } + } + + container := corev1.Container{ + Args: []string{ + fmt.Sprintf("--logs.rules.label-filters=%s:%s", tenantApplication, opaDefaultLabelMatcher), + }, + } + + if err := mergo.Merge(&d.Spec.Template.Spec.Containers[gwIndex], container, mergo.WithAppendSlice); err != nil { + return kverrors.Wrap(err, "failed to merge container") + } + + return nil +} + // ConfigureGatewayService merges the OpenPolicyAgent sidecar metrics port into // the service spec. With this the metrics are exposed through the same service. func ConfigureGatewayService(s *corev1.ServiceSpec) error { diff --git a/operator/internal/manifests/rules_config.go b/operator/internal/manifests/rules_config.go index d6f3d4890d..532073ca7d 100644 --- a/operator/internal/manifests/rules_config.go +++ b/operator/internal/manifests/rules_config.go @@ -3,7 +3,9 @@ package manifests import ( "fmt" + lokiv1 "github.com/grafana/loki/operator/apis/loki/v1" "github.com/grafana/loki/operator/internal/manifests/internal/rules" + "github.com/grafana/loki/operator/internal/manifests/openshift" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -26,6 +28,11 @@ func RulesConfigMapShards(opts *Options) ([]*corev1.ConfigMap, error) { shardedCM := NewShardedConfigMap(template, RulesConfigMapName(opts.Name)) for _, r := range opts.AlertingRules { + r := r + if opts.Stack.Tenants != nil { + configureAlertingRuleForMode(&r, opts.Stack.Tenants.Mode) + } + c, err := rules.MarshalAlertingRule(r) if err != nil { return nil, err @@ -73,3 +80,14 @@ func newConfigMapTemplate(opts *Options, l map[string]string) *corev1.ConfigMap func (rn RuleName) toString() string { return fmt.Sprintf("%s%s%s.yaml", rn.tenantID, rulePartsSeparator, rn.filename) } + +func configureAlertingRuleForMode(ar *lokiv1.AlertingRule, mode lokiv1.ModeType) { + switch mode { + case lokiv1.Static, lokiv1.Dynamic: + // Do nothing + case lokiv1.OpenshiftLogging: + openshift.AlertingRuleTenantLabels(ar) + case lokiv1.OpenshiftNetwork: + // Do nothing + } +}