Skip to content

Commit

Permalink
Refactor Blueprint.WalkModules
Browse files Browse the repository at this point in the history
* Add safe-version to avoid useless `return nil`;
* Supply `ModulePath` to the "dangerous" version;
* Use `WalkModules` instead of nested for-loops in few cases.
  • Loading branch information
mr0re1 committed Jan 4, 2024
1 parent 2ad353b commit 57bd88d
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 71 deletions.
28 changes: 16 additions & 12 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,10 @@ func (g DeploymentGroup) Kind() ModuleKind {
// Module return the module with the given ID
func (bp *Blueprint) Module(id ModuleID) (*Module, error) {
var mod *Module
bp.WalkModules(func(m *Module) error {
bp.WalkModulesSafe(func(m *Module) {
if m.ID == id {
mod = m
}
return nil
})
if mod == nil {
return nil, UnknownModuleError{id}
Expand All @@ -106,13 +105,12 @@ func (bp *Blueprint) Module(id ModuleID) (*Module, error) {
func (bp Blueprint) SuggestModuleIDHint(id ModuleID) (string, bool) {
clMod := ""
minDist := -1
bp.WalkModules(func(m *Module) error {
bp.WalkModulesSafe(func(m *Module) {
dist := levenshtein.Distance(string(m.ID), string(id), nil)
if minDist == -1.0 || dist < minDist {
minDist = dist
clMod = string(m.ID)
}
return nil
})

if clMod != "" && minDist <= maxHintDist {
Expand Down Expand Up @@ -322,9 +320,8 @@ func (bp Blueprint) ListUnusedVariables() []string {
ns := map[string]cty.Value{
"vars": bp.Vars.AsObject(),
}
bp.WalkModules(func(m *Module) error {
bp.WalkModulesSafe(func(m *Module) {
ns["module_"+string(m.ID)] = m.Settings.AsObject()
return nil
})
for _, v := range bp.Validators {
ns["validator_"+v.Validator] = v.Inputs.AsObject()
Expand Down Expand Up @@ -398,11 +395,10 @@ func (dc DeploymentConfig) ExportBlueprint(outputFilename string) error {

// addKindToModules sets the kind to 'terraform' when empty.
func (bp *Blueprint) addKindToModules() {
bp.WalkModules(func(m *Module) error {
bp.WalkModulesSafe(func(m *Module) {
if m.Kind == UnknownKind {
m.Kind = TerraformKind
}
return nil
})
}

Expand Down Expand Up @@ -440,7 +436,7 @@ func checkModulesAndGroups(bp Blueprint) error {

// validateModuleUseReferences verifies that any used modules exist and
// are in the correct group
func validateModuleUseReferences(p modulePath, mod Module, bp Blueprint) error {
func validateModuleUseReferences(p ModulePath, mod Module, bp Blueprint) error {
errs := Errors{}
for iu, used := range mod.Use {
errs.At(p.Use.At(iu), validateModuleReference(bp, mod, used))
Expand Down Expand Up @@ -636,21 +632,29 @@ func IsProductOfModuleUse(v cty.Value) []ModuleID {
}

// WalkModules walks all modules in the blueprint and calls the walker function
func (bp *Blueprint) WalkModules(walker func(*Module) error) error {
func (bp *Blueprint) WalkModules(walker func(ModulePath, *Module) error) error {
for ig := range bp.DeploymentGroups {
g := &bp.DeploymentGroups[ig]
for im := range g.Modules {
p := Root.Groups.At(ig).Modules.At(im)
m := &g.Modules[im]
if err := walker(m); err != nil {
if err := walker(p, m); err != nil {
return err
}
}
}
return nil
}

func (bp *Blueprint) WalkModulesSafe(walker func(*Module)) {
bp.WalkModules(func(_ ModulePath, m *Module) error {
walker(m)
return nil
})
}

// validate every module setting in the blueprint containing a reference
func validateModuleSettingReferences(p modulePath, m Module, bp Blueprint) error {
func validateModuleSettingReferences(p ModulePath, m Module, bp Blueprint) error {
errs := Errors{}
for k, v := range m.Settings.Items() {
for _, r := range valueReferences(v) {
Expand Down
36 changes: 13 additions & 23 deletions pkg/config/expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ func (dc *DeploymentConfig) expand() error {
return err
}

if err := dc.applyGlobalVariables(); err != nil {
return err
}
dc.applyGlobalVariables()

if err := validateInputsAllModules(dc.Config); err != nil {
return err
Expand All @@ -64,16 +62,14 @@ func (dc *DeploymentConfig) expand() error {

func validateInputsAllModules(bp Blueprint) error {
errs := Errors{}
for ig, g := range bp.DeploymentGroups {
for im, m := range g.Modules {
p := Root.Groups.At(ig).Modules.At(im)
errs.Add(validateModuleInputs(p, m, bp))
}
}
bp.WalkModules(func(p ModulePath, m *Module) error {
errs.Add(validateModuleInputs(p, *m, bp))
return nil
})
return errs.OrNil()
}

func validateModuleInputs(mp modulePath, m Module, bp Blueprint) error {
func validateModuleInputs(mp ModulePath, m Module, bp Blueprint) error {
mi := m.InfoOrDie()
errs := Errors{}
for _, input := range mi.Inputs {
Expand Down Expand Up @@ -197,7 +193,7 @@ func useModule(mod *Module, use Module) {
// applyUseModules applies variables from modules listed in the "use" field
// when/if applicable
func (dc *DeploymentConfig) applyUseModules() error {
return dc.Config.WalkModules(func(m *Module) error {
return dc.Config.WalkModules(func(_ ModulePath, m *Module) error {
for _, u := range m.Use {
used, err := dc.Config.Module(u)
if err != nil { // should never happen
Expand Down Expand Up @@ -233,9 +229,8 @@ func (dc *DeploymentConfig) combineLabels() {
gl := mergeMaps(defaults, vars.Get(labels).AsValueMap())
vars.Set(labels, cty.ObjectVal(gl))

dc.Config.WalkModules(func(mod *Module) error {
dc.Config.WalkModulesSafe(func(mod *Module) {
combineModuleLabels(mod, *dc)
return nil
})
}

Expand Down Expand Up @@ -270,7 +265,7 @@ func mergeMaps(ms ...map[string]cty.Value) map[string]cty.Value {
return r
}

func (bp Blueprint) applyGlobalVarsInModule(mod *Module) error {
func (bp Blueprint) applyGlobalVarsInModule(mod *Module) {
mi := mod.InfoOrDie()
for _, input := range mi.Inputs {
// Module setting exists? Nothing more needs to be done.
Expand All @@ -289,15 +284,12 @@ func (bp Blueprint) applyGlobalVarsInModule(mod *Module) error {
mod.Settings.Set(input.Name, cty.StringVal(string(mod.ID)))
}
}
return nil
}

// applyGlobalVariables takes any variables defined at the global level and
// applies them to module settings if not already set.
func (dc *DeploymentConfig) applyGlobalVariables() error {
return dc.Config.WalkModules(func(mod *Module) error {
return dc.Config.applyGlobalVarsInModule(mod)
})
func (dc *DeploymentConfig) applyGlobalVariables() {
dc.Config.WalkModulesSafe(dc.Config.applyGlobalVarsInModule)
}

// AutomaticOutputName generates unique deployment-group-level output names
Expand Down Expand Up @@ -400,15 +392,14 @@ func FindIntergroupReferences(v cty.Value, mod Module, bp Blueprint) []Reference
// find all intergroup references and add them to source Module.Outputs
func (bp *Blueprint) populateOutputs() {
refs := map[Reference]bool{}
bp.WalkModules(func(m *Module) error {
bp.WalkModulesSafe(func(m *Module) {
rs := FindIntergroupReferences(m.Settings.AsObject(), *m, *bp)
for _, r := range rs {
refs[r] = true
}
return nil
})

bp.WalkModules(func(m *Module) error {
bp.WalkModulesSafe(func(m *Module) {
for r := range refs {
if r.Module != m.ID {
continue // find IGC references pointing to this module
Expand All @@ -423,7 +414,6 @@ func (bp *Blueprint) populateOutputs() {
})

}
return nil
})
}

Expand Down
19 changes: 1 addition & 18 deletions pkg/config/expand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,6 @@ func (s *MySuite) TestApplyGlobalVariables(c *C) {
dc := s.getDeploymentConfigForTest()
mod := &dc.Config.DeploymentGroups[0].Modules[0]

// Test no inputs, none required
c.Check(dc.applyGlobalVariables(), IsNil)

// Test no inputs, one required, doesn't exist in globals
setTestModuleInfo(*mod, modulereader.ModuleInfo{
Inputs: []modulereader.VarInfo{{
Expand All @@ -334,25 +331,11 @@ func (s *MySuite) TestApplyGlobalVariables(c *C) {

// Test no input, one required, exists in globals
dc.Config.Vars.Set("gold", cty.StringVal("val"))
c.Check(dc.applyGlobalVariables(), IsNil)
dc.applyGlobalVariables()
c.Assert(
mod.Settings.Get("gold"),
DeepEquals,
GlobalRef("gold").AsExpression().AsValue())

// Test one input, one required
mod.Settings.Set("reqVar", cty.StringVal("val"))
c.Assert(dc.applyGlobalVariables(), IsNil)

// Test one input, none required, exists in globals
setTestModuleInfo(*mod, modulereader.ModuleInfo{
Inputs: []modulereader.VarInfo{{
Name: "gold",
Type: cty.String,
Required: false,
}},
})
c.Assert(dc.applyGlobalVariables(), IsNil)
}

func (s *zeroSuite) TestIsSimpleVariable(c *C) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/config/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,10 @@ type groupPath struct {
basePath
Name basePath `path:".group"`
Backend backendPath `path:".terraform_backend"`
Modules arrayPath[modulePath] `path:".modules"`
Modules arrayPath[ModulePath] `path:".modules"`
}

type modulePath struct {
type ModulePath struct {
basePath
Source basePath `path:".source"`
Kind basePath `path:".kind"`
Expand Down
6 changes: 3 additions & 3 deletions pkg/config/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func validateVars(vars Dict) error {
return errs.OrNil()
}

func validateModule(p modulePath, m Module, bp Blueprint) error {
func validateModule(p ModulePath, m Module, bp Blueprint) error {
// Source/Kind validations are required to pass to perform other validations
if m.Source == "" {
return BpError{p.Source, EmptyModuleSource}
Expand Down Expand Up @@ -113,7 +113,7 @@ func validateModule(p modulePath, m Module, bp Blueprint) error {
OrNil()
}

func validateOutputs(p modulePath, mod Module, info modulereader.ModuleInfo) error {
func validateOutputs(p ModulePath, mod Module, info modulereader.ModuleInfo) error {
errs := Errors{}
outputs := info.GetOutputsAsMap()

Expand All @@ -133,7 +133,7 @@ type moduleVariables struct {
}

func validateSettings(
p modulePath,
p ModulePath,
mod Module,
info modulereader.ModuleInfo) error {

Expand Down
3 changes: 1 addition & 2 deletions pkg/validators/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,11 @@ func testApisEnabled(bp config.Blueprint, inputs config.Dict) error {
return err
}
apis := map[string]bool{}
bp.WalkModules(func(m *config.Module) error {
bp.WalkModulesSafe(func(m *config.Module) {
services := m.InfoOrDie().Metadata.Spec.Requirements.Services
for _, api := range services {
apis[api] = true
}
return nil
})
return TestApisEnabled(p, maps.Keys(apis))
}
Expand Down
18 changes: 7 additions & 11 deletions pkg/validators/semantic.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,15 @@ func testModuleNotUsed(bp config.Blueprint, inputs config.Dict) error {
return err
}
errs := config.Errors{}
for ig, g := range bp.DeploymentGroups {
for im, m := range g.Modules {
ums := m.ListUnusedModules()
p := config.Root.Groups.At(ig).Modules.At(im).Use

for iu, u := range m.Use {
if slices.Contains(ums, u) {
errs.At(p.At(iu), fmt.Errorf(unusedModuleMsg, m.ID, u))
}
bp.WalkModules(func(p config.ModulePath, m *config.Module) error {
ums := m.ListUnusedModules()
for iu, u := range m.Use {
if slices.Contains(ums, u) {
errs.At(p.Use.At(iu), fmt.Errorf(unusedModuleMsg, m.ID, u))
}
}
}

return nil
})
return errs.OrNil()
}

Expand Down

0 comments on commit 57bd88d

Please sign in to comment.