mirror of
https://github.com/turbot/steampipe.git
synced 2025-12-19 18:12:43 -05:00
remove dashboard service from service cmd
This commit is contained in:
152
cmd/service.go
152
cmd/service.go
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/turbot/steampipe-plugin-sdk/v5/sperr"
|
"github.com/turbot/steampipe-plugin-sdk/v5/sperr"
|
||||||
"github.com/turbot/steampipe/pkg/cmdconfig"
|
"github.com/turbot/steampipe/pkg/cmdconfig"
|
||||||
"github.com/turbot/steampipe/pkg/constants"
|
"github.com/turbot/steampipe/pkg/constants"
|
||||||
"github.com/turbot/steampipe/pkg/dashboard/dashboardserver"
|
|
||||||
"github.com/turbot/steampipe/pkg/db/db_local"
|
"github.com/turbot/steampipe/pkg/db/db_local"
|
||||||
"github.com/turbot/steampipe/pkg/display"
|
"github.com/turbot/steampipe/pkg/display"
|
||||||
"github.com/turbot/steampipe/pkg/error_helpers"
|
"github.com/turbot/steampipe/pkg/error_helpers"
|
||||||
@@ -69,20 +68,9 @@ connection from any Postgres compatible database client.`,
|
|||||||
AddStringFlag(constants.ArgServicePassword, "", "Set the database password for this session").
|
AddStringFlag(constants.ArgServicePassword, "", "Set the database password for this session").
|
||||||
// default is false and hides the database user password from service start prompt
|
// default is false and hides the database user password from service start prompt
|
||||||
AddBoolFlag(constants.ArgServiceShowPassword, false, "View database password for connecting from another machine").
|
AddBoolFlag(constants.ArgServiceShowPassword, false, "View database password for connecting from another machine").
|
||||||
// dashboard server
|
|
||||||
AddBoolFlag(constants.ArgDashboard, false, "Run the dashboard webserver with the service").
|
|
||||||
AddStringFlag(constants.ArgDashboardListen, string(dashboardserver.ListenTypeNetwork), "Accept connections from: local (localhost only) or network (open) (dashboard)").
|
|
||||||
AddIntFlag(constants.ArgDashboardPort, constants.DashboardServerDefaultPort, "Report server port").
|
|
||||||
// foreground enables the service to run in the foreground - till exit
|
// foreground enables the service to run in the foreground - till exit
|
||||||
AddBoolFlag(constants.ArgForeground, false, "Run the service in the foreground").
|
AddBoolFlag(constants.ArgForeground, false, "Run the service in the foreground").
|
||||||
|
|
||||||
// flags relevant only if the --dashboard arg is used:
|
|
||||||
AddStringSliceFlag(constants.ArgVarFile, nil, "Specify an .spvar file containing variable values (only applies if '--dashboard' flag is also set)").
|
|
||||||
// NOTE: use StringArrayFlag for ArgVariable, not StringSliceFlag
|
|
||||||
// Cobra will interpret values passed to a StringSliceFlag as CSV,
|
|
||||||
// where args passed to StringArrayFlag are not parsed and used raw
|
|
||||||
AddStringArrayFlag(constants.ArgVariable, nil, "Specify the value of a variable (only applies if '--dashboard' flag is also set)").
|
|
||||||
|
|
||||||
// hidden flags for internal use
|
// hidden flags for internal use
|
||||||
AddStringFlag(constants.ArgInvoker, string(constants.InvokerService), "Invoked by \"service\" or \"query\"", cmdconfig.FlagOptions.Hidden())
|
AddStringFlag(constants.ArgInvoker, string(constants.InvokerService), "Invoked by \"service\" or \"query\"", cmdconfig.FlagOptions.Hidden())
|
||||||
|
|
||||||
@@ -179,17 +167,17 @@ func runServiceStartCmd(cmd *cobra.Command, _ []string) {
|
|||||||
error_helpers.FailOnError(invoker.IsValid())
|
error_helpers.FailOnError(invoker.IsValid())
|
||||||
}
|
}
|
||||||
|
|
||||||
startResult, dashboardState, dbServiceStarted := startService(ctx, listenAddresses, port, invoker)
|
startResult, dbServiceStarted := startService(ctx, listenAddresses, port, invoker)
|
||||||
alreadyRunning := !dbServiceStarted
|
alreadyRunning := !dbServiceStarted
|
||||||
|
|
||||||
printStatus(ctx, startResult.DbState, startResult.PluginManagerState, dashboardState, alreadyRunning)
|
printStatus(ctx, startResult.DbState, startResult.PluginManagerState, alreadyRunning)
|
||||||
|
|
||||||
if viper.GetBool(constants.ArgForeground) {
|
if viper.GetBool(constants.ArgForeground) {
|
||||||
runServiceInForeground(ctx)
|
runServiceInForeground(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func startService(ctx context.Context, listenAddresses []string, port int, invoker constants.Invoker) (_ *db_local.StartResult, _ *dashboardserver.DashboardServiceState, dbServiceStarted bool) {
|
func startService(ctx context.Context, listenAddresses []string, port int, invoker constants.Invoker) (_ *db_local.StartResult, dbServiceStarted bool) {
|
||||||
statushooks.Show(ctx)
|
statushooks.Show(ctx)
|
||||||
defer statushooks.Done(ctx)
|
defer statushooks.Done(ctx)
|
||||||
log.Printf("[TRACE] startService - listenAddresses=%q", listenAddresses)
|
log.Printf("[TRACE] startService - listenAddresses=%q", listenAddresses)
|
||||||
@@ -233,25 +221,7 @@ func startService(ctx context.Context, listenAddresses []string, port int, invok
|
|||||||
|
|
||||||
dbServiceStarted = startResult.Status == db_local.ServiceStarted
|
dbServiceStarted = startResult.Status == db_local.ServiceStarted
|
||||||
|
|
||||||
var dashboardState *dashboardserver.DashboardServiceState
|
return startResult, dbServiceStarted
|
||||||
if viper.GetBool(constants.ArgDashboard) {
|
|
||||||
dashboardState, err = dashboardserver.GetDashboardServiceState()
|
|
||||||
if err != nil {
|
|
||||||
tryToStopServices(ctx)
|
|
||||||
exitCode = constants.ExitCodeServiceStartupFailure
|
|
||||||
error_helpers.FailOnError(err)
|
|
||||||
}
|
|
||||||
if dashboardState == nil {
|
|
||||||
dashboardState, err = startDashboardServer(ctx)
|
|
||||||
if err != nil {
|
|
||||||
tryToStopServices(ctx)
|
|
||||||
exitCode = constants.ExitCodeServiceStartupFailure
|
|
||||||
error_helpers.FailOnError(err)
|
|
||||||
}
|
|
||||||
dbServiceStarted = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return startResult, dashboardState, dbServiceStarted
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startServiceAndRefreshConnections(ctx context.Context, listenAddresses []string, port int, invoker constants.Invoker) *db_local.StartResult {
|
func startServiceAndRefreshConnections(ctx context.Context, listenAddresses []string, port int, invoker constants.Invoker) *db_local.StartResult {
|
||||||
@@ -277,44 +247,6 @@ func tryToStopServices(ctx context.Context) {
|
|||||||
if _, err := db_local.StopServices(ctx, false, constants.InvokerService); err != nil {
|
if _, err := db_local.StopServices(ctx, false, constants.InvokerService); err != nil {
|
||||||
error_helpers.ShowError(ctx, err)
|
error_helpers.ShowError(ctx, err)
|
||||||
}
|
}
|
||||||
// stop the dashboard service
|
|
||||||
if err := dashboardserver.StopDashboardService(ctx); err != nil {
|
|
||||||
error_helpers.ShowError(ctx, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func startDashboardServer(ctx context.Context) (*dashboardserver.DashboardServiceState, error) {
|
|
||||||
var dashboardState *dashboardserver.DashboardServiceState
|
|
||||||
var err error
|
|
||||||
|
|
||||||
serverPort := dashboardserver.ListenPort(viper.GetInt(constants.ArgDashboardPort))
|
|
||||||
serverListen := dashboardserver.ListenType(viper.GetString(constants.ArgDashboardListen))
|
|
||||||
|
|
||||||
dashboardState, err = dashboardserver.GetDashboardServiceState()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if dashboardState == nil {
|
|
||||||
// try stopping the previous service
|
|
||||||
// StopDashboardService does nothing if the service is not running
|
|
||||||
err = dashboardserver.StopDashboardService(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// start dashboard service
|
|
||||||
err = dashboardserver.RunForService(ctx, serverListen, serverPort)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// get the updated state
|
|
||||||
dashboardState, err = dashboardserver.GetDashboardServiceState()
|
|
||||||
if err != nil {
|
|
||||||
error_helpers.ShowWarning(fmt.Sprintf("Started Dashboard server, but could not retrieve state: %v", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dashboardState, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServiceInForeground(ctx context.Context) {
|
func runServiceInForeground(ctx context.Context) {
|
||||||
@@ -342,7 +274,6 @@ func runServiceInForeground(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
case <-sigIntChannel:
|
case <-sigIntChannel:
|
||||||
fmt.Print("\r")
|
fmt.Print("\r")
|
||||||
dashboardserver.StopDashboardService(ctx)
|
|
||||||
// if we have received this signal, then the user probably wants to shut down
|
// if we have received this signal, then the user probably wants to shut down
|
||||||
// everything. Shutdowns MUST NOT happen in cancellable contexts
|
// everything. Shutdowns MUST NOT happen in cancellable contexts
|
||||||
connectedClients, err := db_local.GetClientCount(context.Background())
|
connectedClients, err := db_local.GetClientCount(context.Background())
|
||||||
@@ -387,14 +318,14 @@ func runServiceRestartCmd(cmd *cobra.Command, _ []string) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
dbStartResult, currentDashboardState := restartService(ctx)
|
dbStartResult := restartService(ctx)
|
||||||
|
|
||||||
if dbStartResult != nil {
|
if dbStartResult != nil {
|
||||||
printStatus(ctx, dbStartResult.DbState, dbStartResult.PluginManagerState, currentDashboardState, false)
|
printStatus(ctx, dbStartResult.DbState, dbStartResult.PluginManagerState, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restartService(ctx context.Context) (_ *db_local.StartResult, _ *dashboardserver.DashboardServiceState) {
|
func restartService(ctx context.Context) (_ *db_local.StartResult) {
|
||||||
statushooks.Show(ctx)
|
statushooks.Show(ctx)
|
||||||
defer statushooks.Done(ctx)
|
defer statushooks.Done(ctx)
|
||||||
|
|
||||||
@@ -406,10 +337,6 @@ func restartService(ctx context.Context) (_ *db_local.StartResult, _ *dashboards
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// along with the current dashboard state - maybe nil
|
|
||||||
currentDashboardState, err := dashboardserver.GetDashboardServiceState()
|
|
||||||
error_helpers.FailOnError(err)
|
|
||||||
|
|
||||||
// stop db
|
// stop db
|
||||||
stopStatus, err := db_local.StopServices(ctx, viper.GetBool(constants.ArgForce), constants.InvokerService)
|
stopStatus, err := db_local.StopServices(ctx, viper.GetBool(constants.ArgForce), constants.InvokerService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -429,13 +356,6 @@ to force a restart.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop the running dashboard server
|
|
||||||
err = dashboardserver.StopDashboardService(ctx)
|
|
||||||
if err != nil {
|
|
||||||
exitCode = constants.ExitCodeServiceStopFailure
|
|
||||||
error_helpers.FailOnErrorWithMessage(err, "could not stop dashboard service")
|
|
||||||
}
|
|
||||||
|
|
||||||
// the DB must be installed and therefore is a noop,
|
// the DB must be installed and therefore is a noop,
|
||||||
// and EnsureDBInstalled also checks and installs the latest FDW
|
// and EnsureDBInstalled also checks and installs the latest FDW
|
||||||
err = db_local.EnsureDBInstalled(ctx)
|
err = db_local.EnsureDBInstalled(ctx)
|
||||||
@@ -455,17 +375,7 @@ to force a restart.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the dashboard was running, start it
|
return dbStartResult
|
||||||
if currentDashboardState != nil {
|
|
||||||
err = dashboardserver.RunForService(ctx, dashboardserver.ListenType(currentDashboardState.ListenType), dashboardserver.ListenPort(currentDashboardState.Port))
|
|
||||||
error_helpers.FailOnError(err)
|
|
||||||
|
|
||||||
// reload the state
|
|
||||||
currentDashboardState, err = dashboardserver.GetDashboardServiceState()
|
|
||||||
error_helpers.FailOnError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dbStartResult, currentDashboardState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServiceStatusCmd(cmd *cobra.Command, _ []string) {
|
func runServiceStatusCmd(cmd *cobra.Command, _ []string) {
|
||||||
@@ -488,17 +398,16 @@ func runServiceStatusCmd(cmd *cobra.Command, _ []string) {
|
|||||||
} else {
|
} else {
|
||||||
dbState, dbStateErr := db_local.GetState()
|
dbState, dbStateErr := db_local.GetState()
|
||||||
pmState, pmStateErr := pluginmanager.LoadState()
|
pmState, pmStateErr := pluginmanager.LoadState()
|
||||||
dashboardState, dashboardStateErr := dashboardserver.GetDashboardServiceState()
|
|
||||||
|
|
||||||
if dbStateErr != nil || pmStateErr != nil {
|
if dbStateErr != nil || pmStateErr != nil {
|
||||||
error_helpers.ShowError(ctx, composeStateError(dbStateErr, pmStateErr, dashboardStateErr))
|
error_helpers.ShowError(ctx, composeStateError(dbStateErr, pmStateErr))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
printStatus(ctx, dbState, pmState, dashboardState, false)
|
printStatus(ctx, dbState, pmState, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func composeStateError(dbStateErr error, pmStateErr error, dashboardStateErr error) error {
|
func composeStateError(dbStateErr error, pmStateErr error) error {
|
||||||
msg := "could not get Steampipe service status:"
|
msg := "could not get Steampipe service status:"
|
||||||
|
|
||||||
if dbStateErr != nil {
|
if dbStateErr != nil {
|
||||||
@@ -509,10 +418,6 @@ func composeStateError(dbStateErr error, pmStateErr error, dashboardStateErr err
|
|||||||
msg = fmt.Sprintf(`%s
|
msg = fmt.Sprintf(`%s
|
||||||
failed to get plugin manager state: %s`, msg, pmStateErr.Error())
|
failed to get plugin manager state: %s`, msg, pmStateErr.Error())
|
||||||
}
|
}
|
||||||
if dashboardStateErr != nil {
|
|
||||||
msg = fmt.Sprintf(`%s
|
|
||||||
failed to get dashboard server state: %s`, msg, pmStateErr.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New(msg)
|
return errors.New(msg)
|
||||||
}
|
}
|
||||||
@@ -540,9 +445,8 @@ func runServiceStopCmd(cmd *cobra.Command, _ []string) {
|
|||||||
|
|
||||||
force := cmdconfig.Viper().GetBool(constants.ArgForce)
|
force := cmdconfig.Viper().GetBool(constants.ArgForce)
|
||||||
if force {
|
if force {
|
||||||
dashboardStopError := dashboardserver.StopDashboardService(ctx)
|
|
||||||
status, dbStopError = db_local.StopServices(ctx, force, constants.InvokerService)
|
status, dbStopError = db_local.StopServices(ctx, force, constants.InvokerService)
|
||||||
dbStopError = error_helpers.CombineErrors(dbStopError, dashboardStopError)
|
dbStopError = error_helpers.CombineErrors(dbStopError)
|
||||||
if dbStopError != nil {
|
if dbStopError != nil {
|
||||||
exitCode = constants.ExitCodeServiceStopFailure
|
exitCode = constants.ExitCodeServiceStopFailure
|
||||||
error_helpers.FailOnError(dbStopError)
|
error_helpers.FailOnError(dbStopError)
|
||||||
@@ -554,12 +458,6 @@ func runServiceStopCmd(cmd *cobra.Command, _ []string) {
|
|||||||
error_helpers.FailOnErrorWithMessage(dbStopError, "could not stop Steampipe service")
|
error_helpers.FailOnErrorWithMessage(dbStopError, "could not stop Steampipe service")
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboardState, err := dashboardserver.GetDashboardServiceState()
|
|
||||||
if err != nil {
|
|
||||||
exitCode = constants.ExitCodeServiceStopFailure
|
|
||||||
error_helpers.FailOnErrorWithMessage(err, "could not stop Steampipe service")
|
|
||||||
}
|
|
||||||
|
|
||||||
if dbState == nil {
|
if dbState == nil {
|
||||||
fmt.Println("Steampipe service is not running.")
|
fmt.Println("Steampipe service is not running.")
|
||||||
return
|
return
|
||||||
@@ -569,14 +467,6 @@ func runServiceStopCmd(cmd *cobra.Command, _ []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if dashboardState != nil {
|
|
||||||
err = dashboardserver.StopDashboardService(ctx)
|
|
||||||
if err != nil {
|
|
||||||
exitCode = constants.ExitCodeServiceStopFailure
|
|
||||||
error_helpers.FailOnErrorWithMessage(err, "could not stop dashboard server")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if there are any connected clients to the service
|
// check if there are any connected clients to the service
|
||||||
connectedClients, err := db_local.GetClientCount(ctx)
|
connectedClients, err := db_local.GetClientCount(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -666,7 +556,7 @@ func getServiceProcessDetails(process *psutils.Process) (string, string, string,
|
|||||||
return fmt.Sprintf("%d", process.Pid), installDir, port, listenType
|
return fmt.Sprintf("%d", process.Pid), installDir, port, listenType
|
||||||
}
|
}
|
||||||
|
|
||||||
func printStatus(ctx context.Context, dbState *db_local.RunningDBInstanceInfo, pmState *pluginmanager.State, dashboardState *dashboardserver.DashboardServiceState, alreadyRunning bool) {
|
func printStatus(ctx context.Context, dbState *db_local.RunningDBInstanceInfo, pmState *pluginmanager.State, alreadyRunning bool) {
|
||||||
if dbState == nil && !pmState.Running {
|
if dbState == nil && !pmState.Running {
|
||||||
fmt.Println("Service is not running")
|
fmt.Println("Service is not running")
|
||||||
return
|
return
|
||||||
@@ -739,25 +629,11 @@ Database:
|
|||||||
connectionStr,
|
connectionStr,
|
||||||
)
|
)
|
||||||
|
|
||||||
dashboardMsg := ""
|
|
||||||
|
|
||||||
if dashboardState != nil {
|
|
||||||
browserUrl := fmt.Sprintf("http://%s:%d/", dashboardState.Listen[0], dashboardState.Port)
|
|
||||||
dashboardMsg = fmt.Sprintf(`
|
|
||||||
Dashboard:
|
|
||||||
|
|
||||||
Host(s): %v
|
|
||||||
Port: %v
|
|
||||||
URL: %v
|
|
||||||
`, strings.Join(dashboardState.Listen, ", "), dashboardState.Port, browserUrl)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dbState.Invoker == constants.InvokerService {
|
if dbState.Invoker == constants.InvokerService {
|
||||||
statusMessage = fmt.Sprintf(
|
statusMessage = fmt.Sprintf(
|
||||||
"%s%s%s%s",
|
"%s%s%s",
|
||||||
prefix,
|
prefix,
|
||||||
postgresMsg,
|
postgresMsg,
|
||||||
dashboardMsg,
|
|
||||||
suffix,
|
suffix,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,228 +0,0 @@
|
|||||||
package dashboardserver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/shirou/gopsutil/process"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/turbot/steampipe/pkg/constants"
|
|
||||||
"github.com/turbot/steampipe/pkg/dashboard/dashboardassets"
|
|
||||||
"github.com/turbot/steampipe/pkg/error_helpers"
|
|
||||||
"github.com/turbot/steampipe/pkg/filepaths"
|
|
||||||
"github.com/turbot/steampipe/pkg/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ServiceState string
|
|
||||||
|
|
||||||
const (
|
|
||||||
ServiceStateRunning ServiceState = "running"
|
|
||||||
ServiceStateError ServiceState = "error"
|
|
||||||
ServiceStateStructVersion = 20220411
|
|
||||||
)
|
|
||||||
|
|
||||||
type DashboardServiceState struct {
|
|
||||||
State ServiceState `json:"state"`
|
|
||||||
Error string `json:"error"`
|
|
||||||
Pid int `json:"pid"`
|
|
||||||
Port int `json:"port"`
|
|
||||||
ListenType string `json:"listen_type"`
|
|
||||||
Listen []string `json:"listen"`
|
|
||||||
StructVersion int64 `json:"struct_version"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadServiceStateFile() (*DashboardServiceState, error) {
|
|
||||||
state := &DashboardServiceState{}
|
|
||||||
stateBytes, err := os.ReadFile(filepaths.DashboardServiceStateFilePath())
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(stateBytes, state)
|
|
||||||
return state, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DashboardServiceState) Save() error {
|
|
||||||
// set struct version
|
|
||||||
s.StructVersion = ServiceStateStructVersion
|
|
||||||
|
|
||||||
versionFilePath := filepaths.DashboardServiceStateFilePath()
|
|
||||||
return s.write(versionFilePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DashboardServiceState) write(path string) error {
|
|
||||||
versionFileJSON, err := json.MarshalIndent(s, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Error while writing version file", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.WriteFile(path, versionFileJSON, 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDashboardServiceState() (*DashboardServiceState, error) {
|
|
||||||
state, err := loadServiceStateFile()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if state == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
pidExists, err := utils.PidExists(state.Pid)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !pidExists {
|
|
||||||
return nil, os.Remove(filepaths.DashboardServiceStateFilePath())
|
|
||||||
}
|
|
||||||
return state, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func StopDashboardService(ctx context.Context) error {
|
|
||||||
state, err := GetDashboardServiceState()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if state == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pidExists, err := utils.PidExists(state.Pid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !pidExists {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
process, err := process.NewProcessWithContext(ctx, int32(state.Pid))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = process.SendSignalWithContext(ctx, syscall.SIGINT)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.Remove(filepaths.DashboardServiceStateFilePath())
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunForService spanws an execution of the 'steampipe dashboard' command.
|
|
||||||
// It is used when starting/restarting the steampipe service with the --dashboard flag set
|
|
||||||
func RunForService(ctx context.Context, serverListen ListenType, serverPort ListenPort) error {
|
|
||||||
self, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the state file (if any)
|
|
||||||
os.Remove(filepaths.DashboardServiceStateFilePath())
|
|
||||||
|
|
||||||
err = dashboardassets.Ensure(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
error_helpers.FailOnError(serverPort.IsValid())
|
|
||||||
error_helpers.FailOnError(serverListen.IsValid())
|
|
||||||
|
|
||||||
// NOTE: args must be specified <arg>=<arg val>, as each entry in this array is a separate arg passed to cobra
|
|
||||||
args := []string{
|
|
||||||
"dashboard",
|
|
||||||
fmt.Sprintf("--%s=%s", constants.ArgDashboardListen, string(serverListen)),
|
|
||||||
fmt.Sprintf("--%s=%d", constants.ArgDashboardPort, serverPort),
|
|
||||||
fmt.Sprintf("--%s=%s", constants.ArgInstallDir, filepaths.SteampipeDir),
|
|
||||||
fmt.Sprintf("--%s=%s", constants.ArgModLocation, viper.GetString(constants.ArgModLocation)),
|
|
||||||
fmt.Sprintf("--%s=true", constants.ArgServiceMode),
|
|
||||||
fmt.Sprintf("--%s=false", constants.ArgInput),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, variableArg := range viper.GetStringSlice(constants.ArgVariable) {
|
|
||||||
args = append(args, fmt.Sprintf("--%s=%s", constants.ArgVariable, variableArg))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, varFile := range viper.GetStringSlice(constants.ArgVarFile) {
|
|
||||||
args = append(args, fmt.Sprintf("--%s=%s", constants.ArgVarFile, varFile))
|
|
||||||
}
|
|
||||||
cmd := exec.Command(
|
|
||||||
self,
|
|
||||||
args...,
|
|
||||||
)
|
|
||||||
cmd.Env = os.Environ()
|
|
||||||
|
|
||||||
// set group pgid attributes on the command to ensure the process is not shutdown when its parent terminates
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
Setpgid: true,
|
|
||||||
Foreground: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cmd.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return waitForDashboardService(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// when started as a service, 'steampipe dashboard' always writes a
|
|
||||||
// state file in 'internal' with the outcome - even on failures
|
|
||||||
// this function polls for the file and loads up the error, if any
|
|
||||||
func waitForDashboardService(ctx context.Context) error {
|
|
||||||
utils.LogTime("db.waitForDashboardServerStartup start")
|
|
||||||
defer utils.LogTime("db.waitForDashboardServerStartup end")
|
|
||||||
|
|
||||||
pingTimer := time.NewTicker(constants.ServicePingInterval)
|
|
||||||
timeoutAt := time.After(time.Duration(viper.GetInt(constants.ArgDashboardStartTimeout)) * time.Second)
|
|
||||||
defer pingTimer.Stop()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-pingTimer.C:
|
|
||||||
// poll for the state file.
|
|
||||||
// when it comes up, return it
|
|
||||||
state, err := loadServiceStateFile()
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
// if the file hasn't been generated yet, that means 'dashboard' is still booting up
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// there was an unexpected error
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if state == nil {
|
|
||||||
// no state file yet
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the state file for an error
|
|
||||||
if len(state.Error) > 0 {
|
|
||||||
// there was an error during start up
|
|
||||||
// remove the state file, since we don't need it anymore
|
|
||||||
os.Remove(filepaths.DashboardServiceStateFilePath())
|
|
||||||
// and return the error from the state file
|
|
||||||
return errors.New(state.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// we loaded the state and there was no error
|
|
||||||
return nil
|
|
||||||
case <-timeoutAt:
|
|
||||||
return fmt.Errorf("dashboard server startup timed out")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteServiceStateFile(state *DashboardServiceState) error {
|
|
||||||
stateBytes, err := json.MarshalIndent(state, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.WriteFile(filepaths.DashboardServiceStateFilePath(), stateBytes, 0666)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user