Skip to content

Commit 2398a6f

Browse files
authored
chore: Tracking DAG nodes in components (#4998)
* chore: Tracking dependencies and dependents in components * chore: Track dependents when adjusting dependencies * fix: Addressing lints
1 parent 60e62ef commit 2398a6f

File tree

11 files changed

+410
-326
lines changed

11 files changed

+410
-326
lines changed

cli/commands/find/find.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,15 @@ func discoveredToFound(configs component.Components, opts *Options) (FoundConfig
186186
}
187187
}
188188

189-
if !opts.Dependencies || len(config.Dependencies) == 0 {
189+
if !opts.Dependencies || len(config.Dependencies()) == 0 {
190190
foundCfgs = append(foundCfgs, foundCfg)
191191

192192
continue
193193
}
194194

195-
foundCfg.Dependencies = make([]string, len(config.Dependencies))
195+
foundCfg.Dependencies = make([]string, len(config.Dependencies()))
196196

197-
for i, dep := range config.Dependencies {
197+
for i, dep := range config.Dependencies() {
198198
relDepPath, err := filepath.Rel(opts.WorkingDir, dep.Path)
199199
if err != nil {
200200
errs = append(errs, errors.New(err))

cli/commands/list/list.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,15 +211,15 @@ func discoveredToListed(configs component.Components, opts *Options) (ListedConf
211211
Path: relPath,
212212
}
213213

214-
if len(config.Dependencies) == 0 {
214+
if len(config.Dependencies()) == 0 {
215215
listedCfgs = append(listedCfgs, listedCfg)
216216

217217
continue
218218
}
219219

220-
listedCfg.Dependencies = make([]*ListedConfig, len(config.Dependencies))
220+
listedCfg.Dependencies = make([]*ListedConfig, len(config.Dependencies()))
221221

222-
for i, dep := range config.Dependencies {
222+
for i, dep := range config.Dependencies() {
223223
relDepPath, err := filepath.Rel(opts.WorkingDir, dep.Path)
224224
if err != nil {
225225
errs = append(errs, errors.New(err))

internal/component/component.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,44 @@ type Component struct {
3232
Kind Kind
3333
Path string
3434

35-
Dependencies Components
35+
dependencies Components
36+
dependents Components
3637

3738
External bool
3839
}
3940

41+
// AddDependency adds a dependency to the Component and vice versa.
42+
//
43+
// Using this method ensure that the dependency graph is properly maintained,
44+
// making it easier to look up dependents and dependencies on a given component
45+
// without the entire graph available.
46+
func (c *Component) AddDependency(dependency *Component) {
47+
c.dependencies = append(c.dependencies, dependency)
48+
49+
dependency.dependents = append(dependency.dependents, c)
50+
}
51+
52+
// AddDependent adds a dependent to the Component and vice versa.
53+
//
54+
// Using this method ensure that the dependency graph is properly maintained,
55+
// making it easier to look up dependents and dependencies on a given component
56+
// without the entire graph available.
57+
func (c *Component) AddDependent(dependent *Component) {
58+
c.dependents = append(c.dependents, dependent)
59+
60+
dependent.dependencies = append(dependent.dependencies, c)
61+
}
62+
63+
// Dependencies returns the dependencies of the Component.
64+
func (c *Component) Dependencies() Components {
65+
return c.dependencies
66+
}
67+
68+
// Dependents returns the dependents of the Component.
69+
func (c *Component) Dependents() Components {
70+
return c.dependents
71+
}
72+
4073
// DiscoveryContext is the context in which
4174
// a Component was discovered.
4275
//
@@ -134,7 +167,7 @@ func (c Components) CycleCheck() (*Component, error) {
134167
visited[component.Path] = true
135168
inPath[component.Path] = true
136169

137-
for _, dep := range component.Dependencies {
170+
for _, dep := range component.Dependencies() {
138171
if err := checkCycle(dep); err != nil {
139172
return err
140173
}

internal/component/component_test.go

Lines changed: 33 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -64,88 +64,56 @@ func TestComponentsCycleCheck(t *testing.T) {
6464
t.Parallel()
6565

6666
tests := []struct {
67+
setupFunc func() component.Components
6768
name string
68-
configs component.Components
6969
errorExpected bool
7070
}{
7171
{
7272
name: "no cycles",
73-
configs: component.Components{
74-
{
75-
Path: "a",
76-
Dependencies: component.Components{
77-
{Path: "b"},
78-
},
79-
},
80-
{Path: "b"},
73+
setupFunc: func() component.Components {
74+
a := &component.Component{Path: "a"}
75+
b := &component.Component{Path: "b"}
76+
a.AddDependency(b)
77+
return component.Components{a, b}
8178
},
8279
errorExpected: false,
8380
},
8481
{
8582
name: "direct cycle",
86-
configs: component.Components{
87-
{
88-
Path: "a",
89-
Dependencies: component.Components{
90-
{
91-
Path: "b",
92-
Dependencies: component.Components{
93-
{Path: "a"},
94-
},
95-
},
96-
},
97-
},
98-
{Path: "b"},
83+
setupFunc: func() component.Components {
84+
a := &component.Component{Path: "a"}
85+
b := &component.Component{Path: "b"}
86+
a.AddDependency(b)
87+
b.AddDependency(a)
88+
return component.Components{a, b}
9989
},
10090
errorExpected: true,
10191
},
10292
{
10393
name: "indirect cycle",
104-
configs: component.Components{
105-
{
106-
Path: "a",
107-
Dependencies: component.Components{
108-
{
109-
Path: "b",
110-
Dependencies: component.Components{
111-
{
112-
Path: "c",
113-
Dependencies: component.Components{
114-
{Path: "a"},
115-
},
116-
},
117-
},
118-
},
119-
},
120-
},
121-
{Path: "b"},
122-
{Path: "c"},
94+
setupFunc: func() component.Components {
95+
a := &component.Component{Path: "a"}
96+
b := &component.Component{Path: "b"}
97+
c := &component.Component{Path: "c"}
98+
a.AddDependency(b)
99+
b.AddDependency(c)
100+
c.AddDependency(a)
101+
return component.Components{a, b, c}
123102
},
124103
errorExpected: true,
125104
},
126105
{
127106
name: "diamond dependency - no cycle",
128-
configs: component.Components{
129-
{
130-
Path: "a",
131-
Dependencies: component.Components{
132-
{Path: "b"},
133-
{Path: "c"},
134-
},
135-
},
136-
{
137-
Path: "b",
138-
Dependencies: component.Components{
139-
{Path: "d"},
140-
},
141-
},
142-
{
143-
Path: "c",
144-
Dependencies: component.Components{
145-
{Path: "d"},
146-
},
147-
},
148-
{Path: "d"},
107+
setupFunc: func() component.Components {
108+
a := &component.Component{Path: "a"}
109+
b := &component.Component{Path: "b"}
110+
c := &component.Component{Path: "c"}
111+
d := &component.Component{Path: "d"}
112+
a.AddDependency(b)
113+
a.AddDependency(c)
114+
b.AddDependency(d)
115+
c.AddDependency(d)
116+
return component.Components{a, b, c, d}
149117
},
150118
errorExpected: false,
151119
},
@@ -155,7 +123,9 @@ func TestComponentsCycleCheck(t *testing.T) {
155123
t.Run(tt.name, func(t *testing.T) {
156124
t.Parallel()
157125

158-
cfg, err := tt.configs.CycleCheck()
126+
configs := tt.setupFunc()
127+
128+
cfg, err := configs.CycleCheck()
159129
if tt.errorExpected {
160130
require.Error(t, err)
161131
assert.Contains(t, err.Error(), "cycle detected")

internal/discovery/discovery.go

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ func String(c *component.Component) string {
387387
// ContainsDependencyInAncestry returns true if the Component or any of
388388
// its dependencies contains the given path as a dependency.
389389
func ContainsDependencyInAncestry(c *component.Component, path string) bool {
390-
for _, dep := range c.Dependencies {
390+
for _, dep := range c.Dependencies() {
391391
if dep.Path == path {
392392
return true
393393
}
@@ -965,7 +965,7 @@ func (d *Discovery) Discover(
965965
l.Debugf("Errors: %w", err)
966966
}
967967

968-
components = dependencyDiscovery.cfgs
968+
components = dependencyDiscovery.components
969969

970970
return nil
971971
})
@@ -1017,7 +1017,7 @@ func (d *Discovery) Discover(
10171017
// DependencyDiscovery is the configuration for a DependencyDiscovery.
10181018
type DependencyDiscovery struct {
10191019
discoveryContext *component.DiscoveryContext
1020-
cfgs component.Components
1020+
components component.Components
10211021
parserOptions []hclparse.Option
10221022
depthRemaining int
10231023
discoverExternal bool
@@ -1028,9 +1028,9 @@ type DependencyDiscovery struct {
10281028
// DependencyDiscoveryOption is a function that modifies a DependencyDiscovery.
10291029
type DependencyDiscoveryOption func(*DependencyDiscovery)
10301030

1031-
func NewDependencyDiscovery(cfgs component.Components, depthRemaining int) *DependencyDiscovery {
1031+
func NewDependencyDiscovery(components component.Components, depthRemaining int) *DependencyDiscovery {
10321032
return &DependencyDiscovery{
1033-
cfgs: cfgs,
1033+
components: components,
10341034
depthRemaining: depthRemaining,
10351035
}
10361036
}
@@ -1070,7 +1070,7 @@ func (d *DependencyDiscovery) WithDiscoveryContext(discoveryContext *component.D
10701070
func (d *DependencyDiscovery) DiscoverAllDependencies(ctx context.Context, l log.Logger, opts *options.TerragruntOptions) error {
10711071
errs := []error{}
10721072

1073-
for _, cfg := range d.cfgs {
1073+
for _, cfg := range d.components {
10741074
if cfg.Kind == component.Stack {
10751075
continue
10761076
}
@@ -1088,7 +1088,12 @@ func (d *DependencyDiscovery) DiscoverAllDependencies(ctx context.Context, l log
10881088
return nil
10891089
}
10901090

1091-
func (d *DependencyDiscovery) DiscoverDependencies(ctx context.Context, l log.Logger, opts *options.TerragruntOptions, dCfg *component.Component) error {
1091+
func (d *DependencyDiscovery) DiscoverDependencies(
1092+
ctx context.Context,
1093+
l log.Logger,
1094+
opts *options.TerragruntOptions,
1095+
dComponent *component.Component,
1096+
) error {
10921097
if d.depthRemaining <= 0 {
10931098
return errors.New("max dependency depth reached while discovering dependencies")
10941099
}
@@ -1099,19 +1104,19 @@ func (d *DependencyDiscovery) DiscoverDependencies(ctx context.Context, l log.Lo
10991104

11001105
// Stack configs don't have dependencies (at least for now),
11011106
// so we can return early.
1102-
if dCfg.Kind == component.Stack {
1107+
if dComponent.Kind == component.Stack {
11031108
return nil
11041109
}
11051110

11061111
// This should only happen if we're discovering an ancestor dependency.
1107-
if dCfg.Parsed == nil {
1108-
err := Parse(dCfg, ctx, l, opts, d.suppressParseErrors, d.parserOptions)
1112+
if dComponent.Parsed == nil {
1113+
err := Parse(dComponent, ctx, l, opts, d.suppressParseErrors, d.parserOptions)
11091114
if err != nil {
11101115
return errors.New(err)
11111116
}
11121117
}
11131118

1114-
dependencyBlocks := dCfg.Parsed.TerragruntDependencies
1119+
dependencyBlocks := dComponent.Parsed.TerragruntDependencies
11151120

11161121
depPaths := make([]string, 0, len(dependencyBlocks))
11171122

@@ -1127,16 +1132,16 @@ func (d *DependencyDiscovery) DiscoverDependencies(ctx context.Context, l log.Lo
11271132
depPath := dependency.ConfigPath.AsString()
11281133

11291134
if !filepath.IsAbs(depPath) {
1130-
depPath = filepath.Join(dCfg.Path, depPath)
1135+
depPath = filepath.Join(dComponent.Path, depPath)
11311136
}
11321137

11331138
depPaths = append(depPaths, depPath)
11341139
}
11351140

1136-
if dCfg.Parsed.Dependencies != nil {
1137-
for _, dependency := range dCfg.Parsed.Dependencies.Paths {
1141+
if dComponent.Parsed.Dependencies != nil {
1142+
for _, dependency := range dComponent.Parsed.Dependencies.Paths {
11381143
if !filepath.IsAbs(dependency) {
1139-
dependency = filepath.Join(dCfg.Path, dependency)
1144+
dependency = filepath.Join(dComponent.Path, dependency)
11401145
}
11411146

11421147
depPaths = append(depPaths, dependency)
@@ -1160,11 +1165,11 @@ func (d *DependencyDiscovery) DiscoverDependencies(ctx context.Context, l log.Lo
11601165
for depPath := range deduped {
11611166
external := true
11621167

1163-
for _, c := range d.cfgs {
1168+
for _, c := range d.components {
11641169
if c.Path == depPath {
11651170
external = false
11661171

1167-
dCfg.Dependencies = append(dCfg.Dependencies, c)
1172+
dComponent.AddDependency(c)
11681173

11691174
continue
11701175
}
@@ -1186,10 +1191,10 @@ func (d *DependencyDiscovery) DiscoverDependencies(ctx context.Context, l log.Lo
11861191
ext.DiscoveryContext = d.discoveryContext
11871192
}
11881193

1189-
dCfg.Dependencies = append(dCfg.Dependencies, ext)
1194+
dComponent.AddDependency(ext)
11901195

11911196
if d.discoverExternal {
1192-
d.cfgs = append(d.cfgs, ext)
1197+
d.components = append(d.components, ext)
11931198

11941199
err := d.DiscoverDependencies(ctx, l, opts, ext)
11951200
if err != nil {

0 commit comments

Comments
 (0)