Merge pull request #491 from monnand/cadvisor-update-3

Retrieve machine spec from cAdvisor
This commit is contained in:
Daniel Smith 2014-07-17 16:01:52 -07:00
commit f6bbcff93a
6 changed files with 158 additions and 47 deletions

View File

@ -29,8 +29,12 @@ import (
)
type ContainerInfoGetter interface {
// GetContainerInfo returns information about a container.
GetContainerInfo(host, podID, containerID string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
GetMachineInfo(host string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
// GetRootInfo returns information about the root container on a machine.
GetRootInfo(host string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
// GetMachineInfo returns the machine's information like number of cores, memory capacity.
GetMachineInfo(host string) (*info.MachineInfo, error)
}
type HTTPContainerInfoGetter struct {
@ -38,6 +42,35 @@ type HTTPContainerInfoGetter struct {
Port int
}
func (self *HTTPContainerInfoGetter) GetMachineInfo(host string) (*info.MachineInfo, error) {
request, err := http.NewRequest(
"GET",
fmt.Sprintf("http://%v/spec",
net.JoinHostPort(host, strconv.Itoa(self.Port)),
),
nil,
)
if err != nil {
return nil, err
}
response, err := self.Client.Do(request)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("trying to get machine spec from %v; received status %v",
host, response.Status)
}
var minfo info.MachineInfo
err = json.NewDecoder(response.Body).Decode(&minfo)
if err != nil {
return nil, err
}
return &minfo, nil
}
func (self *HTTPContainerInfoGetter) getContainerInfo(host, path string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
var body io.Reader
if req != nil {
@ -69,9 +102,8 @@ func (self *HTTPContainerInfoGetter) getContainerInfo(host, path string, req *in
return nil, fmt.Errorf("trying to get info for %v from %v; received status %v",
path, host, response.Status)
}
decoder := json.NewDecoder(response.Body)
var cinfo info.ContainerInfo
err = decoder.Decode(&cinfo)
err = json.NewDecoder(response.Body).Decode(&cinfo)
if err != nil {
return nil, err
}
@ -86,6 +118,6 @@ func (self *HTTPContainerInfoGetter) GetContainerInfo(host, podID, containerID s
)
}
func (self *HTTPContainerInfoGetter) GetMachineInfo(host string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
func (self *HTTPContainerInfoGetter) GetRootInfo(host string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return self.getContainerInfo(host, "", req)
}

View File

@ -53,9 +53,8 @@ func testHTTPContainerInfoGetter(
expectedPath, r.URL.Path)
}
decoder := json.NewDecoder(r.Body)
var receivedReq info.ContainerInfoRequest
err := decoder.Decode(&receivedReq)
err := json.NewDecoder(r.Body).Decode(&receivedReq)
if err != nil {
t.Fatal(err)
}
@ -67,8 +66,7 @@ func testHTTPContainerInfoGetter(
if !reflect.DeepEqual(expectedReq, &receivedReq) {
t.Errorf("received wrong request")
}
encoder := json.NewEncoder(w)
err = encoder.Encode(cinfo)
err = json.NewEncoder(w).Encode(cinfo)
if err != nil {
t.Fatal(err)
}
@ -93,7 +91,7 @@ func testHTTPContainerInfoGetter(
if len(podID) > 0 && len(containerID) > 0 {
receivedContainerInfo, err = containerInfoGetter.GetContainerInfo(parts[0], podID, containerID, req)
} else {
receivedContainerInfo, err = containerInfoGetter.GetMachineInfo(parts[0], req)
receivedContainerInfo, err = containerInfoGetter.GetRootInfo(parts[0], req)
}
if status == 0 || status == http.StatusOK {
if err != nil {
@ -125,7 +123,7 @@ func TestHTTPContainerInfoGetterGetContainerInfoSuccessfully(t *testing.T) {
testHTTPContainerInfoGetter(req, cinfo, "somePodID", "containerNameInK8S", 0, t)
}
func TestHTTPContainerInfoGetterGetMachineInfoSuccessfully(t *testing.T) {
func TestHTTPContainerInfoGetterGetRootInfoSuccessfully(t *testing.T) {
req := &info.ContainerInfoRequest{
NumStats: 10,
NumSamples: 10,
@ -155,7 +153,7 @@ func TestHTTPContainerInfoGetterGetContainerInfoWithError(t *testing.T) {
testHTTPContainerInfoGetter(req, cinfo, "somePodID", "containerNameInK8S", http.StatusNotFound, t)
}
func TestHTTPContainerInfoGetterGetMachineInfoWithError(t *testing.T) {
func TestHTTPContainerInfoGetterGetRootInfoWithError(t *testing.T) {
req := &info.ContainerInfoRequest{
NumStats: 10,
NumSamples: 10,
@ -169,3 +167,39 @@ func TestHTTPContainerInfoGetterGetMachineInfoWithError(t *testing.T) {
)
testHTTPContainerInfoGetter(req, cinfo, "", "", http.StatusNotFound, t)
}
func TestHTTPGetMachineInfo(t *testing.T) {
mspec := &info.MachineInfo{
NumCores: 4,
MemoryCapacity: 2048,
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
err := json.NewEncoder(w).Encode(mspec)
if err != nil {
t.Fatal(err)
}
}))
hostURL, err := url.Parse(ts.URL)
if err != nil {
t.Fatal(err)
}
parts := strings.Split(hostURL.Host, ":")
port, err := strconv.Atoi(parts[1])
if err != nil {
t.Fatal(err)
}
containerInfoGetter := &HTTPContainerInfoGetter{
Client: http.DefaultClient,
Port: port,
}
received, err := containerInfoGetter.GetMachineInfo(parts[0])
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(received, mspec) {
t.Errorf("received wrong machine spec")
}
}

View File

@ -769,11 +769,15 @@ func (kl *Kubelet) GetContainerInfo(manifestID, containerName string, req *info.
return kl.statsFromContainerPath(fmt.Sprintf("/docker/%s", dockerContainer.ID), req)
}
// GetMachineStats returns stats (from Cadvisor) of current machine.
func (kl *Kubelet) GetMachineStats(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
// GetRootInfo returns stats (from Cadvisor) of current machine (root container).
func (kl *Kubelet) GetRootInfo(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return kl.statsFromContainerPath("/", req)
}
func (kl *Kubelet) GetMachineInfo() (*info.MachineInfo, error) {
return kl.CadvisorClient.MachineInfo()
}
func (kl *Kubelet) healthy(container api.Container, dockerContainer *docker.APIContainers) (health.Status, error) {
// Give the container 60 seconds to start up.
if container.LivenessProbe == nil {

View File

@ -948,7 +948,7 @@ func areSamePercentiles(
}
}
func TestGetContainerStats(t *testing.T) {
func TestGetContainerInfo(t *testing.T) {
containerID := "ab2cdf"
containerPath := fmt.Sprintf("/docker/%v", containerID)
containerInfo := &info.ContainerInfo{
@ -1001,7 +1001,7 @@ func TestGetContainerStats(t *testing.T) {
mockCadvisor.AssertExpectations(t)
}
func TestGetMachineStats(t *testing.T) {
func TestGetRooInfo(t *testing.T) {
containerPath := "/"
containerInfo := &info.ContainerInfo{
ContainerReference: info.ContainerReference{
@ -1032,7 +1032,7 @@ func TestGetMachineStats(t *testing.T) {
}
// If the container name is an empty string, then it means the root container.
stats, err := kubelet.GetMachineStats(req)
stats, err := kubelet.GetRootInfo(req)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -1044,7 +1044,7 @@ func TestGetMachineStats(t *testing.T) {
mockCadvisor.AssertExpectations(t)
}
func TestGetContainerStatsWithoutCadvisor(t *testing.T) {
func TestGetContainerInfoWithoutCadvisor(t *testing.T) {
kubelet, _, fakeDocker := makeTestKubelet(t)
fakeDocker.containerList = []docker.APIContainers{
{
@ -1071,7 +1071,7 @@ func TestGetContainerStatsWithoutCadvisor(t *testing.T) {
}
}
func TestGetContainerStatsWhenCadvisorFailed(t *testing.T) {
func TestGetContainerInfoWhenCadvisorFailed(t *testing.T) {
containerID := "ab2cdf"
containerPath := fmt.Sprintf("/docker/%v", containerID)
@ -1107,7 +1107,7 @@ func TestGetContainerStatsWhenCadvisorFailed(t *testing.T) {
mockCadvisor.AssertExpectations(t)
}
func TestGetContainerStatsOnNonExistContainer(t *testing.T) {
func TestGetContainerInfoOnNonExistContainer(t *testing.T) {
mockCadvisor := &mockCadvisorClient{}
kubelet, _, fakeDocker := makeTestKubelet(t)

View File

@ -44,7 +44,8 @@ type Server struct {
// For testablitiy.
type kubeletInterface interface {
GetContainerInfo(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
GetMachineStats(req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
GetRootInfo(req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
GetMachineInfo() (*info.MachineInfo, error)
GetPodInfo(name string) (api.PodInfo, error)
}
@ -108,6 +109,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
w.Write(data)
case strings.HasPrefix(u.Path, "/stats"):
s.serveStats(w, req)
case strings.HasPrefix(u.Path, "/spec"):
info, err := s.Kubelet.GetMachineInfo()
if err != nil {
s.error(w, err)
return
}
data, err := json.Marshal(info)
if err != nil {
s.error(w, err)
return
}
w.Header().Add("Content-type", "application/json")
w.Write(data)
default:
s.DelegateHandler.ServeHTTP(w, req)
}
@ -119,8 +133,7 @@ func (s *Server) serveStats(w http.ResponseWriter, req *http.Request) {
var stats *info.ContainerInfo
var err error
var query info.ContainerInfoRequest
decoder := json.NewDecoder(req.Body)
err = decoder.Decode(&query)
err = json.NewDecoder(req.Body).Decode(&query)
if err != nil && err != io.EOF {
s.error(w, err)
return
@ -128,7 +141,7 @@ func (s *Server) serveStats(w http.ResponseWriter, req *http.Request) {
switch len(components) {
case 1:
// Machine stats
stats, err = s.Kubelet.GetMachineStats(&query)
stats, err = s.Kubelet.GetRootInfo(&query)
case 2:
// pod stats
// TODO(monnand) Implement this

View File

@ -33,9 +33,10 @@ import (
)
type fakeKubelet struct {
infoFunc func(name string) (api.PodInfo, error)
containerStatsFunc func(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
machineStatsFunc func(query *info.ContainerInfoRequest) (*info.ContainerInfo, error)
infoFunc func(name string) (api.PodInfo, error)
containerInfoFunc func(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
rootInfoFunc func(query *info.ContainerInfoRequest) (*info.ContainerInfo, error)
machineInfoFunc func() (*info.MachineInfo, error)
}
func (fk *fakeKubelet) GetPodInfo(name string) (api.PodInfo, error) {
@ -43,11 +44,15 @@ func (fk *fakeKubelet) GetPodInfo(name string) (api.PodInfo, error) {
}
func (fk *fakeKubelet) GetContainerInfo(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return fk.containerStatsFunc(podID, containerName, req)
return fk.containerInfoFunc(podID, containerName, req)
}
func (fk *fakeKubelet) GetMachineStats(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return fk.machineStatsFunc(req)
func (fk *fakeKubelet) GetRootInfo(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return fk.rootInfoFunc(req)
}
func (fk *fakeKubelet) GetMachineInfo() (*info.MachineInfo, error) {
return fk.machineInfoFunc()
}
type serverTestFramework struct {
@ -147,9 +152,9 @@ func TestPodInfo(t *testing.T) {
}
}
func TestContainerStats(t *testing.T) {
func TestContainerInfo(t *testing.T) {
fw := makeServerTest()
expectedStats := &info.ContainerInfo{
expectedInfo := &info.ContainerInfo{
StatsPercentiles: &info.ContainerStatsPercentiles{
MaxMemoryUsage: 1024001,
CpuUsagePercentiles: []info.Percentile{
@ -166,11 +171,11 @@ func TestContainerStats(t *testing.T) {
}
expectedPodID := "somepod"
expectedContainerName := "goodcontainer"
fw.fakeKubelet.containerStatsFunc = func(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
fw.fakeKubelet.containerInfoFunc = func(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
if podID != expectedPodID || containerName != expectedContainerName {
return nil, fmt.Errorf("bad podID or containerName: podID=%v; containerName=%v", podID, containerName)
}
return expectedStats, nil
return expectedInfo, nil
}
resp, err := http.Get(fw.testHTTPServer.URL + fmt.Sprintf("/stats/%v/%v", expectedPodID, expectedContainerName))
@ -178,20 +183,19 @@ func TestContainerStats(t *testing.T) {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedStats info.ContainerInfo
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&receivedStats)
var receivedInfo info.ContainerInfo
err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
if err != nil {
t.Fatalf("received invalid json data: %v", err)
}
if !reflect.DeepEqual(&receivedStats, expectedStats) {
t.Errorf("received wrong data: %#v", receivedStats)
if !reflect.DeepEqual(&receivedInfo, expectedInfo) {
t.Errorf("received wrong data: %#v", receivedInfo)
}
}
func TestMachineStats(t *testing.T) {
func TestRootInfo(t *testing.T) {
fw := makeServerTest()
expectedStats := &info.ContainerInfo{
expectedInfo := &info.ContainerInfo{
StatsPercentiles: &info.ContainerStatsPercentiles{
MaxMemoryUsage: 1024001,
CpuUsagePercentiles: []info.Percentile{
@ -206,8 +210,8 @@ func TestMachineStats(t *testing.T) {
},
},
}
fw.fakeKubelet.machineStatsFunc = func(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return expectedStats, nil
fw.fakeKubelet.rootInfoFunc = func(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return expectedInfo, nil
}
resp, err := http.Get(fw.testHTTPServer.URL + "/stats")
@ -215,13 +219,37 @@ func TestMachineStats(t *testing.T) {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedStats info.ContainerInfo
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&receivedStats)
var receivedInfo info.ContainerInfo
err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
if err != nil {
t.Fatalf("received invalid json data: %v", err)
}
if !reflect.DeepEqual(&receivedStats, expectedStats) {
t.Errorf("received wrong data: %#v", receivedStats)
if !reflect.DeepEqual(&receivedInfo, expectedInfo) {
t.Errorf("received wrong data: %#v", receivedInfo)
}
}
func TestMachineInfo(t *testing.T) {
fw := makeServerTest()
expectedInfo := &info.MachineInfo{
NumCores: 4,
MemoryCapacity: 1024,
}
fw.fakeKubelet.machineInfoFunc = func() (*info.MachineInfo, error) {
return expectedInfo, nil
}
resp, err := http.Get(fw.testHTTPServer.URL + "/spec")
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedInfo info.MachineInfo
err = json.NewDecoder(resp.Body).Decode(&receivedInfo)
if err != nil {
t.Fatalf("received invalid json data: %v", err)
}
if !reflect.DeepEqual(&receivedInfo, expectedInfo) {
t.Errorf("received wrong data: %#v", receivedInfo)
}
}