6dba98cf38
Various parts of the code were using "walkers" to iterate over the controller's sandboxes, and the only condition for all of them was to find the sandbox for a given container-ID. Iterating over all sandboxes was also sub-optimal, because on Windows, the ContainerID is used as Sandbox-ID, which can be used to lookup the sandbox from the "sandboxes" map on the controller. This patch implements a GetSandbox method on the controller that looks up the sandbox for a given container-ID, using the most optimal approach (depending on the platform). The new method can return errors for invalid (empty) container-IDs, and a "not found" error to allow consumers to detect non-existing sandboxes, or potentially invalid IDs. This new method replaces the (non-exported) Daemon.getNetworkSandbox(), which was only used internally, in favor of directly accessing the controller's method. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
287 lines
7 KiB
Go
287 lines
7 KiB
Go
//go:build !windows
|
|
|
|
package libnetwork
|
|
|
|
import (
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/docker/docker/errdefs"
|
|
"github.com/docker/docker/internal/testutils/netnsutils"
|
|
"github.com/docker/docker/libnetwork/config"
|
|
"github.com/docker/docker/libnetwork/ipamapi"
|
|
"github.com/docker/docker/libnetwork/netlabel"
|
|
"github.com/docker/docker/libnetwork/options"
|
|
"github.com/docker/docker/libnetwork/osl"
|
|
"gotest.tools/v3/assert"
|
|
is "gotest.tools/v3/assert/cmp"
|
|
)
|
|
|
|
func getTestEnv(t *testing.T, opts ...[]NetworkOption) (*Controller, []*Network) {
|
|
const netType = "bridge"
|
|
c, err := New(
|
|
OptionBoltdbWithRandomDBFile(t),
|
|
config.OptionDriverConfig(netType, map[string]any{
|
|
netlabel.GenericData: options.Generic{"EnableIPForwarding": true},
|
|
}),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(c.Stop)
|
|
|
|
if len(opts) == 0 {
|
|
return c, nil
|
|
}
|
|
|
|
nwList := make([]*Network, 0, len(opts))
|
|
for i, opt := range opts {
|
|
name := "test_nw_" + strconv.Itoa(i)
|
|
newOptions := []NetworkOption{
|
|
NetworkOptionGeneric(options.Generic{
|
|
netlabel.GenericData: options.Generic{"BridgeName": name},
|
|
}),
|
|
}
|
|
newOptions = append(newOptions, opt...)
|
|
n, err := c.NewNetwork(netType, name, "", newOptions...)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
nwList = append(nwList, n)
|
|
}
|
|
|
|
return c, nwList
|
|
}
|
|
|
|
func TestControllerGetSandbox(t *testing.T) {
|
|
ctrlr, _ := getTestEnv(t)
|
|
t.Run("invalid id", func(t *testing.T) {
|
|
const cID = ""
|
|
sb, err := ctrlr.GetSandbox(cID)
|
|
_, ok := err.(ErrInvalidID)
|
|
assert.Check(t, ok, "expected ErrInvalidID, got %[1]v (%[1]T)", err)
|
|
assert.Check(t, is.Nil(sb))
|
|
})
|
|
t.Run("not found", func(t *testing.T) {
|
|
const cID = "container-id-with-no-sandbox"
|
|
sb, err := ctrlr.GetSandbox(cID)
|
|
assert.Check(t, errdefs.IsNotFound(err), "expected a ErrNotFound, got %[1]v (%[1]T)", err)
|
|
assert.Check(t, is.Nil(sb))
|
|
})
|
|
t.Run("existing sandbox", func(t *testing.T) {
|
|
const cID = "test-container-id"
|
|
expected, err := ctrlr.NewSandbox(cID)
|
|
assert.Check(t, err)
|
|
|
|
sb, err := ctrlr.GetSandbox(cID)
|
|
assert.Check(t, err)
|
|
assert.Check(t, is.Equal(sb.ContainerID(), cID))
|
|
assert.Check(t, is.Equal(sb.ID(), expected.ID()))
|
|
assert.Check(t, is.Equal(sb.Key(), expected.Key()))
|
|
assert.Check(t, is.Equal(sb.ContainerID(), expected.ContainerID()))
|
|
|
|
err = sb.Delete()
|
|
assert.Check(t, err)
|
|
|
|
sb, err = ctrlr.GetSandbox(cID)
|
|
assert.Check(t, errdefs.IsNotFound(err), "expected a ErrNotFound, got %[1]v (%[1]T)", err)
|
|
assert.Check(t, is.Nil(sb))
|
|
})
|
|
}
|
|
|
|
func TestSandboxAddEmpty(t *testing.T) {
|
|
ctrlr, _ := getTestEnv(t)
|
|
|
|
sbx, err := ctrlr.NewSandbox("sandbox0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := sbx.Delete(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(ctrlr.sandboxes) != 0 {
|
|
t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes))
|
|
}
|
|
|
|
osl.GC()
|
|
}
|
|
|
|
// // If different priorities are specified, internal option and ipv6 addresses mustn't influence endpoint order
|
|
func TestSandboxAddMultiPrio(t *testing.T) {
|
|
defer netnsutils.SetupTestOSContext(t)()
|
|
|
|
opts := [][]NetworkOption{
|
|
{NetworkOptionEnableIPv6(true), NetworkOptionIpam(ipamapi.DefaultIPAM, "", nil, []*IpamConf{{PreferredPool: "fe90::/64"}}, nil)},
|
|
{NetworkOptionInternalNetwork()},
|
|
{},
|
|
}
|
|
|
|
ctrlr, nws := getTestEnv(t, opts...)
|
|
|
|
sbx, err := ctrlr.NewSandbox("sandbox1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sid := sbx.ID()
|
|
|
|
ep1, err := nws[0].CreateEndpoint("ep1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ep2, err := nws[1].CreateEndpoint("ep2")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ep3, err := nws[2].CreateEndpoint("ep3")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := ep1.Join(sbx, JoinOptionPriority(1)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := ep2.Join(sbx, JoinOptionPriority(2)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := ep3.Join(sbx, JoinOptionPriority(3)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if ctrlr.sandboxes[sid].endpoints[0].ID() != ep3.ID() {
|
|
t.Fatal("Expected ep3 to be at the top of the heap. But did not find ep3 at the top of the heap")
|
|
}
|
|
|
|
if len(sbx.Endpoints()) != 3 {
|
|
t.Fatal("Expected 3 endpoints to be connected to the sandbox.")
|
|
}
|
|
|
|
if err := ep3.Leave(sbx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if ctrlr.sandboxes[sid].endpoints[0].ID() != ep2.ID() {
|
|
t.Fatal("Expected ep2 to be at the top of the heap after removing ep3. But did not find ep2 at the top of the heap")
|
|
}
|
|
|
|
if err := ep2.Leave(sbx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if ctrlr.sandboxes[sid].endpoints[0].ID() != ep1.ID() {
|
|
t.Fatal("Expected ep1 to be at the top of the heap after removing ep2. But did not find ep1 at the top of the heap")
|
|
}
|
|
|
|
// Re-add ep3 back
|
|
if err := ep3.Join(sbx, JoinOptionPriority(3)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if ctrlr.sandboxes[sid].endpoints[0].ID() != ep3.ID() {
|
|
t.Fatal("Expected ep3 to be at the top of the heap after adding ep3 back. But did not find ep3 at the top of the heap")
|
|
}
|
|
|
|
if err := sbx.Delete(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(ctrlr.sandboxes) != 0 {
|
|
t.Fatalf("controller sandboxes is not empty. len = %d", len(ctrlr.sandboxes))
|
|
}
|
|
|
|
osl.GC()
|
|
}
|
|
|
|
func TestSandboxAddSamePrio(t *testing.T) {
|
|
defer netnsutils.SetupTestOSContext(t)()
|
|
|
|
opts := [][]NetworkOption{
|
|
{},
|
|
{},
|
|
{NetworkOptionEnableIPv6(true), NetworkOptionIpam(ipamapi.DefaultIPAM, "", nil, []*IpamConf{{PreferredPool: "fe90::/64"}}, nil)},
|
|
{NetworkOptionInternalNetwork()},
|
|
}
|
|
|
|
ctrlr, nws := getTestEnv(t, opts...)
|
|
|
|
sbx, err := ctrlr.NewSandbox("sandbox1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sid := sbx.ID()
|
|
|
|
epNw1, err := nws[1].CreateEndpoint("ep1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
epIPv6, err := nws[2].CreateEndpoint("ep2")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
epInternal, err := nws[3].CreateEndpoint("ep3")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
epNw0, err := nws[0].CreateEndpoint("ep4")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := epNw1.Join(sbx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := epIPv6.Join(sbx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := epInternal.Join(sbx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := epNw0.Join(sbx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// order should now be: epIPv6, epNw0, epNw1, epInternal
|
|
if len(sbx.Endpoints()) != 4 {
|
|
t.Fatal("Expected 4 endpoints to be connected to the sandbox.")
|
|
}
|
|
|
|
// IPv6 has precedence over IPv4
|
|
if ctrlr.sandboxes[sid].endpoints[0].ID() != epIPv6.ID() {
|
|
t.Fatal("Expected epIPv6 to be at the top of the heap. But did not find epIPv6 at the top of the heap")
|
|
}
|
|
|
|
// internal network has lowest precedence
|
|
if ctrlr.sandboxes[sid].endpoints[3].ID() != epInternal.ID() {
|
|
t.Fatal("Expected epInternal to be at the bottom of the heap. But did not find epInternal at the bottom of the heap")
|
|
}
|
|
|
|
if err := epIPv6.Leave(sbx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// 'test_nw_0' has precedence over 'test_nw_1'
|
|
if ctrlr.sandboxes[sid].endpoints[0].ID() != epNw0.ID() {
|
|
t.Fatal("Expected epNw0 to be at the top of the heap after removing epIPv6. But did not find epNw0 at the top of the heap")
|
|
}
|
|
|
|
if err := epNw1.Leave(sbx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := sbx.Delete(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(ctrlr.sandboxes) != 0 {
|
|
t.Fatalf("controller containers is not empty. len = %d", len(ctrlr.sandboxes))
|
|
}
|
|
|
|
osl.GC()
|
|
}
|