mirror of
https://github.com/turbot/steampipe.git
synced 2025-12-19 18:12:43 -05:00
156 lines
5.6 KiB
Go
156 lines
5.6 KiB
Go
package pluginmanager
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os/exec"
|
|
"syscall"
|
|
|
|
"github.com/hashicorp/go-hclog"
|
|
"github.com/hashicorp/go-plugin"
|
|
"github.com/turbot/steampipe-plugin-sdk/v5/logging"
|
|
"github.com/turbot/steampipe/pkg/constants"
|
|
"github.com/turbot/steampipe/pkg/constants/runtime"
|
|
"github.com/turbot/steampipe/pkg/filepaths"
|
|
pb "github.com/turbot/steampipe/pluginmanager_service/grpc/proto"
|
|
pluginshared "github.com/turbot/steampipe/pluginmanager_service/grpc/shared"
|
|
)
|
|
|
|
// StartNewInstance loads the plugin manager state, stops any previous instance and instantiates a new plugin manager
|
|
func StartNewInstance(steampipeExecutablePath string) error {
|
|
// try to load the plugin manager state
|
|
state, err := LoadPluginManagerState()
|
|
if err != nil {
|
|
log.Printf("[WARN] plugin manager StartNewInstance() - load state failed: %s", err)
|
|
return err
|
|
}
|
|
|
|
if state.Running {
|
|
log.Printf("[TRACE] plugin manager StartNewInstance() found previous instance of plugin manager still running - stopping it")
|
|
// stop the current instance
|
|
if err := stop(state); err != nil {
|
|
log.Printf("[WARN] failed to stop previous instance of plugin manager: %s", err)
|
|
return err
|
|
}
|
|
}
|
|
return start(steampipeExecutablePath)
|
|
}
|
|
|
|
// start plugin manager, without checking it is already running
|
|
// we need to be provided with the exe path as we have no way of knowing where the steampipe exe it
|
|
// when ther plugin mananager is first started by steampipe, we derive the exe path from the running process and
|
|
// store it in the plugin manager state file - then if the fdw needs to start the plugin manager it knows how to
|
|
func start(steampipeExecutablePath string) error {
|
|
// note: we assume the install dir has been assigned to file_paths.SteampipeDir
|
|
// - this is done both by the FDW and Steampipe
|
|
pluginManagerCmd := exec.Command(steampipeExecutablePath,
|
|
"plugin-manager",
|
|
"--"+constants.ArgInstallDir, filepaths.SteampipeDir,
|
|
"--"+constants.ArgAppName, runtime.PgClientAppName)
|
|
// set attributes on the command to ensure the process is not shutdown when its parent terminates
|
|
pluginManagerCmd.SysProcAttr = &syscall.SysProcAttr{
|
|
Setpgid: true,
|
|
}
|
|
|
|
// discard logging from the plugin manager client (plugin manager logs will still flow through to the log file
|
|
// as this is set up in the pluginb manager)
|
|
logger := logging.NewLogger(&hclog.LoggerOptions{Name: "plugin", Output: io.Discard})
|
|
|
|
// launch the plugin manager the plugin process
|
|
client := plugin.NewClient(&plugin.ClientConfig{
|
|
HandshakeConfig: pluginshared.Handshake,
|
|
Plugins: pluginshared.PluginMap,
|
|
Cmd: pluginManagerCmd,
|
|
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
|
|
Logger: logger,
|
|
})
|
|
if _, err := client.Start(); err != nil {
|
|
log.Printf("[WARN] plugin manager start() failed to start GRPC client for plugin manager: %s", err)
|
|
return err
|
|
}
|
|
|
|
// create a plugin manager state.
|
|
state := NewPluginManagerState(steampipeExecutablePath, client.ReattachConfig())
|
|
|
|
log.Printf("[TRACE] start: started plugin manager, pid %d", state.Pid)
|
|
|
|
// now save the state
|
|
return state.Save()
|
|
}
|
|
|
|
// Stop loads the plugin manager state and if a running instance is found, stop it
|
|
func Stop() error {
|
|
// try to load the plugin manager state
|
|
state, err := LoadPluginManagerState()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if state == nil || !state.Running {
|
|
// nothing to do
|
|
return nil
|
|
}
|
|
return stop(state)
|
|
}
|
|
|
|
// stop the running plugin manager instance
|
|
func stop(state *PluginManagerState) error {
|
|
log.Printf("[INFO] plugin manager stop")
|
|
pluginManager, err := NewPluginManagerClient(state)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("[TRACE] pluginManager.Shutdown")
|
|
// tell plugin manager to kill all plugins
|
|
_, err = pluginManager.Shutdown(&pb.ShutdownRequest{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Printf("[TRACE] pluginManager state.kill")
|
|
// now kill the plugin manager
|
|
return state.kill()
|
|
}
|
|
|
|
// GetPluginManager connects to a running plugin manager
|
|
func GetPluginManager() (pluginshared.PluginManager, error) {
|
|
return getPluginManager(true)
|
|
}
|
|
|
|
// getPluginManager determines whether the plugin manager is running
|
|
// if not,and if startIfNeeded is true, it starts the manager
|
|
// it then returns a plugin manager client
|
|
func getPluginManager(startIfNeeded bool) (pluginshared.PluginManager, error) {
|
|
// try to load the plugin manager state
|
|
state, err := LoadPluginManagerState()
|
|
if err != nil {
|
|
log.Printf("[WARN] failed to load plugin manager state: %s", err.Error())
|
|
return nil, err
|
|
}
|
|
// if we did not load it and there was no error, it means the plugin manager is not running
|
|
// we cannot start it as we do not know the correct steampipe exe path - which is stored in the state
|
|
// this is not expected - we would expect the plugin manager to have been started with the datatbase
|
|
if state == nil {
|
|
return nil, fmt.Errorf("plugin manager is not running and there is no state file")
|
|
}
|
|
// if the plugin manager is not running, it must have crashed/terminated
|
|
if !state.Running {
|
|
log.Printf("[TRACE] GetPluginManager called but plugin manager not running")
|
|
// is we are not already recursing, start the plugin manager then recurse back into this function
|
|
if startIfNeeded {
|
|
log.Printf("[TRACE] calling StartNewInstance()")
|
|
// start the plugin manager
|
|
if err := start(state.Executable); err != nil {
|
|
return nil, err
|
|
}
|
|
// recurse in, setting startIfNeeded to false to avoid further recursion on failure
|
|
return getPluginManager(false)
|
|
}
|
|
// not retrying - just fail
|
|
return nil, fmt.Errorf("plugin manager is not running")
|
|
}
|
|
log.Printf("[TRACE] plugin manager is running - returning client")
|
|
return NewPluginManagerClient(state)
|
|
}
|