Skip to content

Commit

Permalink
Merge pull request #3096 from apostasie/dev-3095
Browse files Browse the repository at this point in the history
[Proposal] Implement namespacing for networks
  • Loading branch information
AkihiroSuda authored Jun 19, 2024
2 parents 4100ec5 + 5dd36b6 commit e9d1691
Show file tree
Hide file tree
Showing 16 changed files with 76 additions and 40 deletions.
2 changes: 1 addition & 1 deletion cmd/nerdctl/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ func shellCompleteNetworkNames(cmd *cobra.Command, exclude []string) ([]string,
excludeMap[ex] = struct{}{}
}

e, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath)
e, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath, netutil.WithNamespace(globalOptions.Namespace))
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
Expand Down
25 changes: 25 additions & 0 deletions cmd/nerdctl/network_inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,28 @@ func TestNetworkInspect(t *testing.T) {
}
assert.DeepEqual(base.T, expectedIPAM, got.IPAM)
}

func TestNetworkWithNamespace(t *testing.T) {
testutil.DockerIncompatible(t)

t.Parallel()

tID := testutil.Identifier(t)
base := testutil.NewBase(t)
baseOther := testutil.NewBaseWithNamespace(t, tID)

tearDown := func() {
base.Cmd("network", "rm", tID).Run()
baseOther.Cmd("namespace", "remove", tID).Run()
}
tearDown()
t.Cleanup(tearDown)

base.Cmd("network", "create", tID).AssertOK()

// Other namespace cannot inspect, prune, see, or remove this network
baseOther.Cmd("network", "inspect", tID).AssertFail()
baseOther.Cmd("network", "prune", "-f").AssertOutNotContains(tID)
baseOther.Cmd("network", "ls").AssertOutNotContains(tID)
baseOther.Cmd("network", "remove", tID).AssertFail()
}
24 changes: 0 additions & 24 deletions cmd/nerdctl/network_remove_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,6 @@ import (
"gotest.tools/v3/assert"
)

func TestNetworkRemoveInOtherNamespace(t *testing.T) {
if rootlessutil.IsRootless() {
t.Skip("test skipped for remove rootless network")
}
if testutil.GetTarget() == testutil.Docker {
t.Skip("test skipped for docker")
}
// --namespace=nerdctl-test
base := testutil.NewBase(t)
// --namespace=nerdctl-other
baseOther := testutil.NewBaseWithNamespace(t, "nerdctl-other")
networkName := testutil.Identifier(t)

base.Cmd("network", "create", networkName).AssertOK()
defer base.Cmd("network", "rm", networkName).AssertOK()

tID := testutil.Identifier(t)
base.Cmd("run", "-d", "--net", networkName, "--name", tID, testutil.AlpineImage, "sleep", "infinity").AssertOK()
defer base.Cmd("rm", "-f", tID).Run()

// delete network in namespace nerdctl-other
baseOther.Cmd("network", "rm", networkName).AssertFail()
}

