From fb0a7a9693b01443d2db7a5f76fc565bd018d9af Mon Sep 17 00:00:00 2001 From: Tim Hockin Date: Mon, 22 Dec 2014 15:02:16 -0800 Subject: [PATCH] Add apiserver proxy support for pods. This is useful for testing mostly. --- pkg/registry/pod/rest.go | 44 +++++++++++++ pkg/registry/pod/rest_test.go | 114 +++++++++++++++++++++++++++++++--- 2 files changed, 149 insertions(+), 9 deletions(-) diff --git a/pkg/registry/pod/rest.go b/pkg/registry/pod/rest.go index 270f1aaa8ec..3b78a068608 100644 --- a/pkg/registry/pod/rest.go +++ b/pkg/registry/pod/rest.go @@ -18,6 +18,7 @@ package pod import ( "fmt" + "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" @@ -175,3 +176,46 @@ func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE return rs.registry.GetPod(ctx, pod.Name) }), nil } + +// ResourceLocation returns a URL to which one can send traffic for the specified pod. +func (rs *REST) ResourceLocation(ctx api.Context, id string) (string, 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 { + return "", 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] + } + + obj, err := rs.Get(ctx, name) + if err != nil { + return "", err + } + pod := obj.(*api.Pod) + if pod == nil { + return "", nil + } + + // Try to figure out a port. + if port == "" { + for i := range pod.Spec.Containers { + if len(pod.Spec.Containers[i].Ports) > 0 { + port = fmt.Sprintf("%d", pod.Spec.Containers[i].Ports[0].ContainerPort) + break + } + } + } + + // We leave off the scheme ('http://') because we have no idea what sort of server + // is listening at this endpoint. + loc := pod.Status.PodIP + if port != "" { + loc += fmt.Sprintf(":%s", port) + } + return loc, nil +} diff --git a/pkg/registry/pod/rest_test.go b/pkg/registry/pod/rest_test.go index e93853c69f6..0b63cf726b6 100644 --- a/pkg/registry/pod/rest_test.go +++ b/pkg/registry/pod/rest_test.go @@ -443,15 +443,6 @@ func TestCreatePod(t *testing.T) { } } -type FakePodInfoGetter struct { - info api.PodInfo - err error -} - -func (f *FakePodInfoGetter) GetPodInfo(host, podNamespace string, podID string) (api.PodContainerInfo, error) { - return api.PodContainerInfo{ContainerInfo: f.info}, f.err -} - func TestCreatePodWithConflictingNamespace(t *testing.T) { storage := REST{} pod := &api.Pod{ @@ -487,3 +478,108 @@ func TestUpdatePodWithConflictingNamespace(t *testing.T) { t.Errorf("Expected 'Pod.Namespace does not match the provided context' error, got '%v'", err.Error()) } } + +func TestResourceLocation(t *testing.T) { + expectedIP := "1.2.3.4" + testCases := []struct { + pod api.Pod + query string + location string + }{ + { + pod: api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "foo"}, + }, + query: "foo", + location: expectedIP, + }, + { + pod: api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "foo"}, + }, + query: "foo:12345", + location: expectedIP + ":12345", + }, + { + pod: api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Spec: api.PodSpec{ + Containers: []api.Container{ + {Name: "ctr"}, + }, + }, + }, + query: "foo", + location: expectedIP, + }, + { + pod: api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Spec: api.PodSpec{ + Containers: []api.Container{ + {Name: "ctr", Ports: []api.Port{{ContainerPort: 9376}}}, + }, + }, + }, + query: "foo", + location: expectedIP + ":9376", + }, + { + pod: api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Spec: api.PodSpec{ + Containers: []api.Container{ + {Name: "ctr", Ports: []api.Port{{ContainerPort: 9376}}}, + }, + }, + }, + query: "foo:12345", + location: expectedIP + ":12345", + }, + { + pod: api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Spec: api.PodSpec{ + Containers: []api.Container{ + {Name: "ctr1"}, + {Name: "ctr2", Ports: []api.Port{{ContainerPort: 9376}}}, + }, + }, + }, + query: "foo", + location: expectedIP + ":9376", + }, + { + pod: api.Pod{ + ObjectMeta: api.ObjectMeta{Name: "foo"}, + Spec: api.PodSpec{ + Containers: []api.Container{ + {Name: "ctr1", Ports: []api.Port{{ContainerPort: 9376}}}, + {Name: "ctr2", Ports: []api.Port{{ContainerPort: 1234}}}, + }, + }, + }, + query: "foo", + location: expectedIP + ":9376", + }, + } + + for _, tc := range testCases { + podRegistry := registrytest.NewPodRegistry(nil) + podRegistry.Pod = &tc.pod + storage := &REST{ + registry: podRegistry, + podCache: &fakeCache{statusToReturn: &api.PodStatus{PodIP: expectedIP}}, + } + + redirector := apiserver.Redirector(storage) + location, err := redirector.ResourceLocation(api.NewDefaultContext(), tc.query) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + + if location != tc.location { + t.Errorf("Expected %v, but got %v", tc.location, location) + } + } +}