Provisioning: Expose dashboard config within grafana (#99947)

pull/99956/head
Ryan McKinley 4 months ago committed by GitHub
parent 295c0afc35
commit 8c0b812874
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 41
      pkg/services/provisioning/dashboards/config_reader.go
  2. 14
      pkg/services/provisioning/dashboards/config_reader_test.go
  3. 2
      pkg/services/provisioning/dashboards/dashboard.go
  4. 7
      pkg/services/provisioning/dashboards/types.go
  5. 3
      pkg/services/provisioning/datasources/config_reader.go
  6. 22
      pkg/services/provisioning/utils/utils.go

@ -11,14 +11,45 @@ import (
"gopkg.in/yaml.v3"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/provisioning/utils"
)
type configReader struct {
path string
log log.Logger
orgService org.Service
path string
log log.Logger
orgExists utils.OrgExists
}
func ReadDashboardConfig(dir string) ([]*DashboardProvisioning, error) {
var cfg []*DashboardProvisioning
cr := &configReader{
path: dir,
}
files, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
for _, file := range files {
if !strings.HasSuffix(file.Name(), ".yaml") && !strings.HasSuffix(file.Name(), ".yml") {
continue
}
parsedDashboards, err := cr.parseConfigs(file)
if err != nil {
return nil, fmt.Errorf("could not parse provisioning config file: %s error: %v", file.Name(), err)
}
for _, config := range parsedDashboards {
cfg = append(cfg, &DashboardProvisioning{
config: *config,
})
}
}
return cfg, nil
}
func (cr *configReader) parseConfigs(file fs.DirEntry) ([]*config, error) {
@ -96,7 +127,7 @@ func (cr *configReader) readConfig(ctx context.Context) ([]*config, error) {
dashboard.OrgID = 1
}
if err := utils.CheckOrgExists(ctx, cr.orgService, dashboard.OrgID); err != nil {
if err := cr.orgExists(ctx, dashboard.OrgID); err != nil {
return nil, fmt.Errorf("failed to provision dashboards with %q reader: %w", dashboard.Name, err)
}

@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/org/orgtest"
"github.com/grafana/grafana/pkg/services/provisioning/utils"
)
var (
@ -25,10 +26,11 @@ func TestDashboardsAsConfig(t *testing.T) {
logger := log.New("test-logger")
// store := db.InitTestDB(t)
orgFake := orgtest.NewOrgServiceFake()
orgExists := utils.NewOrgExistsChecker(orgFake)
t.Run("Should fail if orgs don't exist in the database", func(t *testing.T) {
orgFake.ExpectedError = org.ErrOrgNotFound
cfgProvider := configReader{path: appliedDefaults, log: logger, orgService: orgFake}
cfgProvider := configReader{path: appliedDefaults, log: logger, orgExists: orgExists}
_, err := cfgProvider.readConfig(context.Background())
require.Error(t, err)
assert.True(t, errors.Is(err, org.ErrOrgNotFound))
@ -36,7 +38,7 @@ func TestDashboardsAsConfig(t *testing.T) {
})
t.Run("default values should be applied", func(t *testing.T) {
cfgProvider := configReader{path: appliedDefaults, log: logger, orgService: orgFake}
cfgProvider := configReader{path: appliedDefaults, log: logger, orgExists: orgExists}
cfg, err := cfgProvider.readConfig(context.Background())
require.NoError(t, err)
@ -47,7 +49,7 @@ func TestDashboardsAsConfig(t *testing.T) {
t.Run("Can read config file version 1 format", func(t *testing.T) {
t.Setenv("TEST_VAR", "general")
cfgProvider := configReader{path: simpleDashboardConfig, log: logger, orgService: orgFake}
cfgProvider := configReader{path: simpleDashboardConfig, log: logger, orgExists: orgExists}
cfg, err := cfgProvider.readConfig(context.Background())
require.NoError(t, err)
@ -55,7 +57,7 @@ func TestDashboardsAsConfig(t *testing.T) {
})
t.Run("Can read config file in version 0 format", func(t *testing.T) {
cfgProvider := configReader{path: oldVersion, log: logger, orgService: orgFake}
cfgProvider := configReader{path: oldVersion, log: logger, orgExists: orgExists}
cfg, err := cfgProvider.readConfig(context.Background())
require.NoError(t, err)
@ -63,7 +65,7 @@ func TestDashboardsAsConfig(t *testing.T) {
})
t.Run("Should skip invalid path", func(t *testing.T) {
cfgProvider := configReader{path: "/invalid-directory", log: logger, orgService: orgFake}
cfgProvider := configReader{path: "/invalid-directory", log: logger, orgExists: orgExists}
cfg, err := cfgProvider.readConfig(context.Background())
if err != nil {
t.Fatalf("readConfig return an error %v", err)
@ -73,7 +75,7 @@ func TestDashboardsAsConfig(t *testing.T) {
})
t.Run("Should skip broken config files", func(t *testing.T) {
cfgProvider := configReader{path: brokenConfigs, log: logger, orgService: orgFake}
cfgProvider := configReader{path: brokenConfigs, log: logger, orgExists: orgExists}
cfg, err := cfgProvider.readConfig(context.Background())
if err != nil {
t.Fatalf("readConfig return an error %v", err)

@ -42,7 +42,7 @@ func (provider *Provisioner) HasDashboardSources() bool {
// New returns a new DashboardProvisioner
func New(ctx context.Context, configDirectory string, provisioner dashboards.DashboardProvisioningService, orgService org.Service, dashboardStore utils.DashboardStore, folderService folder.Service) (DashboardProvisioner, error) {
logger := log.New("provisioning.dashboard")
cfgReader := &configReader{path: configDirectory, log: logger, orgService: orgService}
cfgReader := &configReader{path: configDirectory, log: logger, orgExists: utils.NewOrgExistsChecker(orgService)}
configs, err := cfgReader.readConfig(ctx)
if err != nil {
return nil, fmt.Errorf("%v: %w", "Failed to read dashboards config", err)

@ -36,6 +36,13 @@ type configV0 struct {
AllowUIUpdates bool `json:"allowUiUpdates" yaml:"allowUiUpdates"`
}
// Access to dashboard provisioning config
// Exposes the internal config outside this package with as few changes as possible
// NOTE: these provisioning configs will eventually be replaced with: /apis/provisioning.grafana.app/
type DashboardProvisioning struct {
config
}
type configVersion struct {
APIVersion int64 `json:"apiVersion" yaml:"apiVersion"`
}

@ -131,7 +131,8 @@ func (cr *configReader) validateDefaultUniqueness(ctx context.Context, datasourc
}
func (cr *configReader) validateAccessAndOrgID(ctx context.Context, ds *upsertDataSourceFromConfig) error {
if err := utils.CheckOrgExists(ctx, cr.orgService, ds.OrgID); err != nil {
checker := utils.NewOrgExistsChecker(cr.orgService)
if err := checker(ctx, ds.OrgID); err != nil {
return err
}

@ -13,14 +13,20 @@ type DashboardStore interface {
GetDashboard(context.Context, *dashboards.GetDashboardQuery) (*dashboards.Dashboard, error)
}
func CheckOrgExists(ctx context.Context, orgService org.Service, orgID int64) error {
query := org.GetOrgByIDQuery{ID: orgID}
_, err := orgService.GetByID(ctx, &query)
if err != nil {
if errors.Is(err, org.ErrOrgNotFound) {
return err
// Throw an error if the org does not exist
type OrgExists = func(ctx context.Context, orgID int64) error
// Use the org service to check if an org exists
func NewOrgExistsChecker(orgService org.Service) OrgExists {
return func(ctx context.Context, orgID int64) error {
query := org.GetOrgByIDQuery{ID: orgID}
_, err := orgService.GetByID(ctx, &query)
if err != nil {
if errors.Is(err, org.ErrOrgNotFound) {
return err
}
return fmt.Errorf("failed to check whether org. with the given ID exists: %w", err)
}
return fmt.Errorf("failed to check whether org. with the given ID exists: %w", err)
return nil
}
return nil
}

Loading…
Cancel
Save