Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into alertmanager-test
Browse files Browse the repository at this point in the history
  • Loading branch information
TheoBrigitte committed Dec 17, 2024
2 parents a1f396f + 42ad5b3 commit 460dd3a
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 135 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.10.2] - 2024-12-17

### Added

- Add Alertmanager controller

### Changed

- Change SSO settings configuration to use the Grafana admin API instead of app user-values.

## [0.10.1] - 2024-12-12

### Fixed
Expand Down Expand Up @@ -198,7 +204,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Initialize project and create heartbeat for the installation.

[Unreleased]: https://github.com/giantswarm/observability-operator/compare/v0.10.1...HEAD
[Unreleased]: https://github.com/giantswarm/observability-operator/compare/v0.10.2...HEAD
[0.10.2]: https://github.com/giantswarm/observability-operator/compare/v0.10.1...v0.10.2
[0.10.1]: https://github.com/giantswarm/observability-operator/compare/v0.10.0...v0.10.1
[0.10.0]: https://github.com/giantswarm/observability-operator/compare/v0.9.1...v0.10.0
[0.9.1]: https://github.com/giantswarm/observability-operator/compare/v0.9.0...v0.9.1
Expand Down
1 change: 1 addition & 0 deletions api/v1alpha1/grafanaorganization_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type GrafanaOrganizationSpec struct {
// DisplayName is the name displayed when viewing the organization in Grafana. It can be different from the actual org's name.
// +kubebuilder:example="Giant Swarm"
// +kubebuilder:validation:MinLength=1
// +kubebuilder:unique=true
DisplayName string `json:"displayName"`

// Access rules defines user permissions for interacting with the organization in Grafana.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/onsi/gomega v1.36.1
github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.23
github.com/pkg/errors v0.9.1
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.79.0
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.79.1
github.com/prometheus/alertmanager v0.27.0
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/common v0.61.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.79.0 h1:IiCqr23V8SexkXkPmK+6tS/Ped/oCVhXSSmLacEATy4=
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.79.0/go.mod h1:AVMP4QEW8xuGWnxaWSpI3kKjP9fDA31nO68zsyREJZA=
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.79.1 h1:Dwk9xYZOd8gq+nhlZREvHbQ6enj3yjC5HPFOdcReqGw=
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.79.1/go.mod h1:AVMP4QEW8xuGWnxaWSpI3kKjP9fDA31nO68zsyREJZA=
github.com/prometheus/alertmanager v0.27.0 h1:V6nTa2J5V4s8TG4C4HtrBP/WNSebCCTYGGv4qecA/+I=
github.com/prometheus/alertmanager v0.27.0/go.mod h1:8Ia/R3urPmbzJ8OsdvmZvIprDwvwmYCmUbwBL+jlPOE=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
Expand Down
2 changes: 1 addition & 1 deletion helm/observability-operator/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: observability-operator
description: The observability-operator manages the Giant Swarm observability platform.
home: https://github.com/giantswarm/observability-operator
icon: https://s.giantswarm.io/app-icons/giantswarm/1/light.svg
version: 0.10.1
version: 0.10.2
appVersion: 0.0.2
annotations:
application.giantswarm.io/team: "atlas"
Expand Down
92 changes: 47 additions & 45 deletions internal/controller/grafanaorganization_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ import (
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

"github.com/giantswarm/observability-operator/pkg/config"
grafanaclient "github.com/giantswarm/observability-operator/pkg/grafana/client"
appv1 "github.com/giantswarm/apiextensions-application/api/v1alpha1"

"github.com/giantswarm/observability-operator/api/v1alpha1"
"github.com/giantswarm/observability-operator/internal/controller/predicates"
"github.com/giantswarm/observability-operator/pkg/config"
"github.com/giantswarm/observability-operator/pkg/grafana"
"github.com/giantswarm/observability-operator/pkg/grafana/templating"
grafanaclient "github.com/giantswarm/observability-operator/pkg/grafana/client"
)

// GrafanaOrganizationReconciler reconciles a GrafanaOrganization object
Expand Down Expand Up @@ -140,7 +140,18 @@ func (r *GrafanaOrganizationReconciler) SetupWithManager(mgr ctrl.Manager) error

// Sort organizations by orgID to ensure the order is deterministic.
// This is important to prevent incorrect ordering of organizations on grafana restarts.
slices.SortStableFunc(organizations.Items, compareOrganizationsByID)
slices.SortStableFunc(organizations.Items, func(i, j v1alpha1.GrafanaOrganization) int {
// if both orgs have a nil orgID, they are equal
// if one org has a nil orgID, it is higher than the other as it was not created in Grafana yet
if i.Status.OrgID == 0 && j.Status.OrgID == 0 {
return 0
} else if i.Status.OrgID == 0 {
return 1
} else if j.Status.OrgID == 0 {
return -1
}
return cmp.Compare(i.Status.OrgID, j.Status.OrgID)
})

// Reconcile all grafana organizations when the grafana pod is recreated
requests := make([]reconcile.Request, 0, len(organizations.Items))
Expand All @@ -158,19 +169,6 @@ func (r *GrafanaOrganizationReconciler) SetupWithManager(mgr ctrl.Manager) error
Complete(r)
}

