Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update kubelet package to use most recent cAdvisor's code #480

Merged
merged 2 commits into from Jul 16, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 0 additions & 14 deletions pkg/api/types.go
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
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) GetContainerInfo(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
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)
GetContainerInfo(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.GetContainerInfo(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
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) GetContainerInfo(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