Merge pull request #2942 from brendandburns/kubelet

revert the revert along with fixes to make it pass e2e tests.
This commit is contained in:
Daniel Smith 2014-12-15 13:17:52 -08:00
commit f1f20f820a
16 changed files with 121 additions and 43 deletions

View File

@ -62,7 +62,7 @@ var (
type fakeKubeletClient struct{} 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 // This is a horrible hack to get around the fact that we can't provide
// different port numbers per kubelet... // different port numbers per kubelet...
var c client.PodInfoGetter var c client.PodInfoGetter

View File

@ -25,6 +25,7 @@ var Scheme = runtime.NewScheme()
func init() { func init() {
Scheme.AddKnownTypes("", Scheme.AddKnownTypes("",
&PodContainerInfo{},
&PodList{}, &PodList{},
&Pod{}, &Pod{},
&ReplicationControllerList{}, &ReplicationControllerList{},
@ -52,6 +53,7 @@ func init() {
Scheme.AddKnownTypeWithName("", "MinionList", &NodeList{}) Scheme.AddKnownTypeWithName("", "MinionList", &NodeList{})
} }
func (*PodContainerInfo) IsAnAPIObject() {}
func (*Pod) IsAnAPIObject() {} func (*Pod) IsAnAPIObject() {}
func (*PodList) IsAnAPIObject() {} func (*PodList) IsAnAPIObject() {}
func (*ReplicationController) IsAnAPIObject() {} func (*ReplicationController) IsAnAPIObject() {}

View File

@ -410,6 +410,13 @@ type ContainerStatus struct {
// PodInfo contains one entry for every container with available info. // PodInfo contains one entry for every container with available info.
type PodInfo map[string]ContainerStatus 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{} type RestartPolicyAlways struct{}
// TODO(dchen1107): Define what kinds of failures should restart. // TODO(dchen1107): Define what kinds of failures should restart.

View File

@ -31,6 +31,7 @@ func init() {
api.Scheme.AddKnownTypes("v1beta1", api.Scheme.AddKnownTypes("v1beta1",
&Pod{}, &Pod{},
&PodContainerInfo{},
&PodList{}, &PodList{},
&ReplicationController{}, &ReplicationController{},
&ReplicationControllerList{}, &ReplicationControllerList{},
@ -58,6 +59,7 @@ func init() {
} }
func (*Pod) IsAnAPIObject() {} func (*Pod) IsAnAPIObject() {}
func (*PodContainerInfo) IsAnAPIObject() {}
func (*PodList) IsAnAPIObject() {} func (*PodList) IsAnAPIObject() {}
func (*ReplicationController) IsAnAPIObject() {} func (*ReplicationController) IsAnAPIObject() {}
func (*ReplicationControllerList) IsAnAPIObject() {} func (*ReplicationControllerList) IsAnAPIObject() {}

View File

@ -366,6 +366,12 @@ type ContainerStatus struct {
// PodInfo contains one entry for every container with available info. // PodInfo contains one entry for every container with available info.
type PodInfo map[string]ContainerStatus 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{} type RestartPolicyAlways struct{}
// TODO(dchen1107): Define what kinds of failures should restart // TODO(dchen1107): Define what kinds of failures should restart

View File

@ -31,6 +31,7 @@ func init() {
api.Scheme.AddKnownTypes("v1beta2", api.Scheme.AddKnownTypes("v1beta2",
&Pod{}, &Pod{},
&PodContainerInfo{},
&PodList{}, &PodList{},
&ReplicationController{}, &ReplicationController{},
&ReplicationControllerList{}, &ReplicationControllerList{},
@ -58,6 +59,7 @@ func init() {
} }
func (*Pod) IsAnAPIObject() {} func (*Pod) IsAnAPIObject() {}
func (*PodContainerInfo) IsAnAPIObject() {}
func (*PodList) IsAnAPIObject() {} func (*PodList) IsAnAPIObject() {}
func (*ReplicationController) IsAnAPIObject() {} func (*ReplicationController) IsAnAPIObject() {}
func (*ReplicationControllerList) IsAnAPIObject() {} func (*ReplicationControllerList) IsAnAPIObject() {}

View File

@ -331,6 +331,12 @@ type ContainerStatus struct {
// PodInfo contains one entry for every container with available info. // PodInfo contains one entry for every container with available info.
type PodInfo map[string]ContainerStatus 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{} type RestartPolicyAlways struct{}
// TODO(dchen1107): Define what kinds of failures should restart. // TODO(dchen1107): Define what kinds of failures should restart.

View File

@ -26,6 +26,7 @@ var Codec = runtime.CodecFor(api.Scheme, "v1beta3")
func init() { func init() {
api.Scheme.AddKnownTypes("v1beta3", api.Scheme.AddKnownTypes("v1beta3",
&PodContainerInfo{},
&Pod{}, &Pod{},
&PodList{}, &PodList{},
&PodTemplate{}, &PodTemplate{},
@ -53,6 +54,7 @@ func init() {
api.Scheme.AddKnownTypeWithName("v1beta3", "MinionList", &NodeList{}) api.Scheme.AddKnownTypeWithName("v1beta3", "MinionList", &NodeList{})
} }
func (*PodContainerInfo) IsAnAPIObject() {}
func (*Pod) IsAnAPIObject() {} func (*Pod) IsAnAPIObject() {}
func (*PodList) IsAnAPIObject() {} func (*PodList) IsAnAPIObject() {}
func (*PodTemplate) IsAnAPIObject() {} func (*PodTemplate) IsAnAPIObject() {}

View File

@ -427,6 +427,13 @@ type ContainerStatus struct {
// PodInfo contains one entry for every container with available info. // PodInfo contains one entry for every container with available info.
type PodInfo map[string]ContainerStatus 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{} type RestartPolicyAlways struct{}
// TODO(dchen1107): Define what kinds of failures should restart. // TODO(dchen1107): Define what kinds of failures should restart.

View File

@ -17,7 +17,6 @@ limitations under the License.
package client package client
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -26,6 +25,7 @@ import (
"strconv" "strconv"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/health" "github.com/GoogleCloudPlatform/kubernetes/pkg/health"
) )
@ -48,7 +48,7 @@ type KubeletHealthChecker interface {
type PodInfoGetter interface { type PodInfoGetter interface {
// GetPodInfo returns information about all containers which are part // GetPodInfo returns information about all containers which are part
// Returns an api.PodInfo, or an error if one occurs. // 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. // HTTPKubeletClient is the default implementation of PodInfoGetter and KubeletHealthchecker, accesses the kubelet over HTTP.
@ -89,35 +89,35 @@ func (c *HTTPKubeletClient) url(host string) string {
} }
// GetPodInfo gets information about the specified pod. // 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( request, err := http.NewRequest(
"GET", "GET",
fmt.Sprintf( fmt.Sprintf(
"%s/podInfo?podID=%s&podNamespace=%s", "%s/api/v1beta1/podInfo?podID=%s&podNamespace=%s",
c.url(host), c.url(host),
podID, podID,
podNamespace), podNamespace),
nil) nil)
info := api.PodContainerInfo{}
if err != nil { if err != nil {
return nil, err return info, err
} }
response, err := c.Client.Do(request) response, err := c.Client.Do(request)
if err != nil { if err != nil {
return nil, err return info, err
} }
defer response.Body.Close() defer response.Body.Close()
if response.StatusCode == http.StatusNotFound { if response.StatusCode == http.StatusNotFound {
return nil, ErrPodInfoNotAvailable return info, ErrPodInfoNotAvailable
} }
body, err := ioutil.ReadAll(response.Body) body, err := ioutil.ReadAll(response.Body)
if err != nil { if err != nil {
return nil, err return info, err
} }
// Check that this data can be unmarshalled // Check that this data can be unmarshalled
info := api.PodInfo{} err = latest.Codec.DecodeInto(body, &info)
err = json.Unmarshal(body, &info)
if err != nil { if err != nil {
return nil, err return info, err
} }
return info, nil return info, nil
} }
@ -132,8 +132,8 @@ func (c *HTTPKubeletClient) HealthCheck(host string) (health.Status, error) {
type FakeKubeletClient struct{} type FakeKubeletClient struct{}
// GetPodInfo is a fake implementation of PodInfoGetter.GetPodInfo. // GetPodInfo is a fake implementation of PodInfoGetter.GetPodInfo.
func (c FakeKubeletClient) GetPodInfo(host, podNamespace string, podID string) (api.PodInfo, error) { func (c FakeKubeletClient) GetPodInfo(host, podNamespace string, podID string) (api.PodContainerInfo, error) {
return api.PodInfo{}, errors.New("Not Implemented") return api.PodContainerInfo{}, errors.New("Not Implemented")
} }
func (c FakeKubeletClient) HealthCheck(host string) (health.Status, error) { func (c FakeKubeletClient) HealthCheck(host string) (health.Status, error) {

View File

@ -31,8 +31,10 @@ import (
) )
func TestHTTPKubeletClient(t *testing.T) { func TestHTTPKubeletClient(t *testing.T) {
expectObj := api.PodInfo{ expectObj := api.PodContainerInfo{
"myID": api.ContainerStatus{}, ContainerInfo: map[string]api.ContainerStatus{
"myID": {},
},
} }
body, err := json.Marshal(expectObj) body, err := json.Marshal(expectObj)
if err != nil { if err != nil {
@ -68,14 +70,16 @@ func TestHTTPKubeletClient(t *testing.T) {
} }
// reflect.DeepEqual(expectObj, gotObj) doesn't handle blank times well // 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) t.Errorf("Unexpected response. Expected: %#v, received %#v", expectObj, gotObj)
} }
} }
func TestHTTPKubeletClientNotFound(t *testing.T) { func TestHTTPKubeletClientNotFound(t *testing.T) {
expectObj := api.PodInfo{ expectObj := api.PodContainerInfo{
"myID": api.ContainerStatus{}, ContainerInfo: map[string]api.ContainerStatus{
"myID": {},
},
} }
_, err := json.Marshal(expectObj) _, err := json.Marshal(expectObj)
if err != nil { if err != nil {

View File

@ -35,6 +35,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/healthz" "github.com/GoogleCloudPlatform/kubernetes/pkg/healthz"
"github.com/GoogleCloudPlatform/kubernetes/pkg/httplog" "github.com/GoogleCloudPlatform/kubernetes/pkg/httplog"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/google/cadvisor/info" "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. // InstallDefaultHandlers registers the default set of supported HTTP request patterns with the mux.
func (s *Server) InstallDefaultHandlers() { func (s *Server) InstallDefaultHandlers() {
healthz.InstallHandler(s.mux) 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("/boundPods", s.handleBoundPods)
s.mux.HandleFunc("/stats/", s.handleStats) s.mux.HandleFunc("/stats/", s.handleStats)
s.mux.HandleFunc("/spec/", s.handleSpec) s.mux.HandleFunc("/spec/", s.handleSpec)
@ -249,8 +251,16 @@ func (s *Server) handleBoundPods(w http.ResponseWriter, req *http.Request) {
w.Write(data) 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 // 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) u, err := url.ParseRequestURI(req.RequestURI)
if err != nil { if err != nil {
s.error(w, err) s.error(w, err)
@ -286,7 +296,7 @@ func (s *Server) handlePodInfo(w http.ResponseWriter, req *http.Request) {
s.error(w, err) s.error(w, err)
return return
} }
data, err := json.Marshal(info) data, err := exportPodInfo(info, versioned)
if err != nil { if err != nil {
s.error(w, err) s.error(w, err)
return return
@ -437,3 +447,23 @@ func (s *Server) serveStats(w http.ResponseWriter, req *http.Request) {
w.Write(data) w.Write(data)
return 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
}

View File

@ -33,7 +33,7 @@ type PodCache struct {
containerInfo client.PodInfoGetter containerInfo client.PodInfoGetter
pods pod.Registry pods pod.Registry
// This is a map of pod id to a map of container name to the // 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 podLock sync.Mutex
} }
@ -42,7 +42,7 @@ func NewPodCache(info client.PodInfoGetter, pods pod.Registry) *PodCache {
return &PodCache{ return &PodCache{
containerInfo: info, containerInfo: info,
pods: pods, 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. // GetPodInfo implements the PodInfoGetter.GetPodInfo.
// The returned value should be treated as read-only. // The returned value should be treated as read-only.
// TODO: Remove the host from this call, it's totally unnecessary. // 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() p.podLock.Lock()
defer p.podLock.Unlock() defer p.podLock.Unlock()
value, ok := p.podInfo[makePodCacheKey(podNamespace, podID)] value, ok := p.podInfo[makePodCacheKey(podNamespace, podID)]
if !ok { if !ok {
return nil, client.ErrPodInfoNotAvailable return api.PodContainerInfo{}, client.ErrPodInfoNotAvailable
} }
return value, nil return value, nil
} }

View File

@ -28,11 +28,11 @@ type FakePodInfoGetter struct {
host string host string
id string id string
namespace string namespace string
data api.PodInfo data api.PodContainerInfo
err error 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.host = host
f.id = id f.id = id
f.namespace = namespace f.namespace = namespace
@ -42,11 +42,15 @@ func (f *FakePodInfoGetter) GetPodInfo(host, namespace, id string) (api.PodInfo,
func TestPodCacheGetDifferentNamespace(t *testing.T) { func TestPodCacheGetDifferentNamespace(t *testing.T) {
cache := NewPodCache(nil, nil) cache := NewPodCache(nil, nil)
expectedDefault := api.PodInfo{ expectedDefault := api.PodContainerInfo{
"foo": api.ContainerStatus{}, ContainerInfo: api.PodInfo{
"foo": api.ContainerStatus{},
},
} }
expectedOther := api.PodInfo{ expectedOther := api.PodContainerInfo{
"bar": api.ContainerStatus{}, ContainerInfo: api.PodInfo{
"bar": api.ContainerStatus{},
},
} }
cache.podInfo[makePodCacheKey(api.NamespaceDefault, "foo")] = expectedDefault cache.podInfo[makePodCacheKey(api.NamespaceDefault, "foo")] = expectedDefault
@ -72,8 +76,10 @@ func TestPodCacheGetDifferentNamespace(t *testing.T) {
func TestPodCacheGet(t *testing.T) { func TestPodCacheGet(t *testing.T) {
cache := NewPodCache(nil, nil) cache := NewPodCache(nil, nil)
expected := api.PodInfo{ expected := api.PodContainerInfo{
"foo": api.ContainerStatus{}, ContainerInfo: api.PodInfo{
"foo": api.ContainerStatus{},
},
} }
cache.podInfo[makePodCacheKey(api.NamespaceDefault, "foo")] = expected cache.podInfo[makePodCacheKey(api.NamespaceDefault, "foo")] = expected
@ -93,14 +99,16 @@ func TestPodCacheGetMissing(t *testing.T) {
if err == nil { if err == nil {
t.Errorf("Unexpected non-error: %#v", err) t.Errorf("Unexpected non-error: %#v", err)
} }
if info != nil { if !reflect.DeepEqual(info, api.PodContainerInfo{}) {
t.Errorf("Unexpected info: %#v", info) t.Errorf("Unexpected info: %#v", info)
} }
} }
func TestPodGetPodInfoGetter(t *testing.T) { func TestPodGetPodInfoGetter(t *testing.T) {
expected := api.PodInfo{ expected := api.PodContainerInfo{
"foo": api.ContainerStatus{}, ContainerInfo: api.PodInfo{
"foo": api.ContainerStatus{},
},
} }
fake := FakePodInfoGetter{ fake := FakePodInfoGetter{
data: expected, data: expected,
@ -133,8 +141,10 @@ func TestPodUpdateAllContainers(t *testing.T) {
pods := []api.Pod{pod} pods := []api.Pod{pod}
mockRegistry := registrytest.NewPodRegistry(&api.PodList{Items: pods}) mockRegistry := registrytest.NewPodRegistry(&api.PodList{Items: pods})
expected := api.PodInfo{ expected := api.PodContainerInfo{
"foo": api.ContainerStatus{}, ContainerInfo: api.PodInfo{
"foo": api.ContainerStatus{},
},
} }
fake := FakePodInfoGetter{ fake := FakePodInfoGetter{
data: expected, data: expected,

View File

@ -230,8 +230,8 @@ func (rs *REST) fillPodInfo(pod *api.Pod) {
return return
} }
} }
pod.Status.Info = info pod.Status.Info = info.ContainerInfo
netContainerInfo, ok := info["net"] netContainerInfo, ok := pod.Status.Info["net"]
if ok { if ok {
if netContainerInfo.PodIP != "" { if netContainerInfo.PodIP != "" {
pod.Status.PodIP = netContainerInfo.PodIP pod.Status.PodIP = netContainerInfo.PodIP

View File

@ -566,8 +566,8 @@ type FakePodInfoGetter struct {
err error err error
} }
func (f *FakePodInfoGetter) GetPodInfo(host, podNamespace string, podID string) (api.PodInfo, error) { func (f *FakePodInfoGetter) GetPodInfo(host, podNamespace string, podID string) (api.PodContainerInfo, error) {
return f.info, f.err return api.PodContainerInfo{ContainerInfo: f.info}, f.err
} }
func TestFillPodInfo(t *testing.T) { func TestFillPodInfo(t *testing.T) {