mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 13:50:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			729 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			729 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // +build linux
 | |
| 
 | |
| /*
 | |
| Copyright 2015 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package bandwidth
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"reflect"
 | |
| 	"strings"
 | |
| 	"testing"
 | |
| 
 | |
| 	"k8s.io/apimachinery/pkg/api/resource"
 | |
| 	"k8s.io/utils/exec"
 | |
| 	fakeexec "k8s.io/utils/exec/testing"
 | |
| )
 | |
| 
 | |
| var tcClassOutput = `class htb 1:1 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b 
 | |
| class htb 1:2 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b 
 | |
| class htb 1:3 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b 
 | |
| class htb 1:4 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b 
 | |
| `
 | |
| 
 | |
| var tcClassOutput2 = `class htb 1:1 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b 
 | |
| class htb 1:2 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b 
 | |
| class htb 1:3 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b 
 | |
| class htb 1:4 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b 
 | |
| class htb 1:5 root prio 0 rate 10000bit ceil 10000bit burst 1600b cburst 1600b 
 | |
| `
 | |
| 
 | |
| func TestNextClassID(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		output    string
 | |
| 		expectErr bool
 | |
| 		expected  int
 | |
| 		err       error
 | |
| 	}{
 | |
| 		{
 | |
| 			output:   tcClassOutput,
 | |
| 			expected: 5,
 | |
| 		},
 | |
| 		{
 | |
| 			output:   "\n",
 | |
| 			expected: 1,
 | |
| 		},
 | |
| 		{
 | |
| 			expected:  -1,
 | |
| 			expectErr: true,
 | |
| 			err:       errors.New("test error"),
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range tests {
 | |
| 		fcmd := fakeexec.FakeCmd{
 | |
| 			CombinedOutputScript: []fakeexec.FakeAction{
 | |
| 				func() ([]byte, []byte, error) { return []byte(test.output), nil, test.err },
 | |
| 			},
 | |
| 		}
 | |
| 		fexec := fakeexec.FakeExec{
 | |
| 			CommandScript: []fakeexec.FakeCommandAction{
 | |
| 				func(cmd string, args ...string) exec.Cmd {
 | |
| 					return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
 | |
| 				},
 | |
| 			},
 | |
| 		}
 | |
| 		shaper := &tcShaper{e: &fexec}
 | |
| 		class, err := shaper.nextClassID()
 | |
| 		if test.expectErr {
 | |
| 			if err == nil {
 | |
| 				t.Errorf("unexpected non-error")
 | |
| 			}
 | |
| 		} else {
 | |
| 			if err != nil {
 | |
| 				t.Errorf("unexpected error: %v", err)
 | |
| 			}
 | |
| 			if class != test.expected {
 | |
| 				t.Errorf("expected: %d, found %d", test.expected, class)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestHexCIDR(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name      string
 | |
| 		input     string
 | |
| 		output    string
 | |
| 		expectErr bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:   "IPv4 masked",
 | |
| 			input:  "1.2.3.4/16",
 | |
| 			output: "01020000/ffff0000",
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "IPv4 host",
 | |
| 			input:  "172.17.0.2/32",
 | |
| 			output: "ac110002/ffffffff",
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "IPv6 masked",
 | |
| 			input:  "2001:dead:beef::cafe/64",
 | |
| 			output: "2001deadbeef00000000000000000000/ffffffffffffffff0000000000000000",
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "IPv6 host",
 | |
| 			input:  "2001::5/128",
 | |
| 			output: "20010000000000000000000000000005/ffffffffffffffffffffffffffffffff",
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "invalid CIDR",
 | |
| 			input:     "foo",
 | |
| 			expectErr: true,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range tests {
 | |
| 		output, err := hexCIDR(test.input)
 | |
| 		if test.expectErr {
 | |
| 			if err == nil {
 | |
| 				t.Errorf("case %s: unexpected non-error", test.name)
 | |
| 			}
 | |
| 		} else {
 | |
| 			if err != nil {
 | |
| 				t.Errorf("case %s: unexpected error: %v", test.name, err)
 | |
| 			}
 | |
| 			if output != test.output {
 | |
| 				t.Errorf("case %s: expected: %s, saw: %s",
 | |
| 					test.name, test.output, output)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestAsciiCIDR(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name      string
 | |
| 		input     string
 | |
| 		output    string
 | |
| 		expectErr bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:   "IPv4",
 | |
| 			input:  "01020000/ffff0000",
 | |
| 			output: "1.2.0.0/16",
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "IPv4 host",
 | |
| 			input:  "ac110002/ffffffff",
 | |
| 			output: "172.17.0.2/32",
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "IPv6",
 | |
| 			input:  "2001deadbeef00000000000000000000/ffffffffffffffff0000000000000000",
 | |
| 			output: "2001:dead:beef::/64",
 | |
| 		},
 | |
| 		{
 | |
| 			name:   "IPv6 host",
 | |
| 			input:  "20010000000000000000000000000005/ffffffffffffffffffffffffffffffff",
 | |
| 			output: "2001::5/128",
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "invalid CIDR",
 | |
| 			input:     "malformed",
 | |
| 			expectErr: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "non-hex IP",
 | |
| 			input:     "nonhex/32",
 | |
| 			expectErr: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:      "non-hex mask",
 | |
| 			input:     "01020000/badmask",
 | |
| 			expectErr: true,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range tests {
 | |
| 		output, err := asciiCIDR(test.input)
 | |
| 		if test.expectErr {
 | |
| 			if err == nil {
 | |
| 				t.Errorf("case %s: unexpected non-error", test.name)
 | |
| 			}
 | |
| 		} else {
 | |
| 			if err != nil {
 | |
| 				t.Errorf("case %s: unexpected error: %v", test.name, err)
 | |
| 			}
 | |
| 			if output != test.output {
 | |
| 				t.Errorf("case %s: expected: %s, saw: %s",
 | |
| 					test.name, test.output, output)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var tcFilterOutput = `filter parent 1: protocol ip pref 1 u32 
 | |
| filter parent 1: protocol ip pref 1 u32 fh 800: ht divisor 1 
 | |
| filter parent 1: protocol ip pref 1 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 
 | |
|   match ac110002/ffffffff at 16
 | |
| filter parent 1: protocol ip pref 1 u32 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:2 
 | |
|   match 01020000/ffff0000 at 16
 | |
| `
 | |
| var tcFilterOutputNewVersion = `filter parent 1: protocol ip pref 1 u32
 | |
| filter parent 1: protocol ip pref 1 u32 chain 0 fh 800: ht divisor 1
 | |
| filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 not_in_hw
 | |
|   match ac110002/ffffffff at 16
 | |
| filter parent 1: protocol ip pref 1 u32 chain 0 fh 800::801 order 2049 key ht 800 bkt 0 flowid 1:2 not_in_hw
 | |
|   match 01020000/ffff0000 at 16
 | |
| `
 | |
| 
 | |
| func TestFindCIDRClass(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		cidr           string
 | |
| 		output         string
 | |
| 		expectErr      bool
 | |
| 		expectNotFound bool
 | |
| 		expectedClass  string
 | |
| 		expectedHandle string
 | |
| 		err            error
 | |
| 	}{
 | |
| 		{
 | |
| 			cidr:           "172.17.0.2/32",
 | |
| 			output:         tcFilterOutput,
 | |
| 			expectedClass:  "1:1",
 | |
| 			expectedHandle: "800::800",
 | |
| 		},
 | |
| 		{
 | |
| 			cidr:           "1.2.3.4/16",
 | |
| 			output:         tcFilterOutput,
 | |
| 			expectedClass:  "1:2",
 | |
| 			expectedHandle: "800::801",
 | |
| 		},
 | |
| 		{
 | |
| 			cidr:           "2.2.3.4/16",
 | |
| 			output:         tcFilterOutput,
 | |
| 			expectNotFound: true,
 | |
| 		},
 | |
| 		{
 | |
| 			cidr:           "172.17.0.2/32",
 | |
| 			output:         tcFilterOutputNewVersion,
 | |
| 			expectedClass:  "1:1",
 | |
| 			expectedHandle: "800::800",
 | |
| 		},
 | |
| 		{
 | |
| 			cidr:           "1.2.3.4/16",
 | |
| 			output:         tcFilterOutputNewVersion,
 | |
| 			expectedClass:  "1:2",
 | |
| 			expectedHandle: "800::801",
 | |
| 		},
 | |
| 		{
 | |
| 			cidr:           "2.2.3.4/16",
 | |
| 			output:         tcFilterOutputNewVersion,
 | |
| 			expectNotFound: true,
 | |
| 		},
 | |
| 		{
 | |
| 			err:       errors.New("test error"),
 | |
| 			expectErr: true,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range tests {
 | |
| 		fcmd := fakeexec.FakeCmd{
 | |
| 			CombinedOutputScript: []fakeexec.FakeAction{
 | |
| 				func() ([]byte, []byte, error) { return []byte(test.output), nil, test.err },
 | |
| 			},
 | |
| 		}
 | |
| 		fexec := fakeexec.FakeExec{
 | |
| 			CommandScript: []fakeexec.FakeCommandAction{
 | |
| 				func(cmd string, args ...string) exec.Cmd {
 | |
| 					return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
 | |
| 				},
 | |
| 			},
 | |
| 		}
 | |
| 		shaper := &tcShaper{e: &fexec}
 | |
| 		classAndHandle, found, err := shaper.findCIDRClass(test.cidr)
 | |
| 		if test.expectErr {
 | |
| 			if err == nil {
 | |
| 				t.Errorf("unexpected non-error")
 | |
| 			}
 | |
| 		} else {
 | |
| 			if err != nil {
 | |
| 				t.Errorf("unexpected error: %v", err)
 | |
| 			}
 | |
| 			if test.expectNotFound {
 | |
| 				if found {
 | |
| 					t.Errorf("unexpectedly found an interface: %s", classAndHandle)
 | |
| 				}
 | |
| 			} else {
 | |
| 				if classAndHandle[0][0] != test.expectedClass {
 | |
| 					t.Errorf("expected class: %s, found %s", test.expectedClass, classAndHandle)
 | |
| 				}
 | |
| 				if classAndHandle[0][1] != test.expectedHandle {
 | |
| 					t.Errorf("expected handle: %s, found %s", test.expectedHandle, classAndHandle)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetCIDRs(t *testing.T) {
 | |
| 	fcmd := fakeexec.FakeCmd{
 | |
| 		CombinedOutputScript: []fakeexec.FakeAction{
 | |
| 			func() ([]byte, []byte, error) { return []byte(tcFilterOutput), nil, nil },
 | |
| 		},
 | |
| 	}
 | |
| 	fexec := fakeexec.FakeExec{
 | |
| 		CommandScript: []fakeexec.FakeCommandAction{
 | |
| 			func(cmd string, args ...string) exec.Cmd {
 | |
| 				return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	shaper := &tcShaper{e: &fexec}
 | |
| 	cidrs, err := shaper.GetCIDRs()
 | |
| 	if err != nil {
 | |
| 		t.Errorf("unexpected error: %v", err)
 | |
| 	}
 | |
| 	expectedCidrs := []string{"172.17.0.2/32", "1.2.0.0/16"}
 | |
| 	if !reflect.DeepEqual(cidrs, expectedCidrs) {
 | |
| 		t.Errorf("expected: %v, saw: %v", expectedCidrs, cidrs)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestLimit(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		cidr          string
 | |
| 		ingress       *resource.Quantity
 | |
| 		egress        *resource.Quantity
 | |
| 		expectErr     bool
 | |
| 		expectedCalls int
 | |
| 		err           error
 | |
| 	}{
 | |
| 		{
 | |
| 			cidr:          "1.2.3.4/32",
 | |
| 			ingress:       resource.NewQuantity(10, resource.DecimalSI),
 | |
| 			egress:        resource.NewQuantity(20, resource.DecimalSI),
 | |
| 			expectedCalls: 6,
 | |
| 		},
 | |
| 		{
 | |
| 			cidr:          "1.2.3.4/32",
 | |
| 			ingress:       resource.NewQuantity(10, resource.DecimalSI),
 | |
| 			egress:        nil,
 | |
| 			expectedCalls: 3,
 | |
| 		},
 | |
| 		{
 | |
| 			cidr:          "1.2.3.4/32",
 | |
| 			ingress:       nil,
 | |
| 			egress:        resource.NewQuantity(20, resource.DecimalSI),
 | |
| 			expectedCalls: 3,
 | |
| 		},
 | |
| 		{
 | |
| 			cidr:          "1.2.3.4/32",
 | |
| 			ingress:       nil,
 | |
| 			egress:        nil,
 | |
| 			expectedCalls: 0,
 | |
| 		},
 | |
| 		{
 | |
| 			err:       errors.New("test error"),
 | |
| 			ingress:   resource.NewQuantity(10, resource.DecimalSI),
 | |
| 			egress:    resource.NewQuantity(20, resource.DecimalSI),
 | |
| 			expectErr: true,
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		fcmd := fakeexec.FakeCmd{
 | |
| 			CombinedOutputScript: []fakeexec.FakeAction{
 | |
| 				func() ([]byte, []byte, error) { return []byte(tcClassOutput), nil, test.err },
 | |
| 				func() ([]byte, []byte, error) { return []byte{}, nil, test.err },
 | |
| 				func() ([]byte, []byte, error) { return []byte{}, nil, test.err },
 | |
| 				func() ([]byte, []byte, error) { return []byte(tcClassOutput2), nil, test.err },
 | |
| 				func() ([]byte, []byte, error) { return []byte{}, nil, test.err },
 | |
| 				func() ([]byte, []byte, error) { return []byte{}, nil, test.err },
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		fexec := fakeexec.FakeExec{
 | |
| 			CommandScript: []fakeexec.FakeCommandAction{
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 			},
 | |
| 		}
 | |
| 		iface := "cbr0"
 | |
| 		shaper := &tcShaper{e: &fexec, iface: iface}
 | |
| 		if err := shaper.Limit(test.cidr, test.ingress, test.egress); err != nil && !test.expectErr {
 | |
| 			t.Errorf("unexpected error: %v", err)
 | |
| 			return
 | |
| 		} else if err == nil && test.expectErr {
 | |
| 			t.Error("unexpected non-error")
 | |
| 			return
 | |
| 		}
 | |
| 		// No more testing in the error case
 | |
| 		if test.expectErr {
 | |
| 			if fcmd.CombinedOutputCalls != 1 {
 | |
| 				t.Errorf("unexpected number of calls: %d, expected: 1", fcmd.CombinedOutputCalls)
 | |
| 			}
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		if fcmd.CombinedOutputCalls != test.expectedCalls {
 | |
| 			t.Errorf("unexpected number of calls: %d, expected: %d", fcmd.CombinedOutputCalls, test.expectedCalls)
 | |
| 		}
 | |
| 
 | |
| 		for ix := range fcmd.CombinedOutputLog {
 | |
| 			output := fcmd.CombinedOutputLog[ix]
 | |
| 			if output[0] != "tc" {
 | |
| 				t.Errorf("unexpected command: %s, expected tc", output[0])
 | |
| 			}
 | |
| 			if output[4] != iface {
 | |
| 				t.Errorf("unexpected interface: %s, expected %s (%v)", output[4], iface, output)
 | |
| 			}
 | |
| 			if ix == 1 {
 | |
| 				var expectedRate string
 | |
| 				if test.ingress != nil {
 | |
| 					expectedRate = makeKBitString(test.ingress)
 | |
| 				} else {
 | |
| 					expectedRate = makeKBitString(test.egress)
 | |
| 				}
 | |
| 				if output[11] != expectedRate {
 | |
| 					t.Errorf("unexpected ingress: %s, expected: %s", output[11], expectedRate)
 | |
| 				}
 | |
| 				if output[8] != "1:5" {
 | |
| 					t.Errorf("unexpected class: %s, expected: %s", output[8], "1:5")
 | |
| 				}
 | |
| 			}
 | |
| 			if ix == 2 {
 | |
| 				if output[15] != test.cidr {
 | |
| 					t.Errorf("unexpected cidr: %s, expected: %s", output[15], test.cidr)
 | |
| 				}
 | |
| 				if output[17] != "1:5" {
 | |
| 					t.Errorf("unexpected class: %s, expected: %s", output[17], "1:5")
 | |
| 				}
 | |
| 			}
 | |
| 			if ix == 4 {
 | |
| 				if output[11] != makeKBitString(test.egress) {
 | |
| 					t.Errorf("unexpected egress: %s, expected: %s", output[11], makeKBitString(test.egress))
 | |
| 				}
 | |
| 				if output[8] != "1:6" {
 | |
| 					t.Errorf("unexpected class: %s, expected: %s", output[8], "1:6")
 | |
| 				}
 | |
| 			}
 | |
| 			if ix == 5 {
 | |
| 				if output[15] != test.cidr {
 | |
| 					t.Errorf("unexpected cidr: %s, expected: %s", output[15], test.cidr)
 | |
| 				}
 | |
| 				if output[17] != "1:6" {
 | |
| 					t.Errorf("unexpected class: %s, expected: %s", output[17], "1:5")
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestReset(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		cidr           string
 | |
| 		err            error
 | |
| 		expectErr      bool
 | |
| 		expectedHandle string
 | |
| 		expectedClass  string
 | |
| 	}{
 | |
| 		{
 | |
| 			cidr:           "1.2.3.4/16",
 | |
| 			expectedHandle: "800::801",
 | |
| 			expectedClass:  "1:2",
 | |
| 		},
 | |
| 		{
 | |
| 			cidr:           "172.17.0.2/32",
 | |
| 			expectedHandle: "800::800",
 | |
| 			expectedClass:  "1:1",
 | |
| 		},
 | |
| 		{
 | |
| 			err:       errors.New("test error"),
 | |
| 			expectErr: true,
 | |
| 		},
 | |
| 	}
 | |
| 	for _, test := range tests {
 | |
| 		fcmd := fakeexec.FakeCmd{
 | |
| 			CombinedOutputScript: []fakeexec.FakeAction{
 | |
| 				func() ([]byte, []byte, error) { return []byte(tcFilterOutput), nil, test.err },
 | |
| 				func() ([]byte, []byte, error) { return []byte{}, nil, test.err },
 | |
| 				func() ([]byte, []byte, error) { return []byte{}, nil, test.err },
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		fexec := fakeexec.FakeExec{
 | |
| 			CommandScript: []fakeexec.FakeCommandAction{
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 			},
 | |
| 		}
 | |
| 		iface := "cbr0"
 | |
| 		shaper := &tcShaper{e: &fexec, iface: iface}
 | |
| 
 | |
| 		if err := shaper.Reset(test.cidr); err != nil && !test.expectErr {
 | |
| 			t.Errorf("unexpected error: %v", err)
 | |
| 			return
 | |
| 		} else if test.expectErr && err == nil {
 | |
| 			t.Error("unexpected non-error")
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		// No more testing in the error case
 | |
| 		if test.expectErr {
 | |
| 			if fcmd.CombinedOutputCalls != 1 {
 | |
| 				t.Errorf("unexpected number of calls: %d, expected: 1", fcmd.CombinedOutputCalls)
 | |
| 			}
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		if fcmd.CombinedOutputCalls != 3 {
 | |
| 			t.Errorf("unexpected number of calls: %d, expected: 3", fcmd.CombinedOutputCalls)
 | |
| 		}
 | |
| 
 | |
| 		for ix := range fcmd.CombinedOutputLog {
 | |
| 			output := fcmd.CombinedOutputLog[ix]
 | |
| 			if output[0] != "tc" {
 | |
| 				t.Errorf("unexpected command: %s, expected tc", output[0])
 | |
| 			}
 | |
| 			if output[4] != iface {
 | |
| 				t.Errorf("unexpected interface: %s, expected %s (%v)", output[4], iface, output)
 | |
| 			}
 | |
| 			if ix == 1 && output[12] != test.expectedHandle {
 | |
| 				t.Errorf("unexpected handle: %s, expected: %s", output[12], test.expectedHandle)
 | |
| 			}
 | |
| 			if ix == 2 && output[8] != test.expectedClass {
 | |
| 				t.Errorf("unexpected class: %s, expected: %s", output[8], test.expectedClass)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| var tcQdisc = "qdisc htb 1: root refcnt 2 r2q 10 default 30 direct_packets_stat 0\n"
 | |
| 
 | |
| func TestReconcileInterfaceExists(t *testing.T) {
 | |
| 	fcmd := fakeexec.FakeCmd{
 | |
| 		CombinedOutputScript: []fakeexec.FakeAction{
 | |
| 			func() ([]byte, []byte, error) { return []byte(tcQdisc), nil, nil },
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	fexec := fakeexec.FakeExec{
 | |
| 		CommandScript: []fakeexec.FakeCommandAction{
 | |
| 			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 		},
 | |
| 	}
 | |
| 	iface := "cbr0"
 | |
| 	shaper := &tcShaper{e: &fexec, iface: iface}
 | |
| 	err := shaper.ReconcileInterface()
 | |
| 	if err != nil {
 | |
| 		t.Errorf("unexpected error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	if fcmd.CombinedOutputCalls != 1 {
 | |
| 		t.Errorf("unexpected number of calls: %d", fcmd.CombinedOutputCalls)
 | |
| 	}
 | |
| 
 | |
| 	output := fcmd.CombinedOutputLog[0]
 | |
| 	if len(output) != 5 {
 | |
| 		t.Errorf("unexpected command: %v", output)
 | |
| 	}
 | |
| 	if output[0] != "tc" {
 | |
| 		t.Errorf("unexpected command: %s", output[0])
 | |
| 	}
 | |
| 	if output[4] != iface {
 | |
| 		t.Errorf("unexpected interface: %s, expected %s", output[4], iface)
 | |
| 	}
 | |
| 	if output[2] != "show" {
 | |
| 		t.Errorf("unexpected action: %s", output[2])
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func testReconcileInterfaceHasNoData(t *testing.T, output string) {
 | |
| 	fcmd := fakeexec.FakeCmd{
 | |
| 		CombinedOutputScript: []fakeexec.FakeAction{
 | |
| 			func() ([]byte, []byte, error) { return []byte(output), nil, nil },
 | |
| 			func() ([]byte, []byte, error) { return []byte(output), nil, nil },
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	fexec := fakeexec.FakeExec{
 | |
| 		CommandScript: []fakeexec.FakeCommandAction{
 | |
| 			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 			func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 		},
 | |
| 	}
 | |
| 	iface := "cbr0"
 | |
| 	shaper := &tcShaper{e: &fexec, iface: iface}
 | |
| 	err := shaper.ReconcileInterface()
 | |
| 	if err != nil {
 | |
| 		t.Errorf("unexpected error: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	if fcmd.CombinedOutputCalls != 2 {
 | |
| 		t.Errorf("unexpected number of calls: %d", fcmd.CombinedOutputCalls)
 | |
| 	}
 | |
| 
 | |
| 	for ix, output := range fcmd.CombinedOutputLog {
 | |
| 		if output[0] != "tc" {
 | |
| 			t.Errorf("unexpected command: %s", output[0])
 | |
| 		}
 | |
| 		if output[4] != iface {
 | |
| 			t.Errorf("unexpected interface: %s, expected %s", output[4], iface)
 | |
| 		}
 | |
| 		if ix == 0 {
 | |
| 			if len(output) != 5 {
 | |
| 				t.Errorf("unexpected command: %v", output)
 | |
| 			}
 | |
| 			if output[2] != "show" {
 | |
| 				t.Errorf("unexpected action: %s", output[2])
 | |
| 			}
 | |
| 		}
 | |
| 		if ix == 1 {
 | |
| 			if len(output) != 11 {
 | |
| 				t.Errorf("unexpected command: %v", output)
 | |
| 			}
 | |
| 			if output[2] != "add" {
 | |
| 				t.Errorf("unexpected action: %s", output[2])
 | |
| 			}
 | |
| 			if output[7] != "1:" {
 | |
| 				t.Errorf("unexpected root class: %s", output[7])
 | |
| 			}
 | |
| 			if output[8] != "htb" {
 | |
| 				t.Errorf("unexpected qdisc algo: %s", output[8])
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestReconcileInterfaceDoesntExist(t *testing.T) {
 | |
| 	testReconcileInterfaceHasNoData(t, "\n")
 | |
| }
 | |
| 
 | |
| var tcQdiscNoqueue = "qdisc noqueue 0: root refcnt 2 \n"
 | |
| 
 | |
| func TestReconcileInterfaceExistsWithNoqueue(t *testing.T) {
 | |
| 	testReconcileInterfaceHasNoData(t, tcQdiscNoqueue)
 | |
| }
 | |
| 
 | |
| var tcQdiscWrong = []string{
 | |
| 	"qdisc htb 2: root refcnt 2 r2q 10 default 30 direct_packets_stat 0\n",
 | |
| 	"qdisc foo 1: root refcnt 2 r2q 10 default 30 direct_packets_stat 0\n",
 | |
| }
 | |
| 
 | |
| func TestReconcileInterfaceIsWrong(t *testing.T) {
 | |
| 	for _, test := range tcQdiscWrong {
 | |
| 		fcmd := fakeexec.FakeCmd{
 | |
| 			CombinedOutputScript: []fakeexec.FakeAction{
 | |
| 				func() ([]byte, []byte, error) { return []byte(test), nil, nil },
 | |
| 				func() ([]byte, []byte, error) { return []byte("\n"), nil, nil },
 | |
| 				func() ([]byte, []byte, error) { return []byte("\n"), nil, nil },
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		fexec := fakeexec.FakeExec{
 | |
| 			CommandScript: []fakeexec.FakeCommandAction{
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 				func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
 | |
| 			},
 | |
| 		}
 | |
| 		iface := "cbr0"
 | |
| 		shaper := &tcShaper{e: &fexec, iface: iface}
 | |
| 		err := shaper.ReconcileInterface()
 | |
| 		if err != nil {
 | |
| 			t.Errorf("unexpected error: %v", err)
 | |
| 		}
 | |
| 
 | |
| 		if fcmd.CombinedOutputCalls != 3 {
 | |
| 			t.Errorf("unexpected number of calls: %d", fcmd.CombinedOutputCalls)
 | |
| 		}
 | |
| 
 | |
| 		for ix, output := range fcmd.CombinedOutputLog {
 | |
| 			if output[0] != "tc" {
 | |
| 				t.Errorf("unexpected command: %s", output[0])
 | |
| 			}
 | |
| 			if output[4] != iface {
 | |
| 				t.Errorf("unexpected interface: %s, expected %s", output[4], iface)
 | |
| 			}
 | |
| 			if ix == 0 {
 | |
| 				if len(output) != 5 {
 | |
| 					t.Errorf("unexpected command: %v", output)
 | |
| 				}
 | |
| 				if output[2] != "show" {
 | |
| 					t.Errorf("unexpected action: %s", output[2])
 | |
| 				}
 | |
| 			}
 | |
| 			if ix == 1 {
 | |
| 				if len(output) != 8 {
 | |
| 					t.Errorf("unexpected command: %v", output)
 | |
| 				}
 | |
| 				if output[2] != "delete" {
 | |
| 					t.Errorf("unexpected action: %s", output[2])
 | |
| 				}
 | |
| 				if output[7] != strings.Split(test, " ")[2] {
 | |
| 					t.Errorf("unexpected class: %s, expected: %s", output[7], strings.Split(test, " ")[2])
 | |
| 				}
 | |
| 			}
 | |
| 			if ix == 2 {
 | |
| 				if len(output) != 11 {
 | |
| 					t.Errorf("unexpected command: %v", output)
 | |
| 				}
 | |
| 				if output[7] != "1:" {
 | |
| 					t.Errorf("unexpected root class: %s", output[7])
 | |
| 				}
 | |
| 				if output[8] != "htb" {
 | |
| 					t.Errorf("unexpected qdisc algo: %s", output[8])
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |