123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631 |
- package daemon // import "github.com/docker/docker/integration/daemon"
- import (
- "bytes"
- "fmt"
- "io"
- "net/http"
- "net/http/httptest"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "strings"
- "syscall"
- "testing"
- "github.com/docker/docker/api/types"
- containertypes "github.com/docker/docker/api/types/container"
- "github.com/docker/docker/api/types/mount"
- "github.com/docker/docker/api/types/volume"
- "github.com/docker/docker/daemon/config"
- "github.com/docker/docker/errdefs"
- "github.com/docker/docker/integration/internal/container"
- "github.com/docker/docker/integration/internal/process"
- "github.com/docker/docker/pkg/stdcopy"
- "github.com/docker/docker/testutil"
- "github.com/docker/docker/testutil/daemon"
- "gotest.tools/v3/assert"
- is "gotest.tools/v3/assert/cmp"
- "gotest.tools/v3/icmd"
- "gotest.tools/v3/poll"
- "gotest.tools/v3/skip"
- )
- func TestConfigDaemonID(t *testing.T) {
- skip.If(t, runtime.GOOS == "windows")
- _ = testutil.StartSpan(baseContext, t)
- d := daemon.New(t)
- defer d.Stop(t)
- d.Start(t, "--iptables=false")
- info := d.Info(t)
- assert.Check(t, info.ID != "")
- d.Stop(t)
- // Verify that (if present) the engine-id file takes precedence
- const engineID = "this-is-the-engine-id"
- idFile := filepath.Join(d.RootDir(), "engine-id")
- assert.Check(t, os.Remove(idFile))
- // Using 0644 to allow rootless daemons to read the file (ideally
- // we'd chown the file to have the remapped user as owner).
- err := os.WriteFile(idFile, []byte(engineID), 0o644)
- assert.NilError(t, err)
- d.Start(t, "--iptables=false")
- info = d.Info(t)
- assert.Equal(t, info.ID, engineID)
- d.Stop(t)
- }
- func TestDaemonConfigValidation(t *testing.T) {
- skip.If(t, runtime.GOOS == "windows")
- ctx := testutil.StartSpan(baseContext, t)
- d := daemon.New(t)
- dockerBinary, err := d.BinaryPath()
- assert.NilError(t, err)
- params := []string{"--validate", "--config-file"}
- dest := os.Getenv("DOCKER_INTEGRATION_DAEMON_DEST")
- if dest == "" {
- dest = os.Getenv("DEST")
- }
- testdata := filepath.Join(dest, "..", "..", "integration", "daemon", "testdata")
- const (
- validOut = "configuration OK"
- failedOut = "unable to configure the Docker daemon with file"
- )
- tests := []struct {
- name string
- args []string
- expectedOut string
- }{
- {
- name: "config with no content",
- args: append(params, filepath.Join(testdata, "empty-config-1.json")),
- expectedOut: validOut,
- },
- {
- name: "config with {}",
- args: append(params, filepath.Join(testdata, "empty-config-2.json")),
- expectedOut: validOut,
- },
- {
- name: "invalid config",
- args: append(params, filepath.Join(testdata, "invalid-config-1.json")),
- expectedOut: failedOut,
- },
- {
- name: "malformed config",
- args: append(params, filepath.Join(testdata, "malformed-config.json")),
- expectedOut: failedOut,
- },
- {
- name: "valid config",
- args: append(params, filepath.Join(testdata, "valid-config-1.json")),
- expectedOut: validOut,
- },
- }
- for _, tc := range tests {
- tc := tc
- t.Run(tc.name, func(t *testing.T) {
- t.Parallel()
- _ = testutil.StartSpan(ctx, t)
- cmd := exec.Command(dockerBinary, tc.args...)
- out, err := cmd.CombinedOutput()
- assert.Check(t, is.Contains(string(out), tc.expectedOut))
- if tc.expectedOut == failedOut {
- assert.ErrorContains(t, err, "", "expected an error, but got none")
- } else {
- assert.NilError(t, err)
- }
- })
- }
- }
- func TestConfigDaemonSeccompProfiles(t *testing.T) {
- skip.If(t, runtime.GOOS == "windows")
- ctx := testutil.StartSpan(baseContext, t)
- d := daemon.New(t)
- defer d.Stop(t)
- tests := []struct {
- doc string
- profile string
- expectedProfile string
- }{
- {
- doc: "empty profile set",
- profile: "",
- expectedProfile: config.SeccompProfileDefault,
- },
- {
- doc: "default profile",
- profile: config.SeccompProfileDefault,
- expectedProfile: config.SeccompProfileDefault,
- },
- {
- doc: "unconfined profile",
- profile: config.SeccompProfileUnconfined,
- expectedProfile: config.SeccompProfileUnconfined,
- },
- }
- for _, tc := range tests {
- tc := tc
- t.Run(tc.doc, func(t *testing.T) {
- _ = testutil.StartSpan(ctx, t)
- d.Start(t, "--seccomp-profile="+tc.profile)
- info := d.Info(t)
- assert.Assert(t, is.Contains(info.SecurityOptions, "name=seccomp,profile="+tc.expectedProfile))
- d.Stop(t)
- cfg := filepath.Join(d.RootDir(), "daemon.json")
- err := os.WriteFile(cfg, []byte(`{"seccomp-profile": "`+tc.profile+`"}`), 0o644)
- assert.NilError(t, err)
- d.Start(t, "--config-file", cfg)
- info = d.Info(t)
- assert.Assert(t, is.Contains(info.SecurityOptions, "name=seccomp,profile="+tc.expectedProfile))
- d.Stop(t)
- })
- }
- }
- func TestDaemonProxy(t *testing.T) {
- skip.If(t, runtime.GOOS == "windows", "cannot start multiple daemons on windows")
- skip.If(t, os.Getenv("DOCKER_ROOTLESS") != "", "cannot connect to localhost proxy in rootless environment")
- ctx := testutil.StartSpan(baseContext, t)
- newProxy := func(rcvd *string, t *testing.T) *httptest.Server {
- s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- *rcvd = r.Host
- w.Header().Set("Content-Type", "application/json")
- _, _ = w.Write([]byte("OK"))
- }))
- t.Cleanup(s.Close)
- return s
- }
- const userPass = "myuser:mypassword@"
- // Configure proxy through env-vars
- t.Run("environment variables", func(t *testing.T) {
- t.Parallel()
- ctx := testutil.StartSpan(ctx, t)
- var received string
- proxyServer := newProxy(&received, t)
- d := daemon.New(t, daemon.WithEnvVars(
- "HTTP_PROXY="+proxyServer.URL,
- "HTTPS_PROXY="+proxyServer.URL,
- "NO_PROXY=example.com",
- ))
- c := d.NewClientT(t)
- d.Start(t, "--iptables=false")
- defer d.Stop(t)
- info := d.Info(t)
- assert.Check(t, is.Equal(info.HTTPProxy, proxyServer.URL))
- assert.Check(t, is.Equal(info.HTTPSProxy, proxyServer.URL))
- assert.Check(t, is.Equal(info.NoProxy, "example.com"))
- _, err := c.ImagePull(ctx, "example.org:5000/some/image:latest", types.ImagePullOptions{})
- assert.ErrorContains(t, err, "", "pulling should have failed")
- assert.Equal(t, received, "example.org:5000")
- // Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
- _, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
- assert.ErrorContains(t, err, "", "pulling should have failed")
- assert.Equal(t, received, "example.org:5000", "should not have used proxy")
- })
- // Configure proxy through command-line flags
- t.Run("command-line options", func(t *testing.T) {
- t.Parallel()
- ctx := testutil.StartSpan(ctx, t)
- var received string
- proxyServer := newProxy(&received, t)
- d := daemon.New(t, daemon.WithEnvVars(
- "HTTP_PROXY="+"http://"+userPass+"from-env-http.invalid",
- "http_proxy="+"http://"+userPass+"from-env-http.invalid",
- "HTTPS_PROXY="+"https://"+userPass+"myuser:mypassword@from-env-https-invalid",
- "https_proxy="+"https://"+userPass+"myuser:mypassword@from-env-https-invalid",
- "NO_PROXY=ignore.invalid",
- "no_proxy=ignore.invalid",
- ))
- d.Start(t, "--iptables=false", "--http-proxy", proxyServer.URL, "--https-proxy", proxyServer.URL, "--no-proxy", "example.com")
- defer d.Stop(t)
- c := d.NewClientT(t)
- info := d.Info(t)
- assert.Check(t, is.Equal(info.HTTPProxy, proxyServer.URL))
- assert.Check(t, is.Equal(info.HTTPSProxy, proxyServer.URL))
- assert.Check(t, is.Equal(info.NoProxy, "example.com"))
- ok, _ := d.ScanLogsT(ctx, t, daemon.ScanLogsMatchAll(
- "overriding existing proxy variable with value from configuration",
- "http_proxy",
- "HTTP_PROXY",
- "https_proxy",
- "HTTPS_PROXY",
- "no_proxy",
- "NO_PROXY",
- ))
- assert.Assert(t, ok)
- ok, logs := d.ScanLogsT(ctx, t, daemon.ScanLogsMatchString(userPass))
- assert.Assert(t, !ok, "logs should not contain the non-sanitized proxy URL: %s", logs)
- _, err := c.ImagePull(ctx, "example.org:5001/some/image:latest", types.ImagePullOptions{})
- assert.ErrorContains(t, err, "", "pulling should have failed")
- assert.Equal(t, received, "example.org:5001")
- // Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
- _, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
- assert.ErrorContains(t, err, "", "pulling should have failed")
- assert.Equal(t, received, "example.org:5001", "should not have used proxy")
- })
- // Configure proxy through configuration file
- t.Run("configuration file", func(t *testing.T) {
- t.Parallel()
- ctx := testutil.StartSpan(ctx, t)
- var received string
- proxyServer := newProxy(&received, t)
- d := daemon.New(t, daemon.WithEnvVars(
- "HTTP_PROXY="+"http://"+userPass+"from-env-http.invalid",
- "http_proxy="+"http://"+userPass+"from-env-http.invalid",
- "HTTPS_PROXY="+"https://"+userPass+"myuser:mypassword@from-env-https-invalid",
- "https_proxy="+"https://"+userPass+"myuser:mypassword@from-env-https-invalid",
- "NO_PROXY=ignore.invalid",
- "no_proxy=ignore.invalid",
- ))
- c := d.NewClientT(t)
- configFile := filepath.Join(d.RootDir(), "daemon.json")
- configJSON := fmt.Sprintf(`{"proxies":{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}}`, proxyServer.URL)
- assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0o644))
- d.Start(t, "--iptables=false", "--config-file", configFile)
- defer d.Stop(t)
- info := d.Info(t)
- assert.Check(t, is.Equal(info.HTTPProxy, proxyServer.URL))
- assert.Check(t, is.Equal(info.HTTPSProxy, proxyServer.URL))
- assert.Check(t, is.Equal(info.NoProxy, "example.com"))
- d.ScanLogsT(ctx, t, daemon.ScanLogsMatchAll(
- "overriding existing proxy variable with value from configuration",
- "http_proxy",
- "HTTP_PROXY",
- "https_proxy",
- "HTTPS_PROXY",
- "no_proxy",
- "NO_PROXY",
- ))
- _, err := c.ImagePull(ctx, "example.org:5002/some/image:latest", types.ImagePullOptions{})
- assert.ErrorContains(t, err, "", "pulling should have failed")
- assert.Equal(t, received, "example.org:5002")
- // Test NoProxy: example.com should not hit the proxy, and "received" variable should not be changed.
- _, err = c.ImagePull(ctx, "example.com/some/image:latest", types.ImagePullOptions{})
- assert.ErrorContains(t, err, "", "pulling should have failed")
- assert.Equal(t, received, "example.org:5002", "should not have used proxy")
- })
- // Conflicting options (passed both through command-line options and config file)
- t.Run("conflicting options", func(t *testing.T) {
- ctx := testutil.StartSpan(ctx, t)
- const (
- proxyRawURL = "https://" + userPass + "example.org"
- proxyURL = "https://xxxxx:xxxxx@example.org"
- )
- d := daemon.New(t)
- configFile := filepath.Join(d.RootDir(), "daemon.json")
- configJSON := fmt.Sprintf(`{"proxies":{"http-proxy":%[1]q, "https-proxy": %[1]q, "no-proxy": "example.com"}}`, proxyRawURL)
- assert.NilError(t, os.WriteFile(configFile, []byte(configJSON), 0o644))
- err := d.StartWithError("--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com", "--config-file", configFile, "--validate")
- assert.ErrorContains(t, err, "daemon exited during startup")
- expected := fmt.Sprintf(
- `the following directives are specified both as a flag and in the configuration file: http-proxy: (from flag: %[1]s, from file: %[1]s), https-proxy: (from flag: %[1]s, from file: %[1]s), no-proxy: (from flag: example.com, from file: example.com)`,
- proxyURL,
- )
- poll.WaitOn(t, d.PollCheckLogs(ctx, daemon.ScanLogsMatchString(expected)))
- })
- // Make sure values are sanitized when reloading the daemon-config
- t.Run("reload sanitized", func(t *testing.T) {
- t.Parallel()
- ctx := testutil.StartSpan(ctx, t)
- const (
- proxyRawURL = "https://" + userPass + "example.org"
- proxyURL = "https://xxxxx:xxxxx@example.org"
- )
- d := daemon.New(t)
- d.Start(t, "--iptables=false", "--http-proxy", proxyRawURL, "--https-proxy", proxyRawURL, "--no-proxy", "example.com")
- defer d.Stop(t)
- err := d.Signal(syscall.SIGHUP)
- assert.NilError(t, err)
- poll.WaitOn(t, d.PollCheckLogs(ctx, daemon.ScanLogsMatchAll("Reloaded configuration:", proxyURL)))
- ok, logs := d.ScanLogsT(ctx, t, daemon.ScanLogsMatchString(userPass))
- assert.Assert(t, !ok, "logs should not contain the non-sanitized proxy URL: %s", logs)
- })
- }
- func TestLiveRestore(t *testing.T) {
- skip.If(t, runtime.GOOS == "windows", "cannot start multiple daemons on windows")
- _ = testutil.StartSpan(baseContext, t)
- t.Run("volume references", testLiveRestoreVolumeReferences)
- t.Run("autoremove", testLiveRestoreAutoRemove)
- }
- func testLiveRestoreAutoRemove(t *testing.T) {
- skip.If(t, testEnv.IsRootless(), "restarted rootless daemon will have a new process namespace")
- t.Parallel()
- ctx := testutil.StartSpan(baseContext, t)
- run := func(t *testing.T) (*daemon.Daemon, func(), string) {
- d := daemon.New(t)
- d.StartWithBusybox(ctx, t, "--live-restore", "--iptables=false")
- t.Cleanup(func() {
- d.Stop(t)
- d.Cleanup(t)
- })
- tmpDir := t.TempDir()
- apiClient := d.NewClientT(t)
- cID := container.Run(ctx, t, apiClient,
- container.WithBind(tmpDir, "/v"),
- // Run until a 'stop' file is created.
- container.WithCmd("sh", "-c", "while [ ! -f /v/stop ]; do sleep 0.1; done"),
- container.WithAutoRemove)
- t.Cleanup(func() { apiClient.ContainerRemove(ctx, cID, containertypes.RemoveOptions{Force: true}) })
- finishContainer := func() {
- file, err := os.Create(filepath.Join(tmpDir, "stop"))
- assert.NilError(t, err, "Failed to create 'stop' file")
- file.Close()
- }
- return d, finishContainer, cID
- }
- t.Run("engine restart shouldnt kill alive containers", func(t *testing.T) {
- d, finishContainer, cID := run(t)
- d.Restart(t, "--live-restore", "--iptables=false")
- apiClient := d.NewClientT(t)
- _, err := apiClient.ContainerInspect(ctx, cID)
- assert.NilError(t, err, "Container shouldn't be removed after engine restart")
- finishContainer()
- poll.WaitOn(t, container.IsRemoved(ctx, apiClient, cID))
- })
- t.Run("engine restart should remove containers that exited", func(t *testing.T) {
- d, finishContainer, cID := run(t)
- apiClient := d.NewClientT(t)
- // Get PID of the container process.
- inspect, err := apiClient.ContainerInspect(ctx, cID)
- assert.NilError(t, err)
- pid := inspect.State.Pid
- d.Stop(t)
- finishContainer()
- poll.WaitOn(t, process.NotAlive(pid))
- d.Start(t, "--live-restore", "--iptables=false")
- poll.WaitOn(t, container.IsRemoved(ctx, apiClient, cID))
- })
- }
- func testLiveRestoreVolumeReferences(t *testing.T) {
- t.Parallel()
- ctx := testutil.StartSpan(baseContext, t)
- d := daemon.New(t)
- d.StartWithBusybox(ctx, t, "--live-restore", "--iptables=false")
- defer func() {
- d.Stop(t)
- d.Cleanup(t)
- }()
- c := d.NewClientT(t)
- runTest := func(t *testing.T, policy containertypes.RestartPolicyMode) {
- t.Run(string(policy), func(t *testing.T) {
- ctx := testutil.StartSpan(ctx, t)
- volName := "test-live-restore-volume-references-" + string(policy)
- _, err := c.VolumeCreate(ctx, volume.CreateOptions{Name: volName})
- assert.NilError(t, err)
- // Create a container that uses the volume
- m := mount.Mount{
- Type: mount.TypeVolume,
- Source: volName,
- Target: "/foo",
- }
- cID := container.Run(ctx, t, c, container.WithMount(m), container.WithCmd("top"), container.WithRestartPolicy(policy))
- defer c.ContainerRemove(ctx, cID, containertypes.RemoveOptions{Force: true})
- // Stop the daemon
- d.Restart(t, "--live-restore", "--iptables=false")
- // Try to remove the volume
- err = c.VolumeRemove(ctx, volName, false)
- assert.ErrorContains(t, err, "volume is in use")
- _, err = c.VolumeInspect(ctx, volName)
- assert.NilError(t, err)
- })
- }
- t.Run("restartPolicy", func(t *testing.T) {
- runTest(t, containertypes.RestartPolicyAlways)
- runTest(t, containertypes.RestartPolicyUnlessStopped)
- runTest(t, containertypes.RestartPolicyOnFailure)
- runTest(t, containertypes.RestartPolicyDisabled)
- })
- // Make sure that the local volume driver's mount ref count is restored
- // Addresses https://github.com/moby/moby/issues/44422
- t.Run("local volume with mount options", func(t *testing.T) {
- ctx := testutil.StartSpan(ctx, t)
- v, err := c.VolumeCreate(ctx, volume.CreateOptions{
- Driver: "local",
- Name: "test-live-restore-volume-references-local",
- DriverOpts: map[string]string{
- "type": "tmpfs",
- "device": "tmpfs",
- },
- })
- assert.NilError(t, err)
- m := mount.Mount{
- Type: mount.TypeVolume,
- Source: v.Name,
- Target: "/foo",
- }
- const testContent = "hello"
- cID := container.Run(ctx, t, c, container.WithMount(m), container.WithCmd("sh", "-c", "echo "+testContent+">>/foo/test.txt; sleep infinity"))
- defer c.ContainerRemove(ctx, cID, containertypes.RemoveOptions{Force: true})
- // Wait until container creates a file in the volume.
- poll.WaitOn(t, func(t poll.LogT) poll.Result {
- stat, err := c.ContainerStatPath(ctx, cID, "/foo/test.txt")
- if err != nil {
- if errdefs.IsNotFound(err) {
- return poll.Continue("file doesn't yet exist")
- }
- return poll.Error(err)
- }
- if int(stat.Size) != len(testContent)+1 {
- return poll.Error(fmt.Errorf("unexpected test file size: %d", stat.Size))
- }
- return poll.Success()
- })
- d.Restart(t, "--live-restore", "--iptables=false")
- // Try to remove the volume
- // This should fail since its used by a container
- err = c.VolumeRemove(ctx, v.Name, false)
- assert.ErrorContains(t, err, "volume is in use")
- t.Run("volume still mounted", func(t *testing.T) {
- skip.If(t, testEnv.IsRootless(), "restarted rootless daemon has a new mount namespace and it won't have the previous mounts")
- // Check if a new container with the same volume has access to the previous content.
- // This fails if the volume gets unmounted at startup.
- cID2 := container.Run(ctx, t, c, container.WithMount(m), container.WithCmd("cat", "/foo/test.txt"))
- defer c.ContainerRemove(ctx, cID2, containertypes.RemoveOptions{Force: true})
- poll.WaitOn(t, container.IsStopped(ctx, c, cID2))
- inspect, err := c.ContainerInspect(ctx, cID2)
- if assert.Check(t, err) {
- assert.Check(t, is.Equal(inspect.State.ExitCode, 0), "volume doesn't have the same file")
- }
- logs, err := c.ContainerLogs(ctx, cID2, containertypes.LogsOptions{ShowStdout: true})
- assert.NilError(t, err)
- defer logs.Close()
- var stdoutBuf bytes.Buffer
- _, err = stdcopy.StdCopy(&stdoutBuf, io.Discard, logs)
- assert.NilError(t, err)
- assert.Check(t, is.Equal(strings.TrimSpace(stdoutBuf.String()), testContent))
- })
- // Remove that container which should free the references in the volume
- err = c.ContainerRemove(ctx, cID, containertypes.RemoveOptions{Force: true})
- assert.NilError(t, err)
- // Now we should be able to remove the volume
- err = c.VolumeRemove(ctx, v.Name, false)
- assert.NilError(t, err)
- })
- // Make sure that we don't panic if the container has bind-mounts
- // (which should not be "restored")
- // Regression test for https://github.com/moby/moby/issues/45898
- t.Run("container with bind-mounts", func(t *testing.T) {
- ctx := testutil.StartSpan(ctx, t)
- m := mount.Mount{
- Type: mount.TypeBind,
- Source: os.TempDir(),
- Target: "/foo",
- }
- cID := container.Run(ctx, t, c, container.WithMount(m), container.WithCmd("top"))
- defer c.ContainerRemove(ctx, cID, containertypes.RemoveOptions{Force: true})
- d.Restart(t, "--live-restore", "--iptables=false")
- err := c.ContainerRemove(ctx, cID, containertypes.RemoveOptions{Force: true})
- assert.NilError(t, err)
- })
- }
- func TestDaemonDefaultBridgeWithFixedCidrButNoBip(t *testing.T) {
- skip.If(t, runtime.GOOS == "windows")
- ctx := testutil.StartSpan(baseContext, t)
- bridgeName := "ext-bridge1"
- d := daemon.New(t, daemon.WithEnvVars("DOCKER_TEST_CREATE_DEFAULT_BRIDGE="+bridgeName))
- defer func() {
- d.Stop(t)
- d.Cleanup(t)
- }()
- defer func() {
- // No need to clean up when running this test in rootless mode, as the
- // interface is deleted when the daemon is stopped and the netns
- // reclaimed by the kernel.
- if !testEnv.IsRootless() {
- deleteInterface(t, bridgeName)
- }
- }()
- d.StartWithBusybox(ctx, t, "--bridge", bridgeName, "--fixed-cidr", "192.168.130.0/24")
- }
- func deleteInterface(t *testing.T, ifName string) {
- icmd.RunCommand("ip", "link", "delete", ifName).Assert(t, icmd.Success)
- icmd.RunCommand("iptables", "-t", "nat", "--flush").Assert(t, icmd.Success)
- icmd.RunCommand("iptables", "--flush").Assert(t, icmd.Success)
- }
|