package cluster import ( "errors" "k8s.io/kubernetes/pkg/api" "testing" ) // Used as a stub for golang's net.LookupIP to avoid actual DNS lookups in tests. func dummyDnsResolver(hostname string) ([]string, error) { // This method should not end up getting called for actual IPs, so we only // need to map out the hostnames we'll be "resolving": domains := map[string][]string{ "something.example.com": {"192.168.1.1"}, "multiaddress.example.com": {"192.168.1.2", "10.0.1.1"}, "localhost": {"127.0.0.1"}, } ip, ok := domains[hostname] if !ok { return nil, errors.New("Dummy DNS lookup error") } return ip, nil } func ipTester(t *testing.T, serverUrl string, expectedIps ...string) { hostnames, err := resolveServerIP(serverUrl, dummyDnsResolver) // If given no expected IPs we assume the caller wanted an error to occur: if len(expectedIps) == 0 || expectedIps == nil { if err == nil { t.Errorf("No expected IPs given, error expected but none occurred. Got hostnames: %s", hostnames) return } return } if err != nil || len(hostnames) == 0 { t.Errorf("Unable to resolve IP from: %s, error: %s", serverUrl, err) return } if len(hostnames) != len(expectedIps) { t.Errorf("Expected %d IPs, got: %d", len(expectedIps), len(hostnames)) return } for _, expectedIp := range expectedIps { found := false for _, hostResult := range hostnames { if hostResult == expectedIp { found = true } } if !found { t.Errorf("Missing expected IP: %s, got: %s", expectedIp, hostnames) return } } } func TestServerResolve(t *testing.T) { ipTester(t, "https://something.example.com:8443/api/", "192.168.1.1") } func TestServerResolveFails(t *testing.T) { // Our dummy DNS resolver function doesn't know this host: ipTester(t, "https://somethingelse.example.com:8443/api/") } func TestServerResolveMultiAddress(t *testing.T) { ipTester(t, "https://multiaddress.example.com:8443/api/", "192.168.1.2", "10.0.1.1") } func TestServerResolveNoPort(t *testing.T) { ipTester(t, "https://something.example.com/api/", "192.168.1.1") } func TestServerResolveNoPortNoPath(t *testing.T) { ipTester(t, "https://something.example.com", "192.168.1.1") } func TestServerResolveNoPath(t *testing.T) { ipTester(t, "https://something.example.com:8443", "192.168.1.1") } func TestServerResolveLocalhost(t *testing.T) { ipTester(t, "https://localhost:8443/api/", "127.0.0.1") } func TestServerResolveIP(t *testing.T) { ipTester(t, "https://192.168.1.1:8443/api/", "192.168.1.1") } func TestServerResolveIPNoPort(t *testing.T) { ipTester(t, "https://192.168.1.1/api/", "192.168.1.1") } func TestServerResolveIPNoPortNoPath(t *testing.T) { ipTester(t, "https://192.168.1.1", "192.168.1.1") } func TestServerResolveIPNoPath(t *testing.T) { ipTester(t, "https://192.168.1.1:8443", "192.168.1.1") } func TestServerResolveIPv6(t *testing.T) { ipTester(t, "https://[2001:4860:0:2001::6]:8443/api/", "2001:4860:0:2001::6") } func TestServerResolveIPv6UpperCaseFull(t *testing.T) { // net.IP normalizes to lowercase and shortens: ipTester(t, "https://[FE80:0000:0000:0000:0202:B3FF:FE1E:8329]:8443/api/", "fe80::202:b3ff:fe1e:8329") } func TestServerResolveIPv6NoPort(t *testing.T) { ipTester(t, "https://2001:4860:0:2001::6/api/", "2001:4860:0:2001::6") } func TestServerResolveIPv6BracesNoPort(t *testing.T) { // Technically bad syntax so expect an error: ipTester(t, "https://[2001:4860:0:2001::6]/api/") } func TestServerResolveIPv6NoPortNoPath(t *testing.T) { ipTester(t, "https://2001:4860:0:2001::6", "2001:4860:0:2001::6") } func TestServerResolveIPv6NoPath(t *testing.T) { ipTester(t, "https://[2001:4860:0:2001::6]:8443", "2001:4860:0:2001::6") } func TestServerResolveIPv6BadBraces(t *testing.T) { ipTester(t, "https://[[2001:4860:0:2001::6]:8443") ipTester(t, "https://[[2001:4860:0:2001::6]]:8443") ipTester(t, "https://2001:4860:0:2001::6]]:8443") } func TestServerResolveBadURL(t *testing.T) { ipTester(t, "thisdoesntlooklikeaurl") } // createNode creates a dummy Kubernetes Node object with the IP addresses we request. func createNode(name string, ipAddresses []string) api.Node { // Create a Kube NodeAddress for each given IP address string: addresses := make([]api.NodeAddress, len(ipAddresses)) for _, addr := range ipAddresses { // We don't really care what the type is, we check them all looking for any match: addresses = append(addresses, api.NodeAddress{Type: api.NodeExternalIP, Address: addr}) } status := api.NodeStatus{Addresses: addresses} node := api.Node{ObjectMeta: api.ObjectMeta{Name: name}, Status: status} return node } func TestScanNodesMatchFound(t *testing.T) { nodes := make([]api.Node, 3) nodes = append(nodes, createNode("node1", []string{"192.168.1.1", "10.0.0.1", "24.222.0.1"})) nodes = append(nodes, createNode("node2", []string{"192.168.1.2", "10.0.0.2", "24.222.0.2"})) nodes = append(nodes, createNode("node3", []string{"192.168.1.3", "10.0.0.3", "24.222.0.3"})) r := searchNodesForIP(nodes, []string{"24.222.0.3"}) if len(r.Errors()) > 0 { t.Error("Unexpected error attempting to locate node with IP") } if len(r.Warnings()) > 0 { t.Error("Unexpected warning attempting to locate node with IP") } } func TestScanNodesAnyIPv6MatchFound(t *testing.T) { nodes := make([]api.Node, 3) nodes = append(nodes, createNode("node1", []string{"2001:4860:0:2001::6"})) nodes = append(nodes, createNode("node2", []string{"3001:4860:0:2001::6"})) nodes = append(nodes, createNode("node3", []string{"4001:4860:0:2001::6"})) // First server IP won't match, second will: r := searchNodesForIP(nodes, []string{"10.0.55.55", "2001:4860:0:2001::6"}) if len(r.Errors()) > 0 { t.Error("Unexpected error attempting to locate node with IP") } if len(r.Warnings()) > 0 { t.Error("Unexpected warning attempting to locate node with IP") } }