Skip to content

Commit

Permalink
Add integration tests for default network creation.
Browse files Browse the repository at this point in the history
Signed-off-by: Nashwan Azhari <[email protected]>
  • Loading branch information
aznashwan committed Nov 24, 2022
1 parent 9288a21 commit 033dada
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 0 deletions.
195 changes: 195 additions & 0 deletions cmd/nerdctl/network_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
Copyright The containerd 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 main

import (
"bytes"
"fmt"
"os"
"path/filepath"
"strconv"
"testing"
"text/template"

ncdefaults "github.com/containerd/nerdctl/pkg/defaults"
"github.com/containerd/nerdctl/pkg/labels"
"github.com/containerd/nerdctl/pkg/netutil"
"github.com/containerd/nerdctl/pkg/testutil"

"gotest.tools/v3/assert"
)

const preExistingNetworkConfigTemplate = `
{
"cniVersion": "0.2.0",
"name": "{{ .network_name }}",
"type": "nat",
"master": "Ethernet",
"ipam": {
"subnet": "{{ .subnet }}",
"routes": [
{
"GW": "{{ .gateway }}"
}
]
},
"capabilities": {
"portMappings": true,
"dns": true
}
}
`

// Tests whether nerdctl properly creates the default network on any
// `nerdctl network` subcommand in a fresh environment.
func TestDefaultNetworkCreation(t *testing.T) {
testutil.RequiresRoot(t)
testutil.DockerIncompatible(t)
base := testutil.NewBase(t)

// NOTE: to prevent subnet collisions when attempting to recreate the default
// network in the isolated CNI config dir we'll be using, we must first delete
// the network in the default CNI config dir.
defaultCniEnv := netutil.CNIEnv{
Path: ncdefaults.CNIPath(),
NetconfPath: ncdefaults.CNINetConfPath(),
}
defaultNet, err := defaultCniEnv.GetDefaultNetworkConfig()
assert.NilError(t, err)
if defaultNet != nil {
assert.NilError(t, defaultCniEnv.RemoveNetwork(defaultNet))
}

// NOTE: any `nerdctl network` subcommand will result in the attempted creation
// of the default nerdctl network so we create a tempdir for the CNI conf path
// to ensure an empty env for this test.
cniConfTestDir := t.TempDir()
cniEnv := netutil.CNIEnv{
Path: t.TempDir(), // irrelevant for this test
NetconfPath: cniConfTestDir,
}
// Ensure no default network config is not present:
defaultNetConf, err := cniEnv.GetDefaultNetworkConfig()
assert.NilError(t, err)
assert.Assert(t, defaultNetConf == nil)

base.Cmd("network", "--cni-netconfpath", cniConfTestDir, "ls").AssertOutContains(netutil.DefaultNetworkName)

// Ensure no default network config is present now:
defaultNetConf, err = cniEnv.GetDefaultNetworkConfig()
assert.NilError(t, err)
assert.Assert(t, defaultNetConf != nil)

// Check network config file present:
stat, err := os.Stat(defaultNetConf.File)
assert.NilError(t, err)
firstConfigModTime := stat.ModTime()

// Check default network label present:
assert.Assert(t, defaultNetConf.NerdctlLabels != nil)
lstr, ok := (*defaultNetConf.NerdctlLabels)[labels.NerdctlDefaultNetwork]
assert.Assert(t, ok)
boolv, err := strconv.ParseBool(lstr)
assert.NilError(t, err)
assert.Assert(t, boolv)

// Ensure network isn't created twice or accidentally re-created:
base.Cmd("network", "--cni-netconfpath", cniConfTestDir, "ls").AssertOutContains(netutil.DefaultNetworkName)

// Check for any other network config files:
files := []os.FileInfo{}
walkF := func(p string, info os.FileInfo, err error) error {
files = append(files, info)
return nil
}
err = filepath.Walk(cniConfTestDir, walkF)
assert.NilError(t, err)
assert.Assert(t, len(files) == 2) // files[0] is the entry for '.'
assert.Assert(t, filepath.Join(cniConfTestDir, files[1].Name()) == defaultNetConf.File)
assert.Assert(t, firstConfigModTime == files[1].ModTime())
}

