Skip to content

Commit

Permalink
add datasources to org cr's status (#144)
Browse files Browse the repository at this point in the history
* add org CRD in helm chart

* layout foundations for CRD with kubebuilder

* remove useless files

* update CRD names

* add generated crd

* applying suggestions

* fix naming issues

* update Dockerfile with copy command for the api package

* update changelog

* add symlink to crd in helm chart and updated crd

* fix symlink

* fix symlink

* Update main.go

Co-authored-by: Quentin Bisson <[email protected]>

* add symlink back

* add finalizer

* Apply suggestions from code review

Co-authored-by: Théo Brigitte <[email protected]>

* regenerate crd

* update sample

* add logic to grafana organization reconciler

* fix build

* Split grafana client creation in its own folder

* add exclusions to golangci

* add crds template in chart

* Apply suggestions from code review

Co-authored-by: Quentin Bisson <[email protected]>

* update const names in files

* Update pkg/grafana/client/client.go

Co-authored-by: Quentin Bisson <[email protected]>

* fix const name in client

* rework getAdminCredentials function

* remove unused variable in client

* fix missing return statement error

* add authorizations for grafanaorganizations in operator rbac

* Apply suggestions from code review

Co-authored-by: Quentin Bisson <[email protected]>

* fix merging errors

* rename main org

* sort imports

* Enable grafanaOrganization creation

* first draft for org creation

* refactor code

* Apply suggestions from code review

Co-authored-by: Quentin Bisson <[email protected]>

* make orgID and dataSource ID int

* try out dirty trick

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Jose Armesto <[email protected]>

* update error handling

* Apply suggestions from code review

Co-authored-by: Théo Brigitte <[email protected]>

* fix errors in code

* remove connection to Grafana check

* enhance error handling

* rename variables

* add back deleted code

* changelog

* update crd sample

* add debug logs to solve current issue

* fix grafana admin secret name for admin-user

* remove debug logs

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Quentin Bisson <[email protected]>

* Apply suggestions from code review

Co-authored-by: Quentin Bisson <[email protected]>

* fix name error

* refacto code

* refactor code

* refactor code for better readability

* fix grafana isNotFound error handling

* add organization type

* remove one level of indentation

* improve status update handling

* add and fix comments

* remove unecessary type casting

* refacto and add todos to make the code cleaner

