Files
steampipe/pkg/interactive/interactive_client_init.go

136 lines
3.6 KiB
Go

package interactive
import (
"context"
"fmt"
"log"
"time"
"github.com/spf13/viper"
"github.com/turbot/go-kit/helpers"
"github.com/turbot/steampipe/pkg/constants"
"github.com/turbot/steampipe/pkg/db/db_common"
"github.com/turbot/steampipe/pkg/utils"
"github.com/turbot/steampipe/pkg/workspace"
)
// init data has arrived, handle any errors/warnings/messages
func (c *InteractiveClient) handleInitResult(ctx context.Context, initResult *db_common.InitResult) {
// try to take an execution lock, so that we don't end up showing warnings and errors
// while an execution is underway
c.executionLock.Lock()
defer c.executionLock.Unlock()
if initResult.Error != nil {
c.ClosePrompt(AfterPromptCloseExit)
// add newline to ensure error is not printed at end of current prompt line
fmt.Println()
utils.ShowError(ctx, initResult.Error)
return
}
if utils.IsContextCancelled(ctx) {
c.ClosePrompt(AfterPromptCloseExit)
// add newline to ensure error is not printed at end of current prompt line
fmt.Println()
utils.ShowError(ctx, initResult.Error)
log.Printf("[TRACE] prompt context has been cancelled - not handling init result")
return
}
if initResult.HasMessages() {
c.interactivePrompt.ClearLine()
fmt.Println()
initResult.DisplayMessages()
}
// We need to render the prompt here to make sure that it comes back
// after the messages have been displayed
c.interactivePrompt.Render()
// tell the workspace to reset the prompt after displaying async filewatcher messages
c.initData.Workspace.SetOnFileWatcherEventMessages(func() { c.interactivePrompt.Render() })
}
func (c *InteractiveClient) readInitDataStream(ctx context.Context) {
defer func() {
if r := recover(); r != nil {
c.interactivePrompt.ClearScreen()
utils.ShowError(ctx, helpers.ToError(r))
}
// whatever happens, set initialisationComplete
c.initialisationComplete = true
}()
<-c.initData.Loaded
defer func() { c.initResultChan <- c.initData.Result }()
if c.initData.Result.Error != nil {
return
}
// asyncronously fetch the schema
if err := c.loadSchema(); err != nil {
c.initData.Result.Error = err
return
}
log.Printf("[TRACE] readInitDataStream - data has arrived")
// start the workspace file watcher
if viper.GetBool(constants.ArgWatch) {
// provide an explicit error handler which re-renders the prompt after displaying the error
if err := c.initData.Workspace.SetupWatcher(ctx, c.initData.Client, c.workspaceWatcherErrorHandler); err != nil {
c.initData.Result.Error = err
}
}
}
func (c *InteractiveClient) workspaceWatcherErrorHandler(ctx context.Context, err error) {
fmt.Println()
utils.ShowError(ctx, err)
c.interactivePrompt.Render()
}
// return whether the client is initialises
// there are 3 conditions>
func (c *InteractiveClient) isInitialised() bool {
return c.initialisationComplete
}
func (c *InteractiveClient) waitForInitData(ctx context.Context) error {
var initTimeout = 40 * time.Second
ticker := time.NewTicker(20 * time.Millisecond)
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
if c.isInitialised() {
// if there was an error in initialisation, return it
return c.initData.Result.Error
}
case <-time.After(initTimeout):
return fmt.Errorf("timed out waiting for initialisation to complete")
}
}
}
// return the workspace, or nil if not yet initialised
func (c *InteractiveClient) workspace() *workspace.Workspace {
if c.initData == nil {
return nil
}
return c.initData.Workspace
}
// return the client, or nil if not yet initialised
func (c *InteractiveClient) client() db_common.Client {
if c.initData == nil {
return nil
}
return c.initData.Client
}