Skip to content

Commit

Permalink
Fix organization creation (#250)
Browse files Browse the repository at this point in the history
* Fix organizations to always be processed and not returning an error when name is already taken

- Update notFound to match againt runtime.APIError
- Remove assertNameIsAvailable

* only get org by id
  • Loading branch information
TheoBrigitte authored Feb 7, 2025
1 parent b74e054 commit 51f44b9
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 34 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Updated the notFound method to match error using runtime.APIError type and the status code
- Update Grafana pod predicate to not trigger on pod deletion
- Ensure organization is created before proceeding with datasources and sso settings
- Remove error handling when the organization name is already taken, this is handled by the Grafana API

### Fixed

- Fix `failed to find organization with ID: 0` error when creating a new organization
- Fix `getOrgByIdForbidden` error when creating a new organization

### Fixes

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/blang/semver v3.5.1+incompatible
github.com/giantswarm/apiextensions-application v0.6.2
github.com/go-logr/logr v1.4.2
github.com/go-openapi/runtime v0.28.0
github.com/grafana/grafana-openapi-client-go v0.0.0-20250108132429-8d7e1f158f65
github.com/onsi/ginkgo/v2 v2.22.2
github.com/onsi/gomega v1.36.2
Expand Down Expand Up @@ -95,7 +96,6 @@ require (
github.com/go-openapi/analysis v0.23.0 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/loads v0.22.0 // indirect
github.com/go-openapi/runtime v0.28.0 // indirect
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
Expand Down
58 changes: 25 additions & 33 deletions pkg/grafana/grafana.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"context"
_ "embed"
"fmt"
"net/http"
"strconv"
"strings"

"github.com/go-openapi/runtime"
"github.com/grafana/grafana-openapi-client-go/client"
"github.com/grafana/grafana-openapi-client-go/models"
"github.com/pkg/errors"
Expand All @@ -17,6 +18,8 @@ const (
datasourceProxyAccessMode = "proxy"
)

var orgNotFoundError = errors.New("organization not found")

var SharedOrg = Organization{
ID: 1,
Name: "Shared Org",
Expand Down Expand Up @@ -61,20 +64,17 @@ var defaultDatasources = []Datasource{
},
}

func UpsertOrganization(ctx context.Context, grafanaAPI *client.GrafanaHTTPAPI, organization *Organization) error {
func UpsertOrganization(ctx context.Context, grafanaAPI *client.GrafanaHTTPAPI, organization *Organization) (err error) {
logger := log.FromContext(ctx)

err := assertNameIsAvailable(ctx, grafanaAPI, organization)
if err != nil {
return errors.WithStack(err)
}

logger.Info("upserting organization")
found, err := findOrgByID(grafanaAPI, organization.ID)

// Get the current organization stored in Grafana
currentOrganization, err := findOrgByID(grafanaAPI, organization.ID)
if err != nil {
if isNotFound(err) {
if errors.Is(err, orgNotFoundError) {
logger.Info("organization id not found, creating")
// If the CR orgID does not exist in Grafana, then we create the organization

// If organization does not exist in Grafana, create it
createdOrg, err := grafanaAPI.Orgs.CreateOrg(&models.CreateOrgCommand{
Name: organization.Name,
})
Expand All @@ -87,12 +87,13 @@ func UpsertOrganization(ctx context.Context, grafanaAPI *client.GrafanaHTTPAPI,
organization.ID = *createdOrg.Payload.OrgID
return nil
}

logger.Error(err, fmt.Sprintf("failed to find organization with ID: %d", organization.ID))
return errors.WithStack(err)
}

// If both name matches, there is nothing to do.
if found.Name == organization.Name {
if currentOrganization.Name == organization.Name {
logger.Info("the organization already exists in Grafana and does not need to be updated.")
return nil
}
Expand Down Expand Up @@ -246,29 +247,12 @@ func isNotFound(err error) bool {
return false
}

// Parsing error message to find out the error code
return strings.Contains(err.Error(), "(status 404)")
}

// assertNameIsAvailable is a helper function to check if the organization name is available in Grafana
func assertNameIsAvailable(ctx context.Context, grafanaAPI *client.GrafanaHTTPAPI, organization *Organization) error {
logger := log.FromContext(ctx)

found, err := FindOrgByName(grafanaAPI, organization.Name)
if err != nil {
// We only error if we have any error other than a 404
if !isNotFound(err) {
logger.Error(err, fmt.Sprintf("failed to find organization with name: %s", organization.Name))
return errors.WithStack(err)
}

if found != nil {
logger.Error(err, "a grafana organization with the same name already exists. Please choose a different display name.")
return errors.WithStack(err)
}
var apiErr *runtime.APIError
if errors.As(err, &apiErr) {
return apiErr.IsCode(http.StatusNotFound)
}

return nil
return false
}

// FindOrgByName is a wrapper function used to find a Grafana organization by its name
Expand All @@ -286,8 +270,16 @@ func FindOrgByName(grafanaAPI *client.GrafanaHTTPAPI, name string) (*Organizatio

// findOrgByID is a wrapper function used to find a Grafana organization by its id
func findOrgByID(grafanaAPI *client.GrafanaHTTPAPI, orgID int64) (*Organization, error) {
if orgID == 0 {
return nil, orgNotFoundError
}

organization, err := grafanaAPI.Orgs.GetOrgByID(orgID)
if err != nil {
if isNotFound(err) {
return nil, fmt.Errorf("%w: %w", orgNotFoundError, err)
}

return nil, errors.WithStack(err)
}

Expand Down

0 comments on commit 51f44b9

Please sign in to comment.