[#10] dialer: Define interface state (up/down) on initialization

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
Dmitrii Stepanov 2024-10-08 12:40:49 +03:00
parent a762e63bc8
commit 0e75be2ef2
Signed by: dstepanov-yadro
GPG key ID: 237AF1A763293BC0
5 changed files with 73 additions and 3 deletions

View file

@ -3,6 +3,7 @@ package multinet
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"net" "net"
"sync/atomic" "sync/atomic"
) )
@ -18,6 +19,8 @@ const (
BalancerTypeRoundRobin BalancerType = "roundrobin" BalancerTypeRoundRobin BalancerType = "roundrobin"
) )
var errNoSuitableNodeFound = errors.New("no suitale node found")
type balancer interface { type balancer interface {
DialContext(ctx context.Context, s *Subnet, network, address string) (net.Conn, error) DialContext(ctx context.Context, s *Subnet, network, address string) (net.Conn, error)
} }
@ -39,7 +42,7 @@ func (r *roundRobin) DialContext(ctx context.Context, s *Subnet, network, addres
dd.LocalAddr = ii.LocalAddr dd.LocalAddr = ii.LocalAddr
return r.d.dialContext(&dd, ctx, network, address) return r.d.dialContext(&dd, ctx, network, address)
} }
return nil, errors.New("(*roundRobin).DialContext: no suitale node found") return nil, fmt.Errorf("(*roundRobin).DialContext: %w", errNoSuitableNodeFound)
} }
type firstEnabled struct { type firstEnabled struct {
@ -57,5 +60,5 @@ func (r *firstEnabled) DialContext(ctx context.Context, s *Subnet, network, addr
dd.LocalAddr = ii.LocalAddr dd.LocalAddr = ii.LocalAddr
return r.d.dialContext(&dd, ctx, network, address) return r.d.dialContext(&dd, ctx, network, address)
} }
return nil, errors.New("(*firstEnabled).DialContext: no suitale node found") return nil, fmt.Errorf("(*firstEnabled).DialContext: %w", errNoSuitableNodeFound)
} }

View file

@ -151,6 +151,7 @@ func NewDialer(c Config) (Multidialer, error) {
type iface struct { type iface struct {
name string name string
addrs []netip.Prefix addrs []netip.Prefix
down bool
} }
func processIface(info Interface) (iface, error) { func processIface(info Interface) (iface, error) {
@ -168,7 +169,7 @@ func processIface(info Interface) (iface, error) {
addrs = append(addrs, p) addrs = append(addrs, p)
} }
return iface{name: info.Name(), addrs: addrs}, nil return iface{name: info.Name(), addrs: addrs, down: info.Down()}, nil
} }
func processSubnet(subnet string, sources []iface) (Subnet, error) { func processSubnet(subnet string, sources []iface) (Subnet, error) {
@ -185,6 +186,7 @@ func processSubnet(subnet string, sources []iface) (Subnet, error) {
ifs = append(ifs, Source{ ifs = append(ifs, Source{
Name: source.name, Name: source.name,
LocalAddr: &net.TCPAddr{IP: net.IP(src.AsSlice())}, LocalAddr: &net.TCPAddr{IP: net.IP(src.AsSlice())},
Down: source.down,
}) })
} }
} }

View file

@ -114,10 +114,12 @@ func testInterfacesV6() ([]Interface, error) {
type testInterface struct { type testInterface struct {
name string name string
addrs []net.Addr addrs []net.Addr
down bool
} }
func (i *testInterface) Name() string { return i.name } func (i *testInterface) Name() string { return i.name }
func (i *testInterface) Addrs() ([]net.Addr, error) { return i.addrs, nil } func (i *testInterface) Addrs() ([]net.Addr, error) { return i.addrs, nil }
func (i *testInterface) Down() bool { return i.down }
type testAddr struct { type testAddr struct {
network string network string

58
dialer_test.go Normal file
View file

@ -0,0 +1,58 @@
package multinet
import (
"context"
"net"
"testing"
"github.com/stretchr/testify/require"
)
func TestInterfacesDown(t *testing.T) {
t.Run("noop balancer", func(t *testing.T) {
d, err := NewDialer(Config{
Subnets: []string{"10.11.12.0/24"},
InterfaceSource: testDownInterfaces,
})
require.NoError(t, err)
conn, err := d.DialContext(context.Background(), "tcp", "10.11.12.254:8080")
require.ErrorIs(t, err, errNoSuitableNodeFound)
require.Nil(t, conn)
})
t.Run("round robin balancer", func(t *testing.T) {
d, err := NewDialer(Config{
Subnets: []string{"10.11.12.0/24"},
InterfaceSource: testDownInterfaces,
Balancer: BalancerTypeRoundRobin,
})
require.NoError(t, err)
conn, err := d.DialContext(context.Background(), "tcp", "10.11.12.254:8080")
require.ErrorIs(t, err, errNoSuitableNodeFound)
require.Nil(t, conn)
})
}
func testDownInterfaces() ([]Interface, error) {
return []Interface{
&testInterface{
name: "data1",
addrs: []net.Addr{
&testAddr{
network: "tcp",
str: "10.11.12.101/24",
},
},
down: true,
},
&testInterface{
name: "data2",
addrs: []net.Addr{
&testAddr{
network: "tcp",
str: "10.11.12.102/24",
},
},
down: true,
},
}, nil
}

View file

@ -6,6 +6,7 @@ import "net"
type Interface interface { type Interface interface {
Name() string Name() string
Addrs() ([]net.Addr, error) Addrs() ([]net.Addr, error)
Down() bool
} }
type netInterface struct { type netInterface struct {
@ -20,6 +21,10 @@ func (i *netInterface) Addrs() ([]net.Addr, error) {
return i.iface.Addrs() return i.iface.Addrs()
} }
func (i *netInterface) Down() bool {
return i.iface.Flags&net.FlagUp == 0
}
func systemInterfaces() ([]Interface, error) { func systemInterfaces() ([]Interface, error) {
ifaces, err := net.Interfaces() ifaces, err := net.Interfaces()
if err != nil { if err != nil {