Update naming of anonymous resources to ensure consistent names. Closes #1489

This commit is contained in:
kaidaguerre
2022-02-22 15:30:44 +00:00
committed by GitHub
parent 62eb546bcc
commit aedfc6fd34
31 changed files with 206 additions and 276 deletions

View File

@@ -3,8 +3,8 @@ package dashboardevents
import "github.com/turbot/steampipe/dashboard/dashboardinterfaces"
type ExecutionStarted struct {
DashboardNode dashboardinterfaces.DashboardNodeRun `json:"dashboard"`
Session string
Dashboard dashboardinterfaces.DashboardNodeRun `json:"dashboard"`
Session string
}
// IsDashboardEvent implements DashboardEvent interface

View File

@@ -3,8 +3,8 @@ package dashboardevents
import "github.com/turbot/steampipe/dashboard/dashboardinterfaces"
type LeafNodeComplete struct {
Node dashboardinterfaces.DashboardNodeRun
Session string
LeafNode dashboardinterfaces.DashboardNodeRun
Session string
}
// IsDashboardEvent implements DashboardEvent interface

View File

@@ -3,8 +3,9 @@ package dashboardevents
import "github.com/turbot/steampipe/dashboard/dashboardinterfaces"
type LeafNodeError struct {
Node dashboardinterfaces.DashboardNodeRun
Session string
LeafNode dashboardinterfaces.DashboardNodeRun
Session string
Error error
}
// IsDashboardEvent implements DashboardEvent interface

View File

@@ -3,8 +3,8 @@ package dashboardevents
import "github.com/turbot/steampipe/dashboard/dashboardinterfaces"
type LeafNodeProgress struct {
Node dashboardinterfaces.DashboardNodeRun
Session string
LeafNode dashboardinterfaces.DashboardNodeRun
Session string
}
// IsDashboardEvent implements DashboardEvent interface

View File

