From de75a42cb23ad986765924ea166da386f6d2d5d3 Mon Sep 17 00:00:00 2001 From: Yu-Ju Hong Date: Tue, 23 Jun 2015 16:01:12 -0700 Subject: [PATCH] Add a kubelet /runningpods endpoint /runningpods returns a list of pods currently running on the kubelet. The list is composed by examining the container runtime, and may be different from the desired pods to run known by kubelet. This is useful for tests to verify that pods are indeed deleted on nodes. --- pkg/kubelet/container/runtime.go | 18 ++++++++++++++++ pkg/kubelet/kubelet.go | 17 ++++++++++++++++ pkg/kubelet/server.go | 35 ++++++++++++++++++++++++++++---- pkg/kubelet/server_test.go | 5 +++++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/pkg/kubelet/container/runtime.go b/pkg/kubelet/container/runtime.go index c0eeaea422c..3403f401c71 100644 --- a/pkg/kubelet/container/runtime.go +++ b/pkg/kubelet/container/runtime.go @@ -284,6 +284,24 @@ func (p *Pod) FindContainerByName(containerName string) *Container { return nil } +// ToAPIPod converts Pod to api.Pod. Note that if a field in api.Pod has no +// corresponding field in Pod, the field would not be populated. +func (p *Pod) ToAPIPod() *api.Pod { + var pod api.Pod + pod.UID = p.ID + pod.Name = p.Name + pod.Namespace = p.Namespace + pod.Status = p.Status + + for _, c := range p.Containers { + var container api.Container + container.Name = c.Name + container.Image = c.Image + pod.Spec.Containers = append(pod.Spec.Containers, container) + } + return &pod +} + // IsEmpty returns true if the pod is empty. func (p *Pod) IsEmpty() bool { return reflect.DeepEqual(p, &Pod{}) diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 4b9e91890eb..4b6411df78e 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -1850,6 +1850,23 @@ func (kl *Kubelet) GetPods() []*api.Pod { return kl.podManager.GetPods() } +// GetRunningPods returns all pods running on kubelet from looking at the +// container runtime cache. This function converts kubecontainer.Pod to +// api.Pod, so only the fields that exist in both kubecontainer.Pod and +// api.Pod are considered meaningful. +func (kl *Kubelet) GetRunningPods() ([]*api.Pod, error) { + pods, err := kl.runtimeCache.GetPods() + if err != nil { + return nil, err + } + + apiPods := make([]*api.Pod, 0, len(pods)) + for _, pod := range pods { + apiPods = append(apiPods, pod.ToAPIPod()) + } + return apiPods, nil +} + func (kl *Kubelet) GetPodByFullName(podFullName string) (*api.Pod, bool) { return kl.podManager.GetPodByFullName(podFullName) } diff --git a/pkg/kubelet/server.go b/pkg/kubelet/server.go index 32d0c3a7dd3..7d76a62d081 100644 --- a/pkg/kubelet/server.go +++ b/pkg/kubelet/server.go @@ -101,6 +101,7 @@ type HostInterface interface { GetRawContainerInfo(containerName string, req *cadvisorApi.ContainerInfoRequest, subcontainers bool) (map[string]*cadvisorApi.ContainerInfo, error) GetCachedMachineInfo() (*cadvisorApi.MachineInfo, error) GetPods() []*api.Pod + GetRunningPods() ([]*api.Pod, error) GetPodByName(namespace, name string) (*api.Pod, bool) RunInContainer(name string, uid types.UID, container string, cmd []string) ([]byte, error) ExecInContainer(name string, uid types.UID, container string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool) error @@ -148,6 +149,8 @@ func (s *Server) InstallDebuggingHandlers() { s.mux.HandleFunc("/logs/", s.handleLogs) s.mux.HandleFunc("/containerLogs/", s.handleContainerLogs) s.mux.Handle("/metrics", prometheus.Handler()) + // The /runningpods endpoint is used for testing only. + s.mux.HandleFunc("/runningpods", s.handleRunningPods) s.mux.HandleFunc("/debug/pprof/", pprof.Index) s.mux.HandleFunc("/debug/pprof/profile", pprof.Profile) @@ -280,14 +283,38 @@ func (s *Server) handleContainerLogs(w http.ResponseWriter, req *http.Request) { } } -// handlePods returns a list of pod bound to the Kubelet and their spec -func (s *Server) handlePods(w http.ResponseWriter, req *http.Request) { - pods := s.host.GetPods() +// encodePods creates an api.PodList object from pods and returns the encoded +// PodList. +func encodePods(pods []*api.Pod) (data []byte, err error) { podList := new(api.PodList) for _, pod := range pods { podList.Items = append(podList.Items, *pod) } - data, err := latest.Codec.Encode(podList) + return latest.Codec.Encode(podList) +} + +// handlePods returns a list of pods bound to the Kubelet and their spec. +func (s *Server) handlePods(w http.ResponseWriter, req *http.Request) { + pods := s.host.GetPods() + data, err := encodePods(pods) + if err != nil { + s.error(w, err) + return + } + w.Header().Add("Content-type", "application/json") + w.Write(data) +} + +// handleRunningPods returns a list of pods running on Kubelet. The list is +// provided by the container runtime, and is different from the list returned +// by handlePods, which is a set of desired pods to run. +func (s *Server) handleRunningPods(w http.ResponseWriter, req *http.Request) { + pods, err := s.host.GetRunningPods() + if err != nil { + s.error(w, err) + return + } + data, err := encodePods(pods) if err != nil { s.error(w, err) return diff --git a/pkg/kubelet/server_test.go b/pkg/kubelet/server_test.go index 51de9625a75..39c7a4eaa77 100644 --- a/pkg/kubelet/server_test.go +++ b/pkg/kubelet/server_test.go @@ -47,6 +47,7 @@ type fakeKubelet struct { rawInfoFunc func(query *cadvisorApi.ContainerInfoRequest) (map[string]*cadvisorApi.ContainerInfo, error) machineInfoFunc func() (*cadvisorApi.MachineInfo, error) podsFunc func() []*api.Pod + runningPodsFunc func() ([]*api.Pod, error) logFunc func(w http.ResponseWriter, req *http.Request) runFunc func(podFullName string, uid types.UID, containerName string, cmd []string) ([]byte, error) containerVersionFunc func() (kubecontainer.Version, error) @@ -91,6 +92,10 @@ func (fk *fakeKubelet) GetPods() []*api.Pod { return fk.podsFunc() } +func (fk *fakeKubelet) GetRunningPods() ([]*api.Pod, error) { + return fk.runningPodsFunc() +} + func (fk *fakeKubelet) ServeLogs(w http.ResponseWriter, req *http.Request) { fk.logFunc(w, req) }