Skip to content

Commit

Permalink
Add unit 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 29, 2022
1 parent e56d21c commit 6071186
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 0 deletions.
164 changes: 164 additions & 0 deletions pkg/netutil/netutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,43 @@
package netutil

import (
"bytes"
"fmt"
"net"
"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/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
}
}
`

func TestParseIPAMRange(t *testing.T) {
t.Parallel()
type testCase struct {
Expand Down Expand Up @@ -97,3 +128,136 @@ func TestParseIPAMRange(t *testing.T) {
}
}
}

// Tests whether nerdctl properly creates the default network when required.
func TestDefaultNetworkCreation(t *testing.T) {
testutil.RequiresRoot(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 := 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: we create a tempdir for the CNI conf path to ensure an empty env for this test:
cniConfTestDir := t.TempDir()
cniEnv := CNIEnv{
Path: ncdefaults.CNIPath(),
NetconfPath: cniConfTestDir,
}
// Ensure no default network config is not present:
defaultNetConf, err := cniEnv.GetDefaultNetworkConfig()
assert.NilError(t, err)
assert.Assert(t, defaultNetConf == nil)

// Attempt to create the default network:
err = cniEnv.ensureDefaultNetworkConfig()
assert.NilError(t, err)

// 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:
err = cniEnv.ensureDefaultNetworkConfig()
assert.NilError(t, err)

// 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) {
// NOTE: we create a tempdir for the CNI conf path to ensure an empty env for this test:
cniConfTestDir := t.TempDir()
cniEnv := 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": 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)

var listedDefaultNetConf *networkConfig
for _, netConf := range netConfs {
if netConf.Name == 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)

err = cniEnv.ensureDefaultNetworkConfig()
assert.NilError(t, err)

netConfs, err = cniEnv.NetworkList()
assert.NilError(t, err)
defaultNamedNetworksFileDefinitions := []string{}
for _, netConf := range netConfs {
if netConf.Name == 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 6071186

Please sign in to comment.