func compareOrganizationsByID(i, j v1alpha1.GrafanaOrganization) int {
// if both orgs have a nil orgID, they are equal
// if one org has a nil orgID, it is higher than the other as it was not created in Grafana yet
if i.Status.OrgID == 0 && j.Status.OrgID == 0 {
return 0
} else if i.Status.OrgID == 0 {
return 1
} else if j.Status.OrgID == 0 {
return -1
}
return cmp.Compare(i.Status.OrgID, j.Status.OrgID)
}

// reconcileCreate creates the grafanaOrganization.
// reconcileCreate ensures the Grafana organization described in grafanaOrganization CR is created in Grafana.
// This function is also responsible for:
Expand Down Expand Up @@ -214,7 +212,7 @@ func (r GrafanaOrganizationReconciler) reconcileCreate(ctx context.Context, graf
}

// Configure Grafana RBAC
if err := r.configureGrafana(ctx); err != nil {
if err := r.configureGrafanaSSO(ctx); err != nil {
return ctrl.Result{}, errors.WithStack(err)
}

Expand Down Expand Up @@ -251,6 +249,9 @@ func newOrganization(grafanaOrganization *v1alpha1.GrafanaOrganization) grafana.
ID: grafanaOrganization.Status.OrgID,
Name: grafanaOrganization.Spec.DisplayName,
TenantIDs: tenantIDs,
Admins: grafanaOrganization.Spec.RBAC.Admins,
Editors: grafanaOrganization.Spec.RBAC.Editors,
Viewers: grafanaOrganization.Spec.RBAC.Viewers,
}
}

Expand Down Expand Up @@ -343,7 +344,7 @@ func (r GrafanaOrganizationReconciler) reconcileDelete(ctx context.Context, graf
}
}

err := r.configureGrafana(ctx)
err := r.configureGrafanaSSO(ctx)
if err != nil {
return errors.WithStack(err)
}
Expand All @@ -367,7 +368,7 @@ func (r GrafanaOrganizationReconciler) reconcileDelete(ctx context.Context, graf
}

// configureGrafana ensures the RBAC configuration is set in Grafana.
func (r *GrafanaOrganizationReconciler) configureGrafana(ctx context.Context) error {
func (r *GrafanaOrganizationReconciler) configureGrafanaSSO(ctx context.Context) error {
logger := log.FromContext(ctx)

organizationList := v1alpha1.GrafanaOrganizationList{}
Expand All @@ -384,34 +385,35 @@ func (r *GrafanaOrganizationReconciler) configureGrafana(ctx context.Context) er
},
}

_, err = controllerutil.CreateOrPatch(ctx, r.Client, grafanaConfig, func() error {
// We always sort the organizations to ensure the order is deterministic and the configmap is stable
// in order to prevent grafana to restarts.
slices.SortStableFunc(organizationList.Items, compareOrganizationsByID)

config, err := templating.GenerateGrafanaConfiguration(organizationList.Items)
if err != nil {
logger.Error(err, "failed to generate grafana user configmap values.")
return errors.WithStack(err)
}

// TODO: to be removed for next release
// cleanup owner references from the config map, see https://github.com/giantswarm/observability-operator/pull/183
for _, organization := range organizationList.Items {
// nolint:errcheck,gosec // ignore errors, owner references are probably already gone
controllerutil.RemoveOwnerReference(&organization, grafanaConfig, r.Scheme)
}

logger.Info("updating grafana-user-values", "config", config)

grafanaConfig.Data = make(map[string]string)
grafanaConfig.Data["values"] = config
// TODO remove after next release (current: 0.10.1)
if err = r.Client.Delete(ctx, grafanaConfig); client.IgnoreNotFound(err) != nil {
return errors.WithStack(err)
}

return nil
})
// Retrieve the app.
var currentApp appv1.App = appv1.App{
ObjectMeta: metav1.ObjectMeta{
Name: "grafana",
Namespace: "giantswarm",
},
}
err = r.Client.Get(ctx, types.NamespacedName{Name: currentApp.GetName(), Namespace: currentApp.GetNamespace()}, &currentApp)
if err != nil {
return err
}
currentApp.Spec.UserConfig = appv1.AppSpecUserConfig{}
if err = r.Client.Update(ctx, &currentApp); err != nil {
return err
}
// TODO end of section to be removed after next release (current: 0.10.1)