@@ -22,21 +22,22 @@ type CheckRun struct {
ControlExecutionTree *controlexecute.ExecutionTree `json:"execution_tree"`
DashboardName string `json:"dashboard"`
DashboardNode modconfig.DashboardLeafNode `json:"-"`
Path []string `json:"-"`
parent dashboardinterfaces.DashboardNodeParent
runStatus dashboardinterfaces.DashboardRunStatus
executionTree *DashboardExecutionTree
}
func NewCheckRun(resource modconfig.DashboardLeafNode, parent dashboardinterfaces.DashboardNodeParent, executionTree *DashboardExecutionTree) (*CheckRun, error) {
// ensure the tree node name is unique
name := executionTree.GetUniqueName(resource.Name())
// NOTE: for now we MUST declare container/dashboard children inline - therefore we cannot share children between runs in the tree
// (if we supported the children property then we could reuse resources)
// so FOR NOW it is safe to use the node name directly as the run name
name := resource.Name()
r := &CheckRun{
Name: name,
Title: resource.GetTitle(),
Width: resource.GetWidth(),
Path: resource.GetPaths()[0],
DashboardNode: resource,
DashboardName: executionTree.dashboardName,
executionTree: executionTree,
@@ -88,11 +89,6 @@ func (r *CheckRun) GetName() string {
return r.Name
}
// GetPath implements DashboardNodeRun
func (r *CheckRun) GetPath() modconfig.NodePath {
return r.Path
}
// GetRunStatus implements DashboardNodeRun
func (r *CheckRun) GetRunStatus() dashboardinterfaces.DashboardRunStatus {
return r.runStatus
@@ -104,8 +100,9 @@ func (r *CheckRun) SetError(err error) {
r.runStatus = dashboardinterfaces.DashboardRunError
// raise dashboard error event
r.executionTree.workspace.PublishDashboardEvent(&dashboardevents.LeafNodeError{
Node: r,
Session: r.executionTree.sessionId,
LeafNode: r,
Session: r.executionTree.sessionId,
Error: err,
})
// tell parent we are done
r.parent.ChildCompleteChan() <- r
@@ -117,8 +114,8 @@ func (r *CheckRun) SetComplete() {
r.runStatus = dashboardinterfaces.DashboardRunComplete
// raise counter complete event
r.executionTree.workspace.PublishDashboardEvent(&dashboardevents.LeafNodeComplete{
Node: r,
Session: r.executionTree.sessionId,
LeafNode: r,
Session: r.executionTree.sessionId,
})
// tell parent we are done
r.parent.ChildCompleteChan() <- r

View File

@@ -24,7 +24,6 @@ type DashboardContainerRun struct {
NodeType string `json:"node_type"`
Status dashboardinterfaces.DashboardRunStatus `json:"status"`
DashboardName string `json:"report"`
Path []string `json:"-"`
dashboardNode *modconfig.DashboardContainer
parent dashboardinterfaces.DashboardNodeParent
executionTree *DashboardExecutionTree
@@ -34,13 +33,14 @@ type DashboardContainerRun struct {
func NewDashboardContainerRun(container *modconfig.DashboardContainer, parent dashboardinterfaces.DashboardNodeParent, executionTree *DashboardExecutionTree) (*DashboardContainerRun, error) {
children := container.GetChildren()
// ensure the tree node name is unique
name := executionTree.GetUniqueName(container.Name())
// NOTE: for now we MUST declare children inline - therefore we cannot share children between runs in the tree
// (if we supported the children property then we could reuse resources)
// so FOR NOW it is safe to use the container name directly as the run name
name := container.Name()
r := &DashboardContainerRun{
Name: name,
NodeType: modconfig.BlockTypeContainer,
Path: container.Paths[0],
DashboardName: executionTree.dashboardName,
executionTree: executionTree,
parent: parent,
@@ -153,11 +153,6 @@ func (r *DashboardContainerRun) GetName() string {
return r.Name
}
// GetPath implements DashboardNodeRun
func (r *DashboardContainerRun) GetPath() modconfig.NodePath {
return r.Path
}
// GetRunStatus implements DashboardNodeRun
func (r *DashboardContainerRun) GetRunStatus() dashboardinterfaces.DashboardRunStatus {
return r.Status

View File

@@ -23,7 +23,7 @@ func (c *ControlEventHooks) OnStart(ctx context.Context, _ *controlhooks.Control
}
func (c *ControlEventHooks) OnControlEvent(ctx context.Context, _ *controlhooks.ControlProgress) {
event := &dashboardevents.LeafNodeProgress{Node: c.CheckRun}
event := &dashboardevents.LeafNodeProgress{LeafNode: c.CheckRun}
c.CheckRun.executionTree.workspace.PublishDashboardEvent(event)
}

View File

@@ -15,8 +15,6 @@ import (
// DashboardExecutionTree is a structure representing the control result hierarchy
type DashboardExecutionTree struct {
modconfig.UniqueNameProviderBase
Root *DashboardRun
dashboardName string
@@ -80,8 +78,8 @@ func (e *DashboardExecutionTree) Execute(ctx context.Context) error {
e.cancel = cancel
workspace := e.workspace
workspace.PublishDashboardEvent(&dashboardevents.ExecutionStarted{
DashboardNode: e.Root,
Session: e.sessionId,
Dashboard: e.Root,
Session: e.sessionId,
})
defer workspace.PublishDashboardEvent(&dashboardevents.ExecutionComplete{
Dashboard: e.Root,

View File

@@ -27,7 +27,6 @@ type DashboardRun struct {
NodeType string `json:"node_type"`
Status dashboardinterfaces.DashboardRunStatus `json:"status"`
DashboardName string `json:"dashboard"`
Path []string `json:"-"`
dashboardNode *modconfig.Dashboard
parent dashboardinterfaces.DashboardNodeParent
executionTree *DashboardExecutionTree
@@ -37,13 +36,14 @@ type DashboardRun struct {
func NewDashboardRun(dashboard *modconfig.Dashboard, parent dashboardinterfaces.DashboardNodeParent, executionTree *DashboardExecutionTree) (*DashboardRun, error) {
children := dashboard.GetChildren()
// ensure the tree node name is unique
name := executionTree.GetUniqueName(dashboard.Name())
// NOTE: for now we MUST declare container/dashboard children inline - therefore we cannot share children between runs in the tree
// (if we supported the children property then we could reuse resources)
// so FOR NOW it is safe to use the dashboard name directly as the run name
name := dashboard.Name()
r := &DashboardRun{
Name: name,
NodeType: modconfig.BlockTypeDashboard,
Path: dashboard.Paths[0],
DashboardName: executionTree.dashboardName,
executionTree: executionTree,
parent: parent,
@@ -162,11 +162,6 @@ func (r *DashboardRun) GetName() string {
return r.Name
}
// GetPath implements DashboardNodeRun
func (r *DashboardRun) GetPath() modconfig.NodePath {
return r.Path
}
// GetRunStatus implements DashboardNodeRun
func (r *DashboardRun) GetRunStatus() dashboardinterfaces.DashboardRunStatus {
return r.Status

View File

@@ -3,6 +3,7 @@ package dashboardexecute
import (
"context"
"fmt"
"log"
"github.com/turbot/steampipe/dashboard/dashboardevents"
"github.com/turbot/steampipe/dashboard/dashboardinterfaces"
@@ -21,7 +22,6 @@ type LeafRun struct {
DashboardNode modconfig.DashboardLeafNode `json:"properties"`
NodeType string `json:"node_type"`
DashboardName string `json:"dashboard"`
Path []string `json:"-"`
parent dashboardinterfaces.DashboardNodeParent
runStatus dashboardinterfaces.DashboardRunStatus
executionTree *DashboardExecutionTree
@@ -29,14 +29,16 @@ type LeafRun struct {
}
func NewLeafRun(resource modconfig.DashboardLeafNode, parent dashboardinterfaces.DashboardNodeParent, executionTree *DashboardExecutionTree) (*LeafRun, error) {
// ensure the tree node name is unique
name := executionTree.GetUniqueName(resource.Name())
// NOTE: for now we MUST declare container/dashboard children inline - therefore we cannot share children between runs in the tree
// (if we supported the children property then we could reuse resources)
// so FOR NOW it is safe to use the node name directly as the run name
name := resource.Name()
r := &LeafRun{
Name: name,
Title: resource.GetTitle(),
Width: resource.GetWidth(),
Path: resource.GetPaths()[0],
DashboardNode: resource,
DashboardName: executionTree.dashboardName,
executionTree: executionTree,
@@ -65,7 +67,6 @@ func NewLeafRun(resource modconfig.DashboardLeafNode, parent dashboardinterfaces
for name, dep := range runtimeDependencies {
r.runtimeDependencies[name] = NewResolvedRuntimeDependency(dep, executionTree)
}
}
// add r into execution tree
@@ -80,12 +81,14 @@ func (r *LeafRun) Execute(ctx context.Context) error {
return nil
}
log.Printf("[TRACE] LeafRun '%s' Execute()", r.DashboardNode.Name())
// if there are any unresolved runtime dependencies, wait for them
if err := r.waitForRuntimeDependencies(ctx); err != nil {
return err
}
// ok now we have runtime depdencies, we can resolve the query
// ok now we have runtime dependencies, we can resolve the query
queryProvider := r.DashboardNode.(modconfig.QueryProvider)
sql, err := r.executionTree.workspace.ResolveQueryFromQueryProvider(queryProvider, nil)
if err != nil {
@@ -93,13 +96,18 @@ func (r *LeafRun) Execute(ctx context.Context) error {
}
r.SQL = sql
log.Printf("[TRACE] LeafRun '%s' SQL resolved, executing", r.DashboardNode.Name())
queryResult, err := r.executionTree.client.ExecuteSync(ctx, r.SQL)
if err != nil {
log.Printf("[TRACE] LeafRun '%s' query failed: %s", r.DashboardNode.Name(), err.Error())
// set the error status on the counter - this will raise counter error event
r.SetError(err)
return err
}
log.Printf("[TRACE] LeafRun '%s' complete", r.DashboardNode.Name())
r.Data = NewLeafData(queryResult)
// set complete status on counter - this will raise counter complete event
r.SetComplete()
@@ -111,11 +119,6 @@ func (r *LeafRun) GetName() string {
return r.Name
}
// GetPath implements DashboardNodeRun
func (r *LeafRun) GetPath() modconfig.NodePath {
return r.Path
}
// GetRunStatus implements DashboardNodeRun
func (r *LeafRun) GetRunStatus() dashboardinterfaces.DashboardRunStatus {
return r.runStatus
@@ -127,8 +130,8 @@ func (r *LeafRun) SetError(err error) {
r.runStatus = dashboardinterfaces.DashboardRunError
// raise counter error event
r.executionTree.workspace.PublishDashboardEvent(&dashboardevents.LeafNodeError{
Node: r,
Session: r.executionTree.sessionId,
LeafNode: r,
Session: r.executionTree.sessionId,
})
// tell parent we are done
r.parent.ChildCompleteChan() <- r
@@ -140,8 +143,8 @@ func (r *LeafRun) SetComplete() {
r.runStatus = dashboardinterfaces.DashboardRunComplete
// raise counter complete event
r.executionTree.workspace.PublishDashboardEvent(&dashboardevents.LeafNodeComplete{
Node: r,
Session: r.executionTree.sessionId,
LeafNode: r,
Session: r.executionTree.sessionId,
})
// tell parent we are done
r.parent.ChildCompleteChan() <- r
@@ -158,19 +161,27 @@ func (r *LeafRun) ChildrenComplete() bool {
}
func (r *LeafRun) waitForRuntimeDependencies(ctx context.Context) error {
log.Printf("[TRACE] LeafRun '%s' waitForRuntimeDependencies", r.DashboardNode.Name())
for _, resolvedDependency := range r.runtimeDependencies {
// check with the top level dashboard whether the dependency is available
if !resolvedDependency.Resolve() {
log.Printf("[TRACE] waitForRuntimeDependency %s", resolvedDependency.dependency.String())
if err := r.executionTree.waitForRuntimeDependency(ctx, resolvedDependency.dependency); err != nil {
return err
}
}
log.Printf("[TRACE] dependency %s should be available", resolvedDependency.dependency.String())
// now again resolve the dependency value - this sets the arg to have the runtime dependency value
if !resolvedDependency.Resolve() {
log.Printf("[TRACE] dependency %s not resolved after waitForRuntimeDependency returned", resolvedDependency.dependency.String())
// should now be resolved`
return fmt.Errorf("dependency not resolved after waitForRuntimeDependency returned")
return fmt.Errorf("dependency %s not resolved after waitForRuntimeDependency returned", resolvedDependency.dependency.String())
}
}
if len(r.runtimeDependencies) > 0 {
log.Printf("[TRACE] LeafRun '%s' all runtime dependencies ready", r.DashboardNode.Name())
}
return nil
}

View File

@@ -2,8 +2,6 @@ package dashboardinterfaces
import (
"context"
"github.com/turbot/steampipe/steampipeconfig/modconfig"
)
type DashboardRunStatus string
@@ -19,7 +17,6 @@ const (
type DashboardNodeRun interface {
Execute(ctx context.Context) error
GetName() string
GetPath() modconfig.NodePath
GetRunStatus() DashboardRunStatus
SetError(err error)
SetComplete()

View File

@@ -157,7 +157,7 @@ func buildWorkspaceErrorPayload(e *dashboardevents.WorkspaceError) ([]byte, erro
func buildLeafNodeProgressPayload(event *dashboardevents.LeafNodeProgress) ([]byte, error) {
payload := ExecutionPayload{
Action: "leaf_node_progress",
DashboardNode: event.Node,
DashboardNode: event.LeafNode,
}
return json.Marshal(payload)
}
@@ -165,7 +165,7 @@ func buildLeafNodeProgressPayload(event *dashboardevents.LeafNodeProgress) ([]by
func buildLeafNodeCompletePayload(event *dashboardevents.LeafNodeComplete) ([]byte, error) {
payload := ExecutionPayload{
Action: "leaf_node_complete",
DashboardNode: event.Node,
DashboardNode: event.LeafNode,
}
return json.Marshal(payload)
}
@@ -173,11 +173,8 @@ func buildLeafNodeCompletePayload(event *dashboardevents.LeafNodeComplete) ([]by
func buildExecutionStartedPayload(event *dashboardevents.ExecutionStarted) ([]byte, error) {
payload := ExecutionPayload{
Action: "execution_started",
DashboardNode: event.DashboardNode,
DashboardNode: event.Dashboard,
}
a, _ := json.MarshalIndent(payload, "", " ")
b := string(a)
fmt.Println(b)
return json.Marshal(payload)
}
@@ -252,7 +249,7 @@ func (s *Server) HandleWorkspaceUpdate(event dashboardevents.DashboardEvent) {
switch e := event.(type) {
case *dashboardevents.WorkspaceError:
log.Println("[TRACE] Got workspace error event", *e)
log.Printf("[TRACE] WorkspaceError event: %s", e.Error)
payload, payloadError = buildWorkspaceErrorPayload(e)
if payloadError != nil {
return
@@ -261,7 +258,7 @@ func (s *Server) HandleWorkspaceUpdate(event dashboardevents.DashboardEvent) {
outputError(s.context, e.Error)
case *dashboardevents.ExecutionStarted:
log.Println("[TRACE] Got execution started event", *e)
log.Printf("[TRACE] ExecutionStarted event session %s, dashboard %s", e.Session, e.Dashboard.GetName())
payload, payloadError = buildExecutionStartedPayload(e)
if payloadError != nil {
return
@@ -269,13 +266,13 @@ func (s *Server) HandleWorkspaceUpdate(event dashboardevents.DashboardEvent) {
s.mutex.Lock()
s.writePayloadToSession(e.Session, payload)
s.mutex.Unlock()
outputWait(s.context, fmt.Sprintf("Dashboard execution started: %s", e.DashboardNode.GetName()))
outputWait(s.context, fmt.Sprintf("Dashboard execution started: %s", e.Dashboard.GetName()))
case *dashboardevents.LeafNodeError:
log.Println("[TRACE] Got leaf node error event", *e)
log.Printf("[TRACE] LeafNodeError event session %s, node %s, error %s", e.Session, e.LeafNode.GetName(), e.Error)
case *dashboardevents.LeafNodeProgress:
log.Println("[TRACE] Got leaf node complete event", *e)
log.Printf("[TRACE] LeafNodeProgress event session %s, node %s", e.Session, e.LeafNode.GetName())
payload, payloadError = buildLeafNodeProgressPayload(e)
if payloadError != nil {
return
@@ -285,7 +282,7 @@ func (s *Server) HandleWorkspaceUpdate(event dashboardevents.DashboardEvent) {
s.mutex.Unlock()
case *dashboardevents.LeafNodeComplete:
log.Println("[TRACE] Got leaf node complete event", *e)
log.Printf("[TRACE] LeafNodeComplete event session %s, node %s", e.Session, e.LeafNode.GetName())
payload, payloadError = buildLeafNodeCompletePayload(e)
if payloadError != nil {
return
@@ -295,7 +292,7 @@ func (s *Server) HandleWorkspaceUpdate(event dashboardevents.DashboardEvent) {
s.mutex.Unlock()
case *dashboardevents.DashboardChanged:
log.Println("[TRACE] Got dashboard changed event", *e)
log.Println("[TRACE] DashboardChanged event", *e)
deletedDashboards := e.DeletedDashboards
newDashboards := e.NewDashboards
@@ -408,13 +405,13 @@ func (s *Server) HandleWorkspaceUpdate(event dashboardevents.DashboardEvent) {
}
case *dashboardevents.DashboardError:
log.Println("[TRACE] Got dashboard error event", *e)
log.Println("[TRACE] dashboard error event", *e)
case *dashboardevents.DashboardComplete:
log.Println("[TRACE] Got dashboard complete event", *e)
log.Println("[TRACE] dashboard complete event", *e)
case *dashboardevents.ExecutionComplete:
log.Println("[TRACE] Got execution complete event", *e)
log.Println("[TRACE] execution complete event", *e)
payload, payloadError = buildExecutionCompletePayload(e)
if payloadError != nil {
return
@@ -430,12 +427,12 @@ func (s *Server) HandleWorkspaceUpdate(event dashboardevents.DashboardEvent) {
func (s *Server) Init(ctx context.Context) {
// Return list of dashboards on connect
s.webSocket.HandleConnect(func(session *melody.Session) {
log.Println("[TRACE] Client connected")
log.Println("[TRACE] client connected")
s.addSession(session)
})
s.webSocket.HandleDisconnect(func(session *melody.Session) {
log.Println("[TRACE] Client disconnected")
log.Println("[TRACE] client disconnected")
s.clearSession(ctx, session)
})
@@ -445,7 +442,6 @@ func (s *Server) Init(ctx context.Context) {
func (s *Server) handleMessageFunc(ctx context.Context) func(session *melody.Session, msg []byte) {
return func(session *melody.Session, msg []byte) {
log.Println("[TRACE] Got message", string(msg))
// TODO TEMP GET SESSION ID
sessionId := s.getSessionId(session)
@@ -453,6 +449,11 @@ func (s *Server) handleMessageFunc(ctx context.Context) func(session *melody.Ses
var request ClientRequest
// if we could not decode message - ignore
if err := json.Unmarshal(msg, &request); err == nil {
if request.Action != "keep_alive" {
log.Println("[TRACE] message", string(msg))
}
switch request.Action {
case "get_dashboard_metadata":
payload, err := buildDashboardMetadataPayload(s.workspace.GetResourceMaps())

View File

@@ -76,6 +76,8 @@ func LoadMod(modPath string, parentRunCtx *parse.RunContext) (mod *modconfig.Mod
}
// now we have loaded dependencies, set the current mod on the run context
runCtx.CurrentMod = mod
runCtx.PushParent(mod)
defer runCtx.PopParent()
// if flag is set, create pseudo resources by mapping files
var pseudoResources []modconfig.MappableResource

View File

@@ -2,18 +2,39 @@ package modconfig
import (
"fmt"
"strings"
"github.com/hashicorp/hcl/v2"
)
func GetAnonymousResourceShortName(block *hcl.Block, mod *Mod) string {
func GetAnonymousResourceShortName(block *hcl.Block, parent ModTreeItem) string {
var shortName string
anonymous := len(block.Labels) == 0
if anonymous {
shortName = mod.GetUniqueName(fmt.Sprintf("anonymous_%s", block.Type))
shortName = GetUniqueName(block.Type, parent)
} else {
shortName = block.Labels[0]
}
return shortName
}
// GetUniqueName returns a name unique within the scope of this execution tree
func GetUniqueName(blockType string, parent ModTreeItem) string {
// count how many children of this block type the parent has
childCount := 0
for _, child := range parent.GetChildren() {
parsedName, err := ParseResourceName(child.Name())
if err != nil {
// we do not expect this
continue
}
if parsedName.ItemType == blockType {
childCount++
}
}
sanitisedParentName := strings.Replace(parent.GetUnqualifiedName(), ".", "_", -1)
return fmt.Sprintf("%s_anonymous_%s_%d", sanitisedParentName, blockType, childCount)
}

View File

@@ -43,8 +43,8 @@ type Benchmark struct {
parents []ModTreeItem
}
func NewBenchmark(block *hcl.Block, mod *Mod) *Benchmark {
shortName := GetAnonymousResourceShortName(block, mod)
func NewBenchmark(block *hcl.Block, mod *Mod, parent ModTreeItem) *Benchmark {
shortName := GetAnonymousResourceShortName(block, parent)
benchmark := &Benchmark{
ShortName: shortName,
FullName: fmt.Sprintf("%s.benchmark.%s", mod.ShortName, shortName),

View File

@@ -50,8 +50,8 @@ type Control struct {
parents []ModTreeItem
}
func NewControl(block *hcl.Block, mod *Mod) *Control {
shortName := GetAnonymousResourceShortName(block, mod)
func NewControl(block *hcl.Block, mod *Mod, parent ModTreeItem) *Control {
shortName := GetAnonymousResourceShortName(block, parent)
control := &Control{
ShortName: shortName,
FullName: fmt.Sprintf("%s.control.%s", mod.ShortName, shortName),

View File

@@ -49,9 +49,9 @@ type Dashboard struct {
HclType string
}
func NewDashboard(block *hcl.Block, mod *Mod) *Dashboard {
func NewDashboard(block *hcl.Block, mod *Mod, parent ModTreeItem) *Dashboard {
// TODO [reports] think about nested report???
shortName := GetAnonymousResourceShortName(block, mod)
shortName := GetAnonymousResourceShortName(block, parent)
c := &Dashboard{
HclType: block.Type,
ShortName: shortName,
@@ -83,6 +83,11 @@ func (d *Dashboard) Name() string {
// OnDecoded implements HclResource
func (d *Dashboard) OnDecoded(block *hcl.Block) hcl.Diagnostics {
d.setBaseProperties()
d.ChildNames = make([]string, len(d.children))
for i, child := range d.children {
d.ChildNames[i] = child.Name()
}
return nil
}
@@ -200,10 +205,10 @@ func (d *Dashboard) Diff(other *Dashboard) *DashboardTreeItemDiffs {
func (d *Dashboard) SetChildren(children []ModTreeItem) {
d.children = children
d.ChildNames = make([]string, len(children))
for i, child := range children {
d.ChildNames[i] = child.Name()
}
}
func (d *Dashboard) AddChild(child ModTreeItem) {
d.children = append(d.children, child)
}
// GetUnqualifiedName implements DashboardLeafNode, ModTreeItem

View File

@@ -46,8 +46,8 @@ type DashboardCard struct {
metadata *ResourceMetadata
}
func NewDashboardCard(block *hcl.Block, mod *Mod) *DashboardCard {
shortName := GetAnonymousResourceShortName(block, mod)
func NewDashboardCard(block *hcl.Block, mod *Mod, parent ModTreeItem) *DashboardCard {
shortName := GetAnonymousResourceShortName(block, parent)
c := &DashboardCard{
ShortName: shortName,
FullName: fmt.Sprintf("%s.%s.%s", mod.ShortName, block.Type, shortName),

View File

@@ -52,8 +52,8 @@ type DashboardChart struct {
parents []ModTreeItem
}
func NewDashboardChart(block *hcl.Block, mod *Mod) *DashboardChart {
shortName := GetAnonymousResourceShortName(block, mod)
func NewDashboardChart(block *hcl.Block, mod *Mod, parent ModTreeItem) *DashboardChart {
shortName := GetAnonymousResourceShortName(block, parent)
c := &DashboardChart{
ShortName: shortName,
FullName: fmt.Sprintf("%s.%s.%s", mod.ShortName, block.Type, shortName),

View File

@@ -37,8 +37,8 @@ type DashboardContainer struct {
runtimeDependencyGraph *topsort.Graph
}
func NewDashboardContainer(block *hcl.Block, mod *Mod) *DashboardContainer {
shortName := GetAnonymousResourceShortName(block, mod)
func NewDashboardContainer(block *hcl.Block, mod *Mod, parent ModTreeItem) *DashboardContainer {
shortName := GetAnonymousResourceShortName(block, parent)
c := &DashboardContainer{
ShortName: shortName,
FullName: fmt.Sprintf("%s.%s.%s", mod.ShortName, block.Type, shortName),
@@ -67,7 +67,11 @@ func (c *DashboardContainer) Name() string {
}
// OnDecoded implements HclResource
func (c *DashboardContainer) OnDecoded(block *hcl.Block) hcl.Diagnostics {
func (c *DashboardContainer) OnDecoded(*hcl.Block) hcl.Diagnostics {
c.ChildNames = make([]string, len(c.children))
for i, child := range c.children {
c.ChildNames[i] = child.Name()
}
return nil
}
@@ -160,10 +164,10 @@ func (c *DashboardContainer) Diff(other *DashboardContainer) *DashboardTreeItemD
func (c *DashboardContainer) SetChildren(children []ModTreeItem) {
c.children = children
c.ChildNames = make([]string, len(children))
for i, child := range children {
c.ChildNames[i] = child.Name()
}
}
func (c *DashboardContainer) AddChild(child ModTreeItem) {
c.children = append(c.children, child)
}
// GetUnqualifiedName implements DashboardLeafNode, ModTreeItem

View File

@@ -48,8 +48,8 @@ type DashboardHierarchy struct {
parents []ModTreeItem
}
func NewDashboardHierarchy(block *hcl.Block, mod *Mod) *DashboardHierarchy {
shortName := GetAnonymousResourceShortName(block, mod)
func NewDashboardHierarchy(block *hcl.Block, mod *Mod, parent ModTreeItem) *DashboardHierarchy {
shortName := GetAnonymousResourceShortName(block, parent)
h := &DashboardHierarchy{
ShortName: shortName,
FullName: fmt.Sprintf("%s.%s.%s", mod.ShortName, block.Type, shortName),

View File

@@ -44,8 +44,8 @@ type DashboardImage struct {
parents []ModTreeItem
}
func NewDashboardImage(block *hcl.Block, mod *Mod) *DashboardImage {
shortName := GetAnonymousResourceShortName(block, mod)
func NewDashboardImage(block *hcl.Block, mod *Mod, parent ModTreeItem) *DashboardImage {
shortName := GetAnonymousResourceShortName(block, parent)
i := &DashboardImage{
ShortName: shortName,
FullName: fmt.Sprintf("%s.%s.%s", mod.ShortName, block.Type, shortName),

View File

@@ -72,7 +72,7 @@ func (i *DashboardInput) Clone() *DashboardInput {
}
func NewDashboardInput(block *hcl.Block, mod *Mod) *DashboardInput {
func NewDashboardInput(block *hcl.Block, mod *Mod, parent ModTreeItem) *DashboardInput {
// input cannot be anonymous
shortName := block.Labels[0]
i := &DashboardInput{

View File

@@ -47,8 +47,8 @@ type DashboardTable struct {
parents []ModTreeItem
}
func NewDashboardTable(block *hcl.Block, mod *Mod) *DashboardTable {
shortName := GetAnonymousResourceShortName(block, mod)
func NewDashboardTable(block *hcl.Block, mod *Mod, parent ModTreeItem) *DashboardTable {
shortName := GetAnonymousResourceShortName(block, parent)
t := &DashboardTable{
ShortName: shortName,
FullName: fmt.Sprintf("%s.%s.%s", mod.ShortName, block.Type, shortName),

View File

@@ -34,8 +34,8 @@ type DashboardText struct {
parents []ModTreeItem
}
func NewDashboardText(block *hcl.Block, mod *Mod) *DashboardText {
shortName := GetAnonymousResourceShortName(block, mod)
func NewDashboardText(block *hcl.Block, mod *Mod, parent ModTreeItem) *DashboardText {
shortName := GetAnonymousResourceShortName(block, parent)
t := &DashboardText{
ShortName: shortName,
FullName: fmt.Sprintf("%s.%s.%s", mod.ShortName, block.Type, shortName),

View File

@@ -82,7 +82,3 @@ type DashboardLeafNode interface {
type ResourceMapsProvider interface {
GetResourceMaps() *WorkspaceResourceMaps
}
type UniqueNameProvider interface {
GetUniqueName(string) string
}

View File

@@ -24,7 +24,6 @@ const defaultModName = "local"
// Mod is a struct representing a Mod resource
type Mod struct {
ResourceWithMetadataBase
UniqueNameProviderBase
// ShortName is the mod name, e.g. azure_thrifty
ShortName string `cty:"short_name" hcl:"name,label"`

View File

@@ -1,26 +0,0 @@
package modconfig
import "fmt"
type UniqueNameProviderBase struct {
nameMap map[string]struct{}
}
// GetUniqueName returns a name unique within the scope of this execution tree
func (p *UniqueNameProviderBase) GetUniqueName(name string) string {
if p.nameMap == nil {
p.nameMap = make(map[string]struct{})
}
// keep adding a suffix until we get a unique name
uniqueName := name
suffix := 0
for {
if _, ok := p.nameMap[uniqueName]; !ok {
p.nameMap[uniqueName] = struct{}{}
return uniqueName
}
suffix++
uniqueName = fmt.Sprintf("%s_%d", name, suffix)
}
return uniqueName
}

View File

@@ -40,6 +40,7 @@ func decode(runCtx *RunContext) hcl.Diagnostics {
Summary: "failed to determine required dependency order",
Detail: err.Error()})
}
for _, block := range blocks {
resources, res := decodeBlock(block, runCtx)
if !res.Success() {
@@ -162,33 +163,34 @@ func resourceForBlock(block *hcl.Block, runCtx *RunContext) (modconfig.HclResour
var resource modconfig.HclResource
// runCtx already contains the current mod
mod := runCtx.CurrentMod
parent := runCtx.PeekParent()
switch block.Type {
case modconfig.BlockTypeMod:
resource = mod
case modconfig.BlockTypeQuery:
resource = modconfig.NewQuery(block, mod)
case modconfig.BlockTypeControl:
resource = modconfig.NewControl(block, mod)
resource = modconfig.NewControl(block, mod, parent)
case modconfig.BlockTypeBenchmark:
resource = modconfig.NewBenchmark(block, mod)
resource = modconfig.NewBenchmark(block, mod, parent)
case modconfig.BlockTypeDashboard:
resource = modconfig.NewDashboard(block, mod)
resource = modconfig.NewDashboard(block, mod, parent)
case modconfig.BlockTypeContainer:
resource = modconfig.NewDashboardContainer(block, mod)
resource = modconfig.NewDashboardContainer(block, mod, parent)
case modconfig.BlockTypeChart:
resource = modconfig.NewDashboardChart(block, mod)
resource = modconfig.NewDashboardChart(block, mod, parent)
case modconfig.BlockTypeCard:
resource = modconfig.NewDashboardCard(block, mod)
resource = modconfig.NewDashboardCard(block, mod, parent)
case modconfig.BlockTypeHierarchy:
resource = modconfig.NewDashboardHierarchy(block, mod)
resource = modconfig.NewDashboardHierarchy(block, mod, parent)
case modconfig.BlockTypeImage:
resource = modconfig.NewDashboardImage(block, mod)
resource = modconfig.NewDashboardImage(block, mod, parent)
case modconfig.BlockTypeInput:
resource = modconfig.NewDashboardInput(block, mod)
resource = modconfig.NewDashboardInput(block, mod, parent)
case modconfig.BlockTypeTable:
resource = modconfig.NewDashboardTable(block, mod)
resource = modconfig.NewDashboardTable(block, mod, parent)
case modconfig.BlockTypeText:
resource = modconfig.NewDashboardText(block, mod)
resource = modconfig.NewDashboardText(block, mod, parent)
default:
return nil, hcl.Diagnostics{&hcl.Diagnostic{
Severity: hcl.DiagError,
@@ -373,7 +375,7 @@ func invalidParamDiags(resource modconfig.HclResource, block *hcl.Block) *hcl.Di
func decodeDashboard(block *hcl.Block, runCtx *RunContext) (*modconfig.Dashboard, *decodeResult) {
res := &decodeResult{}
dashboard := modconfig.NewDashboard(block, runCtx.CurrentMod)
dashboard := modconfig.NewDashboard(block, runCtx.CurrentMod, runCtx.PeekParent())
// do a partial decode using QueryProviderBlockSchema
// this will be used to pull out attributes which need manual decoding
@@ -410,11 +412,16 @@ func decodeDashboard(block *hcl.Block, runCtx *RunContext) (*modconfig.Dashboard
func decodeDashboardBlocks(content *hcl.BodyContent, dashboard *modconfig.Dashboard, runCtx *RunContext) *decodeResult {
var res = &decodeResult{}
// if children are declared inline as blocks, add them
var children []modconfig.ModTreeItem
var inputs []*modconfig.DashboardInput
// set dashboard as parent on the run context - this is used when generating names for anonymous blocks
runCtx.PushParent(dashboard)
defer func() {
runCtx.PopParent()
}()
for _, b := range content.Blocks {
// use generic block decoding
// decode block
resources, blockRes := decodeBlock(b, runCtx)
res.Merge(blockRes)
if !blockRes.Success() {
@@ -434,16 +441,14 @@ func decodeDashboardBlocks(content *hcl.BodyContent, dashboard *modconfig.Dashbo
// add the resource to the mod
addResourcesToMod(runCtx, resource)
// add to children
// (we expect this cast to always succeed
// add to the dashboard children
// (we expect this cast to always succeed)
if child, ok := resource.(modconfig.ModTreeItem); ok {
children = append(children, child)
dashboard.AddChild(child)
}
}
}
dashboard.SetChildren(children)
err := dashboard.SetInputs(inputs)
if err != nil {
res.addDiags(hcl.Diagnostics{&hcl.Diagnostic{
@@ -459,7 +464,7 @@ func decodeDashboardBlocks(content *hcl.BodyContent, dashboard *modconfig.Dashbo
func decodeDashboardContainer(block *hcl.Block, runCtx *RunContext) (*modconfig.DashboardContainer, *decodeResult) {
res := &decodeResult{}
container := modconfig.NewDashboardContainer(block, runCtx.CurrentMod)
container := modconfig.NewDashboardContainer(block, runCtx.CurrentMod, runCtx.PeekParent())
// do a partial decode using QueryProviderBlockSchema
// this will be used to pull out attributes which need manual decoding
@@ -485,8 +490,12 @@ func decodeDashboardContainer(block *hcl.Block, runCtx *RunContext) (*modconfig.
func decodeDashboardContainerBlocks(content *hcl.BodyContent, dashboardContainer *modconfig.DashboardContainer, runCtx *RunContext) *decodeResult {
var res = &decodeResult{}
// if children are declared inline as blocks, add them
var children []modconfig.ModTreeItem
// set container as parent on the run context - this is used when generating names for anonymous blocks
runCtx.PushParent(dashboardContainer)
defer func() {
runCtx.PopParent()
}()
for _, b := range content.Blocks {
// use generic block decoding
resources, blockRes := decodeBlock(b, runCtx)
@@ -501,21 +510,18 @@ func decodeDashboardContainerBlocks(content *hcl.BodyContent, dashboardContainer
addResourcesToMod(runCtx, resource)
if child, ok := resource.(modconfig.ModTreeItem); ok {
children = append(children, child)
dashboardContainer.AddChild(child)
}
}
}
dashboardContainer.SetChildren(children)
return res
}
func decodeBenchmark(block *hcl.Block, runCtx *RunContext) (*modconfig.Benchmark, *decodeResult) {
res := &decodeResult{}
benchmark := modconfig.NewBenchmark(block, runCtx.CurrentMod)
benchmark := modconfig.NewBenchmark(block, runCtx.CurrentMod, runCtx.PeekParent())
content, diags := block.Body.Content(BenchmarkBlockSchema)
res.handleDecodeDiags(content, benchmark, diags)

View File

@@ -53,6 +53,8 @@ type RunContext struct {
BlockTypeExclusions []string
Variables map[string]*modconfig.Variable
// stack of parent resources for the currently parsed block
parents []modconfig.ModTreeItem
dependencyGraph *topsort.Graph
// map of ReferenceTypeValueMaps keyed by mod
// NOTE: all values from root mod are keyed with "local"
@@ -89,6 +91,21 @@ func (r *RunContext) EnsureWorkspaceLock(mod *modconfig.Mod) error {
return nil
}
func (r *RunContext) PushParent(parent modconfig.ModTreeItem) {
r.parents = append(r.parents, parent)
}
func (r *RunContext) PopParent() modconfig.ModTreeItem {
n := len(r.parents) - 1
res := r.parents[n]
r.parents = r.parents[:n]
return res
}
func (r *RunContext) PeekParent() modconfig.ModTreeItem {
return r.parents[len(r.parents)-1]
}
// VariableValueMap converts a map of variables to a map of the underlying cty value
func VariableValueMap(variables map[string]*modconfig.Variable) map[string]cty.Value {
ret := make(map[string]cty.Value, len(variables))

View File

@@ -1,110 +1,21 @@
query "aws_region_input" {
sql = <<EOQ
select
title as label,
region as value
from
aws_region
where
account_id = '876515858155'
order by
title;
EOQ
}
query "aws_s3_buckets_by_versioning_enabled" {
sql = <<-EOQ
with versioning as (
select
case when versioning_enabled then 'Enabled' else 'Disabled' end as versioning_status,
region
from
aws_s3_bucket
)
select
versioning_status,
count(versioning_status) as "Total"
from
versioning
where
region = $1
group by
versioning_status
EOQ
param "region" {
default = "us-east-1"
}
}
dashboard "inputs" {
title = "Inputs Test"
chart "top_level1"{}
chart "top_level2"{}
input "region" {
sql = query.aws_region_input.sql
width = 3
dashboard "anonymous_naming" {
chart{}
container {
chart {}
chart {}
table{}
}
container {
chart {
type = "donut"
width = 3
query = query.aws_s3_buckets_by_versioning_enabled
args = {
"region" = self.input.region.value
}
title = "AWS IAM Users MFA Status"
series "Total" {
point "Disabled" {
color = "red"
}
point "Enabled" {
color = "green"
}
}
}
chart {}
chart {}
table{}
table{}
}
}
//
//query "q1"{
// sql = "select {1}"
// param "p1"{
// default = "1"
// }
//}
//
//dashboard "r1"{
// input "i1"{
// query = query.q1
// args = {
// "p1" = "FOO"
// }
// }
//
// chart {
// query = query.q1
// args = {
// "p1" = self.input.i1.value
// "p2" = "foo"
// "p3" = self.input.i1.value
// }
// }
// control {
// query = query.q1
// args = [ self.input.i1.value, "foo", self.input.i1.value]
// }
//}
//dashboard "r2"{
// dashboard "derived1" {
// base = dashboard.r1
// }
// dashboard "derived2" {
// base = dashboard.r1
// }
//}