Add IPC support

Signed-off-by: aiordache <anca.iordache@docker.com>
This commit is contained in:
aiordache 2021-03-24 19:01:04 +01:00
parent 9f81314124
commit baeede2c6c
3 changed files with 120 additions and 33 deletions

View File

@ -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 { return InDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
if utils.StringContains(opts.Services, service.Name) { 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: outLoop:
for i := range p.Services { for i := range p.Services {
dependency := getDependentServiceByNetwork(p.Services[i].NetworkMode) networkDependency := getDependentServiceFromMode(p.Services[i].NetworkMode)
if dependency == "" { ipcDependency := getDependentServiceFromMode(p.Services[i].Ipc)
if networkDependency == "" && ipcDependency == "" {
continue continue
} }
if p.Services[i].DependsOn == nil { if p.Services[i].DependsOn == nil {
p.Services[i].DependsOn = make(types.DependsOnConfig) p.Services[i].DependsOn = make(types.DependsOnConfig)
} }
for _, service := range p.Services { for _, service := range p.Services {
if service.Name == dependency { if service.Name == networkDependency || service.Name == ipcDependency {
p.Services[i].DependsOn[service.Name] = types.ServiceDependency{ p.Services[i].DependsOn[service.Name] = types.ServiceDependency{
Condition: types.ServiceConditionStarted, Condition: types.ServiceConditionStarted,
} }
@ -269,7 +271,14 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
resources := getDeployResources(service) 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 { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
@ -305,8 +314,9 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
Mounts: mounts, Mounts: mounts,
CapAdd: strslice.StrSlice(service.CapAdd), CapAdd: strslice.StrSlice(service.CapAdd),
CapDrop: strslice.StrSlice(service.CapDrop), CapDrop: strslice.StrSlice(service.CapDrop),
NetworkMode: networkMode, NetworkMode: container.NetworkMode(networkMode),
Init: service.Init, Init: service.Init,
IpcMode: container.IpcMode(ipcmode),
ReadonlyRootfs: service.ReadOnly, ReadonlyRootfs: service.ReadOnly,
RestartPolicy: getRestartPolicy(service), RestartPolicy: getRestartPolicy(service),
ShmSize: shmSize, ShmSize: shmSize,
@ -328,10 +338,21 @@ func (s *composeService) getCreateOptions(ctx context.Context, p *types.Project,
LogConfig: logConfig, 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 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 { func getRestartPolicy(service types.ServiceConfig) container.RestartPolicy {
var restart container.RestartPolicy var restart container.RestartPolicy
if service.Restart != "" { if service.Restart != "" {
@ -470,12 +491,11 @@ func getVolumesFrom(project *types.Project, volumesFrom []string) ([]string, []s
} }
func getDependentServiceByNetwork(networkMode string) string { func getDependentServiceFromMode(mode string) string {
baseService := "" if strings.HasPrefix(mode, types.NetworkModeServicePrefix) {
if strings.HasPrefix(networkMode, types.NetworkModeServicePrefix) { return mode[len(types.NetworkModeServicePrefix):]
return networkMode[len(types.NetworkModeServicePrefix):]
} }
return baseService return ""
} }
func (s *composeService) buildContainerVolumes(ctx context.Context, p types.Project, service types.ServiceConfig, 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 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) cState, err := GetContextContainerState(ctx)
if err != nil { if err != nil {
return container.NetworkMode("none"), nil return "", nil
} }
observedState := cState.GetContainers() observedState := cState.GetContainers()
depService := getDependentServiceFromMode(mode)
mode := service.NetworkMode if depService != "" {
if mode == "" { depServiceContainers := observedState.filter(isService(depService))
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))
if len(depServiceContainers) > 0 { if len(depServiceContainers) > 0 {
return container.NetworkMode(types.NetworkModeContainerPrefix + depServiceContainers[0].ID), nil return types.NetworkModeContainerPrefix + depServiceContainers[0].ID, nil
} }
return container.NetworkMode("none"), return "", fmt.Errorf(`no containers started for %q in service %q -> %v`,
fmt.Errorf(`no containers started for network_mode %q in service %q -> %v`, mode, serviceName, observedState)
mode, service.Name, observedState)
} }
return container.NetworkMode(mode), nil return mode, nil
} }
func getNetworksForService(s types.ServiceConfig) map[string]*types.ServiceNetworkConfig { func getNetworksForService(s types.ServiceConfig) map[string]*types.ServiceNetworkConfig {

View File

@ -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

View File

@ -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")
})
}