Skip to content

Commit

Permalink
Merge pull request #360 from ndeloof/WithDependentServices
Browse files Browse the repository at this point in the history
  • Loading branch information
ndeloof authored Mar 1, 2023
2 parents e988503 + abe1044 commit 7a7b593
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 20 deletions.
70 changes: 53 additions & 17 deletions types/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type Project struct {
}

// ServiceNames return names for all services in this Compose config
func (p Project) ServiceNames() []string {
func (p *Project) ServiceNames() []string {
var names []string
for _, s := range p.Services {
names = append(names, s.Name)
Expand All @@ -58,7 +58,7 @@ func (p Project) ServiceNames() []string {
}

// VolumeNames return names for all volumes in this Compose config
func (p Project) VolumeNames() []string {
func (p *Project) VolumeNames() []string {
var names []string
for k := range p.Volumes {
names = append(names, k)
Expand All @@ -68,7 +68,7 @@ func (p Project) VolumeNames() []string {
}

// NetworkNames return names for all volumes in this Compose config
func (p Project) NetworkNames() []string {
func (p *Project) NetworkNames() []string {
var names []string
for k := range p.Networks {
names = append(names, k)
Expand All @@ -78,7 +78,7 @@ func (p Project) NetworkNames() []string {
}

// SecretNames return names for all secrets in this Compose config
func (p Project) SecretNames() []string {
func (p *Project) SecretNames() []string {
var names []string
for k := range p.Secrets {
names = append(names, k)
Expand All @@ -88,7 +88,7 @@ func (p Project) SecretNames() []string {
}

// ConfigNames return names for all configs in this Compose config
func (p Project) ConfigNames() []string {
func (p *Project) ConfigNames() []string {
var names []string
for k := range p.Configs {
names = append(names, k)
Expand All @@ -98,7 +98,7 @@ func (p Project) ConfigNames() []string {
}

// GetServices retrieve services by names, or return all services if no name specified
func (p Project) GetServices(names ...string) (Services, error) {
func (p *Project) GetServices(names ...string) (Services, error) {
if len(names) == 0 {
return p.Services, nil
}
Expand All @@ -120,7 +120,7 @@ func (p Project) GetServices(names ...string) (Services, error) {
}

// GetService retrieve a specific service by name
func (p Project) GetService(name string) (ServiceConfig, error) {
func (p *Project) GetService(name string) (ServiceConfig, error) {
services, err := p.GetServices(name)
if err != nil {
return ServiceConfig{}, err
Expand All @@ -131,7 +131,7 @@ func (p Project) GetService(name string) (ServiceConfig, error) {
return services[0], nil
}

func (p Project) AllServices() Services {
func (p *Project) AllServices() Services {
var all Services
all = append(all, p.Services...)
all = append(all, p.DisabledServices...)
Expand All @@ -140,12 +140,16 @@ func (p Project) AllServices() Services {

type ServiceFunc func(service ServiceConfig) error

// WithServices run ServiceFunc on each service and dependencies in dependency order
func (p Project) WithServices(names []string, fn ServiceFunc) error {
return p.withServices(names, fn, map[string]bool{})
// WithServices run ServiceFunc on each service and dependencies according to DependencyPolicy
func (p *Project) WithServices(names []string, fn ServiceFunc, options ...DependencyOption) error {
if len(options) == 0 {
// backward compatibility
options = []DependencyOption{IncludeDependencies}
}
return p.withServices(names, fn, map[string]bool{}, options)
}

func (p Project) withServices(names []string, fn ServiceFunc, seen map[string]bool) error {
func (p *Project) withServices(names []string, fn ServiceFunc, seen map[string]bool, options []DependencyOption) error {
services, err := p.GetServices(names...)
if err != nil {
return err
Expand All @@ -155,9 +159,21 @@ func (p Project) withServices(names []string, fn ServiceFunc, seen map[string]bo
continue
}
seen[service.Name] = true
dependencies := service.GetDependencies()
var dependencies []string
for _, policy := range options {
switch policy {
case IncludeDependents:
dependencies = append(dependencies, p.GetDependentsForService(service)...)
case IncludeDependencies:
dependencies = append(dependencies, service.GetDependencies()...)
case IgnoreDependencies:
// Noop
default:
return fmt.Errorf("unsupported dependency policy %d", policy)
}
}
if len(dependencies) > 0 {
err := p.withServices(dependencies, fn, seen)
err := p.withServices(dependencies, fn, seen, options)
if err != nil {
return err
}
Expand All @@ -169,6 +185,18 @@ func (p Project) withServices(names []string, fn ServiceFunc, seen map[string]bo
return nil
}

func (p *Project) GetDependentsForService(s ServiceConfig) []string {
var dependent []string
for _, service := range p.Services {
for name := range service.DependsOn {
if name == s.Name {
dependent = append(dependent, service.Name)
}
}
}
return dependent
}

// RelativePath resolve a relative path based project's working directory
func (p *Project) RelativePath(path string) string {
if path[0] == '~' {
Expand Down Expand Up @@ -292,8 +320,16 @@ func (p *Project) WithoutUnnecessaryResources() {
p.Configs = configs
}

// ForServices restrict the project model to a subset of services
func (p *Project) ForServices(names []string) error {
type DependencyOption int

const (
IncludeDependencies = iota
IncludeDependents
IgnoreDependencies
)

// ForServices restrict the project model to selected services and dependencies
func (p *Project) ForServices(names []string, options ...DependencyOption) error {
if len(names) == 0 {
// All services
return nil
Expand All @@ -303,7 +339,7 @@ func (p *Project) ForServices(names []string) error {
err := p.WithServices(names, func(service ServiceConfig) error {
set[service.Name] = struct{}{}
return nil
})
}, options...)
if err != nil {
return err
}
Expand Down
32 changes: 30 additions & 2 deletions types/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ func makeProject() Project {
Profiles: []string{"foo"},
DependsOn: map[string]ServiceDependency{"service_1": {}},
}, ServiceConfig{
Name: "service_3",
Profiles: []string{"bar"},
Name: "service_3",
Profiles: []string{"bar"},
DependsOn: map[string]ServiceDependency{"service_2": {}},
}),
Networks: Networks{},
Volumes: Volumes{},
Expand Down Expand Up @@ -149,3 +150,30 @@ func Test_ResolveImages(t *testing.T) {
assert.Equal(t, p.Services[0].Image, test.resolved)
}
}

func TestWithServices(t *testing.T) {
p := makeProject()
var seen []string
err := p.WithServices([]string{"service_3"}, func(service ServiceConfig) error {
seen = append(seen, service.Name)
return nil
}, IncludeDependencies)
assert.NilError(t, err)
assert.DeepEqual(t, seen, []string{"service_1", "service_2", "service_3"})

seen = []string{}
err = p.WithServices([]string{"service_1"}, func(service ServiceConfig) error {
seen = append(seen, service.Name)
return nil
}, IncludeDependents)
assert.NilError(t, err)
assert.DeepEqual(t, seen, []string{"service_3", "service_2", "service_1"})

seen = []string{}
err = p.WithServices([]string{"service_1"}, func(service ServiceConfig) error {
seen = append(seen, service.Name)
return nil
}, IgnoreDependencies)
assert.NilError(t, err)
assert.DeepEqual(t, seen, []string{"service_1"})
}
15 changes: 14 additions & 1 deletion types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ const (
NetworkModeContainerPrefix = ContainerPrefix
)

// GetDependencies retrieve all services this service depends on
// GetDependencies retrieves all services this service depends on
func (s ServiceConfig) GetDependencies() []string {
var dependencies []string
for service := range s.DependsOn {
Expand All @@ -263,6 +263,19 @@ func (s ServiceConfig) GetDependencies() []string {
return dependencies
}

// GetDependents retrieves all services which depend on this service
func (s ServiceConfig) GetDependents(p *Project) []string {
var dependent []string
for _, service := range p.Services {
for name := range service.DependsOn {
if name == s.Name {
dependent = append(dependent, service.Name)
}
}
}
return dependent
}

type set map[string]struct{}

func (s set) append(strs ...string) {
Expand Down

0 comments on commit 7a7b593

Please sign in to comment.