Skip to content
9 changes: 5 additions & 4 deletions operator/cmd/loki-operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,21 +110,22 @@ func main() {
os.Exit(1)
}

if ctrlCfg.Gates.ServiceMonitors && ctrlCfg.Gates.OpenShift.Enabled && ctrlCfg.Gates.OpenShift.Dashboards {
if ctrlCfg.Gates.OpenShift.Enabled {
var ns string
ns, err = operator.GetNamespace()
if err != nil {
logger.Error(err, "unable to read in operator namespace")
os.Exit(1)
}

if err = (&lokictrl.DashboardsReconciler{
if err = (&lokictrl.ClusterScopeReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Log: logger.WithName("controllers").WithName(lokictrl.ControllerNameLokiDashboards),
Log: logger.WithName("controllers").WithName(lokictrl.ControllerNameLokiClusterScope),
OperatorNs: ns,
Dashboards: ctrlCfg.Gates.ServiceMonitors && ctrlCfg.Gates.OpenShift.Dashboards,
}).SetupWithManager(mgr); err != nil {
logger.Error(err, "unable to create controller", "controller", lokictrl.ControllerNameLokiDashboards)
logger.Error(err, "unable to create controller", "controller", lokictrl.ControllerNameLokiClusterScope)
os.Exit(1)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/grafana/loki/operator/internal/handlers"
)

const ControllerNameLokiDashboards = "loki-dashboards"
const ControllerNameLokiClusterScope = "loki-cluster-scope"

var createOrDeletesPred = builder.WithPredicates(predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool { return false },
Expand All @@ -27,18 +27,19 @@ var createOrDeletesPred = builder.WithPredicates(predicate.Funcs{
GenericFunc: func(e event.GenericEvent) bool { return false },
})

// DashboardsReconciler deploys and removes the cluster-global resources needed
// for the metrics dashboards depending on whether any LokiStacks exist.
type DashboardsReconciler struct {
// ClusterScopeReconciler deploys and removes the cluster-global resources needed
// for the metrics dashboards and RBAC resources depending on whether any LokiStacks exist.
type ClusterScopeReconciler struct {
client.Client
Scheme *runtime.Scheme
Log logr.Logger
OperatorNs string
Dashboards bool
}

// Reconcile creates all LokiStack dashboard ConfigMap and PrometheusRule objects on OpenShift clusters when
// the at least one LokiStack custom resource exists or removes all when none.
func (r *DashboardsReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
func (r *ClusterScopeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
var stacks lokiv1.LokiStackList
if err := r.List(ctx, &stacks, client.MatchingLabelsSelector{Selector: labels.Everything()}); err != nil {
return ctrl.Result{}, kverrors.Wrap(err, "failed to list any lokistack instances")
Expand All @@ -47,24 +48,24 @@ func (r *DashboardsReconciler) Reconcile(ctx context.Context, req ctrl.Request)
if len(stacks.Items) == 0 {
// Removes all LokiStack dashboard resources on OpenShift clusters when
// the last LokiStack custom resource is deleted.
if err := handlers.DeleteDashboards(ctx, r.Client, r.OperatorNs); err != nil {
if err := handlers.DeleteClusterScopedResources(ctx, r.Client, r.OperatorNs, stacks); err != nil {
return ctrl.Result{}, kverrors.Wrap(err, "failed to delete dashboard resources")
}
return ctrl.Result{}, nil
}

// Creates all LokiStack dashboard resources on OpenShift clusters when
// the first LokiStack custom resource is created.
if err := handlers.CreateDashboards(ctx, r.Log, r.OperatorNs, r.Client, r.Scheme); err != nil {
if err := handlers.CreateClusterScopedResources(ctx, r.Log, r.Dashboards, r.OperatorNs, r.Client, r.Scheme, stacks); err != nil {
return ctrl.Result{}, kverrors.Wrap(err, "failed to create dashboard resources", "req", req)
}
return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager to only call this controller on create/delete/generic events.
func (r *DashboardsReconciler) SetupWithManager(mgr manager.Manager) error {
func (r *ClusterScopeReconciler) SetupWithManager(mgr manager.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&lokiv1.LokiStack{}, createOrDeletesPred).
Named(ControllerNameLokiDashboards).
Named(ControllerNameLokiClusterScope).
Complete(r)
}
52 changes: 0 additions & 52 deletions operator/internal/handlers/dashboards_create.go

This file was deleted.

31 changes: 0 additions & 31 deletions operator/internal/handlers/dashboards_delete.go

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package handlers

import (
"context"
"fmt"

"github.com/ViaQ/logerr/v2/kverrors"
"github.com/go-logr/logr"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client" //nolint:typecheck
ctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

lokiv1 "github.com/grafana/loki/operator/api/loki/v1"
"github.com/grafana/loki/operator/internal/external/k8s"
"github.com/grafana/loki/operator/internal/manifests"
"github.com/grafana/loki/operator/internal/manifests/openshift"
)

// CreateClusterScopedResources handles the LokiStack cluster scoped create events.
func CreateClusterScopedResources(ctx context.Context, log logr.Logger, dashboards bool, operatorNs string, k k8s.Client, s *runtime.Scheme, stacks lokiv1.LokiStackList) error {
// This has to be done here as to not introduce a circular dependency.
rulerSubjects := make([]rbacv1.Subject, 0, len(stacks.Items))
for _, stack := range stacks.Items {
rulerSubjects = append(rulerSubjects, rbacv1.Subject{
Kind: "ServiceAccount",
Name: manifests.RulerName(stack.Name),
Namespace: stack.Namespace,
})
}
opts := openshift.NewOptionsClusterScope(operatorNs, manifests.ClusterScopeLabels(), rulerSubjects)

objs := openshift.BuildRBAC(opts)
if dashboards {
objs = append(objs, openshift.BuildDashboards(opts)...)
}

var errCount int32
for _, obj := range objs {
desired := obj.DeepCopyObject().(client.Object)
mutateFn := manifests.MutateFuncFor(obj, desired, nil)

op, err := ctrl.CreateOrUpdate(ctx, k, obj, mutateFn)
if err != nil {
log.Error(err, "failed to configure resource")
errCount++
continue
}

msg := fmt.Sprintf("Resource has been %s", op)
switch op {
case ctrlutil.OperationResultNone:
log.V(1).Info(msg)
default:
log.Info(msg)
}
}

if errCount > 0 {
return kverrors.New("failed to configure lokistack cluster-scoped resources")
}

// Delete legacy RBAC resources
// This needs to live here and not in DeleteClusterScopedResources as we want to
// delete the legacy RBAC resources when LokiStack is reconciled and not on delete.
var legacyObjs []client.Object
for _, stack := range stacks.Items {
// This name would clash with the new cluster-scoped resources. Skip it.
if stack.Name == "lokistack" {
continue
}
legacyObjs = append(legacyObjs, openshift.LegacyRBAC(manifests.GatewayName(stack.Name), manifests.RulerName(stack.Name))...)
}
for _, obj := range legacyObjs {
key := client.ObjectKeyFromObject(obj)
if err := k.Delete(ctx, obj, &client.DeleteOptions{}); err != nil {
if apierrors.IsNotFound(err) {
continue
}
return kverrors.Wrap(err, "failed to delete resource", "kind", obj.GetObjectKind(), "key", key)
}
}

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestCreateDashboards_ReturnsResourcesInManagedNamespaces(t *testing.T) {

k.StatusStub = func() client.StatusWriter { return sw }

err := CreateDashboards(context.TODO(), logger, "test", k, scheme)
err := CreateClusterScopedResources(context.TODO(), logger, true, "test", k, scheme, lokiv1.LokiStackList{Items: []lokiv1.LokiStack{stack}})
require.NoError(t, err)

// make sure create was called
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package handlers

import (
"context"

"github.com/ViaQ/logerr/v2/kverrors"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"

lokiv1 "github.com/grafana/loki/operator/api/loki/v1"
"github.com/grafana/loki/operator/internal/external/k8s"
"github.com/grafana/loki/operator/internal/manifests"
"github.com/grafana/loki/operator/internal/manifests/openshift"
)

// DeleteClusterScopedResources removes all cluster-scoped resources.
func DeleteClusterScopedResources(ctx context.Context, k k8s.Client, operatorNs string, stacks lokiv1.LokiStackList) error {
// Since we are deleting we don't need to worry about the subjects.
opts := openshift.NewOptionsClusterScope(operatorNs, manifests.ClusterScopeLabels(), []rbacv1.Subject{})

objs := openshift.BuildRBAC(opts)
objs = append(objs, openshift.BuildDashboards(opts)...)

for _, obj := range objs {
key := client.ObjectKeyFromObject(obj)
if err := k.Delete(ctx, obj, &client.DeleteOptions{}); err != nil {
if apierrors.IsNotFound(err) {
continue
}
return kverrors.Wrap(err, "failed to delete dashboard", "kind", obj.GetObjectKind(), "key", key)
}
}
return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,19 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"

v1 "github.com/grafana/loki/operator/api/loki/v1"
"github.com/grafana/loki/operator/internal/external/k8s/k8sfakes"
"github.com/grafana/loki/operator/internal/manifests/openshift"
)

func TestDeleteDashboards(t *testing.T) {
objs, err := openshift.BuildDashboards("operator-ns")
require.NoError(t, err)
opts := openshift.NewOptionsClusterScope("operator-ns", nil, nil)
objs := openshift.BuildRBAC(opts)
objs = append(objs, openshift.BuildDashboards(opts)...)

k := &k8sfakes.FakeClient{}

err = DeleteDashboards(context.TODO(), k, "operator-ns")
err := DeleteClusterScopedResources(context.TODO(), k, "operator-ns", v1.LokiStackList{})
require.NoError(t, err)
require.Equal(t, k.DeleteCallCount(), len(objs))
}
Expand All @@ -30,6 +32,6 @@ func TestDeleteDashboards_ReturnsNoError_WhenNotFound(t *testing.T) {
return apierrors.NewNotFound(schema.GroupResource{}, "something wasn't found")
}

err := DeleteDashboards(context.TODO(), k, "operator-ns")
err := DeleteClusterScopedResources(context.TODO(), k, "operator-ns", v1.LokiStackList{})
require.NoError(t, err)
}
2 changes: 0 additions & 2 deletions operator/internal/manifests/gateway_tenants.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,6 @@ func configureGatewayObjsForMode(objs []client.Object, opts Options) []client.Ob
}
}

openShiftObjs := openshift.BuildGatewayTenantModeObjects(opts.OpenShiftOptions)
objs = append(objs, openShiftObjs...)
}

return objs
Expand Down
6 changes: 6 additions & 0 deletions operator/internal/manifests/gateway_tenants_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ func TestConfigureDeploymentForMode(t *testing.T) {
"--web.healthchecks.url=http://localhost:8082",
"--opa.skip-tenants=audit,infrastructure",
"--opa.package=lokistack",
"--opa.ssar=true",
"--opa.admin-groups=system:cluster-admins,cluster-admin,dedicated-admin",
"--opa.matcher=kubernetes_namespace_name",
`--openshift.mappings=application=loki.grafana.com`,
Expand Down Expand Up @@ -838,6 +839,7 @@ func TestConfigureDeploymentForMode(t *testing.T) {
"--web.healthchecks.url=http://localhost:8082",
"--opa.skip-tenants=audit,infrastructure",
"--opa.package=lokistack",
"--opa.ssar=true",
"--opa.admin-groups=system:cluster-admins,cluster-admin,dedicated-admin",
"--opa.matcher=kubernetes_namespace_name",
"--tls.internal.server.cert-file=/var/run/tls/http/server/tls.crt",
Expand Down Expand Up @@ -953,6 +955,7 @@ func TestConfigureDeploymentForMode(t *testing.T) {
"--web.healthchecks.url=http://localhost:8082",
"--opa.skip-tenants=audit,infrastructure",
"--opa.package=lokistack",
"--opa.ssar=true",
"--opa.admin-groups=system:cluster-admins,cluster-admin,dedicated-admin",
"--opa.matcher=SrcK8S_Namespace,DstK8S_Namespace",
"--opa.matcher-op=or",
Expand Down Expand Up @@ -1060,6 +1063,7 @@ func TestConfigureDeploymentForMode(t *testing.T) {
"--web.healthchecks.url=http://localhost:8082",
"--opa.skip-tenants=audit,infrastructure",
"--opa.package=lokistack",
"--opa.ssar=true",
"--opa.admin-groups=system:cluster-admins,cluster-admin,dedicated-admin",
"--opa.matcher=SrcK8S_Namespace,DstK8S_Namespace",
"--opa.matcher-op=or",
Expand Down Expand Up @@ -1175,6 +1179,7 @@ func TestConfigureDeploymentForMode(t *testing.T) {
"--web.healthchecks.url=http://localhost:8082",
"--opa.skip-tenants=audit,infrastructure",
"--opa.package=lokistack",
"--opa.ssar=true",
"--opa.admin-groups=custom-admins,other-admins",
"--opa.matcher=kubernetes_namespace_name",
`--openshift.mappings=application=loki.grafana.com`,
Expand Down Expand Up @@ -1272,6 +1277,7 @@ func TestConfigureDeploymentForMode(t *testing.T) {
"--web.healthchecks.url=http://localhost:8082",
"--opa.skip-tenants=audit,infrastructure",
"--opa.package=lokistack",
"--opa.ssar=true",
"--opa.matcher=kubernetes_namespace_name",
`--openshift.mappings=application=loki.grafana.com`,
`--openshift.mappings=infrastructure=loki.grafana.com`,
Expand Down
Loading
Loading