From a6f967b6e7904212d5af37d1b7faa89c859fd16e Mon Sep 17 00:00:00 2001 From: Brendan Burns Date: Mon, 15 Dec 2014 09:29:04 -0800 Subject: [PATCH] Revert "Revert "Add an api object for the kubelet, and a versioned endpoint."" This reverts commit dcfcf315e5943caf1ccc1779e3a6b83beb48fc35. --- cmd/integration/integration.go | 2 +- pkg/api/register.go | 2 ++ pkg/api/serialization_test.go | 2 +- pkg/api/types.go | 7 +++++++ pkg/api/v1beta1/types.go | 6 ++++++ pkg/api/v1beta2/register.go | 2 ++ pkg/api/v1beta2/types.go | 6 ++++++ pkg/api/v1beta3/register.go | 2 ++ pkg/api/v1beta3/types.go | 7 +++++++ pkg/client/kubelet.go | 28 ++++++++++++++------------ pkg/client/kubelet_test.go | 14 ++++++++----- pkg/kubelet/server.go | 36 +++++++++++++++++++++++++++++++--- pkg/master/pod_cache.go | 8 ++++---- pkg/master/pod_cache_test.go | 36 ++++++++++++++++++++++------------ pkg/registry/pod/rest.go | 4 ++-- pkg/registry/pod/rest_test.go | 4 ++-- 16 files changed, 122 insertions(+), 44 deletions(-) diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index 3a5026fd8c..60d2015743 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -62,7 +62,7 @@ var ( type fakeKubeletClient struct{} -func (fakeKubeletClient) GetPodInfo(host, podNamespace, podID string) (api.PodInfo, error) { +func (fakeKubeletClient) GetPodInfo(host, podNamespace, podID string) (api.PodContainerInfo, error) { // This is a horrible hack to get around the fact that we can't provide // different port numbers per kubelet... var c client.PodInfoGetter diff --git a/pkg/api/register.go b/pkg/api/register.go index 013ffcde1a..e31aaf3825 100644 --- a/pkg/api/register.go +++ b/pkg/api/register.go @@ -25,6 +25,7 @@ var Scheme = runtime.NewScheme() func init() { Scheme.AddKnownTypes("", + &PodContainerInfo{}, &PodList{}, &Pod{}, &ReplicationControllerList{}, @@ -52,6 +53,7 @@ func init() { Scheme.AddKnownTypeWithName("", "MinionList", &NodeList{}) } +func (*PodContainerInfo) IsAnAPIObject() {} func (*Pod) IsAnAPIObject() {} func (*PodList) IsAnAPIObject() {} func (*ReplicationController) IsAnAPIObject() {} diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 1f193621c9..4f0b127288 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -203,7 +203,7 @@ func TestList(t *testing.T) { api.Scheme.Log(nil) } -var nonRoundTrippableTypes = util.NewStringSet("ContainerManifest") +var nonRoundTrippableTypes = util.NewStringSet("ContainerManifest", "PodContainerInfo") var nonInternalRoundTrippableTypes = util.NewStringSet("List") func TestRoundTripTypes(t *testing.T) { diff --git a/pkg/api/types.go b/pkg/api/types.go index abd6578996..b1b8e2cc9f 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -410,6 +410,13 @@ type ContainerStatus struct { // PodInfo contains one entry for every container with available info. type PodInfo map[string]ContainerStatus +// PodContainerInfo is a wrapper for PodInfo that can be encode/decoded +type PodContainerInfo struct { + TypeMeta `json:",inline"` + ObjectMeta `json:"metadata,omitempty"` + ContainerInfo PodInfo `json:"containerInfo"` +} + type RestartPolicyAlways struct{} // TODO(dchen1107): Define what kinds of failures should restart. diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index e96823ae66..9a76e2ac32 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -366,6 +366,12 @@ type ContainerStatus struct { // PodInfo contains one entry for every container with available info. type PodInfo map[string]ContainerStatus +// PodContainerInfo is a wrapper for PodInfo that can be encode/decoded +type PodContainerInfo struct { + TypeMeta `json:",inline"` + ContainerInfo PodInfo `json:"containerInfo" description:"information about each container in this pod"` +} + type RestartPolicyAlways struct{} // TODO(dchen1107): Define what kinds of failures should restart diff --git a/pkg/api/v1beta2/register.go b/pkg/api/v1beta2/register.go index 8e85d2e8cd..3180892eca 100644 --- a/pkg/api/v1beta2/register.go +++ b/pkg/api/v1beta2/register.go @@ -31,6 +31,7 @@ func init() { api.Scheme.AddKnownTypes("v1beta2", &Pod{}, + &PodContainerInfo{}, &PodList{}, &ReplicationController{}, &ReplicationControllerList{}, @@ -58,6 +59,7 @@ func init() { } func (*Pod) IsAnAPIObject() {} +func (*PodContainerInfo) IsAnAPIObject() {} func (*PodList) IsAnAPIObject() {} func (*ReplicationController) IsAnAPIObject() {} func (*ReplicationControllerList) IsAnAPIObject() {} diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index a7fd3e1808..bb2b4ad935 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -331,6 +331,12 @@ type ContainerStatus struct { // PodInfo contains one entry for every container with available info. type PodInfo map[string]ContainerStatus +// PodContainerInfo is a wrapper for PodInfo that can be encode/decoded +type PodContainerInfo struct { + TypeMeta `json:",inline"` + ContainerInfo PodInfo `json:"containerInfo" description:"information about each container in this pod"` +} + type RestartPolicyAlways struct{} // TODO(dchen1107): Define what kinds of failures should restart. diff --git a/pkg/api/v1beta3/register.go b/pkg/api/v1beta3/register.go index bc474da413..6e0df08504 100644 --- a/pkg/api/v1beta3/register.go +++ b/pkg/api/v1beta3/register.go @@ -26,6 +26,7 @@ var Codec = runtime.CodecFor(api.Scheme, "v1beta3") func init() { api.Scheme.AddKnownTypes("v1beta3", + &PodContainerInfo{}, &Pod{}, &PodList{}, &PodTemplate{}, @@ -53,6 +54,7 @@ func init() { api.Scheme.AddKnownTypeWithName("v1beta3", "MinionList", &NodeList{}) } +func (*PodContainerInfo) IsAnAPIObject() {} func (*Pod) IsAnAPIObject() {} func (*PodList) IsAnAPIObject() {} func (*PodTemplate) IsAnAPIObject() {} diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index b5c6e3020d..9d026b791a 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -427,6 +427,13 @@ type ContainerStatus struct { // PodInfo contains one entry for every container with available info. type PodInfo map[string]ContainerStatus +// PodContainerInfo is a wrapper for PodInfo that can be encode/decoded +type PodContainerInfo struct { + TypeMeta `json:",inline"` + ObjectMeta `json:"metadata,omitempty"` + ContainerInfo PodInfo `json:"containerInfo" description:"information about each container in this pod"` +} + type RestartPolicyAlways struct{} // TODO(dchen1107): Define what kinds of failures should restart. diff --git a/pkg/client/kubelet.go b/pkg/client/kubelet.go index 62d6a0a962..11090b1363 100644 --- a/pkg/client/kubelet.go +++ b/pkg/client/kubelet.go @@ -17,15 +17,16 @@ limitations under the License. package client import ( - "encoding/json" "errors" "fmt" "io/ioutil" + "log" "net" "net/http" "strconv" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/health" ) @@ -48,7 +49,7 @@ type KubeletHealthChecker interface { type PodInfoGetter interface { // GetPodInfo returns information about all containers which are part // Returns an api.PodInfo, or an error if one occurs. - GetPodInfo(host, podNamespace, podID string) (api.PodInfo, error) + GetPodInfo(host, podNamespace, podID string) (api.PodContainerInfo, error) } // HTTPKubeletClient is the default implementation of PodInfoGetter and KubeletHealthchecker, accesses the kubelet over HTTP. @@ -89,35 +90,36 @@ func (c *HTTPKubeletClient) url(host string) string { } // GetPodInfo gets information about the specified pod. -func (c *HTTPKubeletClient) GetPodInfo(host, podNamespace, podID string) (api.PodInfo, error) { +func (c *HTTPKubeletClient) GetPodInfo(host, podNamespace, podID string) (api.PodContainerInfo, error) { request, err := http.NewRequest( "GET", fmt.Sprintf( - "%s/podInfo?podID=%s&podNamespace=%s", + "%s/api/v1beta1/podInfo?podID=%s&podNamespace=%s", c.url(host), podID, podNamespace), nil) + info := api.PodContainerInfo{} if err != nil { - return nil, err + return info, err } response, err := c.Client.Do(request) if err != nil { - return nil, err + return info, err } defer response.Body.Close() if response.StatusCode == http.StatusNotFound { - return nil, ErrPodInfoNotAvailable + return info, ErrPodInfoNotAvailable } body, err := ioutil.ReadAll(response.Body) if err != nil { - return nil, err + return info, err } // Check that this data can be unmarshalled - info := api.PodInfo{} - err = json.Unmarshal(body, &info) + log.Printf("FOOO: %s", string(body)) + err = latest.Codec.DecodeInto(body, &info) if err != nil { - return nil, err + return info, err } return info, nil } @@ -132,8 +134,8 @@ func (c *HTTPKubeletClient) HealthCheck(host string) (health.Status, error) { type FakeKubeletClient struct{} // GetPodInfo is a fake implementation of PodInfoGetter.GetPodInfo. -func (c FakeKubeletClient) GetPodInfo(host, podNamespace string, podID string) (api.PodInfo, error) { - return api.PodInfo{}, errors.New("Not Implemented") +func (c FakeKubeletClient) GetPodInfo(host, podNamespace string, podID string) (api.PodContainerInfo, error) { + return api.PodContainerInfo{}, errors.New("Not Implemented") } func (c FakeKubeletClient) HealthCheck(host string) (health.Status, error) { diff --git a/pkg/client/kubelet_test.go b/pkg/client/kubelet_test.go index 6c9fb1fe2c..c8fc160f7b 100644 --- a/pkg/client/kubelet_test.go +++ b/pkg/client/kubelet_test.go @@ -31,8 +31,10 @@ import ( ) func TestHTTPKubeletClient(t *testing.T) { - expectObj := api.PodInfo{ - "myID": api.ContainerStatus{}, + expectObj := api.PodContainerInfo{ + ContainerInfo: map[string]api.ContainerStatus{ + "myID": {}, + }, } body, err := json.Marshal(expectObj) if err != nil { @@ -68,14 +70,16 @@ func TestHTTPKubeletClient(t *testing.T) { } // reflect.DeepEqual(expectObj, gotObj) doesn't handle blank times well - if len(gotObj) != len(expectObj) { + if len(gotObj.ContainerInfo) != len(expectObj.ContainerInfo) { t.Errorf("Unexpected response. Expected: %#v, received %#v", expectObj, gotObj) } } func TestHTTPKubeletClientNotFound(t *testing.T) { - expectObj := api.PodInfo{ - "myID": api.ContainerStatus{}, + expectObj := api.PodContainerInfo{ + ContainerInfo: map[string]api.ContainerStatus{ + "myID": {}, + }, } _, err := json.Marshal(expectObj) if err != nil { diff --git a/pkg/kubelet/server.go b/pkg/kubelet/server.go index a32e3cf252..36122bbf28 100644 --- a/pkg/kubelet/server.go +++ b/pkg/kubelet/server.go @@ -35,6 +35,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz" "github.com/GoogleCloudPlatform/kubernetes/pkg/httplog" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/ghodss/yaml" "github.com/golang/glog" "github.com/google/cadvisor/info" @@ -91,7 +92,8 @@ func NewServer(host HostInterface, updates chan<- interface{}, enableDebuggingHa // InstallDefaultHandlers registers the default set of supported HTTP request patterns with the mux. func (s *Server) InstallDefaultHandlers() { healthz.InstallHandler(s.mux) - s.mux.HandleFunc("/podInfo", s.handlePodInfo) + s.mux.HandleFunc("/podInfo", s.handlePodInfoOld) + s.mux.HandleFunc("/api/v1beta1/podInfo", s.handlePodInfoVersioned) s.mux.HandleFunc("/boundPods", s.handleBoundPods) s.mux.HandleFunc("/stats/", s.handleStats) s.mux.HandleFunc("/spec/", s.handleSpec) @@ -249,8 +251,16 @@ func (s *Server) handleBoundPods(w http.ResponseWriter, req *http.Request) { w.Write(data) } +func (s *Server) handlePodInfoOld(w http.ResponseWriter, req *http.Request) { + s.handlePodInfo(w, req, false) +} + +func (s *Server) handlePodInfoVersioned(w http.ResponseWriter, req *http.Request) { + s.handlePodInfo(w, req, true) +} + // handlePodInfo handles podInfo requests against the Kubelet -func (s *Server) handlePodInfo(w http.ResponseWriter, req *http.Request) { +func (s *Server) handlePodInfo(w http.ResponseWriter, req *http.Request, versioned bool) { u, err := url.ParseRequestURI(req.RequestURI) if err != nil { s.error(w, err) @@ -286,7 +296,7 @@ func (s *Server) handlePodInfo(w http.ResponseWriter, req *http.Request) { s.error(w, err) return } - data, err := json.Marshal(info) + data, err := exportPodInfo(info, versioned) if err != nil { s.error(w, err) return @@ -437,3 +447,23 @@ func (s *Server) serveStats(w http.ResponseWriter, req *http.Request) { w.Write(data) return } + +func exportPodInfo(info api.PodInfo, versioned bool) ([]byte, error) { + if versioned { + // TODO: support arbitrary versions here + codec, err := findCodec("v1beta1") + if err != nil { + return nil, err + } + return codec.Encode(&api.PodContainerInfo{ContainerInfo: info}) + } + return json.Marshal(info) +} + +func findCodec(version string) (runtime.Codec, error) { + versions, err := latest.InterfacesFor(version) + if err != nil { + return nil, err + } + return versions.Codec, nil +} diff --git a/pkg/master/pod_cache.go b/pkg/master/pod_cache.go index b9ee60e062..c592a4ae5a 100644 --- a/pkg/master/pod_cache.go +++ b/pkg/master/pod_cache.go @@ -33,7 +33,7 @@ type PodCache struct { containerInfo client.PodInfoGetter pods pod.Registry // This is a map of pod id to a map of container name to the - podInfo map[string]api.PodInfo + podInfo map[string]api.PodContainerInfo podLock sync.Mutex } @@ -42,7 +42,7 @@ func NewPodCache(info client.PodInfoGetter, pods pod.Registry) *PodCache { return &PodCache{ containerInfo: info, pods: pods, - podInfo: map[string]api.PodInfo{}, + podInfo: map[string]api.PodContainerInfo{}, } } @@ -54,12 +54,12 @@ func makePodCacheKey(podNamespace, podID string) string { // GetPodInfo implements the PodInfoGetter.GetPodInfo. // The returned value should be treated as read-only. // TODO: Remove the host from this call, it's totally unnecessary. -func (p *PodCache) GetPodInfo(host, podNamespace, podID string) (api.PodInfo, error) { +func (p *PodCache) GetPodInfo(host, podNamespace, podID string) (api.PodContainerInfo, error) { p.podLock.Lock() defer p.podLock.Unlock() value, ok := p.podInfo[makePodCacheKey(podNamespace, podID)] if !ok { - return nil, client.ErrPodInfoNotAvailable + return api.PodContainerInfo{}, client.ErrPodInfoNotAvailable } return value, nil } diff --git a/pkg/master/pod_cache_test.go b/pkg/master/pod_cache_test.go index a9c5ebd1b5..74dd57672e 100644 --- a/pkg/master/pod_cache_test.go +++ b/pkg/master/pod_cache_test.go @@ -28,11 +28,11 @@ type FakePodInfoGetter struct { host string id string namespace string - data api.PodInfo + data api.PodContainerInfo err error } -func (f *FakePodInfoGetter) GetPodInfo(host, namespace, id string) (api.PodInfo, error) { +func (f *FakePodInfoGetter) GetPodInfo(host, namespace, id string) (api.PodContainerInfo, error) { f.host = host f.id = id f.namespace = namespace @@ -42,11 +42,15 @@ func (f *FakePodInfoGetter) GetPodInfo(host, namespace, id string) (api.PodInfo, func TestPodCacheGetDifferentNamespace(t *testing.T) { cache := NewPodCache(nil, nil) - expectedDefault := api.PodInfo{ - "foo": api.ContainerStatus{}, + expectedDefault := api.PodContainerInfo{ + ContainerInfo: api.PodInfo{ + "foo": api.ContainerStatus{}, + }, } - expectedOther := api.PodInfo{ - "bar": api.ContainerStatus{}, + expectedOther := api.PodContainerInfo{ + ContainerInfo: api.PodInfo{ + "bar": api.ContainerStatus{}, + }, } cache.podInfo[makePodCacheKey(api.NamespaceDefault, "foo")] = expectedDefault @@ -72,8 +76,10 @@ func TestPodCacheGetDifferentNamespace(t *testing.T) { func TestPodCacheGet(t *testing.T) { cache := NewPodCache(nil, nil) - expected := api.PodInfo{ - "foo": api.ContainerStatus{}, + expected := api.PodContainerInfo{ + ContainerInfo: api.PodInfo{ + "foo": api.ContainerStatus{}, + }, } cache.podInfo[makePodCacheKey(api.NamespaceDefault, "foo")] = expected @@ -93,14 +99,16 @@ func TestPodCacheGetMissing(t *testing.T) { if err == nil { t.Errorf("Unexpected non-error: %#v", err) } - if info != nil { + if !reflect.DeepEqual(info, api.PodContainerInfo{}) { t.Errorf("Unexpected info: %#v", info) } } func TestPodGetPodInfoGetter(t *testing.T) { - expected := api.PodInfo{ - "foo": api.ContainerStatus{}, + expected := api.PodContainerInfo{ + ContainerInfo: api.PodInfo{ + "foo": api.ContainerStatus{}, + }, } fake := FakePodInfoGetter{ data: expected, @@ -133,8 +141,10 @@ func TestPodUpdateAllContainers(t *testing.T) { pods := []api.Pod{pod} mockRegistry := registrytest.NewPodRegistry(&api.PodList{Items: pods}) - expected := api.PodInfo{ - "foo": api.ContainerStatus{}, + expected := api.PodContainerInfo{ + ContainerInfo: api.PodInfo{ + "foo": api.ContainerStatus{}, + }, } fake := FakePodInfoGetter{ data: expected, diff --git a/pkg/registry/pod/rest.go b/pkg/registry/pod/rest.go index 181252210a..133ca0302b 100644 --- a/pkg/registry/pod/rest.go +++ b/pkg/registry/pod/rest.go @@ -230,8 +230,8 @@ func (rs *REST) fillPodInfo(pod *api.Pod) { return } } - pod.Status.Info = info - netContainerInfo, ok := info["net"] + pod.Status.Info = info.ContainerInfo + netContainerInfo, ok := pod.Status.Info["net"] if ok { if netContainerInfo.PodIP != "" { pod.Status.PodIP = netContainerInfo.PodIP diff --git a/pkg/registry/pod/rest_test.go b/pkg/registry/pod/rest_test.go index 3aea5a4300..a74e3f6ec4 100644 --- a/pkg/registry/pod/rest_test.go +++ b/pkg/registry/pod/rest_test.go @@ -566,8 +566,8 @@ type FakePodInfoGetter struct { err error } -func (f *FakePodInfoGetter) GetPodInfo(host, podNamespace string, podID string) (api.PodInfo, error) { - return f.info, f.err +func (f *FakePodInfoGetter) GetPodInfo(host, podNamespace string, podID string) (api.PodContainerInfo, error) { + return api.PodContainerInfo{ContainerInfo: f.info}, f.err } func TestFillPodInfo(t *testing.T) {