use containers we expect to start for wait condition

Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
Nicolas De Loof 2023-01-26 09:58:30 +01:00 committed by Guillaume Lours
parent e908f41ec8
commit b5f0a4eefa
4 changed files with 32 additions and 43 deletions

View File

@ -28,7 +28,6 @@ import (
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
moby "github.com/docker/docker/api/types" moby "github.com/docker/docker/api/types"
containerType "github.com/docker/docker/api/types/container" containerType "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
specs "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -281,7 +280,7 @@ func containerEvents(containers Containers, eventFunc func(string) progress.Even
// ServiceConditionRunningOrHealthy is a service condition on status running or healthy // ServiceConditionRunningOrHealthy is a service condition on status running or healthy
const ServiceConditionRunningOrHealthy = "running_or_healthy" const ServiceConditionRunningOrHealthy = "running_or_healthy"
func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, dependencies types.DependsOnConfig) error { func (s *composeService) waitDependencies(ctx context.Context, project *types.Project, dependencies types.DependsOnConfig, containers Containers) error {
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 {
@ -291,10 +290,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
continue continue
} }
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, false, dep) containers = containers.filter(isService(dep))
if err != nil {
return err
}
w.Events(containerEvents(containers, progress.Waiting)) w.Events(containerEvents(containers, progress.Waiting))
dep, config := dep, config dep, config := dep, config
@ -305,7 +301,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
<-ticker.C <-ticker.C
switch config.Condition { switch config.Condition {
case ServiceConditionRunningOrHealthy: case ServiceConditionRunningOrHealthy:
healthy, err := s.isServiceHealthy(ctx, project, dep, true) healthy, err := s.isServiceHealthy(ctx, containers, dep, true)
if err != nil { if err != nil {
return err return err
} }
@ -314,7 +310,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
return nil return nil
} }
case types.ServiceConditionHealthy: case types.ServiceConditionHealthy:
healthy, err := s.isServiceHealthy(ctx, project, dep, false) healthy, err := s.isServiceHealthy(ctx, containers, dep, false)
if err != nil { if err != nil {
w.Events(containerEvents(containers, progress.ErrorEvent)) w.Events(containerEvents(containers, progress.ErrorEvent))
return errors.Wrap(err, "dependency failed to start") return errors.Wrap(err, "dependency failed to start")
@ -324,7 +320,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
return nil return nil
} }
case types.ServiceConditionCompletedSuccessfully: case types.ServiceConditionCompletedSuccessfully:
exited, code, err := s.isServiceCompleted(ctx, project, dep) exited, code, err := s.isServiceCompleted(ctx, containers, dep)
if err != nil { if err != nil {
return err return err
} }
@ -650,12 +646,7 @@ func (s *composeService) connectContainerToNetwork(ctx context.Context, id strin
return nil return nil
} }
func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Project, service string, fallbackRunning bool) (bool, error) { func (s *composeService) isServiceHealthy(ctx context.Context, containers Containers, service string, fallbackRunning bool) (bool, error) {
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, service)
if err != nil {
return false, err
}
if len(containers) == 0 { if len(containers) == 0 {
return false, nil return false, nil
} }
@ -690,11 +681,7 @@ func (s *composeService) isServiceHealthy(ctx context.Context, project *types.Pr
return true, nil return true, nil
} }
func (s *composeService) isServiceCompleted(ctx context.Context, project *types.Project, dep string) (bool, int, error) { func (s *composeService) isServiceCompleted(ctx context.Context, containers Containers, dep string) (bool, int, error) {
containers, err := s.getContainers(ctx, project.Name, oneOffExclude, true, dep)
if err != nil {
return false, 0, err
}
for _, c := range containers { for _, c := range containers {
container, err := s.apiClient().ContainerInspect(ctx, c.ID) container, err := s.apiClient().ContainerInspect(ctx, c.ID)
if err != nil { if err != nil {
@ -707,23 +694,12 @@ func (s *composeService) isServiceCompleted(ctx context.Context, project *types.
return false, 0, nil return false, 0, nil
} }
func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig) error { func (s *composeService) startService(ctx context.Context, project *types.Project, service types.ServiceConfig, containers Containers) 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.DependsOn) err := s.waitDependencies(ctx, project, service.DependsOn, containers)
if err != nil {
return err
}
containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
projectFilter(project.Name),
serviceFilter(service.Name),
oneOffFilter(false),
),
All: true,
})
if err != nil { if err != nil {
return err return err
} }

View File

@ -229,7 +229,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)) assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies, nil))
}) })
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: 1} dbService := types.ServiceConfig{Name: "db", Scale: 1}
@ -239,6 +239,6 @@ func TestWaitDependencies(t *testing.T) {
"db": {Condition: types.ServiceConditionStarted}, "db": {Condition: types.ServiceConditionStarted},
"redis": {Condition: types.ServiceConditionStarted}, "redis": {Condition: types.ServiceConditionStarted},
} }
assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies)) assert.NilError(t, tested.waitDependencies(context.Background(), &project, dependencies, nil))
}) })
} }

View File

@ -76,11 +76,6 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project,
if err := s.ensureImagesExists(ctx, project, opts.QuietPull); err != nil { // all dependencies already checked, but might miss service img if err := s.ensureImagesExists(ctx, project, opts.QuietPull); err != nil { // all dependencies already checked, but might miss service img
return "", err return "", err
} }
if !opts.NoDeps {
if err := s.waitDependencies(ctx, project, service.DependsOn); err != nil {
return "", err
}
}
observedState, err := s.getContainers(ctx, project.Name, oneOffInclude, true) observedState, err := s.getContainers(ctx, project.Name, oneOffInclude, true)
if err != nil { if err != nil {
@ -88,6 +83,11 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project,
} }
updateServices(&service, observedState) updateServices(&service, observedState)
if !opts.NoDeps {
if err := s.waitDependencies(ctx, project, service.DependsOn, observedState); err != nil {
return "", err
}
}
created, err := s.createContainer(ctx, project, service, service.ContainerName, 1, created, err := s.createContainer(ctx, project, service, service.ContainerName, 1,
opts.AutoRemove, opts.UseNetworkAliases, opts.Interactive) opts.AutoRemove, opts.UseNetworkAliases, opts.Interactive)
if err != nil { if err != nil {

View File

@ -18,6 +18,7 @@ package compose
import ( import (
"context" "context"
"github.com/docker/docker/api/types/filters"
"strings" "strings"
"time" "time"
@ -75,13 +76,25 @@ func (s *composeService) start(ctx context.Context, projectName string, options
}) })
} }
err := InDependencyOrder(ctx, project, func(c context.Context, name string) error { var containers Containers
containers, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
projectFilter(project.Name),
oneOffFilter(false),
),
All: true,
})
if err != nil {
return err
}
err = InDependencyOrder(ctx, project, func(c context.Context, name string) error {
service, err := project.GetService(name) service, err := project.GetService(name)
if err != nil { if err != nil {
return err return err
} }
return s.startService(ctx, project, service) return s.startService(ctx, project, service, containers.filter(isService(service.Name)))
}) })
if err != nil { if err != nil {
return err return err
@ -94,7 +107,7 @@ func (s *composeService) start(ctx context.Context, projectName string, options
Condition: getDependencyCondition(s, project), Condition: getDependencyCondition(s, project),
} }
} }
err = s.waitDependencies(ctx, project, depends) err = s.waitDependencies(ctx, project, depends, containers)
if err != nil { if err != nil {
return err return err
} }