From 4e786196a95c9b4fdf52622c43923d1e48f78dce Mon Sep 17 00:00:00 2001 From: QuentinBisson Date: Tue, 10 Dec 2024 12:27:21 +0100 Subject: [PATCH 1/3] add tenant-ids-field --- CHANGELOG.md | 3 ++- api/v1alpha1/grafanaorganization_types.go | 13 ++++++++++++- api/v1alpha1/zz_generated.deepcopy.go | 5 +++++ ...ility.giantswarm.io_grafanaorganizations.yaml | 16 ++++++++++++++-- .../controller/grafanaorganization_controller.go | 9 ++++++++- pkg/grafana/grafana.go | 14 ++++++++++---- 6 files changed, 51 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97724443..ddc81159 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add Mimir Alertmanager datasource +- Add Mimir Alertmanager datasource. +- Add tenant ids field to the grafana organization CR to be able to support multiple tenants into one organization. ### Changed diff --git a/api/v1alpha1/grafanaorganization_types.go b/api/v1alpha1/grafanaorganization_types.go index e050c286..aebfe810 100644 --- a/api/v1alpha1/grafanaorganization_types.go +++ b/api/v1alpha1/grafanaorganization_types.go @@ -32,9 +32,19 @@ type GrafanaOrganizationSpec struct { DisplayName string `json:"displayName"` // Access rules defines user permissions for interacting with the organization in Grafana. - RBAC *RBAC `json:"rbac,omitempty"` + RBAC *RBAC `json:"rbac"` + + // Tenants is a list of tenants that are associated with the Grafana organization. + // +kubebuilder:example={"giantswarm"} + Tenants []TenantID `json:"tenants,omitempty"` } +// TenantID is a unique identifier for a tenant. It must be lowercase. +// +kubebuilder:validation:Pattern="^[a-z]*$" +// +kubebuilder:validation:MinLength=1 +// +kubebuilder:validation:MaxLength=63 +type TenantID string + // RBAC defines the RoleBasedAccessControl configuration for the Grafana organization. // Each fields represents the mapping to a Grafana role: // @@ -62,6 +72,7 @@ type RBAC struct { // GrafanaOrganizationStatus defines the observed state of GrafanaOrganization type GrafanaOrganizationStatus struct { // OrgID is the actual organisation ID in grafana. + // +optional OrgID int64 `json:"orgID"` // DataSources is a list of grafana data sources that are available to the Grafana organization. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ca1a92cd..1cfc51aa 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -106,6 +106,11 @@ func (in *GrafanaOrganizationSpec) DeepCopyInto(out *GrafanaOrganizationSpec) { *out = new(RBAC) (*in).DeepCopyInto(*out) } + if in.Tenants != nil { + in, out := &in.Tenants, &out.Tenants + *out = make([]TenantID, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GrafanaOrganizationSpec. diff --git a/config/crd/observability.giantswarm.io_grafanaorganizations.yaml b/config/crd/observability.giantswarm.io_grafanaorganizations.yaml index 08c7673e..31927d97 100644 --- a/config/crd/observability.giantswarm.io_grafanaorganizations.yaml +++ b/config/crd/observability.giantswarm.io_grafanaorganizations.yaml @@ -76,8 +76,22 @@ spec: required: - admins type: object + tenants: + description: Tenants is a list of tenants that are associated with + the Grafana organization. + example: + - giantswarm + items: + description: TenantID is a unique identifier for a tenant. It must + be lowercase. + maxLength: 63 + minLength: 1 + pattern: ^[a-z]*$ + type: string + type: array required: - displayName + - rbac type: object status: description: GrafanaOrganizationStatus defines the observed state of GrafanaOrganization @@ -104,8 +118,6 @@ spec: description: OrgID is the actual organisation ID in grafana. format: int64 type: integer - required: - - orgID type: object type: object served: true diff --git a/internal/controller/grafanaorganization_controller.go b/internal/controller/grafanaorganization_controller.go index c04c1d80..ca52bea4 100644 --- a/internal/controller/grafanaorganization_controller.go +++ b/internal/controller/grafanaorganization_controller.go @@ -305,9 +305,16 @@ func (r GrafanaOrganizationReconciler) reconcileDelete(ctx context.Context, graf return nil } + // Delete organization in Grafana + var organization = grafana.Organization{ + ID: grafanaOrganization.Status.OrgID, + Name: grafanaOrganization.Spec.DisplayName, + TenantID: grafanaOrganization.Name, + } + // Delete organization in Grafana if it exists if grafanaOrganization.Status.OrgID > 0 { - err := grafana.DeleteByID(ctx, r.GrafanaAPI, grafanaOrganization.Status.OrgID) + err := grafana.DeleteOrganization(ctx, r.GrafanaAPI, organization) if err != nil { return errors.WithStack(err) } diff --git a/pkg/grafana/grafana.go b/pkg/grafana/grafana.go index 5c3e0b30..3c7ffe11 100644 --- a/pkg/grafana/grafana.go +++ b/pkg/grafana/grafana.go @@ -131,16 +131,22 @@ func UpdateOrganization(ctx context.Context, grafanaAPI *client.GrafanaHTTPAPI, return &organization, nil } -func DeleteByID(ctx context.Context, grafanaAPI *client.GrafanaHTTPAPI, id int64) error { +func DeleteOrganization(ctx context.Context, grafanaAPI *client.GrafanaHTTPAPI, organization Organization) error { logger := log.FromContext(ctx) logger.Info("deleting organization") - _, err := findByID(grafanaAPI, id) + _, err := findByID(grafanaAPI, organization.ID) if err != nil { - logger.Error(err, fmt.Sprintf("failed to find organization with ID: %d", id)) + if isNotFound(err) { + logger.Info("organization id was not found, skipping deletion") + // If the CR orgID does not exist in Grafana, then we create the organization + return nil + } + logger.Error(err, fmt.Sprintf("failed to find organization with ID: %d", organization.ID)) + return errors.WithStack(err) } - _, err = grafanaAPI.Orgs.DeleteOrgByID(id) + _, err = grafanaAPI.Orgs.DeleteOrgByID(organization.ID) if err != nil { logger.Error(err, "failed to delete organization") return errors.WithStack(err) From b20e4f3a27993e40bdfc27dfc70adf709019d72f Mon Sep 17 00:00:00 2001 From: Quentin Bisson Date: Tue, 10 Dec 2024 14:07:22 +0100 Subject: [PATCH 2/3] Update CHANGELOG.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hervé Nicol --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddc81159..6488ce45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add Mimir Alertmanager datasource. - Add tenant ids field to the grafana organization CR to be able to support multiple tenants into one organization. +### Fixed + +- Fix grafana organization deletion + ### Changed - Removed organization OwnerReference on grafana-user-values configmap, this fixes an issue where the configmap is removed when the last organization is deleted which prevent Grafana from starting. From a3363b631371e4840297db46c4c189191867eeaf Mon Sep 17 00:00:00 2001 From: Quentin Bisson Date: Tue, 10 Dec 2024 14:07:47 +0100 Subject: [PATCH 3/3] Update CHANGELOG.md --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6488ce45..70bcb316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,14 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add Mimir Alertmanager datasource. - Add tenant ids field to the grafana organization CR to be able to support multiple tenants into one organization. -### Fixed - -- Fix grafana organization deletion - ### Changed - Removed organization OwnerReference on grafana-user-values configmap, this fixes an issue where the configmap is removed when the last organization is deleted which prevent Grafana from starting. +### Fixed + +- Fix grafana organization deletion + ## [0.9.1] - 2024-11-21 ### Fixed