Skip to content

Commit abe1044

Browse files
committed
introduce WithDependentServices to run fn on services in reverse dependency order
Signed-off-by: Nicolas De Loof <[email protected]>
1 parent e988503 commit abe1044

File tree

3 files changed

+97
-20
lines changed

3 files changed

+97
-20
lines changed

types/project.go

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type Project struct {
4848
}
4949

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

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

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

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

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

100100
// GetServices retrieve services by names, or return all services if no name specified
101-
func (p Project) GetServices(names ...string) (Services, error) {
101+
func (p *Project) GetServices(names ...string) (Services, error) {
102102
if len(names) == 0 {
103103
return p.Services, nil
104104
}
@@ -120,7 +120,7 @@ func (p Project) GetServices(names ...string) (Services, error) {
120120
}
121121

122122
// GetService retrieve a specific service by name
123-
func (p Project) GetService(name string) (ServiceConfig, error) {
123+
func (p *Project) GetService(name string) (ServiceConfig, error) {
124124
services, err := p.GetServices(name)
125125
if err != nil {
126126
return ServiceConfig{}, err
@@ -131,7 +131,7 @@ func (p Project) GetService(name string) (ServiceConfig, error) {
131131
return services[0], nil
132132
}
133133

134-
func (p Project) AllServices() Services {
134+
func (p *Project) AllServices() Services {
135135
var all Services
136136
all = append(all, p.Services...)
137137
all = append(all, p.DisabledServices...)
@@ -140,12 +140,16 @@ func (p Project) AllServices() Services {
140140

141141
type ServiceFunc func(service ServiceConfig) error
142142

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

148-
func (p Project) withServices(names []string, fn ServiceFunc, seen map[string]bool) error {
152+
func (p *Project) withServices(names []string, fn ServiceFunc, seen map[string]bool, options []DependencyOption) error {
149153
services, err := p.GetServices(names...)
150154
if err != nil {
151155
return err
@@ -155,9 +159,21 @@ func (p Project) withServices(names []string, fn ServiceFunc, seen map[string]bo
155159
continue
156160
}
157161
seen[service.Name] = true
158-
dependencies := service.GetDependencies()
162+
var dependencies []string
163+
for _, policy := range options {
164+
switch policy {
165+
case IncludeDependents:
166+
dependencies = append(dependencies, p.GetDependentsForService(service)...)
167+
case IncludeDependencies:
168+
dependencies = append(dependencies, service.GetDependencies()...)
169+
case IgnoreDependencies:
170+
// Noop
171+
default:
172+
return fmt.Errorf("unsupported dependency policy %d", policy)
173+
}
174+
}
159175
if len(dependencies) > 0 {
160-
err := p.withServices(dependencies, fn, seen)
176+
err := p.withServices(dependencies, fn, seen, options)
161177
if err != nil {
162178
return err
163179
}
@@ -169,6 +185,18 @@ func (p Project) withServices(names []string, fn ServiceFunc, seen map[string]bo
169185
return nil
170186
}
171187

188+
func (p *Project) GetDependentsForService(s ServiceConfig) []string {
189+
var dependent []string
190+
for _, service := range p.Services {
191+
for name := range service.DependsOn {
192+
if name == s.Name {
193+
dependent = append(dependent, service.Name)
194+
}
195+
}
196+
}
197+
return dependent
198+
}
199+
172200
// RelativePath resolve a relative path based project's working directory
173201
func (p *Project) RelativePath(path string) string {
174202
if path[0] == '~' {
@@ -292,8 +320,16 @@ func (p *Project) WithoutUnnecessaryResources() {
292320
p.Configs = configs
293321
}
294322

295-
// ForServices restrict the project model to a subset of services
296-
func (p *Project) ForServices(names []string) error {
323+
type DependencyOption int
324+
325+
const (
326+
IncludeDependencies = iota
327+
IncludeDependents
328+
IgnoreDependencies
329+
)
330+
331+
// ForServices restrict the project model to selected services and dependencies
332+
func (p *Project) ForServices(names []string, options ...DependencyOption) error {
297333
if len(names) == 0 {
298334
// All services
299335
return nil
@@ -303,7 +339,7 @@ func (p *Project) ForServices(names []string) error {
303339
err := p.WithServices(names, func(service ServiceConfig) error {
304340
set[service.Name] = struct{}{}
305341
return nil
306-
})
342+
}, options...)
307343
if err != nil {
308344
return err
309345
}

types/project_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ func makeProject() Project {
100100
Profiles: []string{"foo"},
101101
DependsOn: map[string]ServiceDependency{"service_1": {}},
102102
}, ServiceConfig{
103-
Name: "service_3",
104-
Profiles: []string{"bar"},
103+
Name: "service_3",
104+
Profiles: []string{"bar"},
105+
DependsOn: map[string]ServiceDependency{"service_2": {}},
105106
}),
106107
Networks: Networks{},
107108
Volumes: Volumes{},
@@ -149,3 +150,30 @@ func Test_ResolveImages(t *testing.T) {
149150
assert.Equal(t, p.Services[0].Image, test.resolved)
150151
}
151152
}
153+
154+
func TestWithServices(t *testing.T) {
155+
p := makeProject()
156+
var seen []string
157+
err := p.WithServices([]string{"service_3"}, func(service ServiceConfig) error {
158+
seen = append(seen, service.Name)
159+
return nil
160+
}, IncludeDependencies)
161+
assert.NilError(t, err)
162+
assert.DeepEqual(t, seen, []string{"service_1", "service_2", "service_3"})
163+
164+
seen = []string{}
165+
err = p.WithServices([]string{"service_1"}, func(service ServiceConfig) error {
166+
seen = append(seen, service.Name)
167+
return nil
168+
}, IncludeDependents)
169+
assert.NilError(t, err)
170+
assert.DeepEqual(t, seen, []string{"service_3", "service_2", "service_1"})
171+
172+
seen = []string{}
173+
err = p.WithServices([]string{"service_1"}, func(service ServiceConfig) error {
174+
seen = append(seen, service.Name)
175+
return nil
176+
}, IgnoreDependencies)
177+
assert.NilError(t, err)
178+
assert.DeepEqual(t, seen, []string{"service_1"})
179+
}

types/types.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ const (
254254
NetworkModeContainerPrefix = ContainerPrefix
255255
)
256256

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

266+
// GetDependents retrieves all services which depend on this service
267+
func (s ServiceConfig) GetDependents(p *Project) []string {
268+
var dependent []string
269+
for _, service := range p.Services {
270+
for name := range service.DependsOn {
271+
if name == s.Name {
272+
dependent = append(dependent, service.Name)
273+
}
274+
}
275+
}
276+
return dependent
277+
}
278+
266279
type set map[string]struct{}
267280

268281
func (s set) append(strs ...string) {

0 commit comments

Comments
 (0)