* add initial org mapping (#133)

* add org CRD in helm chart

* layout foundations for CRD with kubebuilder

* remove useless files

* update CRD names

* add generated crd

* applying suggestions

* fix naming issues

* update Dockerfile with copy command for the api package

* update changelog

* add symlink to crd in helm chart and updated crd

* fix symlink

* fix symlink

* Update main.go

Co-authored-by: Quentin Bisson <[email protected]>

* add symlink back

* add finalizer

* Apply suggestions from code review

Co-authored-by: Théo Brigitte <[email protected]>

* regenerate crd

* update sample

* add logic to grafana organization reconciler

* fix build

* Split grafana client creation in its own folder

* add exclusions to golangci

* add crds template in chart

* Apply suggestions from code review

Co-authored-by: Quentin Bisson <[email protected]>

* update const names in files

* Update pkg/grafana/client/client.go

Co-authored-by: Quentin Bisson <[email protected]>

* fix const name in client

* rework getAdminCredentials function

* remove unused variable in client

* fix missing return statement error

* add authorizations for grafanaorganizations in operator rbac

* Apply suggestions from code review

Co-authored-by: Quentin Bisson <[email protected]>

* fix merging errors

* rename main org

* sort imports

* add initial org mapping

* move crds to helm official repo to respect installation order

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Quentin Bisson <[email protected]>

* Set alloy wal truncate_frequency to 15m (#130)

* try truncate frequency

* Add flag to be able to set truncate_frequency

* set default monitoring agent to alloy (#134)

* Release v0.8.0 (#135)

* fix flag redefined error (#136)

* Release v0.8.1 (#137)

* add logic to grafana organization reconciler (#129)

* add org CRD in helm chart

* layout foundations for CRD with kubebuilder

* remove useless files

* update CRD names

* add generated crd

* applying suggestions

* fix naming issues

* update Dockerfile with copy command for the api package

* update changelog

* add symlink to crd in helm chart and updated crd

* fix symlink

* fix symlink

* Update main.go

Co-authored-by: Quentin Bisson <[email protected]>

* add symlink back

* add finalizer

* Apply suggestions from code review

Co-authored-by: Théo Brigitte <[email protected]>

* regenerate crd

* update sample

* add logic to grafana organization reconciler

* fix build

* Split grafana client creation in its own folder

* add exclusions to golangci

* add crds template in chart

* Apply suggestions from code review

Co-authored-by: Quentin Bisson <[email protected]>

* update const names in files

* Update pkg/grafana/client/client.go

Co-authored-by: Quentin Bisson <[email protected]>

* fix const name in client

* rework getAdminCredentials function

* remove unused variable in client

* fix missing return statement error

* add authorizations for grafanaorganizations in operator rbac

* Apply suggestions from code review

Co-authored-by: Quentin Bisson <[email protected]>

* fix merging errors

* rename main org

* sort imports

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Jose Armesto <[email protected]>

* Apply suggestions from code review

Co-authored-by: Théo Brigitte <[email protected]>

* fix errors in code

* address reviews

* address reviews

---------

Co-authored-by: QuentinBisson <[email protected]>
Co-authored-by: Théo Brigitte <[email protected]>
Co-authored-by: Jose Armesto <[email protected]>

* Update github.com/grafana/grafana-openapi-client-go digest to 9d96c20 (#138)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update module github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring to v0.77.2 (#139)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update internal/controller/grafanaorganization_controller.go

---------

Co-authored-by: QuantumEnigmaa <[email protected]>
Co-authored-by: Zirko <[email protected]>
Co-authored-by: Théo Brigitte <[email protected]>
Co-authored-by: Taylor Bot <[email protected]>
Co-authored-by: Jose Armesto <[email protected]>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix grafana admin password in deployment

* fix grafana admin password in deployment

* Update CHANGELOG.md

* fix go build

* add deletion

* move grafana templating to another pkg

* make datasources field in the CR optional

* add watches over the grafana pod

* add rule for watching & listing pods in clusterrole

* add datasources to org cr's status

* reduce scope of grafana pod watch

* push unfinished changes

* disable configureGrafana function for testing

* fix grafana pod reconciliation

* add configureGrafana back

* create both loki and mimir datasources for each org

* fix datasource creation

* trying a simple datasource addition

* fix watch

* add access mode to datasource

* reverting test code

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Hervé Nicol <[email protected]>

* Apply suggestions from code review

Co-authored-by: Quentin Bisson <[email protected]>

* apply more suggestions

* fix comment

* fix comment

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Hervé Nicol <[email protected]>

* fix comment

* fix comment

* disable alloy-metrics pod logs crds as they are causing issues with alloy-logs (#148)

* Update module github.com/onsi/gomega to v1.35.1 (#151)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update pkg/grafana/grafana.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* refactor

* address-reviews

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* nits

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* add-datasource-in-org

* Update tests/test-values.yaml

* set tenant to anonymous for mimir and alertmanager

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/grafanaorganization_controller.go

* Update internal/controller/predicates/predicates.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update pkg/grafana/grafana.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update pkg/grafana/types.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update pkg/grafana/types.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* fix build

* comment

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* address review

* edit roles for shared org

* edit roles for shared org

* Update internal/controller/grafanaorganization_controller.go

Co-authored-by: Théo Brigitte <[email protected]>

* Update pkg/grafana/templating/templating.go

Co-authored-by: Théo Brigitte <[email protected]>

* reviews

* improve watch predicate

* fix

---------

Co-authored-by: QuentinBisson <[email protected]>
Co-authored-by: Théo Brigitte <[email protected]>
Co-authored-by: Jose Armesto <[email protected]>
Co-authored-by: Taylor Bot <[email protected]>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Hervé Nicol <[email protected]>
  • Loading branch information
7 people authored Nov 19, 2024
1 parent bc5cf0c commit 8748b36
Show file tree
Hide file tree
Showing 9 changed files with 420 additions and 99 deletions.
12 changes: 7 additions & 5 deletions api/v1alpha1/grafanaorganization_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,23 @@ type GrafanaOrganizationStatus struct {

// DataSources is a list of grafana data sources that are available to the Grafana organization.
// +optional
DataSources []DataSources `json:"dataSources"`
DataSources []DataSource `json:"dataSources"`
}

// DataSource defines the name and id for data sources.
type DataSources struct {
type DataSource struct {
// ID is the unique id of the data source.
ID int64 `json:"ID"`

// Name is the name of the data source.
Name string `json:"name"`

// ID is the unique id of the data source.
ID int64 `json:"id"`
}

//+kubebuilder:object:root=true
//+kubebuilder:resource:scope=Cluster
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:JSONPath=".spec.displayName",name=DisplayName,type=string
//+kubebuilder:printcolumn:JSONPath=".status.orgID",name=OrgID,type=integer

// GrafanaOrganization is the Schema describing a Grafana organization. Its lifecycle is managed by the observability-operator.
type GrafanaOrganization struct {
Expand Down
10 changes: 5 additions & 5 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 11 additions & 4 deletions config/crd/observability.giantswarm.io_grafanaorganizations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.3
controller-gen.kubebuilder.io/version: v0.16.4
name: grafanaorganizations.observability.giantswarm.io
spec:
group: observability.giantswarm.io
Expand All @@ -14,7 +14,14 @@ spec:
singular: grafanaorganization
scope: Cluster
versions:
- name: v1alpha1
- additionalPrinterColumns:
- jsonPath: .spec.displayName
name: DisplayName
type: string
- jsonPath: .status.orgID
name: OrgID
type: integer
name: v1alpha1
schema:
openAPIV3Schema:
description: GrafanaOrganization is the Schema describing a Grafana organization.
Expand Down Expand Up @@ -81,15 +88,15 @@ spec:
items:
description: DataSource defines the name and id for data sources.
properties:
id:
ID:
description: ID is the unique id of the data source.
format: int64
type: integer
name:
description: Name is the name of the data source.
type: string
required:
- id
- ID
- name
type: object
type: array
Expand Down
197 changes: 136 additions & 61 deletions internal/controller/grafanaorganization_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ package controller

import (
"context"
"fmt"
"slices"
"strings"

grafanaAPI "github.com/grafana/grafana-openapi-client-go/client"
grafanaAPIModels "github.com/grafana/grafana-openapi-client-go/models"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -79,6 +79,39 @@ func (r *GrafanaOrganizationReconciler) Reconcile(ctx context.Context, req ctrl.
return r.reconcileCreate(ctx, grafanaOrganization)
}

// SetupWithManager sets up the controller with the Manager.
func (r *GrafanaOrganizationReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.GrafanaOrganization{}).
// Watch for grafana pod's status changes
Watches(
&v1.Pod{},
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
var logger = log.FromContext(ctx)
var organizations v1alpha1.GrafanaOrganizationList

err := mgr.GetClient().List(ctx, &organizations)
if err != nil {
logger.Error(err, "failed to list grafana organization CRs")
return []reconcile.Request{}
}

// Reconcile all grafana organizations when the grafana pod is recreated
requests := make([]reconcile.Request, 0, len(organizations.Items))
for _, organization := range organizations.Items {
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: organization.Name,
},
})
}
return requests
}),
builder.WithPredicates(predicates.GrafanaPodRecreatedPredicate{}),
).
Complete(r)
}

// 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 @@ -106,51 +139,119 @@ func (r GrafanaOrganizationReconciler) reconcileCreate(ctx context.Context, graf
return ctrl.Result{}, nil
}

// Ensure the first organization is renamed.
_, err := r.GrafanaAPI.Orgs.UpdateOrg(1, &grafanaAPIModels.UpdateOrgForm{
Name: grafana.SharedOrgName,
})
if err != nil {
logger.Error(err, fmt.Sprintf("failed to rename Main Org. to %s", grafana.SharedOrgName))
// Configure the shared organization in Grafana
if err := r.configureSharedOrg(ctx); err != nil {
return ctrl.Result{}, errors.WithStack(err)
}

// Configure the organization in Grafana
if err := r.configureOrganization(ctx, grafanaOrganization); err != nil {
return ctrl.Result{}, errors.WithStack(err)
}

// TODO add datasources for shared org.
// Update the datasources in the CR's status
if err := r.configureDatasources(ctx, grafanaOrganization); err != nil {
return ctrl.Result{}, errors.WithStack(err)
}

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

return ctrl.Result{}, nil
}

func (r GrafanaOrganizationReconciler) configureSharedOrg(ctx context.Context) error {
logger := log.FromContext(ctx)

sharedOrg := grafana.SharedOrg

logger.Info("configuring shared organization")
if _, err := grafana.UpdateOrganization(ctx, r.GrafanaAPI, sharedOrg); err != nil {
logger.Error(err, "failed to rename shared org")
return errors.WithStack(err)
}

if _, err := grafana.ConfigureDefaultDatasources(ctx, r.GrafanaAPI, sharedOrg); err != nil {
logger.Info("failed to configure datasources for shared org")
return errors.WithStack(err)
}

logger.Info("configured shared org")
return nil
}

func (r GrafanaOrganizationReconciler) configureOrganization(ctx context.Context, grafanaOrganization *v1alpha1.GrafanaOrganization) (err error) {
logger := log.FromContext(ctx)
// Create or update organization in Grafana
var organization = grafana.Organization{
ID: grafanaOrganization.Status.OrgID,
Name: grafanaOrganization.Spec.DisplayName,
var organization = &grafana.Organization{
ID: grafanaOrganization.Status.OrgID,
Name: grafanaOrganization.Spec.DisplayName,
TenantID: grafanaOrganization.Name,
}

if organization.ID == 0 {
// if the CR doesn't have an orgID, create the organization in Grafana
organization, err = grafana.CreateOrganization(ctx, r.GrafanaAPI, organization)
organization, err = grafana.CreateOrganization(ctx, r.GrafanaAPI, *organization)
} else {
organization, err = grafana.UpdateOrganization(ctx, r.GrafanaAPI, organization)
organization, err = grafana.UpdateOrganization(ctx, r.GrafanaAPI, *organization)
}

if err != nil {
return ctrl.Result{}, err
return errors.WithStack(err)
}

// Update CR status if anything was changed
if organization.ID != grafanaOrganization.Status.OrgID {
if grafanaOrganization.Status.OrgID != organization.ID {
logger.Info("updating orgID in the grafanaOrganization status")
grafanaOrganization.Status.OrgID = organization.ID

if err = r.Status().Update(ctx, grafanaOrganization); err != nil {
logger.Error(err, "failed to update grafanaOrganization status")
return ctrl.Result{}, errors.WithStack(err)
return errors.WithStack(err)
}
logger.Info("updated orgID in the grafanaOrganization status")
}

// TODO add datasources for the organization.
return nil
}

func (r GrafanaOrganizationReconciler) configureDatasources(ctx context.Context, grafanaOrganization *v1alpha1.GrafanaOrganization) error {
logger := log.FromContext(ctx)

logger.Info("configuring data sources")

err = r.configureGrafana(ctx)
// Create or update organization in Grafana
var organization = grafana.Organization{
ID: grafanaOrganization.Status.OrgID,
Name: grafanaOrganization.Spec.DisplayName,
TenantID: grafanaOrganization.Name,
}

datasources, err := grafana.ConfigureDefaultDatasources(ctx, r.GrafanaAPI, organization)
if err != nil {
return ctrl.Result{}, errors.WithStack(err)
return errors.WithStack(err)
}
return ctrl.Result{}, nil

var configuredDatasources = make([]v1alpha1.DataSource, len(datasources))
for i, datasource := range datasources {
configuredDatasources[i] = v1alpha1.DataSource{
ID: datasource.ID,
Name: datasource.Name,
}
}

logger.Info("updating datasources in the grafanaOrganization status")
grafanaOrganization.Status.DataSources = configuredDatasources
if err := r.Status().Update(ctx, grafanaOrganization); err != nil {
logger.Error(err, "failed to update the the grafanaOrganization status with datasources information")
return errors.WithStack(err)
}
logger.Info("updated datasources in the grafanaOrganization status")
logger.Info("configured data sources")

return nil
}

// reconcileDelete deletes the grafana organization.
Expand Down Expand Up @@ -199,67 +300,41 @@ func (r GrafanaOrganizationReconciler) reconcileDelete(ctx context.Context, graf
return nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *GrafanaOrganizationReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.GrafanaOrganization{}).
// Watch for grafana pod's status changes
Watches(
&v1.Pod{},
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
k8sClient := mgr.GetClient()
var organizations v1alpha1.GrafanaOrganizationList

err := k8sClient.List(ctx, &organizations)
if err != nil {
log.FromContext(ctx).Error(err, "failed to list grafana organization CRs")
return []reconcile.Request{}
}

// Reconcile all grafana organizations when the grafana pod is recreated
requests := make([]reconcile.Request, 0, len(organizations.Items))
for _, organization := range organizations.Items {
requests = append(requests, reconcile.Request{
NamespacedName: types.NamespacedName{
Name: organization.Name,
},
})
}
return requests
}),
builder.WithPredicates(predicates.GrafanaPodRecreatedPredicate{}),
).
Complete(r)
}

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

organizations := v1alpha1.GrafanaOrganizationList{}
err := r.Client.List(ctx, &organizations)
organizationList := v1alpha1.GrafanaOrganizationList{}
err := r.Client.List(ctx, &organizationList)
if err != nil {
logger.Error(err, "failed to list grafana organizations.")
return errors.WithStack(err)
}

grafanaConfig := v1.ConfigMap{
grafanaConfig := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "grafana-user-values",
Namespace: "giantswarm",
},
}

_, err = controllerutil.CreateOrPatch(ctx, r.Client, &grafanaConfig, func() error {
config, err := templating.GenerateGrafanaConfiguration(organizations.Items)
_, err = controllerutil.CreateOrPatch(ctx, r.Client, grafanaConfig, func() error {
organizations := organizationList.Items
// We always sort the organizations to ensure the order is deterministic and the configmap is stable
// in order to prevent grafana to restarts.
slices.SortFunc(organizations, func(i, j v1alpha1.GrafanaOrganization) int {
return strings.Compare(i.Name, j.Name)
})

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

for _, organization := range organizations.Items {
for _, organization := range organizations {
// Set owner reference to the config map to be able to clean it up when all organizations are deleted
err = controllerutil.SetOwnerReference(&organization, &grafanaConfig, r.Scheme)
err = controllerutil.SetOwnerReference(&organization, grafanaConfig, r.Scheme)
if err != nil {
return errors.WithStack(err)
}
Expand Down
Loading

0 comments on commit 8748b36

Please sign in to comment.