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

     1  package dashboardexecute
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  
     8  	"github.com/turbot/steampipe/pkg/dashboard/dashboardtypes"
     9  	"github.com/turbot/steampipe/pkg/steampipeconfig/modconfig"
    10  )
    11  
    12  // DashboardRun is a struct representing a container run
    13  type DashboardRun struct {
    14  	runtimeDependencyPublisherImpl
    15  
    16  	parent    dashboardtypes.DashboardParent
    17  	dashboard *modconfig.Dashboard
    18  }
    19  
    20  func (r *DashboardRun) AsTreeNode() *dashboardtypes.SnapshotTreeNode {
    21  	res := &dashboardtypes.SnapshotTreeNode{
    22  		Name:     r.Name,
    23  		NodeType: r.NodeType,
    24  		Children: make([]*dashboardtypes.SnapshotTreeNode, 0, len(r.children)),
    25  	}
    26  
    27  	for _, c := range r.children {
    28  		// NOTE: exclude with runs
    29  		if c.GetNodeType() != modconfig.BlockTypeWith {
    30  			res.Children = append(res.Children, c.AsTreeNode())
    31  		}
    32  	}
    33  
    34  	return res
    35  }
    36  
    37  func NewDashboardRun(dashboard *modconfig.Dashboard, parent dashboardtypes.DashboardParent, executionTree *DashboardExecutionTree) (*DashboardRun, error) {
    38  	r := &DashboardRun{
    39  		parent:    parent,
    40  		dashboard: dashboard,
    41  	}
    42  	// create RuntimeDependencyPublisherImpl- this handles 'with' run creation and resolving runtime dependency resolution
    43  	// (we must create after creating the run as it requires a ref to the run)
    44  	r.runtimeDependencyPublisherImpl = newRuntimeDependencyPublisherImpl(dashboard, parent, r, executionTree)
    45  	// add r into execution tree BEFORE creating child runs or initialising runtime depdencies
    46  	// - this is so child runs can find this dashboard run
    47  	executionTree.runs[r.Name] = r
    48  
    49  	// set inputs map on RuntimeDependencyPublisherImpl BEFORE creating child runs
    50  	r.inputs = dashboard.GetInputs()
    51  
    52  	// after setting inputs, init runtime dependencies. this creates with runs and adds them to our children
    53  	err := r.initWiths()
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	err = r.createChildRuns(executionTree)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	// create buffered channel for children to report their completion
    64  	r.createChildCompleteChan()
    65  
    66  	return r, nil
    67  }
    68  
    69  // Initialise implements DashboardTreeRun
    70  func (r *DashboardRun) Initialise(ctx context.Context) {
    71  	// initialise our children
    72  	if err := r.initialiseChildren(ctx); err != nil {
    73  		r.SetError(ctx, err)
    74  	}
    75  }
    76  
    77  // Execute implements DashboardTreeRun
    78  // execute all children and wait for them to complete
    79  func (r *DashboardRun) Execute(ctx context.Context) {
    80  	r.executeChildrenAsync(ctx)
    81  
    82  	// try to set status as running (will be set to blocked if any children are blocked)
    83  	r.setRunning(ctx)
    84  
    85  	// wait for children to complete
    86  	err := <-r.waitForChildrenAsync(ctx)
    87  	if err == nil {
    88  		log.Printf("[TRACE] Execute run %s all children complete, success", r.Name)
    89  		// set complete status on dashboard
    90  		r.SetComplete(ctx)
    91  	} else {
    92  		log.Printf("[TRACE] Execute run %s all children complete, error: %s", r.Name, err.Error())
    93  		r.SetError(ctx, err)
    94  	}
    95  }
    96  
    97  // IsSnapshotPanel implements SnapshotPanel
    98  func (*DashboardRun) IsSnapshotPanel() {}
    99  
   100  // GetInput searches for an input with the given name
   101  func (r *DashboardRun) GetInput(name string) (*modconfig.DashboardInput, bool) {
   102  	return r.dashboard.GetInput(name)
   103  }
   104  
   105  // GetInputsDependingOn returns a list o DashboardInputs which have a runtime dependency on the given input
   106  func (r *DashboardRun) GetInputsDependingOn(changedInputName string) []string {
   107  	var res []string
   108  	for _, input := range r.dashboard.Inputs {
   109  		if input.DependsOnInput(changedInputName) {
   110  			res = append(res, input.UnqualifiedName)
   111  		}
   112  	}
   113  	return res
   114  }
   115  
   116  func (r *DashboardRun) createChildRuns(executionTree *DashboardExecutionTree) error {
   117  	// ask our resource for its children
   118  	children := r.dashboard.GetChildren()
   119  
   120  	for _, child := range children {
   121  		var childRun dashboardtypes.DashboardTreeRun
   122  		var err error
   123  		switch i := child.(type) {
   124  		case *modconfig.DashboardWith:
   125  			// ignore as with runs are created by RuntimeDependencyPublisherImpl
   126  			continue
   127  		case *modconfig.Dashboard:
   128  			childRun, err = NewDashboardRun(i, r, executionTree)
   129  			if err != nil {
   130  				return err
   131  			}
   132  		case *modconfig.DashboardContainer:
   133  			childRun, err = NewDashboardContainerRun(i, r, executionTree)
   134  			if err != nil {
   135  				return err
   136  			}
   137  		case *modconfig.Benchmark, *modconfig.Control:
   138  			childRun, err = NewCheckRun(i.(modconfig.DashboardLeafNode), r, executionTree)
   139  			if err != nil {
   140  				return err
   141  			}
   142  		case *modconfig.DashboardInput:
   143  			// NOTE: clone the input to avoid mutating the original
   144  			// TODO remove the need for this when we refactor input values resolution
   145  			// TODO https://github.com/turbot/steampipe/issues/2864
   146  
   147  			// TACTICAL: as this is a runtime dependency,  set the run name to the 'scoped name'
   148  			// this is to match the name in the panel dependendencies
   149  			// TODO [node_reuse] consider naming https://github.com/turbot/steampipe/issues/2921
   150  			inputRunName := fmt.Sprintf("%s.%s", r.DashboardName, i.UnqualifiedName)
   151  			childRun, err = NewLeafRun(i.Clone(), r, executionTree, setName(inputRunName))
   152  			if err != nil {
   153  				return err
   154  			}
   155  
   156  		default:
   157  			// ensure this item is a DashboardLeafNode
   158  			leafNode, ok := i.(modconfig.DashboardLeafNode)
   159  			if !ok {
   160  				return fmt.Errorf("child %s does not implement DashboardLeafNode", i.Name())
   161  			}
   162  
   163  			childRun, err = NewLeafRun(leafNode, r, executionTree)
   164  			if err != nil {
   165  				return err
   166  			}
   167  		}
   168  
   169  		// should never happen - container children must be either container or counter
   170  		if childRun == nil {
   171  			continue
   172  		}
   173  
   174  		// if our child has not completed, we have not completed
   175  		if childRun.GetRunStatus() == dashboardtypes.RunInitialized {
   176  			r.Status = dashboardtypes.RunInitialized
   177  		}
   178  		r.children = append(r.children, childRun)
   179  	}
   180  	return nil
   181  }