diff --git a/local/compose/create.go b/local/compose/create.go index 0636462b2..e693f14cc 100644 --- a/local/compose/create.go +++ b/local/compose/create.go @@ -100,7 +100,7 @@ func (s *composeService) Create(ctx context.Context, project *types.Project, opt } } - prepareNetworkMode(project) + prepareServicesDependsOn(project) return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error { if utils.StringContains(opts.Services, service.Name) { @@ -142,18 +142,20 @@ func prepareNetworks(project *types.Project) { } } -func prepareNetworkMode(p *types.Project) { +func prepareServicesDependsOn(p *types.Project) { outLoop: for i := range p.Services { - dependency := getDependentServiceByNetwork(p.Services[i].NetworkMode) - if dependency == "" { + networkDependency := getDependentServiceFromMode(p.Services[i].NetworkMode) + ipcDependency := getDependentServiceFromMode(p.Services[i].Ipc) + + if networkDependency == "" && ipcDependency == "" { continue } if p.Services[i].DependsOn == nil { p.Services[i].DependsOn = make(types.DependsOnConfig) } for _, service := range p.Services { - if service.Name == dependency { + if service.Name == networkDependency || service.Name == ipcDependency { p.Services[i].DependsOn[service.Name] = types.ServiceDependency{ Condition: types.ServiceConditionStarted, } @@ -269,7 +271,14 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, resources := getDeployResources(service) - networkMode, err := getNetworkMode(ctx, p, service) + networkMode, err := getMode(ctx, service.Name, service.NetworkMode) + if err != nil { + return nil, nil, nil, err + } + if networkMode == "" { + networkMode = getDefaultNetworkMode(p, service) + } + ipcmode, err := getMode(ctx, service.Name, service.Ipc) if err != nil { return nil, nil, nil, err } @@ -305,8 +314,9 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, Mounts: mounts, CapAdd: strslice.StrSlice(service.CapAdd), CapDrop: strslice.StrSlice(service.CapDrop), - NetworkMode: networkMode, + NetworkMode: container.NetworkMode(networkMode), Init: service.Init, + IpcMode: container.IpcMode(ipcmode), ReadonlyRootfs: service.ReadOnly, RestartPolicy: getRestartPolicy(service), ShmSize: shmSize, @@ -328,10 +338,21 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project, LogConfig: logConfig, } - networkConfig := buildDefaultNetworkConfig(service, networkMode, getContainerName(p.Name, service, number)) + networkConfig := buildDefaultNetworkConfig(service, container.NetworkMode(networkMode), getContainerName(p.Name, service, number)) return &containerConfig, &hostConfig, networkConfig, nil } +func getDefaultNetworkMode(project *types.Project, service types.ServiceConfig) string { + mode := "none" + if len(project.Networks) > 0 { + for name := range getNetworksForService(service) { + mode = project.Networks[name].Name + break + } + } + return mode +} + func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy { var restart container.RestartPolicy if service.Restart != "" { @@ -470,12 +491,11 @@ func getVolumesFrom(project *types.Project, volumesFrom []string) ([]string, []s } -func getDependentServiceByNetwork(networkMode string) string { - baseService := "" - if strings.HasPrefix(networkMode, types.NetworkModeServicePrefix) { - return networkMode[len(types.NetworkModeServicePrefix):] +func getDependentServiceFromMode(mode string) string { + if strings.HasPrefix(mode, types.NetworkModeServicePrefix) { + return mode[len(types.NetworkModeServicePrefix):] } - return baseService + return "" } func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Project, service types.ServiceConfig, @@ -745,33 +765,22 @@ func getAliases(s types.ServiceConfig, c *types.ServiceNetworkConfig) []string { return aliases } -func getNetworkMode(ctx context.Context, p *types.Project, service types.ServiceConfig) (container.NetworkMode, error) { +func getMode(ctx context.Context, serviceName string, mode string) (string, error) { cState, err := GetContextContainerState(ctx) if err != nil { - return container.NetworkMode("none"), nil + return "", nil } observedState := cState.GetContainers() - - mode := service.NetworkMode - if mode == "" { - if len(p.Networks) > 0 { - for name := range getNetworksForService(service) { - return container.NetworkMode(p.Networks[name].Name), nil - } - } - return container.NetworkMode("none"), nil - } - depServiceNetworkMode := getDependentServiceByNetwork(service.NetworkMode) - if depServiceNetworkMode != "" { - depServiceContainers := observedState.filter(isService(depServiceNetworkMode)) + depService := getDependentServiceFromMode(mode) + if depService != "" { + depServiceContainers := observedState.filter(isService(depService)) if len(depServiceContainers) > 0 { - return container.NetworkMode(types.NetworkModeContainerPrefix + depServiceContainers[0].ID), nil + return types.NetworkModeContainerPrefix + depServiceContainers[0].ID, nil } - return container.NetworkMode("none"), - fmt.Errorf(`no containers started for network_mode %q in service %q -> %v`, - mode, service.Name, observedState) + return "", fmt.Errorf(`no containers started for %q in service %q -> %v`, + mode, serviceName, observedState) } - return container.NetworkMode(mode), nil + return mode, nil } func getNetworksForService(s types.ServiceConfig) map[string]*types.ServiceNetworkConfig { diff --git a/local/e2e/compose/fixtures/ipc-test/compose.yaml b/local/e2e/compose/fixtures/ipc-test/compose.yaml new file mode 100644 index 000000000..3ce737642 --- /dev/null +++ b/local/e2e/compose/fixtures/ipc-test/compose.yaml @@ -0,0 +1,13 @@ +services: + service: + image: busybox + command: top + ipc: "service:shareable" + container: + image: busybox + command: top + ipc: "container:ipc_mode_container" + shareable: + image: busybox + command: top + ipc: shareable diff --git a/local/e2e/compose/ipc_test.go b/local/e2e/compose/ipc_test.go new file mode 100644 index 000000000..4ee4d918f --- /dev/null +++ b/local/e2e/compose/ipc_test.go @@ -0,0 +1,65 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package e2e + +import ( + "fmt" + "strings" + "testing" + + "gotest.tools/v3/icmd" + + . "github.com/docker/compose-cli/utils/e2e" +) + +func TestIPC(t *testing.T) { + c := NewParallelE2eCLI(t, binDir) + + const projectName = "ipc_e2e" + var cid string + t.Run("create ipc mode container", func(t *testing.T) { + res := c.RunDockerCmd("run", "-d", "--rm", "--ipc=shareable", "--name", "ipc_mode_container", "busybox", "top") + cid = strings.Trim(res.Stdout(), "\n") + }) + + t.Run("up", func(t *testing.T) { + c.RunDockerCmd("compose", "-f", "./fixtures/ipc-test/compose.yaml", "--project-name", projectName, "up", "-d") + }) + + t.Run("check running project", func(t *testing.T) { + res := c.RunDockerCmd("compose", "-p", projectName, "ps") + res.Assert(t, icmd.Expected{Out: `shareable`}) + }) + + t.Run("check ipcmode in container inspect", func(t *testing.T) { + res := c.RunDockerCmd("inspect", projectName+"_shareable_1") + res.Assert(t, icmd.Expected{Out: `"IpcMode": "shareable",`}) + + res = c.RunDockerCmd("inspect", projectName+"_service_1") + res.Assert(t, icmd.Expected{Out: `"IpcMode": "container:`}) + + res = c.RunDockerCmd("inspect", projectName+"_container_1") + res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`"IpcMode": "container:%s",`, cid)}) + }) + + t.Run("down", func(t *testing.T) { + _ = c.RunDockerCmd("compose", "--project-name", projectName, "down") + }) + t.Run("stop ipc mode container", func(t *testing.T) { + _ = c.RunDockerCmd("stop", "ipc_mode_container") + }) +}