mirror of
https://github.com/turbot/steampipe.git
synced 2025-12-19 09:58:53 -05:00
129 lines
4.8 KiB
Go
129 lines
4.8 KiB
Go
package connection
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
filehelpers "github.com/turbot/go-kit/files"
|
|
"github.com/turbot/go-kit/filewatcher"
|
|
"github.com/turbot/go-kit/helpers"
|
|
"github.com/turbot/pipe-fittings/v2/filepaths"
|
|
"github.com/turbot/steampipe/v2/pkg/cmdconfig"
|
|
"github.com/turbot/steampipe/v2/pkg/constants"
|
|
"github.com/turbot/steampipe/v2/pkg/steampipeconfig"
|
|
)
|
|
|
|
type ConnectionWatcher struct {
|
|
fileWatcherErrorHandler func(error)
|
|
watcher *filewatcher.FileWatcher
|
|
// interface exposing the plugin manager functions we need
|
|
pluginManager pluginManager
|
|
}
|
|
|
|
func NewConnectionWatcher(pluginManager pluginManager) (*ConnectionWatcher, error) {
|
|
w := &ConnectionWatcher{
|
|
pluginManager: pluginManager,
|
|
}
|
|
|
|
configDir := filepaths.EnsureConfigDir()
|
|
log.Printf("[INFO] ConnectionWatcher will watch directory: %s for %s files", configDir, constants.ConfigExtension)
|
|
|
|
watcherOptions := &filewatcher.WatcherOptions{
|
|
Directories: []string{configDir},
|
|
Include: filehelpers.InclusionsFromExtensions([]string{constants.ConfigExtension}),
|
|
ListFlag: filehelpers.FilesRecursive,
|
|
EventMask: fsnotify.Create | fsnotify.Remove | fsnotify.Rename | fsnotify.Write | fsnotify.Chmod,
|
|
OnChange: func(events []fsnotify.Event) {
|
|
log.Printf("[INFO] ConnectionWatcher detected %d file events", len(events))
|
|
for _, event := range events {
|
|
log.Printf("[INFO] ConnectionWatcher event: %s - %s", event.Op, event.Name)
|
|
}
|
|
w.handleFileWatcherEvent(events)
|
|
},
|
|
}
|
|
watcher, err := filewatcher.NewWatcher(watcherOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
w.watcher = watcher
|
|
|
|
// set the file watcher error handler, which will get called when there are parsing errors
|
|
// after a file watcher event
|
|
w.fileWatcherErrorHandler = func(err error) {
|
|
log.Printf("[WARN] failed to reload connection config: %s", err.Error())
|
|
}
|
|
|
|
watcher.Start()
|
|
|
|
log.Printf("[INFO] created ConnectionWatcher")
|
|
return w, nil
|
|
}
|
|
|
|
func (w *ConnectionWatcher) handleFileWatcherEvent([]fsnotify.Event) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
log.Printf("[WARN] ConnectionWatcher caught a panic: %s", helpers.ToError(r).Error())
|
|
}
|
|
}()
|
|
// this is a file system event handler and not bound to any context
|
|
ctx := context.Background()
|
|
|
|
log.Printf("[INFO] ConnectionWatcher handleFileWatcherEvent")
|
|
config, errorsAndWarnings := steampipeconfig.LoadConnectionConfig(context.Background())
|
|
// send notification if there were any errors or warnings
|
|
if !errorsAndWarnings.Empty() {
|
|
w.pluginManager.SendPostgresErrorsAndWarningsNotification(ctx, errorsAndWarnings)
|
|
// if there was an error return
|
|
if errorsAndWarnings.GetError() != nil {
|
|
log.Printf("[WARN] error loading updated connection config: %v", errorsAndWarnings.GetError())
|
|
return
|
|
}
|
|
}
|
|
|
|
log.Printf("[INFO] loaded updated config")
|
|
|
|
// We need to update the viper config and GlobalConfig
|
|
// as these are both used by RefreshConnectionAndSearchPathsWithLocalClient
|
|
|
|
// set the global steampipe config
|
|
log.Printf("[DEBUG] ConnectionWatcher: setting GlobalConfig")
|
|
steampipeconfig.GlobalConfig = config
|
|
|
|
// call on changed callback - we must call this BEFORE calling refresh connections
|
|
// convert config to format expected by plugin manager
|
|
// (plugin manager cannot reference steampipe config to avoid circular deps)
|
|
log.Printf("[DEBUG] ConnectionWatcher: creating connection config map")
|
|
configMap := NewConnectionConfigMap(config.Connections)
|
|
log.Printf("[DEBUG] ConnectionWatcher: calling OnConnectionConfigChanged with %d connections", len(configMap))
|
|
w.pluginManager.OnConnectionConfigChanged(ctx, configMap, config.PluginsInstances)
|
|
log.Printf("[DEBUG] ConnectionWatcher: OnConnectionConfigChanged complete")
|
|
|
|
// The only configurations from GlobalConfig which have
|
|
// impact during Refresh are Database options and the Connections
|
|
// themselves.
|
|
//
|
|
// It is safe to ignore the Workspace Profile here since this
|
|
// code runs in the plugin-manager and has been started with the
|
|
// install-dir properly set from the active Workspace Profile
|
|
//
|
|
// Workspace Profile does not have any setting which can alter
|
|
// behavior in service mode (namely search path). Therefore, it is safe
|
|
// to use the GlobalConfig here and ignore Workspace Profile in general
|
|
log.Printf("[DEBUG] ConnectionWatcher: calling SetDefaultsFromConfig")
|
|
cmdconfig.SetDefaultsFromConfig(steampipeconfig.GlobalConfig.ConfigMap())
|
|
log.Printf("[DEBUG] ConnectionWatcher: SetDefaultsFromConfig complete")
|
|
|
|
log.Printf("[INFO] calling RefreshConnections asyncronously")
|
|
|
|
// call RefreshConnections asyncronously
|
|
// the RefreshConnections implements its own locking to ensure only a single execution and a single queues execution
|
|
go RefreshConnections(ctx, w.pluginManager)
|
|
|
|
log.Printf("[TRACE] File watch event done")
|
|
}
|
|
|
|
func (w *ConnectionWatcher) Close() {
|
|
w.watcher.Close()
|
|
}
|