Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 21 additions & 27 deletions pkg/composer/blueprint/blueprint_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,6 @@ func (b *BaseBlueprintHandler) GetLocalTemplateData() (map[string][]byte, error)
return nil, fmt.Errorf("failed to marshal composed blueprint: %w", err)
}
templateData["blueprint"] = composedBlueprintYAML
templateData["_template/blueprint.yaml"] = composedBlueprintYAML
}

var substitutionValues map[string]any
Expand Down Expand Up @@ -567,6 +566,9 @@ func (b *BaseBlueprintHandler) processOCIArtifact(templateData map[string][]byte
}
}
}
if blueprintData, exists := templateData["blueprint"]; exists {
featureTemplateData["blueprint"] = blueprintData
}

b.featureEvaluator.SetTemplateData(templateData)

Expand Down Expand Up @@ -758,7 +760,6 @@ func (b *BaseBlueprintHandler) getKustomizations() []blueprintv1alpha1.Kustomiza

// walkAndCollectTemplates recursively traverses the specified template directory and collects all files into the
// templateData map. It adds the contents of each file by a normalized relative path key prefixed with "_template/".
// Special files "schema.yaml" and "blueprint.yaml" are also stored under canonical keys ("schema", "blueprint").
// Directory entries are processed recursively. Any file or directory traversal errors are returned.
func (b *BaseBlueprintHandler) walkAndCollectTemplates(templateDir string, templateData map[string][]byte) error {
entries, err := b.shims.ReadDir(templateDir)
Expand Down Expand Up @@ -787,36 +788,23 @@ func (b *BaseBlueprintHandler) walkAndCollectTemplates(templateDir string, templ
relPath = strings.ReplaceAll(relPath, "\\", "/")
key := "_template/" + relPath

switch entry.Name() {
case "schema.yaml":
templateData["schema"] = content
templateData[key] = content
case "blueprint.yaml":
templateData["blueprint"] = content
templateData[key] = content
default:
templateData[key] = content
}
templateData[key] = content
}
}

return nil
}

// processFeatures loads the base blueprint and merges features that match evaluated conditions.
// It loads the base blueprint.yaml from templateData, loads features, evaluates their When expressions
// against the provided config, and merges matching features into the base blueprint. Features and their
// components are merged in deterministic order by feature name.
// processFeatures applies blueprint features by evaluating conditional expressions and merging matching feature content into the blueprint.
// It loads the base blueprint from the template data (from canonical or alternate blueprint file keys), unmarshals and merges it.
// Then, it loads all features from template data, sorts them deterministically by feature name, and for each feature that matches
// its condition (`When`), merges its Terraform components and Kustomizations that also match their conditions. Component and kustomization
// inputs, substitutions, and patches are evaluated and processed per strategy, and the resulting objects are merged or replaced in the blueprint
// according to the specified merge strategy.
func (b *BaseBlueprintHandler) processFeatures(templateData map[string][]byte, config map[string]any) error {
var blueprintData []byte
var exists bool

if blueprintData, exists = templateData["blueprint"]; !exists {
if blueprintData, exists = templateData["_template/blueprint.yaml"]; !exists {
if blueprintData, exists = templateData["blueprint.yaml"]; !exists {
blueprintData = nil
}
}
blueprintData, _ := templateData["_template/blueprint.yaml"]
if blueprintData == nil {
blueprintData, _ = templateData["blueprint"]
}

if blueprintData != nil {
Expand Down Expand Up @@ -903,6 +891,8 @@ func (b *BaseBlueprintHandler) processFeatures(templateData map[string][]byte, c
component.Inputs = b.deepMergeMaps(component.Inputs, filteredInputs)
}
}
} else {
component.Inputs = nil
}

strategy := terraformComponent.Strategy
Expand Down Expand Up @@ -960,7 +950,6 @@ func (b *BaseBlueprintHandler) processFeatures(templateData map[string][]byte, c
}
}

// Clear substitutions as they are used for ConfigMap generation and should not appear in the final blueprint
kustomizationCopy.Substitutions = nil

strategy := kustomization.Strategy
Expand Down Expand Up @@ -1181,7 +1170,9 @@ func (b *BaseBlueprintHandler) processLocalArtifact(templateData map[string][]by
}