// Configure SSO settings in Grafana
organizations := make([]grafana.Organization, len(organizationList.Items))
for i, organization := range organizationList.Items {
organizations[i] = newOrganization(&organization)
}
err = grafana.ConfigureSSOSettings(ctx, r.GrafanaAPI, organizations)
if err != nil {
logger.Error(err, "failed to configure grafana.")
return errors.WithStack(err)
}

Expand Down
83 changes: 83 additions & 0 deletions pkg/grafana/sso_settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package grafana

import (
"context"
"fmt"
"strings"

"github.com/grafana/grafana-openapi-client-go/client"
"github.com/grafana/grafana-openapi-client-go/models"
"github.com/pkg/errors"
"sigs.k8s.io/controller-runtime/pkg/log"
)

const (
grafanaAdminRole = "Admin"
grafanaEditorRole = "Editor"
grafanaViewerRole = "Viewer"
)

func ConfigureSSOSettings(ctx context.Context, grafanaAPI *client.GrafanaHTTPAPI, organizations []Organization) error {
logger := log.FromContext(ctx)

provider := "generic_oauth"
resp, err := grafanaAPI.SsoSettings.GetProviderSettings(provider, nil)
if err != nil {
logger.Error(err, "failed to get sso provider settings.")
return errors.WithStack(err)
}

orgsMapping := generateGrafanaOrgsMapping(organizations)
settings := resp.Payload.Settings.(map[string]interface{})
settings["role_attribute_path"] = "to_string('Viewer')"
settings["org_attribute_path"] = "groups"
settings["org_mapping"] = orgsMapping

logger.Info("Configuring Grafana SSO settings", "provider", provider, "settings", settings)

// Update the provider settings
_, err = grafanaAPI.SsoSettings.UpdateProviderSettings(provider,
&models.UpdateProviderSettingsParamsBody{
ID: resp.Payload.ID,
Provider: resp.Payload.Provider,
Settings: settings,
})

if err != nil {
logger.Error(err, "failed to configure grafana sso.")
return errors.WithStack(err)
}

return nil
}

func generateGrafanaOrgsMapping(organizations []Organization) string {
var orgMappings []string
// TODO: We need to be admins to be able to see the private dashboards for now, remove the 2 GS groups once https://github.com/giantswarm/roadmap/issues/3696 is done.
// Grant Admin role to Giantswarm users logging in via azure active directory.
orgMappings = append(orgMappings, buildOrgMapping(SharedOrg.Name, "giantswarm-ad:giantswarm-admins", grafanaAdminRole))
// Grant Admin role to Giantswarm users logging in via github.
orgMappings = append(orgMappings, buildOrgMapping(SharedOrg.Name, "giantswarm-github:giantswarm:giantswarm-admins", grafanaAdminRole))
// Grant Editor role to every other users.
orgMappings = append(orgMappings, fmt.Sprintf(`"*:%s:%s"`, SharedOrg.Name, grafanaEditorRole))
for _, organization := range organizations {
for _, adminOrgAttribute := range organization.Admins {
orgMappings = append(orgMappings, buildOrgMapping(organization.Name, adminOrgAttribute, grafanaAdminRole))
}
for _, editorOrgAttribute := range organization.Editors {
orgMappings = append(orgMappings, buildOrgMapping(organization.Name, editorOrgAttribute, grafanaEditorRole))
}
for _, viewerOrgAttribute := range organization.Viewers {
orgMappings = append(orgMappings, buildOrgMapping(organization.Name, viewerOrgAttribute, grafanaViewerRole))
}
}

return strings.Join(orgMappings, " ")
}

func buildOrgMapping(organizationName, userOrgAttribute, role string) string {
// We need to escape the colon in the userOrgAttribute
u := strings.ReplaceAll(userOrgAttribute, ":", "\\:")
// We add double quotes to the org mapping to support spaces in display names
return fmt.Sprintf(`"%s:%s:%s"`, u, organizationName, role)
}

This file was deleted.

77 changes: 0 additions & 77 deletions pkg/grafana/templating/templating.go

This file was deleted.

3 changes: 3 additions & 0 deletions pkg/grafana/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ type Organization struct {
ID int64
Name string
TenantIDs []string
Admins []string
Editors []string
Viewers []string
}

type Datasource struct {
Expand Down

0 comments on commit 460dd3a

Please sign in to comment.