瀏覽代碼

Merge pull request #45892 from corhere/libn/overlay-cut-vni-list

libnetwork/drivers/overlay: parse VNI list option in O(1) memory
Bjorn Neergaard 2 年之前
父節點
當前提交
c9397ec8d4

+ 5 - 8
libnetwork/drivers/overlay/ov_network.go

@@ -16,6 +16,7 @@ import (
 
 	"github.com/containerd/containerd/log"
 	"github.com/docker/docker/libnetwork/driverapi"
+	"github.com/docker/docker/libnetwork/drivers/overlay/overlayutils"
 	"github.com/docker/docker/libnetwork/netlabel"
 	"github.com/docker/docker/libnetwork/ns"
 	"github.com/docker/docker/libnetwork/osl"
@@ -111,14 +112,10 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 		return errors.New("no VNI provided")
 	}
 	log.G(context.TODO()).Debugf("overlay: Received vxlan IDs: %s", vnisOpt)
-	vniStrings := strings.Split(vnisOpt, ",")
-	for _, vniStr := range vniStrings {
-		vni, err := strconv.Atoi(vniStr)
-		if err != nil {
-			return fmt.Errorf("invalid vxlan id value %q passed", vniStr)
-		}
-
-		vnis = append(vnis, uint32(vni))
+	var err error
+	vnis, err = overlayutils.AppendVNIList(vnis, vnisOpt)
+	if err != nil {
+		return err
 	}
 
 	if _, ok := optMap[secureOption]; ok {

+ 22 - 0
libnetwork/drivers/overlay/overlayutils/utils.go

@@ -3,6 +3,8 @@ package overlayutils
 
 import (
 	"fmt"
+	"strconv"
+	"strings"
 	"sync"
 )
 
@@ -40,3 +42,23 @@ func VXLANUDPPort() uint32 {
 	defer mutex.RUnlock()
 	return vxlanUDPPort
 }
+
+// AppendVNIList appends the VNI values encoded as a CSV string to slice.
+func AppendVNIList(vnis []uint32, csv string) ([]uint32, error) {
+	for {
+		var (
+			vniStr string
+			found  bool
+		)
+		vniStr, csv, found = strings.Cut(csv, ",")
+		vni, err := strconv.Atoi(vniStr)
+		if err != nil {
+			return vnis, fmt.Errorf("invalid vxlan id value %q passed", vniStr)
+		}
+
+		vnis = append(vnis, uint32(vni))
+		if !found {
+			return vnis, nil
+		}
+	}
+}

+ 82 - 0
libnetwork/drivers/overlay/overlayutils/utils_test.go

@@ -0,0 +1,82 @@
+package overlayutils
+
+import (
+	"testing"
+
+	"gotest.tools/v3/assert"
+	is "gotest.tools/v3/assert/cmp"
+)
+
+func TestAppendVNIList(t *testing.T) {
+	cases := []struct {
+		name    string
+		slice   []uint32
+		csv     string
+		want    []uint32
+		wantErr string
+	}{
+		{
+			name: "NilSlice",
+			csv:  "1,2,3",
+			want: []uint32{1, 2, 3},
+		},
+		{
+			name:    "TrailingComma",
+			csv:     "1,2,3,",
+			want:    []uint32{1, 2, 3},
+			wantErr: `invalid vxlan id value "" passed`,
+		},
+		{
+			name:  "EmptySlice",
+			slice: make([]uint32, 0, 10),
+			csv:   "1,2,3",
+			want:  []uint32{1, 2, 3},
+		},
+		{
+			name:  "ExistingSlice",
+			slice: []uint32{4, 5, 6},
+			csv:   "1,2,3",
+			want:  []uint32{4, 5, 6, 1, 2, 3},
+		},
+		{
+			name:    "InvalidVNI",
+			slice:   []uint32{4, 5, 6},
+			csv:     "1,2,3,abc",
+			want:    []uint32{4, 5, 6, 1, 2, 3},
+			wantErr: "invalid vxlan id value \"abc\" passed",
+		},
+		{
+			name:    "InvalidVNI2",
+			slice:   []uint32{4, 5, 6},
+			csv:     "abc,1,2,3",
+			want:    []uint32{4, 5, 6},
+			wantErr: "invalid vxlan id value \"abc\" passed",
+		},
+	}
+	for _, tt := range cases {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := AppendVNIList(tt.slice, tt.csv)
+			assert.Check(t, is.DeepEqual(tt.want, got))
+			if tt.wantErr == "" {
+				assert.Check(t, err)
+			} else {
+				assert.Check(t, is.ErrorContains(err, tt.wantErr))
+			}
+		})
+	}
+
+	t.Run("DoesNotAllocate", func(t *testing.T) {
+		slice := make([]uint32, 0, 10)
+		csv := "1,2,3,4,5,6,7,8,9,10"
+		want := []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+		allocs := testing.AllocsPerRun(10, func() {
+			var err error
+			slice, err = AppendVNIList(slice[:0], csv)
+			if err != nil {
+				t.Fatal(err)
+			}
+		})
+		assert.Check(t, is.DeepEqual(slice, want))
+		assert.Check(t, is.Equal(int(allocs), 0))
+	})
+}

+ 5 - 9
libnetwork/drivers/overlay/ovmanager/ovmanager.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"net"
 	"strconv"
-	"strings"
 	"sync"
 
 	"github.com/containerd/containerd/log"
@@ -13,6 +12,7 @@ import (
 	"github.com/docker/docker/libnetwork/datastore"
 	"github.com/docker/docker/libnetwork/discoverapi"
 	"github.com/docker/docker/libnetwork/driverapi"
+	"github.com/docker/docker/libnetwork/drivers/overlay/overlayutils"
 	"github.com/docker/docker/libnetwork/netlabel"
 	"github.com/docker/docker/libnetwork/types"
 )
@@ -88,14 +88,10 @@ func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data,
 	for key, val := range option {
 		if key == netlabel.OverlayVxlanIDList {
 			log.G(context.TODO()).Debugf("overlay network option: %s", val)
-			valStrList := strings.Split(val, ",")
-			for _, idStr := range valStrList {
-				vni, err := strconv.Atoi(idStr)
-				if err != nil {
-					return nil, fmt.Errorf("invalid vxlan id value %q passed", idStr)
-				}
-
-				vxlanIDList = append(vxlanIDList, uint32(vni))
+			var err error
+			vxlanIDList, err = overlayutils.AppendVNIList(vxlanIDList, val)
+			if err != nil {
+				return nil, err
 			}
 		} else {
 			opts[key] = val