cmd/tailscale/cli: unit tests for tailscale ip
Updates #20035 Signed-off-by: Adriano Sela Aviles <adriano@tailscale.com>
This commit is contained in:
committed by
Adriano Sela Aviles
parent
edcc2c94d9
commit
913df7e6ea
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/peterbourgon/ff/v3/ffcli"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/tailcfg"
|
||||
)
|
||||
|
||||
var ipCmd = &ffcli.Command{
|
||||
@@ -130,12 +131,18 @@ func serviceAddrsMatchingIP(ctx context.Context, ipStr string) ([]netip.Addr, er
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return allIPsForServiceWithIP(services, ip), nil
|
||||
}
|
||||
|
||||
// allIPsForServiceWithIP returns the Addrs of the service whose VIP addresses
|
||||
// contain ip, or nil if no service matches.
|
||||
func allIPsForServiceWithIP(services map[tailcfg.ServiceName]tailcfg.ServiceDetails, ip netip.Addr) []netip.Addr {
|
||||
for _, svc := range services {
|
||||
if slices.Contains(svc.Addrs, ip) {
|
||||
return svc.Addrs, nil
|
||||
return svc.Addrs
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func peerMatchingIP(st *ipnstate.Status, ipStr string) (ps *ipnstate.PeerStatus, ok bool) {
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) Tailscale Inc & contributors
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
func TestPeerMatchingIP(t *testing.T) {
|
||||
st := &ipnstate.Status{
|
||||
Self: &ipnstate.PeerStatus{
|
||||
TailscaleIPs: []netip.Addr{netip.MustParseAddr("100.64.0.1"), netip.MustParseAddr("fd7a:115c:a1e0::1")},
|
||||
},
|
||||
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
|
||||
key.NewNode().Public(): {
|
||||
TailscaleIPs: []netip.Addr{netip.MustParseAddr("100.64.0.2"), netip.MustParseAddr("fd7a:115c:a1e0::2")},
|
||||
},
|
||||
key.NewNode().Public(): {
|
||||
TailscaleIPs: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ipStr string
|
||||
wantOK bool
|
||||
wantIPs []netip.Addr
|
||||
}{
|
||||
{
|
||||
name: "match_self_v4",
|
||||
ipStr: "100.64.0.1",
|
||||
wantOK: true,
|
||||
wantIPs: []netip.Addr{netip.MustParseAddr("100.64.0.1"), netip.MustParseAddr("fd7a:115c:a1e0::1")},
|
||||
},
|
||||
{
|
||||
name: "match_self_v6",
|
||||
ipStr: "fd7a:115c:a1e0::1",
|
||||
wantOK: true,
|
||||
wantIPs: []netip.Addr{netip.MustParseAddr("100.64.0.1"), netip.MustParseAddr("fd7a:115c:a1e0::1")},
|
||||
},
|
||||
{
|
||||
name: "match_peer_v4",
|
||||
ipStr: "100.64.0.2",
|
||||
wantOK: true,
|
||||
wantIPs: []netip.Addr{netip.MustParseAddr("100.64.0.2"), netip.MustParseAddr("fd7a:115c:a1e0::2")},
|
||||
},
|
||||
{
|
||||
name: "match_peer_single_ip",
|
||||
ipStr: "100.64.0.3",
|
||||
wantOK: true,
|
||||
wantIPs: []netip.Addr{netip.MustParseAddr("100.64.0.3")},
|
||||
},
|
||||
{
|
||||
name: "no_match",
|
||||
ipStr: "100.64.0.99",
|
||||
wantOK: false,
|
||||
},
|
||||
{
|
||||
name: "invalid_ip",
|
||||
ipStr: "not-an-ip",
|
||||
wantOK: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ps, ok := peerMatchingIP(st, tt.ipStr)
|
||||
if ok != tt.wantOK {
|
||||
t.Fatalf("peerMatchingIP(%q) ok = %v, want %v", tt.ipStr, ok, tt.wantOK)
|
||||
}
|
||||
if ok {
|
||||
if len(ps.TailscaleIPs) != len(tt.wantIPs) {
|
||||
t.Fatalf("got %d IPs, want %d", len(ps.TailscaleIPs), len(tt.wantIPs))
|
||||
}
|
||||
for i, ip := range ps.TailscaleIPs {
|
||||
if ip != tt.wantIPs[i] {
|
||||
t.Errorf("IP[%d] = %v, want %v", i, ip, tt.wantIPs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllIPsForServiceWithIP(t *testing.T) {
|
||||
services := map[tailcfg.ServiceName]tailcfg.ServiceDetails{
|
||||
"svc:web": {
|
||||
Name: "svc:web",
|
||||
Addrs: []netip.Addr{
|
||||
netip.MustParseAddr("100.100.0.1"),
|
||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12::1"),
|
||||
},
|
||||
},
|
||||
"svc:api": {
|
||||
Name: "svc:api",
|
||||
Addrs: []netip.Addr{netip.MustParseAddr("100.100.0.2")},
|
||||
},
|
||||
}
|
||||
// Services should have at most 2 addrs (one v4, one v6), but
|
||||
// we handle more gracefully if the server ever returns them.
|
||||
multiAddr := map[tailcfg.ServiceName]tailcfg.ServiceDetails{
|
||||
"svc:multi": {
|
||||
Name: "svc:multi",
|
||||
Addrs: []netip.Addr{
|
||||
netip.MustParseAddr("100.100.0.3"),
|
||||
netip.MustParseAddr("100.100.0.4"),
|
||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12::3"),
|
||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12::4"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
services map[tailcfg.ServiceName]tailcfg.ServiceDetails
|
||||
ip netip.Addr
|
||||
wantIPs []netip.Addr
|
||||
}{
|
||||
{
|
||||
name: "match_service_v4",
|
||||
services: services,
|
||||
ip: netip.MustParseAddr("100.100.0.1"),
|
||||
wantIPs: []netip.Addr{
|
||||
netip.MustParseAddr("100.100.0.1"),
|
||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12::1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "match_service_v6",
|
||||
services: services,
|
||||
ip: netip.MustParseAddr("fd7a:115c:a1e0:ab12::1"),
|
||||
wantIPs: []netip.Addr{
|
||||
netip.MustParseAddr("100.100.0.1"),
|
||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12::1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "match_single_addr_service",
|
||||
services: services,
|
||||
ip: netip.MustParseAddr("100.100.0.2"),
|
||||
wantIPs: []netip.Addr{netip.MustParseAddr("100.100.0.2")},
|
||||
},
|
||||
{
|
||||
name: "match_service_multiple_addrs_v4",
|
||||
services: multiAddr,
|
||||
ip: netip.MustParseAddr("100.100.0.3"),
|
||||
wantIPs: []netip.Addr{
|
||||
netip.MustParseAddr("100.100.0.3"),
|
||||
netip.MustParseAddr("100.100.0.4"),
|
||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12::3"),
|
||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12::4"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "match_service_multiple_addrs_v6",
|
||||
services: multiAddr,
|
||||
ip: netip.MustParseAddr("fd7a:115c:a1e0:ab12::3"),
|
||||
wantIPs: []netip.Addr{
|
||||
netip.MustParseAddr("100.100.0.3"),
|
||||
netip.MustParseAddr("100.100.0.4"),
|
||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12::3"),
|
||||
netip.MustParseAddr("fd7a:115c:a1e0:ab12::4"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no_match",
|
||||
services: services,
|
||||
ip: netip.MustParseAddr("100.100.0.99"),
|
||||
wantIPs: nil,
|
||||
},
|
||||
{
|
||||
name: "empty_services",
|
||||
services: nil,
|
||||
ip: netip.MustParseAddr("100.100.0.1"),
|
||||
wantIPs: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := allIPsForServiceWithIP(tt.services, tt.ip)
|
||||
if len(got) != len(tt.wantIPs) {
|
||||
t.Fatalf("allIPsForServiceWithIP(%v) returned %d IPs, want %d", tt.ip, len(got), len(tt.wantIPs))
|
||||
}
|
||||
for i, ip := range got {
|
||||
if ip != tt.wantIPs[i] {
|
||||
t.Errorf("IP[%d] = %v, want %v", i, ip, tt.wantIPs[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user