func TestNetworkRemove(t *testing.T) {
if rootlessutil.IsRootless() {
t.Skip("test skipped for remove rootless network")
Expand Down
13 changes: 11 additions & 2 deletions docs/cni.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,16 @@ For example:
## Custom networks

You can also customize your CNI network by providing configuration files.
For example you have one configuration file(`/etc/cni/net.d/10-mynet.conf`)

When rootful, the expected root location is `/etc/cni/net.d`.
For rootless, the expected root location is `~/.config/cni/net.d/`

Configuration files (like `10-mynet.conf`) can be placed either in the root location,
or under a subfolder.
If in the root location, this network will be available to all nerdctl namespaces.
If placed in a subfolder, it will be available only to the identically named namespace.

For example, you have one configuration file(`/etc/cni/net.d/10-mynet.conf`)
for `bridge` network:

```json
Expand All @@ -138,7 +147,7 @@ for `bridge` network:
```

This will configure a new CNI network with the name `mynet`, and you can use
this network to create a container:
this network to create a container in any namespace:

```console
# nerdctl run -it --net mynet --rm alpine ip addr show
Expand Down
4 changes: 4 additions & 0 deletions docs/dir.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,9 @@ Data volume

Can be overridden with `nerdctl --cni-netconfpath=<NETCONFPATH>` flag and environment variable `$NETCONFPATH`.

At the top-level of <NETCONFPATH>, network (files) are shared accross all namespaces.
Sub-folders inside <NETCONFPATH> are only available to the namespace bearing the same name,
and its networks definitions are private.

Files:
- `nerdctl-<NWNAME>.conflist`: CNI conf list created by nerdctl
2 changes: 1 addition & 1 deletion pkg/cmd/compose/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import (

// New returns a new *composer.Composer.
func New(client *containerd.Client, globalOptions types.GlobalCommandOptions, options composer.Options, stdout, stderr io.Writer) (*composer.Composer, error) {
cniEnv, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath, netutil.WithDefaultNetwork())
cniEnv, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath, netutil.WithNamespace(globalOptions.Namespace), netutil.WithDefaultNetwork())
if err != nil {
return nil, err
}
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 @@ -152,7 +152,7 @@ func cleanupNetwork(ctx context.Context, container containerd.Container, globalO
case nettype.Host, nettype.None, nettype.Container:
// NOP
case nettype.CNI:
e, err := netutil.NewCNIEnv(globalOpts.CNIPath, globalOpts.CNINetConfPath, netutil.WithDefaultNetwork())
e, err := netutil.NewCNIEnv(globalOpts.CNIPath, globalOpts.CNINetConfPath, netutil.WithNamespace(globalOpts.Namespace), netutil.WithDefaultNetwork())
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/network/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func Create(options types.NetworkCreateOptions, stdout io.Writer) error {
options.Subnets = []string{""}
}

e, err := netutil.NewCNIEnv(options.GOptions.CNIPath, options.GOptions.CNINetConfPath)
e, err := netutil.NewCNIEnv(options.GOptions.CNIPath, options.GOptions.CNINetConfPath, netutil.WithNamespace(options.GOptions.Namespace))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/network/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (

func Inspect(ctx context.Context, options types.NetworkInspectOptions) error {
globalOptions := options.GOptions
e, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath)
e, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath, netutil.WithNamespace(options.GOptions.Namespace))

if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/network/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func List(ctx context.Context, options types.NetworkListOptions) error {
}
}

e, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath)
e, err := netutil.NewCNIEnv(globalOptions.CNIPath, globalOptions.CNINetConfPath, netutil.WithNamespace(options.GOptions.Namespace))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/network/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
)

func Prune(ctx context.Context, client *containerd.Client, options types.NetworkPruneOptions) error {
e, err := netutil.NewCNIEnv(options.GOptions.CNIPath, options.GOptions.CNINetConfPath)
e, err := netutil.NewCNIEnv(options.GOptions.CNIPath, options.GOptions.CNINetConfPath, netutil.WithNamespace(options.GOptions.Namespace))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/network/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
)

func Remove(ctx context.Context, client *containerd.Client, options types.NetworkRemoveOptions) error {
e, err := netutil.NewCNIEnv(options.GOptions.CNIPath, options.GOptions.CNINetConfPath)
e, err := netutil.NewCNIEnv(options.GOptions.CNIPath, options.GOptions.CNINetConfPath, netutil.WithNamespace(options.GOptions.Namespace))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/containerutil/container_network_manager_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type cniNetworkManagerPlatform struct {

// Verifies that the internal network settings are correct.
func (m *cniNetworkManager) VerifyNetworkOptions(_ context.Context) error {
e, err := netutil.NewCNIEnv(m.globalOptions.CNIPath, m.globalOptions.CNINetConfPath, netutil.WithDefaultNetwork())
e, err := netutil.NewCNIEnv(m.globalOptions.CNIPath, m.globalOptions.CNINetConfPath, netutil.WithNamespace(m.globalOptions.Namespace), netutil.WithDefaultNetwork())
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/containerutil/container_network_manager_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type cniNetworkManagerPlatform struct {

// Verifies that the internal network settings are correct.
func (m *cniNetworkManager) VerifyNetworkOptions(_ context.Context) error {
e, err := netutil.NewCNIEnv(m.globalOptions.CNIPath, m.globalOptions.CNINetConfPath, netutil.WithDefaultNetwork())
e, err := netutil.NewCNIEnv(m.globalOptions.CNIPath, m.globalOptions.CNINetConfPath, netutil.WithNamespace(m.globalOptions.Namespace), netutil.WithDefaultNetwork())
if err != nil {
return err
}
Expand Down Expand Up @@ -67,7 +67,7 @@ func (m *cniNetworkManager) VerifyNetworkOptions(_ context.Context) error {
}

func (m *cniNetworkManager) getCNI() (gocni.CNI, error) {
e, err := netutil.NewCNIEnv(m.globalOptions.CNIPath, m.globalOptions.CNINetConfPath, netutil.WithDefaultNetwork())
e, err := netutil.NewCNIEnv(m.globalOptions.CNIPath, m.globalOptions.CNINetConfPath, netutil.WithNamespace(m.globalOptions.Namespace), netutil.WithDefaultNetwork())
if err != nil {
return nil, fmt.Errorf("failed to instantiate CNI env: %s", err)
}
Expand Down
26 changes: 24 additions & 2 deletions pkg/netutil/netutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
type CNIEnv struct {
Path string
NetconfPath string
Namespace string
}

type CNIEnvOpt func(e *CNIEnv) error
Expand Down Expand Up @@ -132,6 +133,16 @@ func WithDefaultNetwork() CNIEnvOpt {
}
}

func WithNamespace(namespace string) CNIEnvOpt {
return func(e *CNIEnv) error {
if err := os.MkdirAll(filepath.Join(e.NetconfPath, namespace), 0755); err != nil {
return err
}
e.Namespace = namespace
return nil
}
}

func NewCNIEnv(cniPath, cniConfPath string, opts ...CNIEnvOpt) (*CNIEnv, error) {
e := CNIEnv{
Path: cniPath,
Expand Down Expand Up @@ -193,7 +204,10 @@ func (e *CNIEnv) FilterNetworks(filterf func(*NetworkConfig) bool) ([]*NetworkCo
}

func (e *CNIEnv) getConfigPathForNetworkName(netName string) string {
return filepath.Join(e.NetconfPath, "nerdctl-"+netName+".conflist")
if netName == DefaultNetworkName || e.Namespace == "" {
return filepath.Join(e.NetconfPath, "nerdctl-"+netName+".conflist")
}
return filepath.Join(e.NetconfPath, e.Namespace, "nerdctl-"+netName+".conflist")
}

func (e *CNIEnv) usedSubnets() ([]*net.IPNet, error) {
Expand Down Expand Up @@ -404,10 +418,18 @@ func (e *CNIEnv) writeNetworkConfig(net *NetworkConfig) error {
// networkConfigList loads config from dir if dir exists.
func (e *CNIEnv) networkConfigList() ([]*NetworkConfig, error) {
l := []*NetworkConfig{}
fileNames, err := libcni.ConfFiles(e.NetconfPath, []string{".conf", ".conflist", ".json"})
common, err := libcni.ConfFiles(e.NetconfPath, []string{".conf", ".conflist", ".json"})
if err != nil {
return nil, err
}
namespaced := []string{}
if e.Namespace != "" {
namespaced, err = libcni.ConfFiles(filepath.Join(e.NetconfPath, e.Namespace), []string{".conf", ".conflist", ".json"})
if err != nil {
return nil, err
}
}
fileNames := append(common, namespaced...)
sort.Strings(fileNames)
for _, fileName := range fileNames {
var lcl *libcni.NetworkConfigList
Expand Down
2 changes: 1 addition & 1 deletion pkg/ocihook/ocihook.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func newHandlerOpts(state *specs.State, dataStore, cniPath, cniNetconfPath strin
case nettype.Host, nettype.None, nettype.Container:
// NOP
case nettype.CNI:
e, err := netutil.NewCNIEnv(cniPath, cniNetconfPath, netutil.WithDefaultNetwork())
e, err := netutil.NewCNIEnv(cniPath, cniNetconfPath, netutil.WithNamespace(namespace), netutil.WithDefaultNetwork())
if err != nil {
return nil, err
}
Expand Down

0 comments on commit e9d1691

Please sign in to comment.