@ -23,9 +23,9 @@ var (
ErrFolderNameMissing = errors . New ( "folder name missing" )
)
// FileReader is responsible for reading dashboards from disc and
// FileReader is responsible for reading dashboards from disk and
// insert/update dashboards to the Grafana database using
// `dashboards.DashboardProvisioningService`
// `dashboards.DashboardProvisioningService`.
type FileReader struct {
Cfg * config
Path string
@ -61,13 +61,13 @@ func NewDashboardFileReader(cfg *config, log log.Logger) (*FileReader, error) {
} , nil
}
// pollChanges periodically runs startWalking Disk based on interval specified in the config.
// pollChanges periodically runs walk Disk based on interval specified in the config.
func ( fr * FileReader ) pollChanges ( ctx context . Context ) {
ticker := time . NewTicker ( time . Duration ( int64 ( time . Second ) * fr . Cfg . UpdateIntervalSeconds ) )
for {
select {
case <- ticker . C :
if err := fr . startWalking Disk( ) ; err != nil {
if err := fr . walk Disk( ) ; err != nil {
fr . log . Error ( "failed to search for dashboards" , "error" , err )
}
case <- ctx . Done ( ) :
@ -76,23 +76,23 @@ func (fr *FileReader) pollChanges(ctx context.Context) {
}
}
// startWalkingDisk traverses the file system for defined path, reads dashboard definition files and applies any change
// to the database.
func ( fr * FileReader ) startWalking Disk( ) error {
// walkDisk traverses the file system for the defined path, reading dashboard definition files,
// and applies any change to the database.
func ( fr * FileReader ) walk Disk( ) error {
fr . log . Debug ( "Start walking disk" , "path" , fr . Path )
resolvedPath := fr . resolvedPath ( )
if _ , err := os . Stat ( resolvedPath ) ; err != nil {
return err
}
provisionedDashboardRefs , err := getProvisionedDashboardByPath ( fr . dashboardProvisioningService , fr . Cfg . Name )
provisionedDashboardRefs , err := getProvisionedDashboards ByPath ( fr . dashboardProvisioningService , fr . Cfg . Name )
if err != nil {
return err
}
// Find relevant files
filesFoundOnDisk := map [ string ] os . FileInfo { }
err = filepath . Walk ( resolvedPath , createWalkFn ( filesFoundOnDisk ) )
if err != nil {
if err := filepath . Walk ( resolvedPath , createWalkFn ( filesFoundOnDisk ) ) ; err != nil {
return err
}
@ -115,7 +115,8 @@ func (fr *FileReader) startWalkingDisk() error {
}
// storeDashboardsInFolder saves dashboards from the filesystem on disk to the folder from config
func ( fr * FileReader ) storeDashboardsInFolder ( filesFoundOnDisk map [ string ] os . FileInfo , dashboardRefs map [ string ] * models . DashboardProvisioning , sanityChecker * provisioningSanityChecker ) error {
func ( fr * FileReader ) storeDashboardsInFolder ( filesFoundOnDisk map [ string ] os . FileInfo ,
dashboardRefs map [ string ] * models . DashboardProvisioning , sanityChecker * provisioningSanityChecker ) error {
folderID , err := getOrCreateFolderID ( fr . Cfg , fr . dashboardProvisioningService , fr . Cfg . Folder )
if err != nil && ! errors . Is ( err , ErrFolderNameMissing ) {
return err
@ -124,17 +125,20 @@ func (fr *FileReader) storeDashboardsInFolder(filesFoundOnDisk map[string]os.Fil
// save dashboards based on json files
for path , fileInfo := range filesFoundOnDisk {
provisioningMetadata , err := fr . saveDashboard ( path , folderID , fileInfo , dashboardRefs )
sanityChecker . track ( provisioningMetadata )
if err != nil {
fr . log . Error ( "failed to save dashboard" , "error" , err )
continue
}
sanityChecker . track ( provisioningMetadata )
}
return nil
}
// storeDashboardsInFoldersFromFilesystemStructure saves dashboards from the filesystem on disk to the same folder
// in grafana as they are in on the filesystem
func ( fr * FileReader ) storeDashboardsInFoldersFromFileStructure ( filesFoundOnDisk map [ string ] os . FileInfo , dashboardRefs map [ string ] * models . DashboardProvisioning , resolvedPath string , sanityChecker * provisioningSanityChecker ) error {
// in Grafana as they are in on the filesystem.
func ( fr * FileReader ) storeDashboardsInFoldersFromFileStructure ( filesFoundOnDisk map [ string ] os . FileInfo ,
dashboardRefs map [ string ] * models . DashboardProvisioning , resolvedPath string , sanityChecker * provisioningSanityChecker ) error {
for path , fileInfo := range filesFoundOnDisk {
folderName := ""
@ -158,20 +162,21 @@ func (fr *FileReader) storeDashboardsInFoldersFromFileStructure(filesFoundOnDisk
}
// handleMissingDashboardFiles will unprovision or delete dashboards which are missing on disk.
func ( fr * FileReader ) handleMissingDashboardFiles ( provisionedDashboardRefs map [ string ] * models . DashboardProvisioning , filesFoundOnDisk map [ string ] os . FileInfo ) {
func ( fr * FileReader ) handleMissingDashboardFiles ( provisionedDashboardRefs map [ string ] * models . DashboardProvisioning ,
filesFoundOnDisk map [ string ] os . FileInfo ) {
// find dashboards to delete since json file is missing
var dashboardToDelete [ ] int64
var dashboards ToDelete [ ] int64
for path , provisioningData := range provisionedDashboardRefs {
_ , existsOnDisk := filesFoundOnDisk [ path ]
if ! existsOnDisk {
dashboardToDelete = append ( dashboardToDelete , provisioningData . DashboardId )
dashboards ToDelete = append ( dashboards ToDelete , provisioningData . DashboardId )
}
}
if fr . Cfg . DisableDeletion {
// If deletion is disabled for the provisioner we just remove provisioning metadata about the dashboard
// so afterwards the dashboard is considered unprovisioned.
for _ , dashboardID := range dashboardToDelete {
for _ , dashboardID := range dashboards ToDelete {
fr . log . Debug ( "unprovisioning provisioned dashboard. missing on disk" , "id" , dashboardID )
err := fr . dashboardProvisioningService . UnprovisionDashboard ( dashboardID )
if err != nil {
@ -179,9 +184,9 @@ func (fr *FileReader) handleMissingDashboardFiles(provisionedDashboardRefs map[s
}
}
} else {
// delete dashboard that are missing json file
for _ , dashboardID := range dashboardToDelete {
fr . log . Debug ( "deleting provisioned dashboard. missing on disk" , "id" , dashboardID )
// delete dashboards missing JSON file
for _ , dashboardID := range dashboards ToDelete {
fr . log . Debug ( "deleting provisioned dashboard, missing on disk" , "id" , dashboardID )
err := fr . dashboardProvisioningService . DeleteProvisionedDashboard ( dashboardID , fr . Cfg . OrgID )
if err != nil {
fr . log . Error ( "failed to delete dashboard" , "id" , dashboardID , "error" , err )
@ -191,7 +196,8 @@ func (fr *FileReader) handleMissingDashboardFiles(provisionedDashboardRefs map[s
}
// saveDashboard saves or updates the dashboard provisioning file at path.
func ( fr * FileReader ) saveDashboard ( path string , folderID int64 , fileInfo os . FileInfo , provisionedDashboardRefs map [ string ] * models . DashboardProvisioning ) ( provisioningMetadata , error ) {
func ( fr * FileReader ) saveDashboard ( path string , folderID int64 , fileInfo os . FileInfo ,
provisionedDashboardRefs map [ string ] * models . DashboardProvisioning ) ( provisioningMetadata , error ) {
provisioningMetadata := provisioningMetadata { }
resolvedFileInfo , err := resolveSymlink ( fileInfo , path )
if err != nil {
@ -211,7 +217,7 @@ func (fr *FileReader) saveDashboard(path string, folderID int64, fileInfo os.Fil
upToDate = true
}
// keeps track of what uid's and title' s we have already provisioned
// keeps track of which UIDs and title s we have already provisioned
dash := jsonFile . dashboard
provisioningMetadata . uid = dash . Dashboard . Uid
provisioningMetadata . identity = dashboardIdentity { title : dash . Dashboard . Title , folderID : dash . Dashboard . FolderId }
@ -241,7 +247,8 @@ func (fr *FileReader) saveDashboard(path string, folderID int64, fileInfo os.Fil
return provisioningMetadata , err
}
func getProvisionedDashboardByPath ( service dashboards . DashboardProvisioningService , name string ) ( map [ string ] * models . DashboardProvisioning , error ) {
func getProvisionedDashboardsByPath ( service dashboards . DashboardProvisioningService , name string ) (
map [ string ] * models . DashboardProvisioning , error ) {
arr , err := service . GetProvisionedDashboardData ( name )
if err != nil {
return nil , err