Skip to content

Commit

Permalink
Update to latest cAdvisor and use data structures directly from cAdvisor
Browse files Browse the repository at this point in the history
  • Loading branch information
monnand committed Jul 15, 2014
1 parent 02ee27c commit 8c573ee
Show file tree
Hide file tree
Showing 36 changed files with 2,454 additions and 893 deletions.
14 changes: 0 additions & 14 deletions pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,6 @@ type Container struct {
LivenessProbe *LivenessProbe `yaml:"livenessProbe,omitempty" json:"livenessProbe,omitempty"`
}

// Percentile represents a pair which contains a percentage from 0 to 100 and
// its corresponding value.
type Percentile struct {
Percentage int `json:"percentage,omitempty"`
Value uint64 `json:"value,omitempty"`
}

// ContainerStats represents statistical information of a container
type ContainerStats struct {
CpuUsagePercentiles []Percentile `json:"cpu_usage_percentiles,omitempty"`
MemoryUsagePercentiles []Percentile `json:"memory_usage_percentiles,omitempty"`
MaxMemoryUsage uint64 `json:"max_memory_usage,omitempty"`
}

// Event is the representation of an event logged to etcd backends
type Event struct {
Event string `json:"event,omitempty"`
Expand Down
49 changes: 17 additions & 32 deletions pkg/kubelet/kubelet.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const defaultChanSize = 1024

// CadvisorInterface is an abstract interface for testability. It abstracts the interface of "github.com/google/cadvisor/client".Client.
type CadvisorInterface interface {
ContainerInfo(name string) (*info.ContainerInfo, error)
ContainerInfo(name string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
MachineInfo() (*info.MachineInfo, error)
}

Expand Down Expand Up @@ -828,57 +828,42 @@ func (kl *Kubelet) getDockerIDFromPodIDAndContainerName(podID, containerName str
return "", errors.New("couldn't find container")
}

func getCadvisorContainerInfoRequest(req *info.ContainerInfoRequest) *info.ContainerInfoRequest {
ret := &info.ContainerInfoRequest{
NumStats: req.NumStats,
CpuUsagePercentiles: req.CpuUsagePercentiles,
MemoryUsagePercentages: req.MemoryUsagePercentages,
}
return ret
}

// This method takes a container's absolute path and returns the stats for the
// container. The container's absolute path refers to its hierarchy in the
// cgroup file system. e.g. The root container, which represents the whole
// machine, has path "/"; all docker containers have path "/docker/<docker id>"
func (kl *Kubelet) statsFromContainerPath(containerPath string) (*api.ContainerStats, error) {
info, err := kl.CadvisorClient.ContainerInfo(containerPath)

func (kl *Kubelet) statsFromContainerPath(containerPath string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
cinfo, err := kl.CadvisorClient.ContainerInfo(containerPath, getCadvisorContainerInfoRequest(req))
if err != nil {
return nil, err
}
// When the stats data for the container is not available yet.
if info.StatsPercentiles == nil {
return nil, nil
}

ret := new(api.ContainerStats)
ret.MaxMemoryUsage = info.StatsPercentiles.MaxMemoryUsage
if len(info.StatsPercentiles.CpuUsagePercentiles) > 0 {
percentiles := make([]api.Percentile, len(info.StatsPercentiles.CpuUsagePercentiles))
for i, p := range info.StatsPercentiles.CpuUsagePercentiles {
percentiles[i].Percentage = p.Percentage
percentiles[i].Value = p.Value
}
ret.CpuUsagePercentiles = percentiles
}
if len(info.StatsPercentiles.MemoryUsagePercentiles) > 0 {
percentiles := make([]api.Percentile, len(info.StatsPercentiles.MemoryUsagePercentiles))
for i, p := range info.StatsPercentiles.MemoryUsagePercentiles {
percentiles[i].Percentage = p.Percentage
percentiles[i].Value = p.Value
}
ret.MemoryUsagePercentiles = percentiles
}
return ret, nil
return cinfo, nil
}

// GetContainerStats returns stats (from Cadvisor) for a container.
func (kl *Kubelet) GetContainerStats(podID, containerName string) (*api.ContainerStats, error) {
func (kl *Kubelet) GetContainerStats(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
if kl.CadvisorClient == nil {
return nil, nil
}
dockerID, err := kl.getDockerIDFromPodIDAndContainerName(podID, containerName)
if err != nil || len(dockerID) == 0 {
return nil, err
}
return kl.statsFromContainerPath(fmt.Sprintf("/docker/%s", string(dockerID)))
return kl.statsFromContainerPath(fmt.Sprintf("/docker/%s", string(dockerID)), req)
}

// GetMachineStats returns stats (from Cadvisor) of current machine.
func (kl *Kubelet) GetMachineStats() (*api.ContainerStats, error) {
return kl.statsFromContainerPath("/")
func (kl *Kubelet) GetMachineStats(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return kl.statsFromContainerPath("/", req)
}

func (kl *Kubelet) healthy(container api.Container, dockerContainer *docker.APIContainers) (HealthCheckStatus, error) {
Expand Down
19 changes: 14 additions & 5 deletions pkg/kubelet/kubelet_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
Expand All @@ -28,6 +29,7 @@ import (

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/google/cadvisor/info"
"gopkg.in/v1/yaml"
)

Expand All @@ -41,8 +43,8 @@ type KubeletServer struct {
// kubeletInterface contains all the kubelet methods required by the server.
// For testablitiy.
type kubeletInterface interface {
GetContainerStats(podID, containerName string) (*api.ContainerStats, error)
GetMachineStats() (*api.ContainerStats, error)
GetContainerStats(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
GetMachineStats(req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
GetPodInfo(name string) (api.PodInfo, error)
}

Expand Down Expand Up @@ -113,18 +115,25 @@ func (s *KubeletServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func (s *KubeletServer) serveStats(w http.ResponseWriter, req *http.Request) {
// /stats/<podid>/<containerName>
components := strings.Split(strings.TrimPrefix(path.Clean(req.URL.Path), "/"), "/")
var stats *api.ContainerStats
var stats *info.ContainerInfo
var err error
var query info.ContainerInfoRequest
decoder := json.NewDecoder(req.Body)
err = decoder.Decode(&query)
if err != nil && err != io.EOF {
s.error(w, err)
return
}
switch len(components) {
case 1:
// Machine stats
stats, err = s.Kubelet.GetMachineStats()
stats, err = s.Kubelet.GetMachineStats(&query)
case 2:
// pod stats
// TODO(monnand) Implement this
errors.New("pod level status currently unimplemented")
case 3:
stats, err = s.Kubelet.GetContainerStats(components[1], components[2])
stats, err = s.Kubelet.GetContainerStats(components[1], components[2], &query)
default:
http.Error(w, "unknown resource.", http.StatusNotFound)
return
Expand Down
69 changes: 37 additions & 32 deletions pkg/kubelet/kubelet_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,25 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/fsouza/go-dockerclient"
"github.com/google/cadvisor/info"
)

type fakeKubelet struct {
infoFunc func(name string) (api.PodInfo, error)
containerStatsFunc func(podID, containerName string) (*api.ContainerStats, error)
machineStatsFunc func() (*api.ContainerStats, error)
containerStatsFunc func(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
machineStatsFunc func(query *info.ContainerInfoRequest) (*info.ContainerInfo, error)
}

func (fk *fakeKubelet) GetPodInfo(name string) (api.PodInfo, error) {
return fk.infoFunc(name)
}

func (fk *fakeKubelet) GetContainerStats(podID, containerName string) (*api.ContainerStats, error) {
return fk.containerStatsFunc(podID, containerName)
func (fk *fakeKubelet) GetContainerStats(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return fk.containerStatsFunc(podID, containerName, req)
}

func (fk *fakeKubelet) GetMachineStats() (*api.ContainerStats, error) {
return fk.machineStatsFunc()
func (fk *fakeKubelet) GetMachineStats(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return fk.machineStatsFunc(req)
}

type serverTestFramework struct {
Expand Down Expand Up @@ -148,22 +149,24 @@ func TestPodInfo(t *testing.T) {

func TestContainerStats(t *testing.T) {
fw := makeServerTest()
expectedStats := &api.ContainerStats{
MaxMemoryUsage: 1024001,
CpuUsagePercentiles: []api.Percentile{
{50, 150},
{80, 180},
{90, 190},
},
MemoryUsagePercentiles: []api.Percentile{
{50, 150},
{80, 180},
{90, 190},
expectedStats := &info.ContainerInfo{
StatsPercentiles: &info.ContainerStatsPercentiles{
MaxMemoryUsage: 1024001,
CpuUsagePercentiles: []info.Percentile{
{50, 150},
{80, 180},
{90, 190},
},
MemoryUsagePercentiles: []info.Percentile{
{50, 150},
{80, 180},
{90, 190},
},
},
}
expectedPodID := "somepod"
expectedContainerName := "goodcontainer"
fw.fakeKubelet.containerStatsFunc = func(podID, containerName string) (*api.ContainerStats, error) {
fw.fakeKubelet.containerStatsFunc = 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)
}
Expand All @@ -175,7 +178,7 @@ func TestContainerStats(t *testing.T) {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedStats api.ContainerStats
var receivedStats info.ContainerInfo
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&receivedStats)
if err != nil {
Expand All @@ -188,20 +191,22 @@ func TestContainerStats(t *testing.T) {

func TestMachineStats(t *testing.T) {
fw := makeServerTest()
expectedStats := &api.ContainerStats{
MaxMemoryUsage: 1024001,
CpuUsagePercentiles: []api.Percentile{
{50, 150},
{80, 180},
{90, 190},
},
MemoryUsagePercentiles: []api.Percentile{
{50, 150},
{80, 180},
{90, 190},
expectedStats := &info.ContainerInfo{
StatsPercentiles: &info.ContainerStatsPercentiles{
MaxMemoryUsage: 1024001,
CpuUsagePercentiles: []info.Percentile{
{50, 150},
{80, 180},
{90, 190},
},
MemoryUsagePercentiles: []info.Percentile{
{50, 150},
{80, 180},
{90, 190},
},
},
}
fw.fakeKubelet.machineStatsFunc = func() (*api.ContainerStats, error) {
fw.fakeKubelet.machineStatsFunc = func(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return expectedStats, nil
}

Expand All @@ -210,7 +215,7 @@ func TestMachineStats(t *testing.T) {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
var receivedStats api.ContainerStats
var receivedStats info.ContainerInfo
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&receivedStats)
if err != nil {
Expand Down
Loading

0 comments on commit 8c573ee

Please sign in to comment.