Skip to content

Commit

Permalink
"ns:" network mode to use existing network namespace
Browse files Browse the repository at this point in the history
Signed-off-by: Dan Cavallaro <[email protected]>
  • Loading branch information
dancavallaro committed Oct 11, 2024
1 parent 57bafac commit b6a6358
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 6 deletions.
5 changes: 3 additions & 2 deletions docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,10 @@ Isolation flags:

Network flags:

- :whale: `--net, --network=(bridge|host|none|container:<container>|<CNI>)`: Connect a container to a network.
- :whale: `--net, --network=(bridge|host|none|container:<container>|ns:<path>|<CNI>)`: Connect a container to a network.
- Default: "bridge"
- 'container:<name|id>': reuse another container's network stack, container has to be precreated.
- `container:<name|id>`: reuse another container's network stack, container has to be precreated.
- :nerd_face: `ns:<path>`: run inside an existing network namespace
- :nerd_face: Unlike Docker, this flag can be specified multiple times (`--net foo --net bar`)
- :whale: `-p, --publish`: Publish a container's port(s) to the host
- :whale: `--dns`: Set custom DNS servers
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/container/kill.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func cleanupNetwork(ctx context.Context, container containerd.Container, globalO
}

switch netType {
case nettype.Host, nettype.None, nettype.Container:
case nettype.Host, nettype.None, nettype.Container, nettype.Namespace:
// NOP
case nettype.CNI:
e, err := netutil.NewCNIEnv(globalOpts.CNIPath, globalOpts.CNINetConfPath, netutil.WithNamespace(globalOpts.Namespace), netutil.WithDefaultNetwork())
Expand Down
2 changes: 1 addition & 1 deletion pkg/composer/serviceparser/serviceparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ func getNetworks(project *types.Project, svc types.ServiceConfig) ([]networkName
return nil, errors.New("net and network_mode must not be set together")
}
if strings.Contains(svc.NetworkMode, ":") {
if !strings.HasPrefix(svc.NetworkMode, "container:") {
if !strings.HasPrefix(svc.NetworkMode, "container:") && !strings.HasPrefix(svc.NetworkMode, "ns:") {
return nil, fmt.Errorf("unsupported network_mode: %q", svc.NetworkMode)
}
}
Expand Down
28 changes: 27 additions & 1 deletion pkg/containerutil/container_network_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ func NewNetworkingOptionsManager(globalOptions types.GlobalCommandOptions, netOp
manager = &containerNetworkManager{globalOptions, netOpts, client}
case nettype.CNI:
manager = &cniNetworkManager{globalOptions, netOpts, client, cniNetworkManagerPlatform{}}
case nettype.Namespace:
// We'll handle Namespace networking identically to Host-mode networking, but
// put the container in the specified network namespace instead of the root.
manager = &hostNetworkManager{globalOptions, netOpts, client}
default:
return nil, fmt.Errorf("unexpected container networking type: %q", netType)
}
Expand Down Expand Up @@ -491,6 +495,23 @@ func copyFileContent(src string, dst string) error {
return nil
}

func getHostNetworkingNamespace(netModeArg string) (oci.SpecOpts, error) {
if !strings.Contains(netModeArg, ":") {
// Use the host root namespace by default
return oci.WithHostNamespace(specs.NetworkNamespace), nil
}

netItems := strings.Split(netModeArg, ":")
if len(netItems) < 2 {
return nil, fmt.Errorf("namespace networking argument format must be 'ns:<path>', got: %q", netModeArg)
}
netnsPath := netItems[1]
return oci.WithLinuxNamespace(specs.LinuxNamespace{
Type: specs.NetworkNamespace,
Path: netnsPath,
}), nil
}

// ContainerNetworkingOpts Returns a slice of `oci.SpecOpts` and `containerd.NewContainerOpts` which represent
// the network specs which need to be applied to the container with the given ID.
func (m *hostNetworkManager) ContainerNetworkingOpts(_ context.Context, containerID string) ([]oci.SpecOpts, []containerd.NewContainerOpts, error) {
Expand Down Expand Up @@ -525,8 +546,13 @@ func (m *hostNetworkManager) ContainerNetworkingOpts(_ context.Context, containe
return nil, nil, err
}

netModeArg := m.netOpts.NetworkSlice[0]
netNamespace, err := getHostNetworkingNamespace(netModeArg)
if err != nil {
return nil, nil, err
}
specs := []oci.SpecOpts{
oci.WithHostNamespace(specs.NetworkNamespace),
netNamespace,
withDedupMounts("/etc/hosts", withCustomHosts(etcHostsPath)),
withDedupMounts("/etc/resolv.conf", withCustomResolvConf(resolvConfPath)),
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/netutil/nettype/nettype.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
Host
CNI
Container
Namespace
)

var netTypeToName = map[interface{}]string{
Expand All @@ -37,6 +38,7 @@ var netTypeToName = map[interface{}]string{
Host: "host",
CNI: "cni",
Container: "container",
Namespace: "ns",
}

func Detect(names []string) (Type, error) {
Expand All @@ -54,6 +56,8 @@ func Detect(names []string) (Type, error) {
tmp = Host
case "container":
tmp = Container
case "ns":
tmp = Namespace
default:
tmp = CNI
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/ocihook/ocihook.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func newHandlerOpts(state *specs.State, dataStore, cniPath, cniNetconfPath strin
}

switch netType {
case nettype.Host, nettype.None, nettype.Container:
case nettype.Host, nettype.None, nettype.Container, nettype.Namespace:
// NOP
case nettype.CNI:
e, err := netutil.NewCNIEnv(cniPath, cniNetconfPath, netutil.WithNamespace(namespace), netutil.WithDefaultNetwork())
Expand Down

0 comments on commit b6a6358

Please sign in to comment.