mirror of
https://github.com/turbot/steampipe.git
synced 2026-05-12 03:01:06 -04:00
959 lines
26 KiB
Go
959 lines
26 KiB
Go
package modconfig
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/spf13/viper"
|
|
"github.com/turbot/steampipe/pkg/constants"
|
|
"github.com/turbot/steampipe/pkg/utils"
|
|
)
|
|
|
|
// ResourceMaps is a struct containing maps of all mod resource types
|
|
// This is provided to avoid db needing to reference workspace package
|
|
type ResourceMaps struct {
|
|
// the parent mod
|
|
Mod *Mod
|
|
|
|
// all mods (including deps)
|
|
Benchmarks map[string]*Benchmark
|
|
Controls map[string]*Control
|
|
Dashboards map[string]*Dashboard
|
|
DashboardCategories map[string]*DashboardCategory
|
|
DashboardCards map[string]*DashboardCard
|
|
DashboardCharts map[string]*DashboardChart
|
|
DashboardContainers map[string]*DashboardContainer
|
|
DashboardEdges map[string]*DashboardEdge
|
|
DashboardFlows map[string]*DashboardFlow
|
|
DashboardGraphs map[string]*DashboardGraph
|
|
DashboardHierarchies map[string]*DashboardHierarchy
|
|
DashboardImages map[string]*DashboardImage
|
|
DashboardInputs map[string]map[string]*DashboardInput
|
|
DashboardTables map[string]*DashboardTable
|
|
DashboardTexts map[string]*DashboardText
|
|
DashboardNodes map[string]*DashboardNode
|
|
GlobalDashboardInputs map[string]*DashboardInput
|
|
Locals map[string]*Local
|
|
Mods map[string]*Mod
|
|
Queries map[string]*Query
|
|
References map[string]*ResourceReference
|
|
// map of snapshot paths, keyed by snapshot name
|
|
Snapshots map[string]string
|
|
Variables map[string]*Variable
|
|
}
|
|
|
|
func NewModResources(mod *Mod) *ResourceMaps {
|
|
res := emptyModResources()
|
|
res.Mod = mod
|
|
res.Mods[mod.GetInstallCacheKey()] = mod
|
|
return res
|
|
}
|
|
|
|
func NewSourceSnapshotModResources(snapshotPaths []string) *ResourceMaps {
|
|
res := emptyModResources()
|
|
res.AddSnapshots(snapshotPaths)
|
|
return res
|
|
}
|
|
|
|
func emptyModResources() *ResourceMaps {
|
|
return &ResourceMaps{
|
|
Controls: make(map[string]*Control),
|
|
Benchmarks: make(map[string]*Benchmark),
|
|
Dashboards: make(map[string]*Dashboard),
|
|
DashboardCards: make(map[string]*DashboardCard),
|
|
DashboardCharts: make(map[string]*DashboardChart),
|
|
DashboardContainers: make(map[string]*DashboardContainer),
|
|
DashboardEdges: make(map[string]*DashboardEdge),
|
|
DashboardFlows: make(map[string]*DashboardFlow),
|
|
DashboardGraphs: make(map[string]*DashboardGraph),
|
|
DashboardHierarchies: make(map[string]*DashboardHierarchy),
|
|
DashboardImages: make(map[string]*DashboardImage),
|
|
DashboardInputs: make(map[string]map[string]*DashboardInput),
|
|
DashboardTables: make(map[string]*DashboardTable),
|
|
DashboardTexts: make(map[string]*DashboardText),
|
|
DashboardNodes: make(map[string]*DashboardNode),
|
|
DashboardCategories: make(map[string]*DashboardCategory),
|
|
GlobalDashboardInputs: make(map[string]*DashboardInput),
|
|
Locals: make(map[string]*Local),
|
|
Mods: make(map[string]*Mod),
|
|
Queries: make(map[string]*Query),
|
|
References: make(map[string]*ResourceReference),
|
|
Snapshots: make(map[string]string),
|
|
Variables: make(map[string]*Variable),
|
|
}
|
|
}
|
|
|
|
// QueryProviders returns a slice of all QueryProviders
|
|
func (m *ResourceMaps) QueryProviders() []QueryProvider {
|
|
res := make([]QueryProvider, m.queryProviderCount())
|
|
idx := 0
|
|
f := func(item HclResource) (bool, error) {
|
|
if queryProvider, ok := item.(QueryProvider); ok {
|
|
res[idx] = queryProvider
|
|
idx++
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
m.WalkResources(f)
|
|
|
|
return res
|
|
}
|
|
|
|
// TopLevelResources returns a new ResourceMaps containing only top level resources (i.e. no dependencies)
|
|
func (m *ResourceMaps) TopLevelResources() *ResourceMaps {
|
|
res := NewModResources(m.Mod)
|
|
|
|
f := func(item HclResource) (bool, error) {
|
|
if modTreeItem, ok := item.(ModTreeItem); ok && modTreeItem.GetMod().FullName == m.Mod.FullName {
|
|
res.AddResource(item)
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
m.WalkResources(f)
|
|
|
|
return res
|
|
}
|
|
|
|
func (m *ResourceMaps) Equals(other *ResourceMaps) bool {
|
|
//TODO use cmp.Equals or similar
|
|
if other == nil {
|
|
return false
|
|
}
|
|
|
|
for name, query := range m.Queries {
|
|
if otherQuery, ok := other.Queries[name]; !ok {
|
|
return false
|
|
} else if !query.Equals(otherQuery) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.Queries {
|
|
if _, ok := m.Queries[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, control := range m.Controls {
|
|
if otherControl, ok := other.Controls[name]; !ok {
|
|
return false
|
|
} else if !control.Equals(otherControl) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.Controls {
|
|
if _, ok := m.Controls[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, benchmark := range m.Benchmarks {
|
|
if otherBenchmark, ok := other.Benchmarks[name]; !ok {
|
|
return false
|
|
} else if !benchmark.Equals(otherBenchmark) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.Benchmarks {
|
|
if _, ok := m.Benchmarks[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, variable := range m.Variables {
|
|
if otherVariable, ok := other.Variables[name]; !ok {
|
|
return false
|
|
} else if !variable.Equals(otherVariable) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.Variables {
|
|
if _, ok := m.Variables[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, dashboard := range m.Dashboards {
|
|
if otherDashboard, ok := other.Dashboards[name]; !ok {
|
|
return false
|
|
} else if !dashboard.Equals(otherDashboard) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.Dashboards {
|
|
if _, ok := m.Dashboards[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, container := range m.DashboardContainers {
|
|
if otherContainer, ok := other.DashboardContainers[name]; !ok {
|
|
return false
|
|
} else if !container.Equals(otherContainer) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardContainers {
|
|
if _, ok := m.DashboardContainers[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, cards := range m.DashboardCards {
|
|
if otherCard, ok := other.DashboardCards[name]; !ok {
|
|
return false
|
|
} else if !cards.Equals(otherCard) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardCards {
|
|
if _, ok := m.DashboardCards[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, charts := range m.DashboardCharts {
|
|
if otherChart, ok := other.DashboardCharts[name]; !ok {
|
|
return false
|
|
} else if !charts.Equals(otherChart) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardCharts {
|
|
if _, ok := m.DashboardCharts[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, flows := range m.DashboardFlows {
|
|
if otherFlow, ok := other.DashboardFlows[name]; !ok {
|
|
return false
|
|
} else if !flows.Equals(otherFlow) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardFlows {
|
|
if _, ok := m.DashboardFlows[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, flows := range m.DashboardGraphs {
|
|
if otherFlow, ok := other.DashboardGraphs[name]; !ok {
|
|
return false
|
|
} else if !flows.Equals(otherFlow) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardGraphs {
|
|
if _, ok := m.DashboardGraphs[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, hierarchies := range m.DashboardHierarchies {
|
|
if otherHierarchy, ok := other.DashboardHierarchies[name]; !ok {
|
|
return false
|
|
} else if !hierarchies.Equals(otherHierarchy) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name := range other.DashboardNodes {
|
|
if _, ok := m.DashboardNodes[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name := range other.DashboardEdges {
|
|
if _, ok := m.DashboardEdges[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardCategories {
|
|
if _, ok := m.DashboardCategories[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, images := range m.DashboardImages {
|
|
if otherImage, ok := other.DashboardImages[name]; !ok {
|
|
return false
|
|
} else if !images.Equals(otherImage) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardImages {
|
|
if _, ok := m.DashboardImages[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, input := range m.GlobalDashboardInputs {
|
|
if otherInput, ok := other.GlobalDashboardInputs[name]; !ok {
|
|
return false
|
|
} else if !input.Equals(otherInput) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardInputs {
|
|
if _, ok := m.DashboardInputs[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for dashboardName, inputsForDashboard := range m.DashboardInputs {
|
|
if otherInputsForDashboard, ok := other.DashboardInputs[dashboardName]; !ok {
|
|
return false
|
|
} else {
|
|
|
|
for name, input := range inputsForDashboard {
|
|
if otherInput, ok := otherInputsForDashboard[name]; !ok {
|
|
return false
|
|
} else if !input.Equals(otherInput) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range otherInputsForDashboard {
|
|
if _, ok := inputsForDashboard[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
for name := range other.DashboardInputs {
|
|
if _, ok := m.DashboardInputs[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, table := range m.DashboardTables {
|
|
if otherTable, ok := other.DashboardTables[name]; !ok {
|
|
return false
|
|
} else if !table.Equals(otherTable) {
|
|
return false
|
|
}
|
|
}
|
|
for name, category := range m.DashboardCategories {
|
|
if otherCategory, ok := other.DashboardCategories[name]; !ok {
|
|
return false
|
|
} else if !category.Equals(otherCategory) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardTables {
|
|
if _, ok := m.DashboardTables[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, text := range m.DashboardTexts {
|
|
if otherText, ok := other.DashboardTexts[name]; !ok {
|
|
return false
|
|
} else if !text.Equals(otherText) {
|
|
return false
|
|
}
|
|
}
|
|
for name := range other.DashboardTexts {
|
|
if _, ok := m.DashboardTexts[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name, reference := range m.References {
|
|
if otherReference, ok := other.References[name]; !ok {
|
|
return false
|
|
} else if !reference.Equals(otherReference) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name := range other.References {
|
|
if _, ok := m.References[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for name := range other.Locals {
|
|
if _, ok := m.Locals[name]; !ok {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// GetResource tries to find a resource with the given name in the ResourceMaps
|
|
// NOTE: this does NOT support inputs, which are NOT uniquely named in a mod
|
|
func (m *ResourceMaps) GetResource(parsedName *ParsedResourceName) (resource HclResource, found bool) {
|
|
modName := parsedName.Mod
|
|
if modName == "" {
|
|
modName = m.Mod.ShortName
|
|
}
|
|
longName := fmt.Sprintf("%s.%s.%s", modName, parsedName.ItemType, parsedName.Name)
|
|
|
|
// NOTE: we could use WalkResources, but this is quicker
|
|
|
|
switch parsedName.ItemType {
|
|
case BlockTypeBenchmark:
|
|
resource, found = m.Benchmarks[longName]
|
|
case BlockTypeControl:
|
|
resource, found = m.Controls[longName]
|
|
case BlockTypeDashboard:
|
|
resource, found = m.Dashboards[longName]
|
|
case BlockTypeCard:
|
|
resource, found = m.DashboardCards[longName]
|
|
case BlockTypeCategory:
|
|
resource, found = m.DashboardCategories[longName]
|
|
case BlockTypeChart:
|
|
resource, found = m.DashboardCharts[longName]
|
|
case BlockTypeContainer:
|
|
resource, found = m.DashboardContainers[longName]
|
|
case BlockTypeEdge:
|
|
resource, found = m.DashboardEdges[longName]
|
|
case BlockTypeFlow:
|
|
resource, found = m.DashboardFlows[longName]
|
|
case BlockTypeGraph:
|
|
resource, found = m.DashboardGraphs[longName]
|
|
case BlockTypeHierarchy:
|
|
resource, found = m.DashboardHierarchies[longName]
|
|
case BlockTypeImage:
|
|
resource, found = m.DashboardImages[longName]
|
|
case BlockTypeNode:
|
|
resource, found = m.DashboardNodes[longName]
|
|
case BlockTypeTable:
|
|
resource, found = m.DashboardTables[longName]
|
|
case BlockTypeText:
|
|
resource, found = m.DashboardTexts[longName]
|
|
case BlockTypeInput:
|
|
// this function only supports global inputs
|
|
// if the input has a parent dashboard, you must use GetDashboardInput
|
|
resource, found = m.GlobalDashboardInputs[longName]
|
|
case BlockTypeQuery:
|
|
resource, found = m.Queries[longName]
|
|
case BlockTypeVariable:
|
|
resource, found = m.Variables[longName]
|
|
}
|
|
return resource, found
|
|
}
|
|
|
|
func (m *ResourceMaps) PopulateReferences() {
|
|
// only populate references if introspection is enabled
|
|
switch viper.GetString(constants.ArgIntrospection) {
|
|
case constants.IntrospectionInfo:
|
|
m.References = make(map[string]*ResourceReference)
|
|
|
|
resourceFunc := func(resource HclResource) (bool, error) {
|
|
if resourceWithMetadata, ok := resource.(ResourceWithMetadata); ok {
|
|
for _, ref := range resourceWithMetadata.GetReferences() {
|
|
m.References[ref.String()] = ref
|
|
}
|
|
|
|
// if this resource is a RuntimeDependencyProvider, add references from any 'withs'
|
|
if nep, ok := resource.(NodeAndEdgeProvider); ok {
|
|
m.populateNodeEdgeProviderRefs(nep)
|
|
} else if rdp, ok := resource.(RuntimeDependencyProvider); ok {
|
|
m.populateWithRefs(resource.GetUnqualifiedName(), rdp, getWithRoot(rdp))
|
|
}
|
|
}
|
|
|
|
// continue walking
|
|
return true, nil
|
|
}
|
|
m.WalkResources(resourceFunc)
|
|
}
|
|
}
|
|
|
|
// populate references for any nodes/edges which have reference a 'with'
|
|
func (m *ResourceMaps) populateNodeEdgeProviderRefs(nep NodeAndEdgeProvider) {
|
|
var withRoots = map[string]WithProvider{}
|
|
for _, n := range nep.GetNodes() {
|
|
// lazy populate with-root
|
|
// (build map keyed by parent
|
|
// - in theory if we inherit some nodes from base, they may have different parents)
|
|
parent := n.parents[0]
|
|
if withRoots[parent.Name()] == nil && len(n.GetRuntimeDependencies()) > 0 {
|
|
withRoots[parent.Name()] = getWithRoot(n)
|
|
}
|
|
m.populateWithRefs(nep.GetUnqualifiedName(), n, withRoots[parent.Name()])
|
|
}
|
|
for _, e := range nep.GetEdges() {
|
|
// lazy populate with root
|
|
parent := e.parents[0]
|
|
if withRoots[parent.Name()] == nil && len(e.GetRuntimeDependencies()) > 0 {
|
|
withRoots[parent.Name()] = getWithRoot(e)
|
|
}
|
|
|
|
m.populateWithRefs(nep.GetUnqualifiedName(), e, withRoots[parent.Name()])
|
|
}
|
|
}
|
|
|
|
// populate references for any 'with' blocks referenced by the RuntimeDependencyProvider
|
|
func (m *ResourceMaps) populateWithRefs(name string, rdp RuntimeDependencyProvider, withRoot WithProvider) {
|
|
// unexpected but behave nicely
|
|
if withRoot == nil {
|
|
return
|
|
}
|
|
for _, r := range rdp.GetRuntimeDependencies() {
|
|
if r.PropertyPath.ItemType == BlockTypeWith {
|
|
// find the with
|
|
w, ok := withRoot.GetWith(r.PropertyPath.ToResourceName())
|
|
if ok {
|
|
for _, withRef := range w.References {
|
|
// build a new reference changing the 'from' to the NodeAndEdgeProvider
|
|
ref := withRef.CloneWithNewFrom(name)
|
|
m.References[ref.String()] = ref
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// search up the tree to find the root resource which will host any referenced 'withs'
|
|
// this will either be a dashboard ot a NodeEdgeProvider
|
|
func getWithRoot(rdp RuntimeDependencyProvider) WithProvider {
|
|
var withRoot, _ = rdp.(WithProvider)
|
|
// get the root resource which 'owns' any withs
|
|
// (if our parent is the Mod, we are the root resource, otherwise traverse up until we find the mod
|
|
parent := rdp.GetParents()[0]
|
|
|
|
for parent.BlockType() != BlockTypeMod {
|
|
if wp, ok := parent.(WithProvider); ok {
|
|
withRoot = wp
|
|
}
|
|
parent = parent.GetParents()[0]
|
|
}
|
|
return withRoot
|
|
}
|
|
|
|
func (m *ResourceMaps) Empty() bool {
|
|
return len(m.Mods)+
|
|
len(m.Queries)+
|
|
len(m.Controls)+
|
|
len(m.Benchmarks)+
|
|
len(m.Variables)+
|
|
len(m.Dashboards)+
|
|
len(m.DashboardContainers)+
|
|
len(m.DashboardCards)+
|
|
len(m.DashboardCharts)+
|
|
len(m.DashboardFlows)+
|
|
len(m.DashboardGraphs)+
|
|
len(m.DashboardHierarchies)+
|
|
len(m.DashboardNodes)+
|
|
len(m.DashboardEdges)+
|
|
len(m.DashboardCategories)+
|
|
len(m.DashboardImages)+
|
|
len(m.DashboardInputs)+
|
|
len(m.DashboardTables)+
|
|
len(m.DashboardTexts)+
|
|
len(m.References) == 0
|
|
}
|
|
|
|
// this is used to create an optimized ResourceMaps containing only the queries which will be run
|
|
func (m *ResourceMaps) addControlOrQuery(provider QueryProvider) {
|
|
switch p := provider.(type) {
|
|
case *Query:
|
|
if p != nil {
|
|
m.Queries[p.FullName] = p
|
|
}
|
|
case *Control:
|
|
if p != nil {
|
|
m.Controls[p.FullName] = p
|
|
}
|
|
}
|
|
}
|
|
|
|
// WalkResources calls resourceFunc for every resource in the mod
|
|
// if any resourceFunc returns false or an error, return immediately
|
|
func (m *ResourceMaps) WalkResources(resourceFunc func(item HclResource) (bool, error)) error {
|
|
for _, r := range m.Benchmarks {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.Controls {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.Dashboards {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardCards {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardCategories {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardCharts {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardContainers {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardEdges {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardFlows {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardGraphs {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardHierarchies {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardImages {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, inputsForDashboard := range m.DashboardInputs {
|
|
for _, r := range inputsForDashboard {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
for _, r := range m.DashboardNodes {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardTables {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.DashboardTexts {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.GlobalDashboardInputs {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.Locals {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
for _, r := range m.Queries {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
// we cannot walk source snapshots as they are not a HclResource
|
|
for _, r := range m.Variables {
|
|
if continueWalking, err := resourceFunc(r); err != nil || !continueWalking {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *ResourceMaps) AddResource(item HclResource) hcl.Diagnostics {
|
|
var diags hcl.Diagnostics
|
|
switch r := item.(type) {
|
|
case *Query:
|
|
name := r.Name()
|
|
if existing, ok := m.Queries[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.Queries[name] = r
|
|
|
|
case *Control:
|
|
name := r.Name()
|
|
if existing, ok := m.Controls[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.Controls[name] = r
|
|
|
|
case *Benchmark:
|
|
name := r.Name()
|
|
if existing, ok := m.Benchmarks[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.Benchmarks[name] = r
|
|
|
|
case *Dashboard:
|
|
name := r.Name()
|
|
if existing, ok := m.Dashboards[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.Dashboards[name] = r
|
|
|
|
case *DashboardContainer:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardContainers[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardContainers[name] = r
|
|
|
|
case *DashboardCard:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardCards[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
} else {
|
|
m.DashboardCards[name] = r
|
|
}
|
|
|
|
case *DashboardChart:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardCharts[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardCharts[name] = r
|
|
|
|
case *DashboardFlow:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardFlows[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardFlows[name] = r
|
|
|
|
case *DashboardGraph:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardGraphs[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardGraphs[name] = r
|
|
|
|
case *DashboardHierarchy:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardHierarchies[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardHierarchies[name] = r
|
|
|
|
case *DashboardNode:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardNodes[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardNodes[name] = r
|
|
|
|
case *DashboardEdge:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardEdges[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardEdges[name] = r
|
|
|
|
case *DashboardCategory:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardCategories[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardCategories[name] = r
|
|
|
|
case *DashboardImage:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardImages[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardImages[name] = r
|
|
|
|
case *DashboardInput:
|
|
// if input has a dashboard asssigned, add to DashboardInputs
|
|
name := r.Name()
|
|
if dashboardName := r.DashboardName; dashboardName != "" {
|
|
inputsForDashboard := m.DashboardInputs[dashboardName]
|
|
if inputsForDashboard == nil {
|
|
inputsForDashboard = make(map[string]*DashboardInput)
|
|
m.DashboardInputs[dashboardName] = inputsForDashboard
|
|
}
|
|
// no need to check for dupes as we have already checked before adding the input to th m od
|
|
inputsForDashboard[name] = r
|
|
break
|
|
}
|
|
|
|
// so Dashboard Input must be global
|
|
if existing, ok := m.GlobalDashboardInputs[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.GlobalDashboardInputs[name] = r
|
|
|
|
case *DashboardTable:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardTables[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardTables[name] = r
|
|
|
|
case *DashboardText:
|
|
name := r.Name()
|
|
if existing, ok := m.DashboardTexts[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.DashboardTexts[name] = r
|
|
|
|
case *Variable:
|
|
// NOTE: add variable by unqualified name
|
|
name := r.UnqualifiedName
|
|
if existing, ok := m.Variables[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.Variables[name] = r
|
|
|
|
case *Local:
|
|
name := r.Name()
|
|
if existing, ok := m.Locals[name]; ok {
|
|
diags = append(diags, checkForDuplicate(existing, item)...)
|
|
break
|
|
}
|
|
m.Locals[name] = r
|
|
|
|
}
|
|
return diags
|
|
}
|
|
|
|
func (m *ResourceMaps) AddSnapshots(snapshotPaths []string) {
|
|
for _, snapshotPath := range snapshotPaths {
|
|
snapshotName := fmt.Sprintf("snapshot.%s", utils.FilenameNoExtension(snapshotPath))
|
|
m.Snapshots[snapshotName] = snapshotPath
|
|
}
|
|
}
|
|
|
|
func (m *ResourceMaps) Merge(others []*ResourceMaps) *ResourceMaps {
|
|
res := NewModResources(m.Mod)
|
|
sourceMaps := append([]*ResourceMaps{m}, others...)
|
|
|
|
for _, source := range sourceMaps {
|
|
for k, v := range source.Benchmarks {
|
|
res.Benchmarks[k] = v
|
|
}
|
|
for k, v := range source.Controls {
|
|
res.Controls[k] = v
|
|
}
|
|
for k, v := range source.Dashboards {
|
|
res.Dashboards[k] = v
|
|
}
|
|
for k, v := range source.DashboardContainers {
|
|
res.DashboardContainers[k] = v
|
|
}
|
|
for k, v := range source.DashboardCards {
|
|
res.DashboardCards[k] = v
|
|
}
|
|
for k, v := range source.DashboardCategories {
|
|
res.DashboardCategories[k] = v
|
|
}
|
|
for k, v := range source.DashboardCharts {
|
|
res.DashboardCharts[k] = v
|
|
}
|
|
for k, v := range source.DashboardEdges {
|
|
res.DashboardEdges[k] = v
|
|
}
|
|
for k, v := range source.DashboardFlows {
|
|
res.DashboardFlows[k] = v
|
|
}
|
|
for k, v := range source.DashboardGraphs {
|
|
res.DashboardGraphs[k] = v
|
|
}
|
|
for k, v := range source.DashboardHierarchies {
|
|
res.DashboardHierarchies[k] = v
|
|
}
|
|
for k, v := range source.DashboardNodes {
|
|
res.DashboardNodes[k] = v
|
|
}
|
|
for k, v := range source.DashboardImages {
|
|
res.DashboardImages[k] = v
|
|
}
|
|
for k, v := range source.DashboardInputs {
|
|
res.DashboardInputs[k] = v
|
|
}
|
|
for k, v := range source.DashboardTables {
|
|
res.DashboardTables[k] = v
|
|
}
|
|
for k, v := range source.DashboardTexts {
|
|
res.DashboardTexts[k] = v
|
|
}
|
|
for k, v := range source.GlobalDashboardInputs {
|
|
res.GlobalDashboardInputs[k] = v
|
|
}
|
|
for k, v := range source.Locals {
|
|
res.Locals[k] = v
|
|
}
|
|
for k, v := range source.Mods {
|
|
res.Mods[k] = v
|
|
}
|
|
for k, v := range source.Queries {
|
|
res.Queries[k] = v
|
|
}
|
|
for k, v := range source.Snapshots {
|
|
res.Snapshots[k] = v
|
|
}
|
|
for k, v := range source.Variables {
|
|
// NOTE: only include variables from root mod - we add in the others separately
|
|
if v.Mod.FullName == m.Mod.FullName {
|
|
res.Variables[k] = v
|
|
}
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func (m *ResourceMaps) queryProviderCount() int {
|
|
numDashboardInputs := 0
|
|
for _, inputs := range m.DashboardInputs {
|
|
numDashboardInputs += len(inputs)
|
|
}
|
|
|
|
numItems :=
|
|
len(m.Controls) +
|
|
len(m.DashboardCards) +
|
|
len(m.DashboardCharts) +
|
|
len(m.DashboardEdges) +
|
|
len(m.DashboardFlows) +
|
|
len(m.DashboardGraphs) +
|
|
len(m.DashboardHierarchies) +
|
|
len(m.DashboardImages) +
|
|
numDashboardInputs +
|
|
len(m.DashboardNodes) +
|
|
len(m.DashboardTables) +
|
|
len(m.GlobalDashboardInputs) +
|
|
len(m.Queries)
|
|
return numItems
|
|
}
|