Preserve sibling inheritance in configuration history and explanations

This commit is contained in:
2026-09-29 22:52:51 +00:00
parent 462e57df3e
commit f916e7d1f9
13 changed files with 615 additions and 20 deletions

View File

@@ -11,9 +11,11 @@ type inheritanceWalker struct {
root **document.Node
origins map[string]Origin
limits projectionLimits
retained *retainedNodeCounter
transfers []inheritanceTransfer
controls []inheritanceControl
replacements []inheritanceReplacement
operations []inheritanceOperation
}
// expandInheritanceTree resolves each destination scope after its enclosing
@@ -24,12 +26,19 @@ func expandInheritanceTree(input *document.Node, limits projectionLimits) (*inhe
}
func expandInheritanceWithOrigins(input *document.Node, inputOrigins map[string]Origin, limits projectionLimits) (*inheritanceScope, []Diagnostic) {
return expandInheritanceWithBudgets(input, inputOrigins, limits, historyNodeLimit)
}
func expandInheritanceWithBudgets(input *document.Node, inputOrigins map[string]Origin, limits projectionLimits, historyLimit int) (*inheritanceScope, []Diagnostic) {
if input == nil || input.Kind != document.Mapping {
return nil, []Diagnostic{{Code: CodeInheritanceInvalid, Severity: SeverityError,
Message: "inheritance requires a mapping document root"}}
}
root := input
walker := inheritanceWalker{root: &root, limits: limits, origins: cloneOriginIndex(inputOrigins)}
walker := inheritanceWalker{
root: &root, limits: limits, origins: cloneOriginIndex(inputOrigins),
retained: newRetainedNodeCounter(historyLimit),
}
if problem := walker.visit(&root, Path{}, true); problem != nil {
return nil, []Diagnostic{*problem}
}
@@ -38,7 +47,7 @@ func expandInheritanceWithOrigins(input *document.Node, inputOrigins map[string]
}
return &inheritanceScope{
root: root, origins: walker.origins, transfers: walker.transfers,
controls: walker.controls, replacements: walker.replacements,
controls: walker.controls, replacements: walker.replacements, operations: walker.operations,
}, nil
}
@@ -59,11 +68,14 @@ func (w *inheritanceWalker) visit(node **document.Node, path Path, unscoped bool
w.controls = append(w.controls, inheritanceControl{
path: referencePath, locations: []Location{location}, cancelled: true,
})
origin := directInheritanceOrigins(reference.Value, referencePath, w.origins)[referencePath.String()]
control := inheritanceControl{path: referencePath, locations: []Location{location}, cancelled: true}
w.operations = append(w.operations, inheritanceOperation{control: &control, controlOrigin: origin})
deleteOriginSubtree(w.origins, referencePath, reference.Value)
*node = withoutInheritanceDirective(*node)
}
}
resolved, diagnostics := expandSiblingDefinitionsWithOrigins(*node, path, w.limits, w.origins)
resolved, diagnostics := expandSiblingDefinitionsWithOrigins(*node, path, w.limits, w.origins, w.retained)
if len(diagnostics) != 0 {
return &diagnostics[0]
}
@@ -75,6 +87,7 @@ func (w *inheritanceWalker) visit(node **document.Node, path Path, unscoped bool
w.transfers = append(w.transfers, resolved.transfers...)
w.controls = append(w.controls, resolved.controls...)
w.replacements = append(w.replacements, resolved.replacements...)
w.operations = append(w.operations, resolved.operations...)
if !inheritanceLowerBoundWithin(*w.root, w.limits.nodes) {
problem := inheritanceBudgetDiagnostic(path, (*node).Location)
return &problem
@@ -251,3 +264,11 @@ func inheritanceBudgetDiagnostic(path Path, location document.Location) Diagnost
Message: "expanded configuration exceeds the projection limit", Primary: &primary,
}
}
func inheritanceHistoryBudgetDiagnostic(path Path, location document.Location) Diagnostic {
primary := publicLocation(location)
return Diagnostic{
Code: CodeExplainLimitExceeded, Severity: SeverityError, Path: path,
Message: "retained inheritance history exceeds the node limit", Primary: &primary,
}
}