Kubelet: Add /stats/container endpoint.

This endpoint exposes container stats for all raw containers on the
machine. The addition is backwards compatible.
This commit is contained in:
Victor Marmol
2015-04-23 10:14:08 -07:00
parent e2f37f81a9
commit c29d328c55
4 changed files with 163 additions and 48 deletions

View File

@@ -17,6 +17,7 @@ limitations under the License.
package kubelet
import (
"bytes"
"encoding/json"
"fmt"
"io"
@@ -44,7 +45,7 @@ type fakeKubelet struct {
podByNameFunc func(namespace, name string) (*api.Pod, bool)
statusFunc func(name string) (api.PodStatus, error)
containerInfoFunc func(podFullName string, uid types.UID, containerName string, req *cadvisorApi.ContainerInfoRequest) (*cadvisorApi.ContainerInfo, error)
rootInfoFunc func(query *cadvisorApi.ContainerInfoRequest) (*cadvisorApi.ContainerInfo, error)
rawInfoFunc func(query *cadvisorApi.ContainerInfoRequest) (map[string]*cadvisorApi.ContainerInfo, error)
machineInfoFunc func() (*cadvisorApi.MachineInfo, error)
podsFunc func() []*api.Pod
logFunc func(w http.ResponseWriter, req *http.Request)
@@ -69,8 +70,8 @@ func (fk *fakeKubelet) GetContainerInfo(podFullName string, uid types.UID, conta
return fk.containerInfoFunc(podFullName, uid, containerName, req)
}
func (fk *fakeKubelet) GetRootInfo(req *cadvisorApi.ContainerInfoRequest) (*cadvisorApi.ContainerInfo, error) {
return fk.rootInfoFunc(req)
func (fk *fakeKubelet) GetRawContainerInfo(containerName string, req *cadvisorApi.ContainerInfoRequest, subcontainers bool) (map[string]*cadvisorApi.ContainerInfo, error) {
return fk.rawInfoFunc(req)
}
func (fk *fakeKubelet) GetContainerRuntimeVersion() (kubecontainer.Version, error) {
@@ -269,9 +270,15 @@ func TestContainerNotFound(t *testing.T) {
func TestRootInfo(t *testing.T) {
fw := newServerTest()
expectedInfo := &cadvisorApi.ContainerInfo{}
fw.fakeKubelet.rootInfoFunc = func(req *cadvisorApi.ContainerInfoRequest) (*cadvisorApi.ContainerInfo, error) {
return expectedInfo, nil
expectedInfo := &cadvisorApi.ContainerInfo{
ContainerReference: cadvisorApi.ContainerReference{
Name: "/",
},
}
fw.fakeKubelet.rawInfoFunc = func(req *cadvisorApi.ContainerInfoRequest) (map[string]*cadvisorApi.ContainerInfo, error) {
return map[string]*cadvisorApi.ContainerInfo{
expectedInfo.Name: expectedInfo,
}, nil
}
resp, err := http.Get(fw.testHTTPServer.URL + "/stats")
@@ -285,7 +292,52 @@ func TestRootInfo(t *testing.T) {
t.Fatalf("received invalid json data: %v", err)
}
if !receivedInfo.Eq(expectedInfo) {
t.Errorf("received wrong data: %#v", receivedInfo)
t.Errorf("received wrong data: %#v, expected %#v", receivedInfo, expectedInfo)
}
}
func TestSubcontainerContainerInfo(t *testing.T) {
fw := newServerTest()
const kubeletContainer = "/kubelet"
const kubeletSubContainer = "/kubelet/sub"
expectedInfo := map[string]*cadvisorApi.ContainerInfo{
kubeletContainer: {
ContainerReference: cadvisorApi.ContainerReference{
Name: kubeletContainer,
},
},
kubeletSubContainer: {
ContainerReference: cadvisorApi.ContainerReference{
Name: kubeletSubContainer,
},
},
}
fw.fakeKubelet.rawInfoFunc = func(req *cadvisorApi.ContainerInfoRequest) (map[string]*cadvisorApi.ContainerInfo, error) {
return expectedInfo, nil
}
request := fmt.Sprintf("{\"containerName\":%q, \"subcontainers\": true}", kubeletContainer)
resp, err := http.Post(fw.testHTTPServer.URL+"/stats/container", "application/json", bytes.NewBuffer([]byte(request)))
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedInfo map[string]*cadvisorApi.ContainerInfo
err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
if err != nil {
t.Fatalf("Received invalid json data: %v", err)
}
if len(receivedInfo) != len(expectedInfo) {
t.Errorf("Received wrong data: %#v, expected %#v", receivedInfo, expectedInfo)
}
for _, containerName := range []string{kubeletContainer, kubeletSubContainer} {
if _, ok := receivedInfo[containerName]; !ok {
t.Errorf("Expected container %q to be present in result: %#v", containerName, receivedInfo)
}
if !receivedInfo[containerName].Eq(expectedInfo[containerName]) {
t.Errorf("Invalid result for %q: Expected %#v, received %#v", containerName, expectedInfo[containerName], receivedInfo[containerName])
}
}
}
@@ -426,29 +478,6 @@ func TestServeRunInContainerWithUID(t *testing.T) {
}
}
// TODO: fix me when pod level stats get implemented
func TestPodsInfo(t *testing.T) {
fw := newServerTest()
resp, err := http.Get(fw.testHTTPServer.URL + "/stats/goodpod")
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusInternalServerError {
t.Errorf("expected status code %d, got %d", http.StatusInternalServerError, resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// copying the response body did not work
t.Fatalf("Cannot copy resp: %#v", err)
}
result := string(body)
if !strings.Contains(result, "pod level status currently unimplemented") {
t.Errorf("expected body contains pod level status currently unimplemented, got %s", result)
}
}
func TestHealthCheck(t *testing.T) {
fw := newServerTest()
fw.fakeKubelet.containerVersionFunc = func() (kubecontainer.Version, error) {