Revert "Revert "Add an api object for the kubelet, and a versioned endpoint.""

This reverts commit dcfcf315e5.
pull/6/head
Brendan Burns 2014-12-15 09:29:04 -08:00
parent 1abc3e423c
commit a6f967b6e7
16 changed files with 122 additions and 44 deletions

View File

@ -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

View File

@ -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() {}

View File

@ -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) {

View File

@ -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.

View File

@ -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

View File

@ -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() {}

View File

@ -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.

View File

@ -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() {}

View File

@ -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.

View File

@ -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) {

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}

View File

@ -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,

View File

@ -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

View File

@ -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) {