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
152 changes: 1 addition & 151 deletions pkg/composer/blueprint/blueprint_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (b *BaseBlueprintHandler) LoadBlueprint() error {
// the file is only written if it does not already exist. The method ensures the target directory exists,
// marshals the blueprint to YAML, and writes the file using the configured shims.
// Terraform inputs and kustomization substitutions are manually cleared to prevent them from appearing in the final blueprint.yaml.
// Also writes patches from Features and local templates to the context patches directory.
// Patches from _template, OCI artifacts, and contexts/ are processed in memory only and not written to disk.
func (b *BaseBlueprintHandler) Write(overwrite ...bool) error {
shouldOverwrite := false
if len(overwrite) > 0 {
Expand All @@ -187,17 +187,6 @@ func (b *BaseBlueprintHandler) Write(overwrite ...bool) error {
return fmt.Errorf("error setting repository defaults: %w", err)
}

for _, kustomization := range b.blueprint.Kustomizations {
strategicMergePatchesToWrite, _ := b.categorizePatches(kustomization)
if len(strategicMergePatchesToWrite) > 0 {
kustomizationWithPatches := kustomization
kustomizationWithPatches.Patches = strategicMergePatchesToWrite
if err := b.writeLocalTemplatePatches(kustomizationWithPatches, shouldOverwrite); err != nil {
return fmt.Errorf("error writing patches to context: %w", err)
}
}
}

if !shouldOverwrite {
if _, err := b.shims.Stat(yamlPath); err == nil {
return nil
Expand Down Expand Up @@ -1208,145 +1197,6 @@ func (b *BaseBlueprintHandler) categorizePatches(kustomization blueprintv1alpha1
return strategicMergePatchesToWrite, inlinePatches
}

// writeLocalTemplatePatches writes patches from local _template to the context patches directory.
// Patches are written to contexts/<context>/patches/<kustomize-name>/ as individual YAML files.
// Each patch file is named using kind-name.yaml format extracted from the patch metadata.
// Patch content is evaluated (jsonnet expressions are processed) before writing.
// If overwrite is false, existing patch files are not overwritten.
// Returns an error if directory creation or file writing fails.
func (b *BaseBlueprintHandler) writeLocalTemplatePatches(kustomization blueprintv1alpha1.Kustomization, overwrite bool) error {
if len(kustomization.Patches) == 0 {
return nil
}

configRoot := b.runtime.ConfigRoot
if configRoot == "" {
return nil
}

patchesDir := filepath.Join(configRoot, "patches", kustomization.Name)
if err := b.shims.MkdirAll(patchesDir, 0755); err != nil {
return fmt.Errorf("failed to create patches directory: %w", err)
}

config := make(map[string]any)
contextValues, err := b.runtime.ConfigHandler.GetContextValues()
if err == nil {
for k, v := range contextValues {
if k != "substitutions" {
config[k] = v
}
}
}

patchMap := make(map[string]string)
for i, patch := range kustomization.Patches {
if patch.Path == "" && patch.Patch == "" {
continue
}

var patchContent string

if patch.Patch != "" {
patchContent = patch.Patch
} else if patch.Path != "" {
patchFileFullPath := filepath.Join(b.runtime.TemplateRoot, patch.Path)
data, err := b.shims.ReadFile(patchFileFullPath)
if err != nil {
continue
}
patchContent = string(data)
evaluated, err := b.featureEvaluator.InterpolateString(patchContent, config, filepath.Dir(patchFileFullPath))
if err != nil {
return fmt.Errorf("failed to evaluate patch file %s: %w", patch.Path, err)
}
patchContent = evaluated
} else {
continue
}

if strings.TrimSpace(patchContent) == "" {
continue
}

var patchFileName string
var doc map[string]any
if err := b.shims.YamlUnmarshal([]byte(patchContent), &doc); err == nil {
if kind, ok := doc["kind"].(string); ok {
if metadata, ok := doc["metadata"].(map[string]any); ok {
if name, ok := metadata["name"].(string); ok {
filename := fmt.Sprintf("%s-%s.yaml", strings.ToLower(kind), name)
invalidChars := []string{"/", "\\", ":", "*", "?", "\"", "<", ">", "|"}
for _, char := range invalidChars {
filename = strings.ReplaceAll(filename, char, "-")
}
patchFileName = filename
}
}
}
}
if patchFileName == "" {
patchFileName = fmt.Sprintf("%d.yaml", i)
}
existingContent, exists := patchMap[patchFileName]
if exists {
var existingDoc, newDoc map[string]any
if err := b.shims.YamlUnmarshal([]byte(existingContent), &existingDoc); err != nil {
return fmt.Errorf("failed to parse existing patch: %w", err)
}
if err := b.shims.YamlUnmarshal([]byte(patchContent), &newDoc); err != nil {
return fmt.Errorf("failed to parse new patch: %w", err)
}
existingKind, existingKindOk := existingDoc["kind"].(string)
var existingName string
var existingNameOk bool
if metadata, ok := existingDoc["metadata"].(map[string]any); ok {
if name, ok := metadata["name"].(string); ok {
existingName = name
existingNameOk = true
}
}
newKind, newKindOk := newDoc["kind"].(string)
var newName string
var newNameOk bool
if metadata, ok := newDoc["metadata"].(map[string]any); ok {
if name, ok := metadata["name"].(string); ok {
newName = name
newNameOk = true
}
}
if existingKindOk && existingNameOk && newKindOk && newNameOk &&
existingKind == newKind && existingName == newName {
merged := b.deepMergeMaps(existingDoc, newDoc)
mergedYAML, err := b.shims.YamlMarshal(merged)
if err != nil {
return fmt.Errorf("failed to marshal merged patch: %w", err)
}
patchMap[patchFileName] = strings.TrimSpace(string(mergedYAML))
} else {
patchMap[patchFileName] = strings.TrimSpace(existingContent) + "\n---\n" + strings.TrimSpace(patchContent)
}
} else {
patchMap[patchFileName] = strings.TrimSpace(patchContent)
}
}

for patchFileName, patchContent := range patchMap {
patchFilePath := filepath.Join(patchesDir, patchFileName)
if !overwrite {
if _, err := b.shims.Stat(patchFilePath); err == nil {
continue
}
}

if err := b.shims.WriteFile(patchFilePath, []byte(patchContent), 0644); err != nil {
return fmt.Errorf("failed to write patch file %s: %w", patchFilePath, err)
}
}

return nil
}

// mergeLegacySpecialVariables merges legacy special variables into the common values map for backward compatibility.
// These variables were previously extracted from config and kustomize/values.yaml and are now merged from the config handler.
// This method can be removed when legacy variable support is no longer needed.
Expand Down
Loading
Loading