mirror of
https://github.com/turbot/steampipe.git
synced 2026-02-24 02:00:29 -05:00
191 lines
5.6 KiB
Go
191 lines
5.6 KiB
Go
package execute
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/spf13/viper"
|
|
"github.com/turbot/steampipe/constants"
|
|
"github.com/turbot/steampipe/db"
|
|
"github.com/turbot/steampipe/steampipeconfig/modconfig"
|
|
)
|
|
|
|
const RootResultGroupName = "root_result_group"
|
|
|
|
// ResultGroup is a struct representing a grouping of control results
|
|
// It may correspond to a Benchmark, or some other arbitrary grouping
|
|
type ResultGroup struct {
|
|
GroupId string `json:"group_id" csv:"group_id"`
|
|
Title string `json:"title" csv:"title"`
|
|
Description string `json:"description" csv:"description"`
|
|
Tags map[string]string `json:"tags"`
|
|
Summary GroupSummary `json:"summary"`
|
|
Groups []*ResultGroup `json:"groups"`
|
|
ControlRuns []*ControlRun `json:"controls"`
|
|
// the control tree item associated with this group(i.e. a mod/benchmark)
|
|
GroupItem modconfig.ControlTreeItem `json:"-"`
|
|
Parent *ResultGroup `json:"-"`
|
|
Duration time.Duration `json:"-"`
|
|
}
|
|
|
|
type GroupSummary struct {
|
|
Status StatusSummary `json:"status"`
|
|
}
|
|
|
|
// NewRootResultGroup creates a ResultGroup to act as the root node of a control execution tree
|
|
func NewRootResultGroup(executionTree *ExecutionTree, rootItems ...modconfig.ControlTreeItem) *ResultGroup {
|
|
root := &ResultGroup{
|
|
GroupId: RootResultGroupName,
|
|
Groups: []*ResultGroup{},
|
|
Tags: make(map[string]string),
|
|
}
|
|
for _, item := range rootItems {
|
|
// if root item is a benchmark, create new result group with root as parent
|
|
if control, ok := item.(*modconfig.Control); ok {
|
|
// if root item is a control, add control run
|
|
executionTree.AddControl(control, root)
|
|
} else {
|
|
// create a result group for this item
|
|
itemGroup := NewResultGroup(executionTree, item, root)
|
|
root.Groups = append(root.Groups, itemGroup)
|
|
}
|
|
}
|
|
return root
|
|
}
|
|
|
|
// NewResultGroup creates a result group from a ControlTreeItem
|
|
func NewResultGroup(executionTree *ExecutionTree, treeItem modconfig.ControlTreeItem, parent *ResultGroup) *ResultGroup {
|
|
group := &ResultGroup{
|
|
GroupId: treeItem.Name(),
|
|
Title: treeItem.GetTitle(),
|
|
Description: treeItem.GetDescription(),
|
|
Tags: treeItem.GetTags(),
|
|
GroupItem: treeItem,
|
|
Parent: parent,
|
|
Groups: []*ResultGroup{},
|
|
}
|
|
// add child groups for children which are benchmarks
|
|
for _, c := range treeItem.GetChildren() {
|
|
if benchmark, ok := c.(*modconfig.Benchmark); ok {
|
|
// create a result group for this item
|
|
benchmarkGroup := NewResultGroup(executionTree, benchmark, group)
|
|
// if the group has any control runs, add to tree
|
|
if benchmarkGroup.ControlRunCount() > 0 {
|
|
// create a new result group with 'group' as the parent
|
|
group.Groups = append(group.Groups, benchmarkGroup)
|
|
}
|
|
}
|
|
if control, ok := c.(*modconfig.Control); ok {
|
|
executionTree.AddControl(control, group)
|
|
}
|
|
}
|
|
|
|
return group
|
|
}
|
|
|
|
// PopulateGroupMap mutates the passed in a map to return all child result groups
|
|
func (r *ResultGroup) PopulateGroupMap(groupMap map[string]*ResultGroup) {
|
|
if groupMap == nil {
|
|
groupMap = make(map[string]*ResultGroup)
|
|
}
|
|
// add self
|
|
groupMap[r.GroupId] = r
|
|
for _, g := range r.Groups {
|
|
g.PopulateGroupMap(groupMap)
|
|
}
|
|
}
|
|
|
|
// AddResult adds a result to the list, updates the summary status
|
|
// (this also updates the status of our parent, all the way up the tree)
|
|
func (r *ResultGroup) AddResult(run *ControlRun) {
|
|
r.ControlRuns = append(r.ControlRuns, run)
|
|
|
|
}
|
|
|
|
func (r *ResultGroup) updateSummary(summary StatusSummary) {
|
|
r.Summary.Status.Skip += summary.Skip
|
|
r.Summary.Status.Alarm += summary.Alarm
|
|
r.Summary.Status.Info += summary.Info
|
|
r.Summary.Status.Ok += summary.Ok
|
|
r.Summary.Status.Error += summary.Error
|
|
if r.Parent != nil {
|
|
r.Parent.updateSummary(summary)
|
|
}
|
|
}
|
|
|
|
func (r *ResultGroup) Execute(ctx context.Context, client *db.Client) int {
|
|
log.Printf("[TRACE] begin ResultGroup.Execute: %s\n", r.GroupId)
|
|
defer log.Printf("[TRACE] end ResultGroup.Execute: %s\n", r.GroupId)
|
|
|
|
// TODO consider executing in order specified in hcl?
|
|
// it may not matter, as we display results in order
|
|
// it is only an issue if there are dependencies, in which case we must run in dependency order
|
|
|
|
startTime := time.Now()
|
|
|
|
var failures = 0
|
|
for _, controlRun := range r.ControlRuns {
|
|
select {
|
|
case <-ctx.Done():
|
|
controlRun.SetError(ctx.Err())
|
|
default:
|
|
if viper.GetBool(constants.ArgDryRun) {
|
|
controlRun.Skip()
|
|
} else {
|
|
controlRun.Start(ctx, client)
|
|
failures += controlRun.Summary.Alarm
|
|
failures += controlRun.Summary.Error
|
|
}
|
|
}
|
|
}
|
|
for _, child := range r.Groups {
|
|
failures += child.Execute(ctx, client)
|
|
}
|
|
|
|
r.Duration = time.Since(startTime)
|
|
|
|
return failures
|
|
}
|
|
|
|
// GetGroupByName finds an immediate child ResultGroup with a specific name
|
|
func (r *ResultGroup) GetGroupByName(name string) *ResultGroup {
|
|
for _, group := range r.Groups {
|
|
if group.GroupId == name {
|
|
return group
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetChildGroupByName finds a nested child ResultGroup with a specific name
|
|
func (r *ResultGroup) GetChildGroupByName(name string) *ResultGroup {
|
|
for _, group := range r.Groups {
|
|
if group.GroupId == name {
|
|
return group
|
|
}
|
|
if child := group.GetChildGroupByName(name); child != nil {
|
|
return child
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetControlRunByName finds a child ControlRun with a specific control name
|
|
func (r *ResultGroup) GetControlRunByName(name string) *ControlRun {
|
|
for _, run := range r.ControlRuns {
|
|
if run.Control.Name() == name {
|
|
return run
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *ResultGroup) ControlRunCount() int {
|
|
count := len(r.ControlRuns)
|
|
for _, g := range r.Groups {
|
|
count += g.ControlRunCount()
|
|
}
|
|
return count
|
|
}
|