diff --git a/wait/all.go b/wait/all.go index fb7eb4e5f3..9bf4cbe8b2 100644 --- a/wait/all.go +++ b/wait/all.go @@ -3,6 +3,7 @@ package wait import ( "context" "errors" + "reflect" "time" ) @@ -62,6 +63,13 @@ func (ms *MultiStrategy) WaitUntilReady(ctx context.Context, target StrategyTarg } for _, strategy := range ms.Strategies { + if strategy == nil || reflect.ValueOf(strategy).IsNil() { + // A module could be appending strategies after part of the container initialization, + // and use wait.ForAll on a not initialized strategy. + // In this case, we just skip the nil strategy. + continue + } + strategyCtx := ctx // Set default Timeout when strategy implements StrategyTimeout diff --git a/wait/all_test.go b/wait/all_test.go index f49d0cbd66..4a8d019f24 100644 --- a/wait/all_test.go +++ b/wait/all_test.go @@ -124,3 +124,57 @@ func TestMultiStrategy_WaitUntilReady(t *testing.T) { }) } } + +func TestMultiStrategy_handleNils(t *testing.T) { + t.Run("nil-strategy", func(t *testing.T) { + strategy := ForAll(nil) + err := strategy.WaitUntilReady(context.Background(), NopStrategyTarget{}) + require.NoError(t, err) + }) + + t.Run("nil-strategy-in-the-middle", func(t *testing.T) { + strategy := ForAll(nil, ForLog("docker")) + err := strategy.WaitUntilReady(context.Background(), NopStrategyTarget{ + ReaderCloser: io.NopCloser(bytes.NewReader([]byte("docker"))), + }) + require.NoError(t, err) + }) + + t.Run("nil-strategy-last", func(t *testing.T) { + strategy := ForAll(ForLog("docker"), nil) + err := strategy.WaitUntilReady(context.Background(), NopStrategyTarget{ + ReaderCloser: io.NopCloser(bytes.NewReader([]byte("docker"))), + }) + require.NoError(t, err) + }) + + t.Run("nil-type-implements-strategy", func(t *testing.T) { + var nilStrategy Strategy + + strategy := ForAll(ForLog("docker"), nilStrategy) + err := strategy.WaitUntilReady(context.Background(), NopStrategyTarget{ + ReaderCloser: io.NopCloser(bytes.NewReader([]byte("docker"))), + }) + require.NoError(t, err) + }) + + t.Run("nil-concrete-value-implements-strategy", func(t *testing.T) { + // Create a nil pointer to a type that implements Strategy + var nilPointerStrategy *nilWaitStrategy + // When we assign it to the interface, the type information is preserved + // but the concrete value is nil + var strategyInterface Strategy = nilPointerStrategy + + strategy := ForAll(ForLog("docker"), strategyInterface) + err := strategy.WaitUntilReady(context.Background(), NopStrategyTarget{ + ReaderCloser: io.NopCloser(bytes.NewReader([]byte("docker"))), + }) + require.NoError(t, err) + }) +} + +type nilWaitStrategy struct{} + +func (s *nilWaitStrategy) WaitUntilReady(_ context.Context, _ StrategyTarget) error { + return nil +}