Provisioning: Expose dashboard config within grafana (#99947)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user