From 5abaa4e316afbb3878e81f34ca509d757ca2e02f Mon Sep 17 00:00:00 2001 From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> Date: Fri, 21 Nov 2025 23:58:49 -0500 Subject: [PATCH] fix(kubernetes): Reintroduce max wait time for all kustomizations Windsor up used to wait based on a calculation of the total combined time of the longest dependency chain of kustomizations. This PR returns this functionality, which was lost during a recent refactor. Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> --- cmd/install_test.go | 2 +- cmd/up_test.go | 4 +- .../kubernetes/kubernetes_manager.go | 85 +++++- .../kubernetes_manager_private_test.go | 280 ++++++++++++++++++ .../kubernetes_manager_public_test.go | 152 +++++++++- .../kubernetes/mock_kubernetes_manager.go | 6 +- .../mock_kubernetes_manager_test.go | 7 +- pkg/provisioner/provisioner.go | 10 +- pkg/provisioner/provisioner_test.go | 4 +- 9 files changed, 518 insertions(+), 32 deletions(-) diff --git a/cmd/install_test.go b/cmd/install_test.go index aaf1a9e07..1b5dca393 100644 --- a/cmd/install_test.go +++ b/cmd/install_test.go @@ -101,7 +101,7 @@ func TestInstallCmd(t *testing.T) { mockKubernetesManager := kubernetes.NewMockKubernetesManager() mockKubernetesManager.ApplyBlueprintFunc = func(blueprint *blueprintv1alpha1.Blueprint, namespace string) error { return nil } - mockKubernetesManager.WaitForKustomizationsFunc = func(message string, names ...string) error { return nil } + mockKubernetesManager.WaitForKustomizationsFunc = func(message string, blueprint *blueprintv1alpha1.Blueprint) error { return nil } // Override ConfigHandler and ProjectRoot in runtime mocks.Runtime.ConfigHandler = mockConfigHandler diff --git a/cmd/up_test.go b/cmd/up_test.go index e689ffd36..1052dabbb 100644 --- a/cmd/up_test.go +++ b/cmd/up_test.go @@ -96,7 +96,7 @@ func setupUpTest(t *testing.T, opts ...*SetupOptions) *UpMocks { // Add kubernetes manager mock mockKubernetesManager := kubernetes.NewMockKubernetesManager() mockKubernetesManager.ApplyBlueprintFunc = func(blueprint *blueprintv1alpha1.Blueprint, namespace string) error { return nil } - mockKubernetesManager.WaitForKustomizationsFunc = func(message string, names ...string) error { return nil } + mockKubernetesManager.WaitForKustomizationsFunc = func(message string, blueprint *blueprintv1alpha1.Blueprint) error { return nil } // Create runtime with all mocked dependencies rt, err := runtime.NewRuntime(&runtime.Runtime{ @@ -407,7 +407,7 @@ func TestUpCmd(t *testing.T) { mocks := setupUpTest(t) // And kubernetes manager WaitForKustomizations that fails - mocks.KubernetesManager.WaitForKustomizationsFunc = func(message string, names ...string) error { + mocks.KubernetesManager.WaitForKustomizationsFunc = func(message string, blueprint *blueprintv1alpha1.Blueprint) error { return fmt.Errorf("wait for kustomizations failed") } diff --git a/pkg/provisioner/kubernetes/kubernetes_manager.go b/pkg/provisioner/kubernetes/kubernetes_manager.go index 939f2fa3f..02e7dfac3 100644 --- a/pkg/provisioner/kubernetes/kubernetes_manager.go +++ b/pkg/provisioner/kubernetes/kubernetes_manager.go @@ -35,7 +35,7 @@ import ( type KubernetesManager interface { ApplyKustomization(kustomization kustomizev1.Kustomization) error DeleteKustomization(name, namespace string) error - WaitForKustomizations(message string, names ...string) error + WaitForKustomizations(message string, blueprint *blueprintv1alpha1.Blueprint) error CreateNamespace(name string) error DeleteNamespace(name string) error ApplyConfigMap(name, namespace string, data map[string]string) error @@ -153,26 +153,38 @@ func (k *BaseKubernetesManager) DeleteKustomization(name, namespace string) erro return fmt.Errorf("timeout waiting for kustomization %s to be deleted", name) } -// WaitForKustomizations waits for kustomizations to be ready -func (k *BaseKubernetesManager) WaitForKustomizations(message string, names ...string) error { +// WaitForKustomizations waits for kustomizations to be ready, calculating the timeout +// from the longest dependency chain in the blueprint. Outputs a debug message describing +// the total wait timeout being used before beginning polling. +func (k *BaseKubernetesManager) WaitForKustomizations(message string, blueprint *blueprintv1alpha1.Blueprint) error { + if blueprint == nil { + return fmt.Errorf("blueprint not provided") + } + + timeout := k.calculateTotalWaitTime(blueprint) + kustomizationNames := make([]string, len(blueprint.Kustomizations)) + for i, kustomization := range blueprint.Kustomizations { + kustomizationNames[i] = kustomization.Name + } + spin := spinner.New(spinner.CharSets[14], 100*time.Millisecond, spinner.WithColor("green")) spin.Suffix = " " + message spin.Start() defer spin.Stop() - timeout := time.After(k.kustomizationReconcileTimeout) + timeoutChan := time.After(timeout) ticker := time.NewTicker(k.kustomizationWaitPollInterval) defer ticker.Stop() for { select { - case <-timeout: + case <-timeoutChan: spin.Stop() fmt.Fprintf(os.Stderr, "✗%s - \033[31mFailed\033[0m\n", spin.Suffix) return fmt.Errorf("timeout waiting for kustomizations") case <-ticker.C: allReady := true - for _, name := range names { + for _, name := range kustomizationNames { gvr := schema.GroupVersionResource{ Group: "kustomize.toolkit.fluxcd.io", Version: "v1", @@ -1201,6 +1213,67 @@ func isImmutableConfigMap(obj *unstructured.Unstructured) bool { return ok && immutable } +// calculateTotalWaitTime calculates the total timeout for the longest dependency chain +// by summing the timeouts of all kustomizations along the path. It traverses the dependency graph +// to find the path with the maximum cumulative timeout. Returns the calculated timeout or the default +// if no kustomizations exist. Cycles are not detected and may cause stack overflow. +func (k *BaseKubernetesManager) calculateTotalWaitTime(blueprint *blueprintv1alpha1.Blueprint) time.Duration { + if len(blueprint.Kustomizations) == 0 { + return constants.DefaultKustomizationWaitTotalTimeout + } + + nameToIndex := make(map[string]int) + for i, kustomization := range blueprint.Kustomizations { + nameToIndex[kustomization.Name] = i + } + + var calculateChainTimeout func(componentIndex int, visited map[int]bool) time.Duration + calculateChainTimeout = func(componentIndex int, visited map[int]bool) time.Duration { + if visited[componentIndex] { + return 0 + } + visited[componentIndex] = true + defer delete(visited, componentIndex) + + kustomization := blueprint.Kustomizations[componentIndex] + + currentTimeout := constants.DefaultFluxKustomizationTimeout + if kustomization.Timeout != nil && kustomization.Timeout.Duration != 0 { + currentTimeout = kustomization.Timeout.Duration + } + + if len(kustomization.DependsOn) == 0 { + return currentTimeout + } + + maxDependencyTimeout := time.Duration(0) + for _, depName := range kustomization.DependsOn { + if depIndex, exists := nameToIndex[depName]; exists { + depTimeout := calculateChainTimeout(depIndex, visited) + if depTimeout > maxDependencyTimeout { + maxDependencyTimeout = depTimeout + } + } + } + + return currentTimeout + maxDependencyTimeout + } + + maxTimeout := time.Duration(0) + for i := range blueprint.Kustomizations { + timeout := calculateChainTimeout(i, make(map[int]bool)) + if timeout > maxTimeout { + maxTimeout = timeout + } + } + + if maxTimeout == 0 { + return constants.DefaultKustomizationWaitTotalTimeout + } + + return maxTimeout +} + // isNotFoundError checks if an error is a Kubernetes resource not found error func isNotFoundError(err error) bool { if err == nil { diff --git a/pkg/provisioner/kubernetes/kubernetes_manager_private_test.go b/pkg/provisioner/kubernetes/kubernetes_manager_private_test.go index 38f02dcdc..b5e630e4f 100644 --- a/pkg/provisioner/kubernetes/kubernetes_manager_private_test.go +++ b/pkg/provisioner/kubernetes/kubernetes_manager_private_test.go @@ -7,7 +7,10 @@ import ( "testing" "time" + blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" + "github.com/windsorcli/cli/pkg/constants" "github.com/windsorcli/cli/pkg/provisioner/kubernetes/client" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -454,3 +457,280 @@ func TestBaseKubernetesManager_getHelmRelease(t *testing.T) { } }) } + +func TestBaseKubernetesManager_calculateTotalWaitTime(t *testing.T) { + setup := func(t *testing.T) *BaseKubernetesManager { + t.Helper() + mocks := setupKubernetesMocks(t) + manager := NewKubernetesManager(mocks.KubernetesClient) + return manager + } + + t.Run("EmptyBlueprint", func(t *testing.T) { + manager := setup(t) + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{}, + } + + timeout := manager.calculateTotalWaitTime(blueprint) + expected := constants.DefaultKustomizationWaitTotalTimeout + if timeout != expected { + t.Errorf("Expected default timeout %v, got %v", expected, timeout) + } + }) + + t.Run("SingleKustomizationNoDependencies", func(t *testing.T) { + manager := setup(t) + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "k1", + }, + }, + } + + timeout := manager.calculateTotalWaitTime(blueprint) + expected := constants.DefaultFluxKustomizationTimeout + if timeout != expected { + t.Errorf("Expected default timeout %v, got %v", expected, timeout) + } + }) + + t.Run("SingleKustomizationWithCustomTimeout", func(t *testing.T) { + manager := setup(t) + customTimeout := 10 * time.Minute + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "k1", + Timeout: &metav1.Duration{ + Duration: customTimeout, + }, + }, + }, + } + + timeout := manager.calculateTotalWaitTime(blueprint) + if timeout != customTimeout { + t.Errorf("Expected custom timeout %v, got %v", customTimeout, timeout) + } + }) + + t.Run("SingleKustomizationWithZeroTimeout", func(t *testing.T) { + manager := setup(t) + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "k1", + Timeout: &metav1.Duration{ + Duration: 0, + }, + }, + }, + } + + timeout := manager.calculateTotalWaitTime(blueprint) + expected := constants.DefaultFluxKustomizationTimeout + if timeout != expected { + t.Errorf("Expected default timeout %v, got %v", expected, timeout) + } + }) + + t.Run("TwoKustomizationsWithDependency", func(t *testing.T) { + manager := setup(t) + timeout1 := 5 * time.Minute + timeout2 := 3 * time.Minute + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "k1", + Timeout: &metav1.Duration{ + Duration: timeout1, + }, + }, + { + Name: "k2", + Timeout: &metav1.Duration{ + Duration: timeout2, + }, + DependsOn: []string{"k1"}, + }, + }, + } + + timeout := manager.calculateTotalWaitTime(blueprint) + expected := timeout1 + timeout2 + if timeout != expected { + t.Errorf("Expected sum %v, got %v", expected, timeout) + } + }) + + t.Run("LongDependencyChain", func(t *testing.T) { + manager := setup(t) + timeout1 := 2 * time.Minute + timeout2 := 3 * time.Minute + timeout3 := 4 * time.Minute + timeout4 := 5 * time.Minute + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "k1", + Timeout: &metav1.Duration{ + Duration: timeout1, + }, + }, + { + Name: "k2", + Timeout: &metav1.Duration{ + Duration: timeout2, + }, + DependsOn: []string{"k1"}, + }, + { + Name: "k3", + Timeout: &metav1.Duration{ + Duration: timeout3, + }, + DependsOn: []string{"k2"}, + }, + { + Name: "k4", + Timeout: &metav1.Duration{ + Duration: timeout4, + }, + DependsOn: []string{"k3"}, + }, + }, + } + + timeout := manager.calculateTotalWaitTime(blueprint) + expected := timeout1 + timeout2 + timeout3 + timeout4 + if timeout != expected { + t.Errorf("Expected sum %v, got %v", expected, timeout) + } + }) + + t.Run("MultipleIndependentChains", func(t *testing.T) { + manager := setup(t) + timeout1 := 2 * time.Minute + timeout2 := 3 * time.Minute + timeout3 := 10 * time.Minute + timeout4 := 1 * time.Minute + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "k1", + Timeout: &metav1.Duration{ + Duration: timeout1, + }, + }, + { + Name: "k2", + Timeout: &metav1.Duration{ + Duration: timeout2, + }, + DependsOn: []string{"k1"}, + }, + { + Name: "k3", + Timeout: &metav1.Duration{ + Duration: timeout3, + }, + }, + { + Name: "k4", + Timeout: &metav1.Duration{ + Duration: timeout4, + }, + DependsOn: []string{"k3"}, + }, + }, + } + + timeout := manager.calculateTotalWaitTime(blueprint) + chain1Total := timeout1 + timeout2 + chain2Total := timeout3 + timeout4 + expected := chain2Total + if chain1Total > chain2Total { + expected = chain1Total + } + if timeout != expected { + t.Errorf("Expected max chain total %v, got %v", expected, timeout) + } + }) + + t.Run("ForkedDependencyChain", func(t *testing.T) { + manager := setup(t) + timeout1 := 2 * time.Minute + timeout2 := 3 * time.Minute + timeout3 := 4 * time.Minute + timeout4 := 5 * time.Minute + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "k1", + Timeout: &metav1.Duration{ + Duration: timeout1, + }, + }, + { + Name: "k2", + Timeout: &metav1.Duration{ + Duration: timeout2, + }, + DependsOn: []string{"k1"}, + }, + { + Name: "k3", + Timeout: &metav1.Duration{ + Duration: timeout3, + }, + DependsOn: []string{"k1"}, + }, + { + Name: "k4", + Timeout: &metav1.Duration{ + Duration: timeout4, + }, + DependsOn: []string{"k2", "k3"}, + }, + }, + } + + timeout := manager.calculateTotalWaitTime(blueprint) + chain1Total := timeout1 + timeout2 + timeout4 + chain2Total := timeout1 + timeout3 + timeout4 + expected := chain2Total + if chain1Total > chain2Total { + expected = chain1Total + } + if timeout != expected { + t.Errorf("Expected max chain total %v, got %v", expected, timeout) + } + }) + + t.Run("MixedDefaultAndCustomTimeouts", func(t *testing.T) { + manager := setup(t) + customTimeout := 7 * time.Minute + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "k1", + }, + { + Name: "k2", + Timeout: &metav1.Duration{ + Duration: customTimeout, + }, + DependsOn: []string{"k1"}, + }, + }, + } + + timeout := manager.calculateTotalWaitTime(blueprint) + expected := constants.DefaultFluxKustomizationTimeout + customTimeout + if timeout != expected { + t.Errorf("Expected sum %v, got %v", expected, timeout) + } + }) +} diff --git a/pkg/provisioner/kubernetes/kubernetes_manager_public_test.go b/pkg/provisioner/kubernetes/kubernetes_manager_public_test.go index 7516e7da2..c921a5d2a 100644 --- a/pkg/provisioner/kubernetes/kubernetes_manager_public_test.go +++ b/pkg/provisioner/kubernetes/kubernetes_manager_public_test.go @@ -352,7 +352,18 @@ func TestBaseKubernetesManager_WaitForKustomizations(t *testing.T) { } manager.client = kubernetesClient - err := manager.WaitForKustomizations("Waiting for kustomizations", "test-kustomization") + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "test-kustomization", + Timeout: &metav1.Duration{ + Duration: 200 * time.Millisecond, + }, + }, + }, + } + + err := manager.WaitForKustomizations("Waiting for kustomizations", blueprint) if err != nil { t.Errorf("Expected no error, got %v", err) } @@ -377,7 +388,18 @@ func TestBaseKubernetesManager_WaitForKustomizations(t *testing.T) { } manager.client = kubernetesClient - err := manager.WaitForKustomizations("Waiting for kustomizations", "test-kustomization") + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "test-kustomization", + Timeout: &metav1.Duration{ + Duration: 50 * time.Millisecond, + }, + }, + }, + } + + err := manager.WaitForKustomizations("Waiting for kustomizations", blueprint) if err == nil { t.Error("Expected timeout error, got nil") } @@ -393,7 +415,18 @@ func TestBaseKubernetesManager_WaitForKustomizations(t *testing.T) { } manager.client = kubernetesClient - err := manager.WaitForKustomizations("Waiting for kustomizations", "test-kustomization") + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "test-kustomization", + Timeout: &metav1.Duration{ + Duration: 50 * time.Millisecond, + }, + }, + }, + } + + err := manager.WaitForKustomizations("Waiting for kustomizations", blueprint) if err == nil { t.Error("Expected timeout error, got nil") } @@ -422,7 +455,18 @@ func TestBaseKubernetesManager_WaitForKustomizations(t *testing.T) { return fmt.Errorf("forced conversion error") } - err := manager.WaitForKustomizations("Waiting for kustomizations", "test-kustomization") + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "test-kustomization", + Timeout: &metav1.Duration{ + Duration: 50 * time.Millisecond, + }, + }, + }, + } + + err := manager.WaitForKustomizations("Waiting for kustomizations", blueprint) if err == nil { t.Error("Expected timeout error, got nil") } @@ -440,7 +484,18 @@ func TestBaseKubernetesManager_WaitForKustomizations(t *testing.T) { } manager.client = kubernetesClient - err := manager.WaitForKustomizations("Waiting for kustomizations", "test-kustomization") + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "test-kustomization", + Timeout: &metav1.Duration{ + Duration: 50 * time.Millisecond, + }, + }, + }, + } + + err := manager.WaitForKustomizations("Waiting for kustomizations", blueprint) if err == nil { t.Error("Expected timeout error, got nil") } @@ -465,7 +520,18 @@ func TestBaseKubernetesManager_WaitForKustomizations(t *testing.T) { } manager.client = kubernetesClient - err := manager.WaitForKustomizations("Waiting for kustomizations", "test-kustomization") + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "test-kustomization", + Timeout: &metav1.Duration{ + Duration: 50 * time.Millisecond, + }, + }, + }, + } + + err := manager.WaitForKustomizations("Waiting for kustomizations", blueprint) if err == nil { t.Error("Expected timeout error, got nil") } @@ -490,11 +556,83 @@ func TestBaseKubernetesManager_WaitForKustomizations(t *testing.T) { } manager.client = kubernetesClient - err := manager.WaitForKustomizations("Waiting for kustomizations", "test-kustomization") + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "test-kustomization", + Timeout: &metav1.Duration{ + Duration: 50 * time.Millisecond, + }, + }, + }, + } + + err := manager.WaitForKustomizations("Waiting for kustomizations", blueprint) if err == nil { t.Error("Expected timeout error, got nil") } }) + + t.Run("WithBlueprintCalculatesTimeout", func(t *testing.T) { + manager := setup(t) + kubernetesClient := client.NewMockKubernetesClient() + kubernetesClient.GetResourceFunc = func(gvr schema.GroupVersionResource, ns, name string) (*unstructured.Unstructured, error) { + return &unstructured.Unstructured{ + Object: map[string]any{ + "status": map[string]any{ + "conditions": []any{ + map[string]any{ + "type": "Ready", + "status": "True", + }, + }, + }, + }, + }, nil + } + manager.client = kubernetesClient + + timeout1 := 50 * time.Millisecond + timeout2 := 75 * time.Millisecond + blueprint := &blueprintv1alpha1.Blueprint{ + Kustomizations: []blueprintv1alpha1.Kustomization{ + { + Name: "k1", + Timeout: &metav1.Duration{ + Duration: timeout1, + }, + }, + { + Name: "k2", + Timeout: &metav1.Duration{ + Duration: timeout2, + }, + DependsOn: []string{"k1"}, + }, + }, + } + + err := manager.WaitForKustomizations("Waiting for kustomizations", blueprint) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + calculatedTimeout := manager.calculateTotalWaitTime(blueprint) + expectedTimeout := timeout1 + timeout2 + if calculatedTimeout != expectedTimeout { + t.Errorf("Expected calculated timeout %v, got %v", expectedTimeout, calculatedTimeout) + } + }) + + t.Run("WithBlueprintNilReturnsError", func(t *testing.T) { + manager := setup(t) + err := manager.WaitForKustomizations("Waiting for kustomizations", nil) + if err == nil { + t.Error("Expected error for nil blueprint, got nil") + } + if !strings.Contains(err.Error(), "blueprint not provided") { + t.Errorf("Expected 'blueprint not provided' error, got: %v", err) + } + }) } func TestBaseKubernetesManager_CreateNamespace(t *testing.T) { diff --git a/pkg/provisioner/kubernetes/mock_kubernetes_manager.go b/pkg/provisioner/kubernetes/mock_kubernetes_manager.go index 96fe917b2..f9b78107b 100644 --- a/pkg/provisioner/kubernetes/mock_kubernetes_manager.go +++ b/pkg/provisioner/kubernetes/mock_kubernetes_manager.go @@ -21,7 +21,7 @@ import ( type MockKubernetesManager struct { ApplyKustomizationFunc func(kustomization kustomizev1.Kustomization) error DeleteKustomizationFunc func(name, namespace string) error - WaitForKustomizationsFunc func(message string, names ...string) error + WaitForKustomizationsFunc func(message string, blueprint *blueprintv1alpha1.Blueprint) error GetKustomizationStatusFunc func(names []string) (map[string]bool, error) CreateNamespaceFunc func(name string) error DeleteNamespaceFunc func(name string) error @@ -71,9 +71,9 @@ func (m *MockKubernetesManager) DeleteKustomization(name, namespace string) erro } // WaitForKustomizations implements KubernetesManager interface -func (m *MockKubernetesManager) WaitForKustomizations(message string, names ...string) error { +func (m *MockKubernetesManager) WaitForKustomizations(message string, blueprint *blueprintv1alpha1.Blueprint) error { if m.WaitForKustomizationsFunc != nil { - return m.WaitForKustomizationsFunc(message, names...) + return m.WaitForKustomizationsFunc(message, blueprint) } return nil } diff --git a/pkg/provisioner/kubernetes/mock_kubernetes_manager_test.go b/pkg/provisioner/kubernetes/mock_kubernetes_manager_test.go index da66914e3..f4d03dfed 100644 --- a/pkg/provisioner/kubernetes/mock_kubernetes_manager_test.go +++ b/pkg/provisioner/kubernetes/mock_kubernetes_manager_test.go @@ -76,12 +76,11 @@ func TestMockKubernetesManager_WaitForKustomizations(t *testing.T) { return NewMockKubernetesManager() } msg := "msg" - name := "n" t.Run("FuncSet", func(t *testing.T) { manager := setup(t) - manager.WaitForKustomizationsFunc = func(m string, n ...string) error { return fmt.Errorf("err") } - err := manager.WaitForKustomizations(msg, name) + manager.WaitForKustomizationsFunc = func(m string, b *blueprintv1alpha1.Blueprint) error { return fmt.Errorf("err") } + err := manager.WaitForKustomizations(msg, nil) if err == nil || err.Error() != "err" { t.Errorf("Expected error 'err', got %v", err) } @@ -89,7 +88,7 @@ func TestMockKubernetesManager_WaitForKustomizations(t *testing.T) { t.Run("FuncNotSet", func(t *testing.T) { manager := setup(t) - err := manager.WaitForKustomizations(msg, name) + err := manager.WaitForKustomizations(msg, nil) if err != nil { t.Errorf("Expected nil, got %v", err) } diff --git a/pkg/provisioner/provisioner.go b/pkg/provisioner/provisioner.go index 1e6a50ab4..04efd9137 100644 --- a/pkg/provisioner/provisioner.go +++ b/pkg/provisioner/provisioner.go @@ -162,7 +162,8 @@ func (i *Provisioner) Install(blueprint *blueprintv1alpha1.Blueprint) error { // Wait waits for kustomizations from the blueprint to be ready. It initializes the kubernetes manager // if needed and polls the status of all kustomizations until they are ready or a timeout occurs. -// Returns an error if the kubernetes manager is not configured, initialization fails, or waiting times out. +// The timeout is calculated from the longest dependency chain in the blueprint. Returns an error if +// the kubernetes manager is not configured, initialization fails, or waiting times out. func (i *Provisioner) Wait(blueprint *blueprintv1alpha1.Blueprint) error { if blueprint == nil { return fmt.Errorf("blueprint not provided") @@ -172,12 +173,7 @@ func (i *Provisioner) Wait(blueprint *blueprintv1alpha1.Blueprint) error { return fmt.Errorf("kubernetes manager not configured") } - kustomizationNames := make([]string, len(blueprint.Kustomizations)) - for i, k := range blueprint.Kustomizations { - kustomizationNames[i] = k.Name - } - - if err := i.KubernetesManager.WaitForKustomizations("⏳ Waiting for kustomizations to be ready", kustomizationNames...); err != nil { + if err := i.KubernetesManager.WaitForKustomizations("⏳ Waiting for kustomizations to be ready", blueprint); err != nil { return fmt.Errorf("failed waiting for kustomizations: %w", err) } diff --git a/pkg/provisioner/provisioner_test.go b/pkg/provisioner/provisioner_test.go index 9ccbe0cfb..d65c0bb75 100644 --- a/pkg/provisioner/provisioner_test.go +++ b/pkg/provisioner/provisioner_test.go @@ -506,7 +506,7 @@ func TestProvisioner_Install(t *testing.T) { func TestProvisioner_Wait(t *testing.T) { t.Run("Success", func(t *testing.T) { mocks := setupProvisionerMocks(t) - mocks.KubernetesManager.WaitForKustomizationsFunc = func(message string, names ...string) error { + mocks.KubernetesManager.WaitForKustomizationsFunc = func(message string, blueprint *blueprintv1alpha1.Blueprint) error { return nil } opts := &Provisioner{ @@ -556,7 +556,7 @@ func TestProvisioner_Wait(t *testing.T) { t.Run("ErrorWaitForKustomizations", func(t *testing.T) { mocks := setupProvisionerMocks(t) - mocks.KubernetesManager.WaitForKustomizationsFunc = func(message string, names ...string) error { + mocks.KubernetesManager.WaitForKustomizationsFunc = func(message string, blueprint *blueprintv1alpha1.Blueprint) error { return fmt.Errorf("wait failed") } opts := &Provisioner{