diff --git a/pkg/registry/minion/rest.go b/pkg/registry/minion/rest.go index 1c4c013e4848..be64f5d7074e 100644 --- a/pkg/registry/minion/rest.go +++ b/pkg/registry/minion/rest.go @@ -24,12 +24,14 @@ import ( "strconv" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors" ) @@ -129,12 +131,21 @@ func MatchNode(label labels.Selector, field fields.Selector) generic.Matcher { // ResourceLocation returns a URL to which one can send traffic for the specified node. func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGetter, ctx api.Context, id string) (*url.URL, http.RoundTripper, error) { - nodeObj, err := getter.Get(ctx, id) + name, portReq, valid := util.SplitPort(id) + if !valid { + return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid node request %q", id)) + } + + nodeObj, err := getter.Get(ctx, name) if err != nil { return nil, nil, err } node := nodeObj.(*api.Node) - host := node.Name + host := node.Name // TODO: use node's IP, don't expect the name to resolve. + + if portReq != "" { + return &url.URL{Host: net.JoinHostPort(host, portReq)}, nil, nil + } scheme, port, transport, err := connection.GetConnectionInfo(host) if err != nil { diff --git a/pkg/registry/pod/rest.go b/pkg/registry/pod/rest.go index 5f81e9c84363..0db19979fe8f 100644 --- a/pkg/registry/pod/rest.go +++ b/pkg/registry/pod/rest.go @@ -21,7 +21,6 @@ import ( "net" "net/http" "net/url" - "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" @@ -31,6 +30,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors" ) @@ -150,16 +150,11 @@ func getPod(getter ResourceGetter, ctx api.Context, name string) (*api.Pod, erro func ResourceLocation(getter ResourceGetter, ctx api.Context, id string) (*url.URL, http.RoundTripper, error) { // Allow ID as "podname" or "podname:port". If port is not specified, // try to use the first defined port on the pod. - parts := strings.Split(id, ":") - if len(parts) > 2 { + name, port, valid := util.SplitPort(id) + if !valid { return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid pod request %q", id)) } - name := parts[0] - port := "" - if len(parts) == 2 { - // TODO: if port is not a number but a "(container)/(portname)", do a name lookup. - port = parts[1] - } + // TODO: if port is not a number but a "(container)/(portname)", do a name lookup. pod, err := getPod(getter, ctx, name) if err != nil { diff --git a/pkg/registry/service/rest.go b/pkg/registry/service/rest.go index fb5b1195ae8b..30e4d4ba6002 100644 --- a/pkg/registry/service/rest.go +++ b/pkg/registry/service/rest.go @@ -23,7 +23,6 @@ import ( "net/http" "net/url" "strconv" - "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" @@ -34,6 +33,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/endpoint" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/minion" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/golang/glog" @@ -206,15 +206,10 @@ var _ = rest.Redirector(&REST{}) // ResourceLocation returns a URL to which one can send traffic for the specified service. func (rs *REST) ResourceLocation(ctx api.Context, id string) (*url.URL, http.RoundTripper, error) { // Allow ID as "svcname" or "svcname:port". - parts := strings.Split(id, ":") - if len(parts) > 2 { + svcName, portStr, valid := util.SplitPort(id) + if !valid { return nil, nil, errors.NewBadRequest(fmt.Sprintf("invalid service request %q", id)) } - svcName := parts[0] - portStr := "" - if len(parts) == 2 { - portStr = parts[1] - } eps, err := rs.endpoints.GetEndpoints(ctx, svcName) if err != nil { diff --git a/pkg/util/port_split.go b/pkg/util/port_split.go new file mode 100644 index 000000000000..014b89b8e0a9 --- /dev/null +++ b/pkg/util/port_split.go @@ -0,0 +1,40 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 ( + "strings" +) + +// Takes a string of the form "name:port" or "name". +// * If id is of the form "name" or "name:", then return (name, "", true) +// * If id is of the form "name:port", then return (name, port, true) +// * Otherwise, return ("", "", false) +// Additionally, name must be non-empty or valid will be returned false. +// +// Port is returned as a string, and it is not required to be numeric (could be +// used for a named port, for example). +func SplitPort(id string) (name, port string, valid bool) { + parts := strings.Split(id, ":") + if len(parts) > 2 { + return "", "", false + } + if len(parts) == 2 { + return parts[0], parts[1], len(parts[0]) > 0 + } + return id, "", len(id) > 0 +} diff --git a/pkg/util/port_split_test.go b/pkg/util/port_split_test.go new file mode 100644 index 000000000000..20b4ff352af0 --- /dev/null +++ b/pkg/util/port_split_test.go @@ -0,0 +1,65 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +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 ( + "testing" +) + +func TestSplitPort(t *testing.T) { + table := []struct { + in string + name, port string + valid bool + }{ + { + in: "aoeu:asdf", + name: "aoeu", + port: "asdf", + valid: true, + }, { + in: "aoeu:", + name: "aoeu", + valid: true, + }, { + in: ":asdf", + name: "", + port: "asdf", + }, { + in: "aoeu:asdf:htns", + }, { + in: "aoeu", + name: "aoeu", + valid: true, + }, { + in: "", + }, + } + + for _, item := range table { + name, port, valid := SplitPort(item.in) + if e, a := item.name, name; e != a { + t.Errorf("%q: Wanted %q, got %q", item.in, e, a) + } + if e, a := item.port, port; e != a { + t.Errorf("%q: Wanted %q, got %q", item.in, e, a) + } + if e, a := item.valid, valid; e != a { + t.Errorf("%q: Wanted %q, got %q", item.in, e, a) + } + } +}