TBR veyron/runtimes/google/ipc/stream/fix: fix a bug in vif cache
Currently, vifs are cached with the actual protocol not with a
client specified protocol. E.g., vif with "wsh" are cached with
"tcp" or "ws". So we cannot find a vif with "wsh" causing unncessary
vif creation. There is also a similar problem with v4 and v6 protocols.
This CL addresses this issue.
MultiPart: 1/2
Change-Id: Id37854ff1c7b515e19bb377b1b9d1e9cc130f178
diff --git a/runtimes/google/ipc/protocols/tcp/init.go b/runtimes/google/ipc/protocols/tcp/init.go
index e1b0891..f0fe01c 100644
--- a/runtimes/google/ipc/protocols/tcp/init.go
+++ b/runtimes/google/ipc/protocols/tcp/init.go
@@ -11,9 +11,9 @@
)
func init() {
- for _, p := range []string{"tcp", "tcp4", "tcp6"} {
- ipc.RegisterProtocol(p, tcpDial, tcpListen)
- }
+ ipc.RegisterProtocol("tcp", net.DialTimeout, net.Listen, "tcp4", "tcp6")
+ ipc.RegisterProtocol("tcp4", net.DialTimeout, net.Listen)
+ ipc.RegisterProtocol("tcp6", net.DialTimeout, net.Listen)
}
func tcpDial(network, address string, timeout time.Duration) (net.Conn, error) {
diff --git a/runtimes/google/ipc/protocols/ws/init.go b/runtimes/google/ipc/protocols/ws/init.go
index f218ca0..f2d9faf 100644
--- a/runtimes/google/ipc/protocols/ws/init.go
+++ b/runtimes/google/ipc/protocols/ws/init.go
@@ -8,7 +8,7 @@
func init() {
// ws, ws4, ws6 represent websocket protocol instances.
- for _, p := range []string{"ws", "ws4", "ws6"} {
- ipc.RegisterProtocol(p, websocket.Dial, websocket.Listener)
- }
+ ipc.RegisterProtocol("ws", websocket.Dial, websocket.Listener, "ws4", "ws6")
+ ipc.RegisterProtocol("ws4", websocket.Dial, websocket.Listener)
+ ipc.RegisterProtocol("ws6", websocket.Dial, websocket.Listener)
}
diff --git a/runtimes/google/ipc/protocols/wsh/init.go b/runtimes/google/ipc/protocols/wsh/init.go
index 146a723..86e6e33 100644
--- a/runtimes/google/ipc/protocols/wsh/init.go
+++ b/runtimes/google/ipc/protocols/wsh/init.go
@@ -9,7 +9,7 @@
)
func init() {
- for _, p := range []string{"wsh", "wsh4", "wsh6"} {
- ipc.RegisterProtocol(p, websocket.HybridDial, websocket.HybridListener)
- }
+ ipc.RegisterProtocol("wsh", websocket.HybridDial, websocket.HybridListener, "tcp4", "tcp6", "ws4", "ws6")
+ ipc.RegisterProtocol("wsh4", websocket.HybridDial, websocket.HybridListener, "tcp4", "ws4")
+ ipc.RegisterProtocol("wsh6", websocket.HybridDial, websocket.HybridListener, "tcp6", "ws6")
}
diff --git a/runtimes/google/ipc/protocols/wsh_nacl/init.go b/runtimes/google/ipc/protocols/wsh_nacl/init.go
index a038a6d..426cc51 100644
--- a/runtimes/google/ipc/protocols/wsh_nacl/init.go
+++ b/runtimes/google/ipc/protocols/wsh_nacl/init.go
@@ -9,7 +9,9 @@
)
func init() {
- for _, p := range []string{"wsh", "wsh4", "wsh6"} {
- ipc.RegisterProtocol(p, websocket.Dial, websocket.Listener)
- }
+ // We limit wsh to ws since in general nacl does not allow direct access
+ // to TCP/UDP networking.
+ ipc.RegisterProtocol("wsh", websocket.Dial, websocket.Listener, "ws4", "ws6")
+ ipc.RegisterProtocol("wsh4", websocket.Dial, websocket.Listener, "ws4")
+ ipc.RegisterProtocol("wsh6", websocket.Dial, websocket.Listener, "ws6")
}
diff --git a/runtimes/google/ipc/sort_endpoints.go b/runtimes/google/ipc/sort_endpoints.go
index 29d738f..9df1f8c 100644
--- a/runtimes/google/ipc/sort_endpoints.go
+++ b/runtimes/google/ipc/sort_endpoints.go
@@ -156,6 +156,9 @@
}
// Special case: if "wsh" has a rank but "wsh4"/"wsh6" don't,
// then they get the same rank as "wsh". Similar for "tcp" and "ws".
+ //
+ // TODO(jhahn): We have similar protocol equivalency checks at a few places.
+ // Figure out a way for this mapping to be shared.
if p := protocol; p == "wsh4" || p == "wsh6" || p == "tcp4" || p == "tcp6" || p == "ws4" || p == "ws6" {
if r, ok := ranks[p[:len(p)-1]]; ok {
return r, nil
diff --git a/runtimes/google/ipc/stream/manager/manager.go b/runtimes/google/ipc/stream/manager/manager.go
index ca08ba1..33f0c58 100644
--- a/runtimes/google/ipc/stream/manager/manager.go
+++ b/runtimes/google/ipc/stream/manager/manager.go
@@ -62,7 +62,7 @@
func (DialTimeout) IPCClientOpt() {}
func dial(network, address string, timeout time.Duration) (net.Conn, error) {
- if d, _ := ipc.RegisteredProtocol(network); d != nil {
+ if d, _, _ := ipc.RegisteredProtocol(network); d != nil {
return d(network, address, timeout)
}
return nil, fmt.Errorf("unknown network %s", network)
@@ -147,7 +147,7 @@
}
func listen(protocol, address string) (net.Listener, error) {
- if _, l := ipc.RegisteredProtocol(protocol); l != nil {
+ if _, l, _ := ipc.RegisteredProtocol(protocol); l != nil {
return l(protocol, address)
}
return nil, fmt.Errorf("unknown network %s", protocol)
diff --git a/runtimes/google/ipc/stream/proxy/proxy.go b/runtimes/google/ipc/stream/proxy/proxy.go
index e7fe36e..0260e2c 100644
--- a/runtimes/google/ipc/stream/proxy/proxy.go
+++ b/runtimes/google/ipc/stream/proxy/proxy.go
@@ -148,7 +148,7 @@
// New creates a new Proxy that listens for network connections on the provided
// (network, address) pair and routes VC traffic between accepted connections.
func New(rid naming.RoutingID, principal security.Principal, network, address, pubAddress string) (*Proxy, error) {
- _, listenFn := ipc.RegisteredProtocol(network)
+ _, listenFn, _ := ipc.RegisteredProtocol(network)
if listenFn == nil {
return nil, fmt.Errorf("unknown network %s", network)
}
diff --git a/runtimes/google/ipc/stream/vif/set.go b/runtimes/google/ipc/stream/vif/set.go
index e8caa2e..0ff5c95 100644
--- a/runtimes/google/ipc/stream/vif/set.go
+++ b/runtimes/google/ipc/stream/vif/set.go
@@ -2,8 +2,11 @@
import (
"math/rand"
+ "net"
"runtime"
"sync"
+
+ "v.io/v23/ipc"
)
// Set implements a set of VIFs keyed by (network, address) of the underlying
@@ -29,35 +32,42 @@
if len(address) == 0 ||
(network == "pipe" && address == "pipe") ||
(runtime.GOOS == "linux" && network == "unix" && address == "@") { // autobind
- // Some network connections (like those created with net.Pipe
- // or Unix sockets) do not end up with distinct conn.RemoteAddrs
- // on distinct net.Conns. For those cases, avoid the cache collisions
- // by disabling cache lookups for them.
+ // Some network connections (like those created with net.Pipe or Unix sockets)
+ // do not end up with distinct net.Addrs on distinct net.Conns. For those cases,
+ // avoid the cache collisions by disabling cache lookups for them.
return nil
}
+ var keys []string
+ _, _, p := ipc.RegisteredProtocol(network)
+ for _, n := range p {
+ keys = append(keys, key(n, address))
+ }
+
s.mu.RLock()
defer s.mu.RUnlock()
- l, ok := s.set[s.key(network, address)]
- if !ok || len(l) == 0 {
- return nil
+ for _, k := range keys {
+ vifs := s.set[k]
+ if len(vifs) > 0 {
+ return vifs[rand.Intn(len(vifs))]
+ }
}
- return l[rand.Intn(len(l))]
+ return nil
}
// Insert adds a VIF to the set
func (s *Set) Insert(vif *VIF) {
addr := vif.conn.RemoteAddr()
- key := s.key(addr.Network(), addr.String())
+ k := key(addr.Network(), addr.String())
s.mu.Lock()
defer s.mu.Unlock()
- l, _ := s.set[key]
- for _, v := range l {
+ vifs := s.set[k]
+ for _, v := range vifs {
if v == vif {
return
}
}
- s.set[key] = append(l, vif)
+ s.set[k] = append(vifs, vif)
vif.addSet(s)
}
@@ -65,16 +75,16 @@
func (s *Set) Delete(vif *VIF) {
vif.removeSet(s)
addr := vif.conn.RemoteAddr()
- key := s.key(addr.Network(), addr.String())
+ k := key(addr.Network(), addr.String())
s.mu.Lock()
defer s.mu.Unlock()
- l, _ := s.set[key]
- for ix, v := range l {
+ vifs := s.set[k]
+ for i, v := range vifs {
if v == vif {
- if len(l) == 1 {
- delete(s.set, key)
+ if len(vifs) == 1 {
+ delete(s.set, k)
} else {
- s.set[key] = append(l[:ix], l[ix+1:]...)
+ s.set[k] = append(vifs[:i], vifs[i+1:]...)
}
return
}
@@ -92,6 +102,18 @@
return l
}
-func (s *Set) key(network, address string) string {
+func key(network, address string) string {
+ if network == "tcp" || network == "ws" {
+ host, _, _ := net.SplitHostPort(address)
+ switch ip := net.ParseIP(host); {
+ case ip == nil:
+ // This may happen when address is a hostname. But we do not care
+ // about it, since vif cannot be found with a hostname anyway.
+ case ip.To4() != nil:
+ network += "4"
+ default:
+ network += "6"
+ }
+ }
return network + ":" + address
}
diff --git a/runtimes/google/ipc/stream/vif/set_test.go b/runtimes/google/ipc/stream/vif/set_test.go
index 274f8c7..bea5190 100644
--- a/runtimes/google/ipc/stream/vif/set_test.go
+++ b/runtimes/google/ipc/stream/vif/set_test.go
@@ -7,142 +7,309 @@
"os"
"path"
"testing"
+ "time"
- "v.io/core/veyron/runtimes/google/ipc/stream/vif"
+ "v.io/v23/ipc"
"v.io/v23/naming"
+
+ _ "v.io/core/veyron/profiles"
+ "v.io/core/veyron/runtimes/google/ipc/stream/vif"
)
-func TestSetWithPipes(t *testing.T) {
- var (
- conn1, conn1s = net.Pipe()
- conn2, _ = net.Pipe()
- addr1 = conn1.RemoteAddr()
- addr2 = conn2.RemoteAddr()
+var supportsIPv6 bool
- set = vif.NewSet()
- )
- // The server is needed to negotiate with the dialed VIF. Otherwise, the
- // InternalNewDialedVIF below will block.
+func init() {
+ ipc.RegisterProtocol("unix", net.DialTimeout, net.Listen)
+
+ // Check whether the platform supports IPv6.
+ ln, err := net.Listen("tcp6", "[::1]:0")
+ defer ln.Close()
+ if err == nil {
+ supportsIPv6 = true
+ }
+}
+
+func newConn(network, address string) (net.Conn, net.Conn, error) {
+ dfunc, lfunc, _ := ipc.RegisteredProtocol(network)
+ ln, err := lfunc(network, address)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer ln.Close()
+
+ done := make(chan net.Conn)
go func() {
- _, err := vif.InternalNewAcceptedVIF(conn1s, naming.FixedRoutingID(2), nil)
+ conn, err := ln.Accept()
if err != nil {
- panic(fmt.Sprintf("failed to create server: %s", err))
+ panic(err)
}
+ conn.Read(make([]byte, 1)) // Read a dummy byte.
+ done <- conn
}()
- vf, err := vif.InternalNewDialedVIF(conn1, naming.FixedRoutingID(1), nil, nil, nil)
+
+ conn, err := dfunc(ln.Addr().Network(), ln.Addr().String(), 1*time.Second)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Write a dummy byte since wsh listener waits for the magic bytes for ws.
+ conn.Write([]byte("."))
+ return conn, <-done, nil
+}
+
+func newVIF(c, s net.Conn) (*vif.VIF, *vif.VIF, error) {
+ done := make(chan *vif.VIF)
+ go func() {
+ vf, err := vif.InternalNewAcceptedVIF(s, naming.FixedRoutingID(0x5), nil)
+ if err != nil {
+ panic(err)
+ }
+ done <- vf
+ }()
+
+ vf, err := vif.InternalNewDialedVIF(c, naming.FixedRoutingID(0xc), nil, nil, nil)
+ if err != nil {
+ return nil, nil, err
+ }
+ return vf, <-done, nil
+}
+
+func diff(a, b []string) []string {
+ m := make(map[string]struct{})
+ for _, x := range b {
+ m[x] = struct{}{}
+ }
+ d := make([]string, 0, len(a))
+ for _, x := range a {
+ if _, ok := m[x]; !ok {
+ d = append(d, x)
+ }
+ }
+ return d
+}
+
+func TestSetBasic(t *testing.T) {
+ sockdir, err := ioutil.TempDir("", "TestSetBasic")
if err != nil {
t.Fatal(err)
}
- if addr1.Network() != addr2.Network() || addr1.String() != addr2.String() {
+ defer os.RemoveAll(sockdir)
+
+ all := ipc.RegisteredProtocols()
+ unknown := naming.UnknownProtocol
+ tests := []struct {
+ network, address string
+ compatibles []string
+ }{
+ {"tcp", "127.0.0.1:0", []string{"tcp", "tcp4", "wsh", "wsh4", unknown}},
+ {"tcp4", "127.0.0.1:0", []string{"tcp", "tcp4", "wsh", "wsh4", unknown}},
+ {"tcp", "[::1]:0", []string{"tcp", "tcp6", "wsh", "wsh6", unknown}},
+ {"tcp6", "[::1]:0", []string{"tcp", "tcp6", "wsh", "wsh6", unknown}},
+ {"ws", "127.0.0.1:0", []string{"ws", "ws4", "wsh", "wsh4", unknown}},
+ {"ws4", "127.0.0.1:0", []string{"ws", "ws4", "wsh", "wsh4", unknown}},
+ {"ws", "[::1]:0", []string{"ws", "ws6", "wsh", "wsh6", unknown}},
+ {"ws6", "[::1]:0", []string{"ws", "ws6", "wsh", "wsh6", unknown}},
+ // wsh dial always uses tcp.
+ {"wsh", "127.0.0.1:0", []string{"tcp", "tcp4", "wsh", "wsh4", unknown}},
+ {"wsh4", "127.0.0.1:0", []string{"tcp", "tcp4", "wsh", "wsh4", unknown}},
+ {"wsh", "[::1]:0", []string{"tcp", "tcp6", "wsh", "wsh6", unknown}},
+ {"wsh6", "[::1]:0", []string{"tcp", "tcp6", "wsh", "wsh6", unknown}},
+ {unknown, "127.0.0.1:0", []string{"tcp", "tcp4", "wsh", "wsh4", unknown}},
+ {unknown, "[::1]:0", []string{"tcp", "tcp6", "wsh", "wsh6", unknown}},
+ {"unix", path.Join(sockdir, "socket"), []string{"unix"}},
+ }
+
+ set := vif.NewSet()
+ for _, test := range tests {
+ if test.address == "[::1]:0" && !supportsIPv6 {
+ continue
+ }
+
+ name := fmt.Sprintf("(%q, %q)", test.network, test.address)
+
+ c, s, err := newConn(test.network, test.address)
+ if err != nil {
+ t.Fatal(err)
+ }
+ vf, _, err := newVIF(c, s)
+ if err != nil {
+ t.Fatal(err)
+ }
+ a := c.RemoteAddr()
+
+ set.Insert(vf)
+ for _, n := range test.compatibles {
+ if found := set.Find(n, a.String()); found == nil {
+ t.Fatalf("%s: Got nil, but want [%v] on Find(%q, %q))", name, vf, n, a)
+ }
+ }
+
+ for _, n := range diff(all, test.compatibles) {
+ if v := set.Find(n, a.String()); v != nil {
+ t.Fatalf("%s: Got [%v], but want nil on Find(%q, %q))", name, v, n, a)
+ }
+ }
+
+ set.Delete(vf)
+ for _, n := range all {
+ if v := set.Find(n, a.String()); v != nil {
+ t.Fatalf("%s: Got [%v], but want nil on Find(%q, %q))", name, v, n, a)
+ }
+ }
+ }
+}
+
+func TestSetWithPipes(t *testing.T) {
+ c1, s1 := net.Pipe()
+ c2, s2 := net.Pipe()
+ a1 := c1.RemoteAddr()
+ a2 := c2.RemoteAddr()
+ if a1.Network() != a2.Network() || a1.String() != a2.String() {
t.Fatalf("This test was intended for distinct connections that have duplicate RemoteAddrs. "+
"That does not seem to be the case with (%q, %q) and (%q, %q)",
- addr1.Network(), addr1, addr2.Network(), addr2)
+ a1.Network(), a1, a2.Network(), a2)
}
- set.Insert(vf)
- if found := set.Find(addr2.Network(), addr2.String()); found != nil {
- t.Fatalf("Got [%v] want nil on Find(%q, %q))", found, addr2.Network(), addr2)
+
+ vf1, _, err := newVIF(c1, s1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ vf2, _, err := newVIF(c2, s2)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ set := vif.NewSet()
+ set.Insert(vf1)
+ if v := set.Find(a1.Network(), a1.String()); v != nil {
+ t.Fatalf("Got [%v], but want nil on Find(%q, %q))", v, a1.Network(), a1)
+ }
+ if l := set.List(); len(l) != 1 || l[0] != vf1 {
+ t.Errorf("Unexpected list of VIFs: %v", l)
+ }
+
+ set.Insert(vf2)
+ if v := set.Find(a2.Network(), a2.String()); v != nil {
+ t.Fatalf("Got [%v], but want nil on Find(%q, %q))", v, a2.Network(), a2)
+ }
+ if l := set.List(); len(l) != 2 || l[0] != vf1 || l[1] != vf2 {
+ t.Errorf("Unexpected list of VIFs: %v", l)
+ }
+
+ set.Delete(vf1)
+ if l := set.List(); len(l) != 1 || l[0] != vf2 {
+ t.Errorf("Unexpected list of VIFs: %v", l)
+ }
+ set.Delete(vf2)
+ if l := set.List(); len(l) != 0 {
+ t.Errorf("Unexpected list of VIFs: %v", l)
}
}
func TestSetWithUnixSocket(t *testing.T) {
- dir, err := ioutil.TempDir("", "TestSetWithFileConn")
+ dir, err := ioutil.TempDir("", "TestSetWithUnixSocket")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
- sockname := path.Join(dir, "socket")
- ln, err := net.Listen("unix", sockname)
- if err != nil {
- t.Fatal(err)
- }
- // Setup the creation of two separate connections.
- // At the listener, they will end up with two distinct connections
- // with the same "address" (empty string).
- go func() {
- for i := 0; i < 2; i++ {
- conn, err := net.Dial("unix", sockname)
- if err != nil {
- ln.Close()
- panic(fmt.Sprintf("dial failure: %s", err))
- }
- go func() {
- if _, err := vif.InternalNewDialedVIF(conn, naming.FixedRoutingID(1), nil, nil, nil); err != nil {
- panic(fmt.Sprintf("failed to dial VIF: %s", err))
- }
- }()
- }
- }()
- conn1, err := ln.Accept()
+ c1, s1, err := newConn("unix", path.Join(dir, "socket1"))
if err != nil {
t.Fatal(err)
}
- conn2, err := ln.Accept()
+ c2, s2, err := newConn("unix", path.Join(dir, "socket2"))
if err != nil {
t.Fatal(err)
}
- addr1 := conn1.RemoteAddr()
- addr2 := conn2.RemoteAddr()
- if addr1.Network() != addr2.Network() || addr1.String() != addr2.String() {
+
+ // The client side address is always unix:@ regardless of socket name.
+ a1 := s1.RemoteAddr()
+ a2 := s2.RemoteAddr()
+ if a1.Network() != a2.Network() || a1.String() != a2.String() {
t.Fatalf("This test was intended for distinct connections that have duplicate RemoteAddrs. "+
"That does not seem to be the case with (%q, %q) and (%q, %q)",
- addr1.Network(), addr1, addr2.Network(), addr2)
+ a1.Network(), a1, a2.Network(), a2)
}
- vif1, err := vif.InternalNewAcceptedVIF(conn1, naming.FixedRoutingID(1), nil)
+
+ _, vf1, err := newVIF(c1, s1)
if err != nil {
t.Fatal(err)
}
- if _, err := vif.InternalNewAcceptedVIF(conn2, naming.FixedRoutingID(1), nil); err != nil {
+ _, vf2, err := newVIF(c2, s2)
+ if err != nil {
t.Fatal(err)
}
+
set := vif.NewSet()
- set.Insert(vif1)
- if found := set.Find(addr2.Network(), addr2.String()); found != nil {
- t.Errorf("Got [%v] want nil on Find(%q, %q)", found, addr2.Network(), addr2)
+ set.Insert(vf1)
+ if v := set.Find(a1.Network(), a1.String()); v != nil {
+ t.Fatalf("Got [%v], but want nil on Find(%q, %q))", v, a1.Network(), a1)
+ }
+ if l := set.List(); len(l) != 1 || l[0] != vf1 {
+ t.Errorf("Unexpected list of VIFs: %v", l)
+ }
+
+ set.Insert(vf2)
+ if v := set.Find(a2.Network(), a2.String()); v != nil {
+ t.Fatalf("Got [%v], but want nil on Find(%q, %q))", v, a2.Network(), a2)
+ }
+ if l := set.List(); len(l) != 2 || l[0] != vf1 || l[1] != vf2 {
+ t.Errorf("Unexpected list of VIFs: %v", l)
+ }
+
+ set.Delete(vf1)
+ if l := set.List(); len(l) != 1 || l[0] != vf2 {
+ t.Errorf("Unexpected list of VIFs: %v", l)
+ }
+ set.Delete(vf2)
+ if l := set.List(); len(l) != 0 {
+ t.Errorf("Unexpected list of VIFs: %v", l)
}
}
-func newVIF(t *testing.T) *vif.VIF {
- _, conn := net.Pipe()
- vif, err := vif.InternalNewAcceptedVIF(conn, naming.FixedRoutingID(2), nil)
+func TestSetInsertDelete(t *testing.T) {
+ c1, s1 := net.Pipe()
+ c2, s2 := net.Pipe()
+ vf1, _, err := newVIF(c1, s1)
if err != nil {
- t.Errorf("vif.InternalNewAcceptedVIF failed: %v", err)
+ t.Fatal(err)
}
- return vif
-}
+ vf2, _, err := newVIF(c2, s2)
+ if err != nil {
+ t.Fatal(err)
+ }
-func TestAddRemoveVIF(t *testing.T) {
set1 := vif.NewSet()
set2 := vif.NewSet()
- vif1 := newVIF(t)
- vif2 := newVIF(t)
- set1.Insert(vif1)
- if l := set1.List(); len(l) != 1 || l[0] != vif1 {
+ set1.Insert(vf1)
+ if l := set1.List(); len(l) != 1 || l[0] != vf1 {
t.Errorf("Unexpected list of VIFs: %v", l)
}
- set1.Insert(vif2)
- if l := set1.List(); len(l) != 2 || l[0] != vif1 || l[1] != vif2 {
+ set1.Insert(vf2)
+ if l := set1.List(); len(l) != 2 || l[0] != vf1 || l[1] != vf2 {
t.Errorf("Unexpected list of VIFs: %v", l)
}
- set2.Insert(vif1)
- set2.Insert(vif2)
+ set2.Insert(vf1)
+ set2.Insert(vf2)
- set1.Delete(vif1)
- if l := set1.List(); len(l) != 1 || l[0] != vif2 {
+ set1.Delete(vf1)
+ if l := set1.List(); len(l) != 1 || l[0] != vf2 {
+ t.Errorf("Unexpected list of VIFs: %v", l)
+ }
+ if l := set2.List(); len(l) != 2 || l[0] != vf1 || l[1] != vf2 {
t.Errorf("Unexpected list of VIFs: %v", l)
}
- vif1.Close()
- if l := set1.List(); len(l) != 1 || l[0] != vif2 {
+ vf1.Close()
+ if l := set1.List(); len(l) != 1 || l[0] != vf2 {
t.Errorf("Unexpected list of VIFs: %v", l)
}
- if l := set2.List(); len(l) != 1 || l[0] != vif2 {
+ if l := set2.List(); len(l) != 1 || l[0] != vf2 {
t.Errorf("Unexpected list of VIFs: %v", l)
}
- vif2.Close()
+ vf2.Close()
if l := set1.List(); len(l) != 0 {
t.Errorf("Unexpected list of VIFs: %v", l)
}