if _, exists := templateData["_template/blueprint.yaml"]; !exists {
return fmt.Errorf("blueprint.yaml not found in artifact template data")
if _, exists := templateData["blueprint"]; !exists {
return fmt.Errorf("blueprint not found in artifact template data")
}
}

if schemaData, exists := templateData["_template/schema.yaml"]; exists {
Expand All @@ -1205,6 +1196,9 @@ func (b *BaseBlueprintHandler) processLocalArtifact(templateData map[string][]by
}
}
}
if blueprintData, exists := templateData["blueprint"]; exists {
featureTemplateData["blueprint"] = blueprintData
}

b.featureEvaluator.SetTemplateData(templateData)

Expand Down
11 changes: 2 additions & 9 deletions pkg/composer/blueprint/blueprint_handler_private_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,13 +328,6 @@ func TestBaseBlueprintHandler_walkAndCollectTemplates(t *testing.T) {
t.Errorf("Expected no error, got: %v", err)
}

if _, exists := templateData["schema"]; !exists {
t.Error("Expected 'schema' key to exist")
}
if _, exists := templateData["blueprint"]; !exists {
t.Error("Expected 'blueprint' key to exist")
}

if _, exists := templateData["_template/schema.yaml"]; !exists {
t.Error("Expected '_template/schema.yaml' key to exist")
}
Expand Down Expand Up @@ -5542,8 +5535,8 @@ metadata:
if err == nil {
t.Fatal("Expected error when blueprint.yaml is missing")
}
if !strings.Contains(err.Error(), "blueprint.yaml not found") {
t.Errorf("Expected error about missing blueprint.yaml, got: %v", err)
if !strings.Contains(err.Error(), "blueprint not found") {
t.Errorf("Expected error about missing blueprint, got: %v", err)
}
})

Expand Down
14 changes: 8 additions & 6 deletions pkg/composer/blueprint/blueprint_handler_public_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2574,10 +2574,12 @@ metadata:

// .jsonnet files are not collected in templateData; they are processed on-demand via jsonnet() function calls during feature evaluation

if content, exists := templateData["_template/blueprint.yaml"]; exists {
if content, exists := templateData["blueprint"]; exists {
if !strings.Contains(string(content), contextName) {
t.Errorf("Expected blueprint content to contain context name '%s', got: %s", contextName, string(content))
}
} else {
t.Error("Expected composed blueprint in templateData")
}

if content, exists := templateData["_template/features/aws.yaml"]; exists {
Expand Down Expand Up @@ -2897,7 +2899,7 @@ terraform:
t.Fatalf("Expected no error, got %v", err)
}

composedBlueprint, exists := templateData["_template/blueprint.yaml"]
composedBlueprint, exists := templateData["blueprint"]
if !exists {
t.Fatal("Expected composed blueprint in templateData")
}
Expand Down Expand Up @@ -2967,7 +2969,7 @@ terraform:
t.Fatalf("Expected no error, got %v", err)
}

composedBlueprint, exists := templateData["_template/blueprint.yaml"]
composedBlueprint, exists := templateData["blueprint"]
if !exists {
t.Fatal("Expected composed blueprint in templateData")
}
Expand Down Expand Up @@ -3067,7 +3069,7 @@ terraform:
t.Fatalf("Expected no error, got %v", err)
}

composedBlueprint, exists := templateData["_template/blueprint.yaml"]
composedBlueprint, exists := templateData["blueprint"]
if !exists {
t.Fatal("Expected composed blueprint in templateData")
}
Expand Down Expand Up @@ -3144,7 +3146,7 @@ kustomize:
t.Fatalf("Expected no error, got %v", err)
}

composedBlueprint, exists := templateData["_template/blueprint.yaml"]
composedBlueprint, exists := templateData["blueprint"]
if !exists {
t.Fatal("Expected composed blueprint in templateData")
}
Expand Down Expand Up @@ -3206,7 +3208,7 @@ metadata:
t.Fatalf("Expected no error, got %v", err)
}

if composedBlueprint, exists := templateData["_template/blueprint.yaml"]; exists {
if composedBlueprint, exists := templateData["blueprint"]; exists {
var blueprint blueprintv1alpha1.Blueprint
if err := yaml.Unmarshal(composedBlueprint, &blueprint); err != nil {
t.Fatalf("Failed to unmarshal blueprint: %v", err)
Expand Down
Loading