wait for dependent service up to delay set by --wait-timeout

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2024-09-23 16:05:53 +02:00 committed by Joana H
parent d2b9456137
commit ce27dba52e
4 changed files with 18 additions and 9 deletions

View File

@ -364,7 +364,12 @@ func containerReasonEvents(containers Containers, eventFunc func(string, string)
const ServiceConditionRunningOrHealthy = "running_or_healthy" const ServiceConditionRunningOrHealthy = "running_or_healthy"
//nolint:gocyclo //nolint:gocyclo
func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, dependant string, dependencies types.DependsOnConfig, containers Containers) error { func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, dependant string, dependencies types.DependsOnConfig, containers Containers, timeout time.Duration) error {
if timeout > 0 {
withTimeout, cancelFunc := context.WithTimeout(ctx, timeout)
defer cancelFunc()
ctx = withTimeout
}
eg, _ := errgroup.WithContext(ctx) eg, _ := errgroup.WithContext(ctx)
w := progress.ContextWriter(ctx) w := progress.ContextWriter(ctx)
for dep, config := range dependencies { for dep, config := range dependencies {
@ -454,7 +459,11 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
} }
}) })
} }
return eg.Wait() err := eg.Wait()
if errors.Is(err, context.DeadlineExceeded) {
return fmt.Errorf("timeout waiting for dependencies")
}
return err
} }
func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceDependency, project *types.Project) (bool, error) { func shouldWaitForDependency(serviceName string, dependencyConfig types.ServiceDependency, project *types.Project) (bool, error) {
@ -760,12 +769,12 @@ func (s *composeService) isServiceCompleted(ctx context.Context, containers Cont
return false, 0, nil return false, 0, nil
} }
func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig, containers Containers) error { func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig, containers Containers, timeout time.Duration) error {
if service.Deploy != nil && service.Deploy.Replicas != nil && *service.Deploy.Replicas == 0 { if service.Deploy != nil && service.Deploy.Replicas != nil && *service.Deploy.Replicas == 0 {
return nil return nil
} }
err := s.waitDependencies(ctx, project, service.Name, service.DependsOn, containers) err := s.waitDependencies(ctx, project, service.Name, service.DependsOn, containers, timeout)
if err != nil { if err != nil {
return err return err
} }

View File

@ -239,7 +239,7 @@ func TestWaitDependencies(t *testing.T) {
"db": {Condition: ServiceConditionRunningOrHealthy}, "db": {Condition: ServiceConditionRunningOrHealthy},
"redis": {Condition: ServiceConditionRunningOrHealthy}, "redis": {Condition: ServiceConditionRunningOrHealthy},
} }
assert.NilError(t, tested.waitDependencies(context.Background(), &project, "", dependencies, nil)) assert.NilError(t, tested.waitDependencies(context.Background(), &project, "", dependencies, nil, 0))
}) })
t.Run("should skip dependencies with condition service_started", func(t *testing.T) { t.Run("should skip dependencies with condition service_started", func(t *testing.T) {
dbService := types.ServiceConfig{Name: "db", Scale: intPtr(1)} dbService := types.ServiceConfig{Name: "db", Scale: intPtr(1)}
@ -252,7 +252,7 @@ func TestWaitDependencies(t *testing.T) {
"db": {Condition: types.ServiceConditionStarted, Required: true}, "db": {Condition: types.ServiceConditionStarted, Required: true},
"redis": {Condition: types.ServiceConditionStarted, Required: true}, "redis": {Condition: types.ServiceConditionStarted, Required: true},
} }
assert.NilError(t, tested.waitDependencies(context.Background(), &project, "", dependencies, nil)) assert.NilError(t, tested.waitDependencies(context.Background(), &project, "", dependencies, nil, 0))
}) })
} }

View File

@ -93,7 +93,7 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project,
} }
if !opts.NoDeps { if !opts.NoDeps {
if err := s.waitDependencies(ctx, project, service.Name, service.DependsOn, observedState); err != nil { if err := s.waitDependencies(ctx, project, service.Name, service.DependsOn, observedState, 0); err != nil {
return "", err return "", err
} }
} }

View File

@ -129,7 +129,7 @@ func (s *composeService) start(ctx context.Context, projectName string, options
return err return err
} }
return s.startService(ctx, project, service, containers) return s.startService(ctx, project, service, containers, options.WaitTimeout)
}) })
if err != nil { if err != nil {
return err return err
@ -149,7 +149,7 @@ func (s *composeService) start(ctx context.Context, projectName string, options
defer cancel() defer cancel()
} }
err = s.waitDependencies(ctx, project, project.Name, depends, containers) err = s.waitDependencies(ctx, project, project.Name, depends, containers, 0)
if err != nil { if err != nil {
if errors.Is(ctx.Err(), context.DeadlineExceeded) { if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return fmt.Errorf("application not healthy after %s", options.WaitTimeout) return fmt.Errorf("application not healthy after %s", options.WaitTimeout)