// Tests whether nerdctl skips the creation of the default network if a
// network bearing the default network name already exists in a
// non-nerdctl-managed network config file.
func TestNetworkWithDefaultNameAlreadyExists(t *testing.T) {
testutil.DockerIncompatible(t)
base := testutil.NewBase(t)

// NOTE: any `nerdctl network` subcommand will result in the attempted creation
// of the default nerdctl network so we create a tempdir for the CNI conf path
// to ensure an empty env for this test.
cniConfTestDir := t.TempDir()
cniEnv := netutil.CNIEnv{
Path: t.TempDir(), // irrelevant for this test
NetconfPath: cniConfTestDir,
}

// Ensure no default network config is not present:
defaultNetConf, err := cniEnv.GetDefaultNetworkConfig()
assert.NilError(t, err)
assert.Assert(t, defaultNetConf == nil)

// Manually define and write a network config file:
values := map[string]string{
"network_name": netutil.DefaultNetworkName,
"subnet": "10.7.1.1/24",
"gateway": "10.7.1.1",
}
tpl, err := template.New("test").Parse(preExistingNetworkConfigTemplate)
assert.NilError(t, err)
buf := &bytes.Buffer{}
assert.NilError(t, tpl.ExecuteTemplate(buf, "test", values))

// Filename is irrelevant as long as it's not nerdctl's:
testConfFile := filepath.Join(cniConfTestDir, fmt.Sprintf("%s.conf", testutil.Identifier(t)))
err = os.WriteFile(testConfFile, buf.Bytes(), 0600)
assert.NilError(t, err)

// Check network is detected:
netConfs, err := cniEnv.NetworkList()
assert.NilError(t, err)
assert.Assert(t, len(netConfs) > 0)

listedDefaultNetConf := netConfs[0]
for _, netConf := range netConfs {
if netConf.Name == netutil.DefaultNetworkName {
listedDefaultNetConf = netConf
break
}
}
assert.Assert(t, listedDefaultNetConf != nil)

defaultNetConf, err = cniEnv.GetDefaultNetworkConfig()
assert.NilError(t, err)
assert.Assert(t, defaultNetConf != nil)
assert.Assert(t, defaultNetConf.File == testConfFile)

// Run `nerdctl network` subcommand and recheck:
base.Cmd("network", "--cni-netconfpath", cniConfTestDir, "ls").AssertOutContains(netutil.DefaultNetworkName)

netConfs, err = cniEnv.NetworkList()
assert.NilError(t, err)
defaultNamedNetworksFileDefinitions := []string{}
for _, netConf := range netConfs {
if netConf.Name == netutil.DefaultNetworkName {
defaultNamedNetworksFileDefinitions = append(defaultNamedNetworksFileDefinitions, netConf.File)
}
}
assert.Assert(t, len(defaultNamedNetworksFileDefinitions) == 1)
assert.Assert(t, defaultNamedNetworksFileDefinitions[0] == testConfFile)
}
31 changes: 31 additions & 0 deletions pkg/testutil/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"io"
"os"
"os/exec"
"os/user"
"runtime"
"strings"
"testing"
Expand Down Expand Up @@ -570,6 +571,36 @@ func RequireSystemService(t testing.TB, sv string) {
}
}

func RequiresRoot(t testing.TB) {
t.Helper()
if runtime.GOOS == "windows" {
t.Logf("permissions-related tests on Windows may behave inconsistently")
return
}

user, err := user.Current()
if err != nil {
t.Fatal(err)
}
if user.Name == "root" {
t.Logf("test is running as root user")
return
}

userGroups, err := user.GroupIds()
if err != nil {
t.Fatal(err)
}
for _, group := range userGroups {
if group == "0" {
t.Logf("test is running as user which is member of the root group")
return
}
}

t.Skipf("test must be run as root or a user in the root group")
}

const Namespace = "nerdctl-test"

func NewBase(t *testing.T) *Base {
Expand Down

0 comments on commit 033dada

Please sign in to comment.