mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 15:58:37 +00:00
Merge pull request #491 from monnand/cadvisor-update-3
Retrieve machine spec from cAdvisor
This commit is contained in:
commit
f6bbcff93a
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user