github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/dashboard/dashboardexecute/dashboard_parent_impl.go (about)

     1  package dashboardexecute
     2  
     3  import (
     4  	"context"
     5  	"github.com/turbot/steampipe/pkg/dashboard/dashboardtypes"
     6  	"github.com/turbot/steampipe/pkg/error_helpers"
     7  	"github.com/turbot/steampipe/pkg/steampipeconfig/modconfig"
     8  	"log"
     9  	"sync"
    10  )
    11  
    12  type DashboardParentImpl struct {
    13  	DashboardTreeRunImpl
    14  	children          []dashboardtypes.DashboardTreeRun
    15  	childCompleteChan chan dashboardtypes.DashboardTreeRun
    16  	// are we blocked by a child run
    17  	blockedByChild  bool
    18  	childStatusLock *sync.Mutex
    19  }
    20  
    21  func newDashboardParentImpl(resource modconfig.DashboardLeafNode, parent dashboardtypes.DashboardParent, run dashboardtypes.DashboardTreeRun, executionTree *DashboardExecutionTree) DashboardParentImpl {
    22  	return DashboardParentImpl{
    23  		DashboardTreeRunImpl: NewDashboardTreeRunImpl(resource, parent, run, executionTree),
    24  		childStatusLock:      new(sync.Mutex),
    25  	}
    26  }
    27  
    28  func (r *DashboardParentImpl) initialiseChildren(ctx context.Context) error {
    29  	var errors []error
    30  	for _, child := range r.children {
    31  		child.Initialise(ctx)
    32  
    33  		if err := child.GetError(); err != nil {
    34  			errors = append(errors, err)
    35  		}
    36  	}
    37  
    38  	return error_helpers.CombineErrors(errors...)
    39  
    40  }
    41  
    42  // GetChildren implements DashboardTreeRun
    43  func (r *DashboardParentImpl) GetChildren() []dashboardtypes.DashboardTreeRun {
    44  	return r.children
    45  }
    46  
    47  // ChildrenComplete implements DashboardTreeRun
    48  func (r *DashboardParentImpl) ChildrenComplete() bool {
    49  	for _, child := range r.children {
    50  		if !child.RunComplete() {
    51  			log.Printf("[TRACE] %s ChildrenComplete child %s NOT complete state %s", r.Name, child.GetName(), child.GetRunStatus())
    52  			return false
    53  		}
    54  	}
    55  
    56  	return true
    57  }
    58  
    59  func (r *DashboardParentImpl) ChildCompleteChan() chan dashboardtypes.DashboardTreeRun {
    60  	return r.childCompleteChan
    61  }
    62  func (r *DashboardParentImpl) createChildCompleteChan() {
    63  	// create buffered child complete chan
    64  	if childCount := len(r.children); childCount > 0 {
    65  		r.childCompleteChan = make(chan dashboardtypes.DashboardTreeRun, childCount)
    66  	}
    67  }
    68  
    69  // if this leaf run has children (including with runs) execute them asynchronously
    70  func (r *DashboardParentImpl) executeChildrenAsync(ctx context.Context) {
    71  	for _, c := range r.children {
    72  		go c.Execute(ctx)
    73  	}
    74  }
    75  
    76  // if this leaf run has with runs execute them asynchronously
    77  func (r *DashboardParentImpl) executeWithsAsync(ctx context.Context) {
    78  	for _, c := range r.children {
    79  		if c.GetNodeType() == modconfig.BlockTypeWith {
    80  			go c.Execute(ctx)
    81  		}
    82  	}
    83  }
    84  
    85  func (r *DashboardParentImpl) waitForChildrenAsync(ctx context.Context) chan error {
    86  	log.Printf("[TRACE] %s waitForChildrenAsync", r.Name)
    87  	var doneChan = make(chan error)
    88  	if len(r.children) == 0 {
    89  		log.Printf("[TRACE] %s waitForChildrenAsync - no children so we're done", r.Name)
    90  		// if there are no children, return a closed channel so we do not wait
    91  		close(doneChan)
    92  		return doneChan
    93  	}
    94  
    95  	go func() {
    96  		// wait for children to complete
    97  		var errors []error
    98  		for !(r.ChildrenComplete()) {
    99  			completeChild := <-r.childCompleteChan
   100  			log.Printf("[TRACE] %s waitForChildrenAsync got child complete for %s", r.Name, completeChild.GetName())
   101  			if completeChild.GetRunStatus().IsError() {
   102  				errors = append(errors, completeChild.GetError())
   103  				log.Printf("[TRACE] %s child %s has error %v", r.Name, completeChild.GetName(), completeChild.GetError())
   104  			}
   105  		}
   106  
   107  		log.Printf("[TRACE] %s ALL children and withs complete, errors: %v", r.Name, errors)
   108  
   109  		// so all children have completed - check for errors
   110  		// TODO [node_reuse] format better error https://github.com/turbot/steampipe/issues/2920
   111  		err := error_helpers.CombineErrors(errors...)
   112  
   113  		// if context is cancelled, just return context cancellation error
   114  		if ctx.Err() != nil {
   115  			err = ctx.Err()
   116  		}
   117  
   118  		doneChan <- err
   119  	}()
   120  
   121  	return doneChan
   122  }
   123  
   124  func (r *DashboardParentImpl) ChildStatusChanged(ctx context.Context) {
   125  	// this function may be called asyncronously by children
   126  	r.childStatusLock.Lock()
   127  	defer r.childStatusLock.Unlock()
   128  
   129  	// if we are currently blocked by a child or we are currently in running state,
   130  	// call setRunning() to determine whether any of our children are now blocked
   131  	if r.blockedByChild || r.GetRunStatus() == dashboardtypes.RunRunning {
   132  		log.Printf("[TRACE] %s ChildStatusChanged - calling setRunning to see if we are still running, status %s blockedByChild %v", r.Name, r.GetRunStatus(), r.blockedByChild)
   133  
   134  		// try setting our status to running again
   135  		r.setRunning(ctx)
   136  	}
   137  }
   138  
   139  // override DashboardTreeRunImpl) setStatus(
   140  func (r *DashboardParentImpl) setRunning(ctx context.Context) {
   141  	// if the run is already complete (for example, canceled), do nothing
   142  	if r.GetRunStatus().IsFinished() {
   143  		log.Printf("[TRACE] %s setRunning - run already terminated - current state %s - NOT setting running", r.Name, r.GetRunStatus())
   144  		return
   145  	}
   146  
   147  	status := dashboardtypes.RunRunning
   148  	// if we are trying to set status to running, check if any of our children are blocked,
   149  	// and if so set our status to blocked
   150  
   151  	// if any children are blocked, we are blocked
   152  	for _, c := range r.children {
   153  		if c.GetRunStatus() == dashboardtypes.RunBlocked {
   154  			status = dashboardtypes.RunBlocked
   155  			r.blockedByChild = true
   156  			break
   157  		}
   158  		// to get here, no children can be blocked - clear blockedByChild
   159  		r.blockedByChild = false
   160  	}
   161  
   162  	// set status if it has changed
   163  	if status != r.GetRunStatus() {
   164  		log.Printf("[TRACE] %s setRunning - setting state %s, blockedByChild %v", r.Name, status, r.blockedByChild)
   165  		r.DashboardTreeRunImpl.setStatus(ctx, status)
   166  	} else {
   167  		log.Printf("[TRACE] %s setRunning - state unchanged %s, blockedByChild %v", r.Name, status, r.blockedByChild)
   168  	}
   169  }