diff --git a/errdefs/errors.go b/errdefs/errors.go index a5440700..1990ddd2 100644 --- a/errdefs/errors.go +++ b/errdefs/errors.go @@ -30,6 +30,9 @@ var ( // ErrIncompatible is returned when a compose project uses an incompatible attribute ErrIncompatible = errors.New("incompatible attribute") + + // ErrDisabled is returned when a resource was found in model but is disabled + ErrDisabled = errors.New("disabled") ) // IsNotFoundError returns true if the unwrapped error is ErrNotFound diff --git a/loader/validate.go b/loader/validate.go index 6f7750e3..973e5213 100644 --- a/loader/validate.go +++ b/loader/validate.go @@ -70,8 +70,11 @@ func checkConsistency(project *types.Project) error { } } - for dependedService := range s.DependsOn { + for dependedService, cfg := range s.DependsOn { if _, err := project.GetService(dependedService); err != nil { + if errors.Is(err, errdefs.ErrDisabled) && !cfg.Required { + continue + } return fmt.Errorf("service %q depends on undefined service %q: %w", s.Name, dependedService, errdefs.ErrInvalid) } } diff --git a/loader/validate_test.go b/loader/validate_test.go index 93ee77fd..d4a2de68 100644 --- a/loader/validate_test.go +++ b/loader/validate_test.go @@ -366,4 +366,45 @@ func TestValidateWatch(t *testing.T) { err := checkConsistency(&project) assert.NilError(t, err) }) + + t.Run("depends on disabled service", func(t *testing.T) { + project := types.Project{ + Services: types.Services{ + "myservice": { + Name: "myservice", + Image: "scratch", + DependsOn: map[string]types.ServiceDependency{ + "other": { + Required: false, + }, + }, + }, + }, + DisabledServices: types.Services{ + "other": { + Image: "scratch", + }, + }, + } + err := checkConsistency(&project) + assert.NilError(t, err) + }) + + t.Run("depends on unknown service", func(t *testing.T) { + project := types.Project{ + Services: types.Services{ + "myservice": { + Name: "myservice", + Image: "scratch", + DependsOn: map[string]types.ServiceDependency{ + "other": { + Required: false, + }, + }, + }, + }, + } + err := checkConsistency(&project) + assert.ErrorContains(t, err, "depends on undefined service") + }) } diff --git a/types/project.go b/types/project.go index b9d18eb5..780dd91c 100644 --- a/types/project.go +++ b/types/project.go @@ -26,6 +26,7 @@ import ( "sort" "github.com/compose-spec/compose-go/v2/dotenv" + "github.com/compose-spec/compose-go/v2/errdefs" "github.com/compose-spec/compose-go/v2/utils" "github.com/distribution/reference" "github.com/mitchellh/copystructure" @@ -216,9 +217,9 @@ func (p *Project) GetService(name string) (ServiceConfig, error) { if !ok { _, ok := p.DisabledServices[name] if ok { - return ServiceConfig{}, fmt.Errorf("service %s is disabled", name) + return ServiceConfig{}, fmt.Errorf("no such service: %s: %w", name, errdefs.ErrDisabled) } - return ServiceConfig{}, fmt.Errorf("no such service: %s", name) + return ServiceConfig{}, fmt.Errorf("no such service: %s: %w", name, errdefs.ErrNotFound) } return service, nil }