diff --git a/pkg/blueprint/blueprint_handler.go b/pkg/blueprint/blueprint_handler.go index 130b33212..da1decc27 100644 --- a/pkg/blueprint/blueprint_handler.go +++ b/pkg/blueprint/blueprint_handler.go @@ -72,6 +72,7 @@ type BaseBlueprintHandler struct { projectRoot string shims *Shims kustomizeData map[string]any + configLoaded bool } // NewBlueprintHandler creates a new instance of BaseBlueprintHandler. @@ -138,13 +139,25 @@ func (b *BaseBlueprintHandler) LoadConfig() error { if err != nil { return err } - return b.processBlueprintData(yamlData, &b.blueprint) + + if err := b.processBlueprintData(yamlData, &b.blueprint); err != nil { + return err + } + + b.configLoaded = true + return nil } // LoadData loads blueprint configuration from a map containing blueprint data. // It marshals the input map to YAML, processes it as a Blueprint object, and updates the handler's blueprint state. // The ociInfo parameter optionally provides OCI artifact source information for source resolution and tracking. +// If config is already loaded from YAML, this is a no-op to preserve resolved state. func (b *BaseBlueprintHandler) LoadData(data map[string]any, ociInfo ...*artifact.OCIArtifactInfo) error { + // If config is already loaded from YAML, don't overwrite with template data + if b.configLoaded { + return nil + } + yamlData, err := b.shims.YamlMarshal(data) if err != nil { return fmt.Errorf("error marshalling blueprint data to yaml: %w", err) diff --git a/pkg/blueprint/blueprint_handler_test.go b/pkg/blueprint/blueprint_handler_test.go index 1ca8993c5..fe6ddd9f5 100644 --- a/pkg/blueprint/blueprint_handler_test.go +++ b/pkg/blueprint/blueprint_handler_test.go @@ -3589,6 +3589,47 @@ func TestBlueprintHandler_LoadData(t *testing.T) { t.Errorf("Expected description to be 'A blueprint from OCI artifact', got %s", metadata.Description) } }) + + t.Run("LoadDataIgnoredWhenConfigAlreadyLoaded", func(t *testing.T) { + // Given a blueprint handler that has already loaded config + handler, _ := setup(t) + + // Load config first (simulates loading from YAML) + err := handler.LoadConfig() + if err != nil { + t.Fatalf("Failed to load config: %v", err) + } + + // Get the original metadata + originalMetadata := handler.GetMetadata() + + // And different blueprint data that would overwrite the config + differentData := map[string]any{ + "kind": "Blueprint", + "apiVersion": "v1alpha1", + "metadata": map[string]any{ + "name": "different-blueprint", + "description": "This should not overwrite the loaded config", + }, + } + + // When loading the different data + err = handler.LoadData(differentData) + + // Then no error should be returned + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + // But the metadata should remain unchanged (LoadData should be ignored) + currentMetadata := handler.GetMetadata() + if currentMetadata.Name != originalMetadata.Name { + t.Errorf("Expected metadata to remain unchanged, but name changed from %s to %s", originalMetadata.Name, currentMetadata.Name) + } + if currentMetadata.Description != originalMetadata.Description { + t.Errorf("Expected metadata to remain unchanged, but description changed from %s to %s", originalMetadata.Description, currentMetadata.Description) + } + }) } func TestBlueprintHandler_Write(t *testing.T) { diff --git a/pkg/pipelines/install.go b/pkg/pipelines/install.go index 1d4899fec..35ede5934 100644 --- a/pkg/pipelines/install.go +++ b/pkg/pipelines/install.go @@ -100,7 +100,12 @@ func (p *InstallPipeline) Execute(ctx context.Context) error { return fmt.Errorf("No blueprint handler found") } - // Phase 1: Process templates for kustomize data + // Phase 1: Load blueprint config (cached if already loaded) + if err := p.blueprintHandler.LoadConfig(); err != nil { + return fmt.Errorf("Error loading blueprint config: %w", err) + } + + // Phase 2: Process templates for kustomize data templateData, err := p.prepareTemplateData(ctx) if err != nil { return fmt.Errorf("failed to prepare template data: %w", err) @@ -110,7 +115,7 @@ func (p *InstallPipeline) Execute(ctx context.Context) error { return fmt.Errorf("failed to process template data: %w", err) } - // Phase 2: Generate kustomize data using generators + // Phase 3: Generate kustomize data using generators if len(renderedData) > 0 { for _, generator := range p.generators { if err := generator.Generate(renderedData, false); err != nil { @@ -119,15 +124,12 @@ func (p *InstallPipeline) Execute(ctx context.Context) error { } } - // Phase 3: Load blueprint config and install - if err := p.blueprintHandler.LoadConfig(); err != nil { - return fmt.Errorf("Error loading blueprint config: %w", err) - } + // Phase 4: Install blueprint if err := p.blueprintHandler.Install(); err != nil { return fmt.Errorf("Error installing blueprint: %w", err) } - // Phase 4: Wait for kustomizations if requested + // Phase 5: Wait for kustomizations if requested waitFlag := ctx.Value("wait") if waitFlag != nil { if wait, ok := waitFlag.(bool); ok && wait {