diff --git a/pkg/proxy/util/endpoints.go b/pkg/proxy/util/endpoints.go new file mode 100644 index 00000000000..32e770d4f94 --- /dev/null +++ b/pkg/proxy/util/endpoints.go @@ -0,0 +1,51 @@ +/* +Copyright 2017 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 util + +import ( + "fmt" + "net" + + "github.com/golang/glog" +) + +// IPPart returns just the IP part of an IP or IP:port or endpoint string. If the IP +// part is an IPv6 address enclosed in brackets (e.g. "[fd00:1::5]:9999"), +// then the brackets are stripped as well. +func IPPart(s string) string { + if ip := net.ParseIP(s); ip != nil { + // IP address without port + return s + } + // Must be IP:port + ip, _, err := net.SplitHostPort(s) + if err != nil { + glog.Errorf("Error parsing '%s': %v", s, err) + return "" + } + return ip +} + +// ToCIDR returns a host address of the form /32 for +// IPv4 and /128 for IPv6 +func ToCIDR(ip net.IP) string { + len := 32 + if ip.To4() == nil { + len = 128 + } + return fmt.Sprintf("%s/%d", ip.String(), len) +} diff --git a/pkg/proxy/util/endpoints_test.go b/pkg/proxy/util/endpoints_test.go new file mode 100644 index 00000000000..618f59e96a8 --- /dev/null +++ b/pkg/proxy/util/endpoints_test.go @@ -0,0 +1,68 @@ +/* +Copyright 2017 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 util + +import ( + "net" + "testing" +) + +func TestIPPart(t *testing.T) { + const noError = "" + + testCases := []struct { + endpoint string + expectedIP string + expectedError string + }{ + {"1.2.3.4", "1.2.3.4", noError}, + {"1.2.3.4:9999", "1.2.3.4", noError}, + {"2001:db8::1:1", "2001:db8::1:1", noError}, + {"[2001:db8::2:2]:9999", "2001:db8::2:2", noError}, + {"1.2.3.4::9999", "", "too many colons"}, + {"1.2.3.4:[0]", "", "unexpected '[' in address"}, + } + + for _, tc := range testCases { + ip := IPPart(tc.endpoint) + if tc.expectedError == noError { + if ip != tc.expectedIP { + t.Errorf("Unexpected IP for %s: Expected: %s, Got %s", tc.endpoint, tc.expectedIP, ip) + } + } else if ip != "" { + t.Errorf("Error did not occur for %s, expected: '%s' error", tc.endpoint, tc.expectedError) + } + } +} + +func TestToCIDR(t *testing.T) { + testCases := []struct { + ip string + expectedAddr string + }{ + {"1.2.3.4", "1.2.3.4/32"}, + {"2001:db8::1:1", "2001:db8::1:1/128"}, + } + + for _, tc := range testCases { + ip := net.ParseIP(tc.ip) + addr := ToCIDR(ip) + if addr != tc.expectedAddr { + t.Errorf("Unexpected host address for %s: Expected: %s, Got %s", tc.ip, tc.expectedAddr, addr) + } + } +}