Repository: moby/moby
File: daemon/libnetwork/sandbox_dns_unix_test.go (Go)

Code before:
	err = sb2.setupDNS()
	assert.NilError(t, err)
	err = sb2.rebuildDNS()
	assert.NilError(t, err)
	dnsOptionsList = getResolvConfOptions(t, sb2.config.resolvConfPath)
	assert.Check(t, is.DeepEqual([]string{"ndots:0"}, dnsOptionsList))
}

func TestNonHostNetDNSRestart(t *testing.T) {
	c, err := New(context.Background(), config.OptionDataDir(t.TempDir()))
	assert.NilError(t, err)

	// Step 1: Create initial sandbox (simulating first container start)
	sb, err := c.NewSandbox(context.Background(), "cnt1")
	assert.NilError(t, err)

	sb.startResolver(false)

	err = sb.setupDNS()
	assert.NilError(t, err)
	err = sb.rebuildDNS()
	assert.NilError(t, err)

	// Step 2: Simulate cri-dockerd modifying the resolv.conf for a Kubernetes pause container.
	// This mimics the behavior where external tools (like cri-dockerd) customize DNS
	// settings for K8s pods, which should be preserved during container restart/unpause.
	resolvConfPath := sb.config.resolvConfPath
	modifiedContent := []byte(`nameserver 10.96.0.10
search default.svc.cluster.local. svc.cluster.local. cluster.local.
options ndots:5
`)
	err = os.WriteFile(resolvConfPath, modifiedContent, 0644)
	assert.NilError(t, err)

	// Step 3: Delete the sandbox (simulating container stop)
	err = sb.Delete(context.Background())
	assert.NilError(t, err)

	// Step 4: Create a new sandbox with OptionRestartOperate (simulating container restart)
	sbRestart, err := c.NewSandbox(context.Background(), "cnt1",
		OptionCreateByRestart(),
		OptionResolvConfPath(resolvConfPath),
	)
	assert.NilError(t, err)
	defer func() {
		if err := sbRestart.Delete(context.Background()); err != nil {
			t.Error(err)
		}
	}()

	sbRestart.startResolver(false)

Review comment (suggestion):
I'd prefer not to mention that very specific use-case. To be honest, it still makes little sense to me that you're trying to work around some of the k8s primitives in such a way, but I'm not going to fight over that.

In general, I think this enhancement makes sense though and we got another  request for that a few days ago. See https://github.com/moby/moby/issues/51570.

Could you please rephrase that in more general terms? Something like 'Step 2: Simulate user manually overwritting the container's resolv.conf'?

This would avoid future maintainers raising an eyebrow at this comment wondering what / if there's a real use case.

Code after:
	err = sb2.setupDNS()
	assert.NilError(t, err)
	err = sb2.rebuildDNS()
	assert.NilError(t, err)
	dnsOptionsList = getResolvConfOptions(t, sb2.config.resolvConfPath)
	assert.Check(t, is.DeepEqual([]string{"ndots:0"}, dnsOptionsList))
}

func TestNonHostNetDNSRestart(t *testing.T) {
	c, err := New(context.Background(), config.OptionDataDir(t.TempDir()))
	assert.NilError(t, err)

	// Step 1: Create initial sandbox (simulating first container start)
	sb, err := c.NewSandbox(context.Background(), "cnt1")
	assert.NilError(t, err)

	defer func() {
		_ = sb.Delete(context.Background())
	}()

	sb.startResolver(false)

	err = sb.setupDNS()
	assert.NilError(t, err)
	err = sb.rebuildDNS()
	assert.NilError(t, err)

	// Step 2: Simulate user manually overwriting the container's resolv.conf
	resolvConfPath := sb.config.resolvConfPath
	modifiedContent := []byte(`nameserver 1.1.1.1`)
	err = os.WriteFile(resolvConfPath, modifiedContent, 0644)
	assert.NilError(t, err)

	// Step 3: Delete the sandbox (simulating container stop)
	err = sb.Delete(context.Background())
	assert.NilError(t, err)

	// Step 4: Create a new sandbox (simulating container restart)
	sbRestart, err := c.NewSandbox(context.Background(), "cnt1",
		OptionResolvConfPath(resolvConfPath),
	)
	assert.NilError(t, err)
	defer func() {
		if err := sbRestart.Delete(context.Background()); err != nil {
			t.Error(err)
		}
	}()

	sbRestart.startResolver(false)

	// Step 5: Call setupDNS on restart - should preserve user modifications