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 }