Skip to content

Commit

Permalink
normalize implicit dependencies as explicit depends_on
Browse files Browse the repository at this point in the history
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
  • Loading branch information
ndeloof committed Feb 16, 2023
1 parent 873a781 commit eeb4020
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 33 deletions.
4 changes: 2 additions & 2 deletions loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ services:
build:
context: ./web
links:
- bar
- db
pid: host
db:
image: db
Expand Down Expand Up @@ -837,7 +837,7 @@ services:
image: web
build: .
links:
- bar
- db
db:
image: db
build:
Expand Down
45 changes: 44 additions & 1 deletion loader/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/compose-spec/compose-go/errdefs"
"github.com/compose-spec/compose-go/types"
Expand Down Expand Up @@ -95,6 +96,37 @@ func normalize(project *types.Project, resolvePaths bool) error {
s.Extends["file"] = &p
}

for _, link := range s.Links {
parts := strings.Split(link, ":")
if len(parts) == 2 {
link = parts[0]
}
s.DependsOn = setIfMissing(s.DependsOn, link, types.ServiceDependency{
Condition: types.ServiceConditionStarted,
Restart: true,
})
}

for _, namespace := range []string{s.NetworkMode, s.Ipc, s.Pid, s.Uts, s.Cgroup} {
if strings.HasPrefix(namespace, types.ServicePrefix) {
name := namespace[len(types.ServicePrefix):]
s.DependsOn = setIfMissing(s.DependsOn, name, types.ServiceDependency{
Condition: types.ServiceConditionStarted,
Restart: true,
})
}
}

for _, vol := range s.VolumesFrom {
if !strings.HasPrefix(s.Pid, types.ContainerPrefix) {
spec := strings.Split(vol, ":")
s.DependsOn = setIfMissing(s.DependsOn, spec[0], types.ServiceDependency{
Condition: types.ServiceConditionStarted,
Restart: false,
})
}
}

err := relocateLogDriver(&s)
if err != nil {
return err
Expand Down Expand Up @@ -131,9 +163,20 @@ func normalize(project *types.Project, resolvePaths bool) error {
return nil
}

// setIfMissing adds a ServiceDependency for service if not already defined
func setIfMissing(d types.DependsOnConfig, service string, dep types.ServiceDependency) types.DependsOnConfig {
if d == nil {
d = types.DependsOnConfig{}
}
if _, ok := d[service]; !ok {
d[service] = dep
}
return d
}

func relocateScale(s *types.ServiceConfig) error {
scale := uint64(s.Scale)
if scale != 1 {
if scale > 1 {
logrus.Warn("`scale` is deprecated. Use the `deploy.replicas` element")
if s.Deploy == nil {
s.Deploy = &types.DeployConfig{}
Expand Down
58 changes: 58 additions & 0 deletions loader/normalize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,61 @@ func TestNormalizeVolumes(t *testing.T) {
assert.NilError(t, err)
assert.DeepEqual(t, expected, project)
}

func TestNormalizeDependsOn(t *testing.T) {
project := types.Project{
Name: "myProject",
Networks: types.Networks{},
Volumes: types.Volumes{},
Services: []types.ServiceConfig{
{
Name: "foo",
DependsOn: map[string]types.ServiceDependency{
"bar": { // explicit depends_on never should be overridden
Condition: types.ServiceConditionHealthy,
Restart: false,
},
},
NetworkMode: "service:zot",
},
{
Name: "bar",
VolumesFrom: []string{"zot"},
},
{
Name: "zot",
},
},
}

expected := `name: myProject
services:
bar:
depends_on:
zot:
condition: service_started
networks:
default: null
volumes_from:
- zot
foo:
depends_on:
bar:
condition: service_healthy
zot:
condition: service_started
restart: true
network_mode: service:zot
zot:
networks:
default: null
networks:
default:
name: myProject_default
`
err := normalize(&project, true)
assert.NilError(t, err)
marshal, err := yaml.Marshal(project)
assert.NilError(t, err)
assert.DeepEqual(t, expected, string(marshal))
}
36 changes: 6 additions & 30 deletions types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,35 +256,11 @@ const (

// GetDependencies retrieve all services this service depends on
func (s ServiceConfig) GetDependencies() []string {
dependencies := make(set)
for dependency := range s.DependsOn {
dependencies.append(dependency)
}
for _, link := range s.Links {
parts := strings.Split(link, ":")
if len(parts) == 2 {
dependencies.append(parts[0])
} else {
dependencies.append(link)
}
}
if strings.HasPrefix(s.NetworkMode, ServicePrefix) {
dependencies.append(s.NetworkMode[len(ServicePrefix):])
}
if strings.HasPrefix(s.Ipc, ServicePrefix) {
dependencies.append(s.Ipc[len(ServicePrefix):])
var dependencies []string
for service := range s.DependsOn {
dependencies = append(dependencies, service)
}
if strings.HasPrefix(s.Pid, ServicePrefix) {
dependencies.append(s.Pid[len(ServicePrefix):])
}
for _, vol := range s.VolumesFrom {
if !strings.HasPrefix(s.Pid, ContainerPrefix) {
spec := strings.Split(vol, ":")
dependencies.append(spec[0])
}
}

return dependencies.toSlice()
return dependencies
}

type set map[string]struct{}
Expand Down Expand Up @@ -357,12 +333,12 @@ type ThrottleDevice struct {
// ShellCommand is a string or list of string args.
//
// When marshaled to YAML, nil command fields will be omitted if `omitempty`
// is specified as a struct tag. Explicitly empty commands (i.e. `[]` or `''`)
// is specified as a struct tag. Explicitly empty commands (i.e. `[]` or ``)
// will serialize to an empty array (`[]`).
//
// When marshaled to JSON, the `omitempty` struct must NOT be specified.
// If the command field is nil, it will be serialized as `null`.
// Explicitly empty commands (i.e. `[]` or `''`) will serialize to an empty
// Explicitly empty commands (i.e. `[]` or ``) will serialize to an empty
// array (`[]`).
//
// The distinction between nil and explicitly empty is important to distinguish
Expand Down

0 comments on commit eeb4020

Please sign in to comment.