Supply volume fs metrics to server/stats/handler.go

* Metrics will not be expose until they are hooked up to a handler
* Metrics are not cached and expose a dos vector, this must be fixed before release or the stats should not be exposed through an api endpoint
This commit is contained in:
Phillip Wittrock
2016-01-14 11:19:26 -08:00
parent 6b20879a7f
commit 3de94cd23c
24 changed files with 2030 additions and 1130 deletions

View File

@@ -20,16 +20,18 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"io/ioutil"
"k8s.io/kubernetes/pkg/api"
client "k8s.io/kubernetes/pkg/client/unversioned"
"k8s.io/kubernetes/pkg/kubelet/server/stats"
"github.com/davecgh/go-spew/spew"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Kubelet", func() {
@@ -66,20 +68,16 @@ var _ = Describe("Kubelet", func() {
})
It("it should print the output to logs", func() {
errs := Retry(time.Minute, time.Second*4, func() error {
Eventually(func() string {
rc, err := cl.Pods(api.NamespaceDefault).GetLogs("busybox", &api.PodLogOptions{}).Stream()
if err != nil {
return err
return ""
}
defer rc.Close()
buf := new(bytes.Buffer)
buf.ReadFrom(rc)
if buf.String() != "'Hello World'\n" {
return fmt.Errorf("Expected %s to match 'Hello World'", buf.String())
}
return nil
})
Expect(errs).To(BeEmpty(), fmt.Sprintf("Failed to get Logs"))
return buf.String()
}, time.Second*30, time.Second*4).Should(Equal("'Hello World'\n"))
})
It("it should be possible to delete", func() {
@@ -101,9 +99,16 @@ var _ = Describe("Kubelet", func() {
createPod(cl, podName, []api.Container{
{
Image: "gcr.io/google_containers/busybox",
Command: []string{"sh", "-c", "echo 'Hello World' | tee ~/file | tee -a ~/file | tee /test-empty-dir | sleep 60"},
Command: []string{"sh", "-c", "echo 'Hello World' | tee ~/file | tee /test-empty-dir-mnt | sleep 60"},
Name: podName + containerSuffix,
VolumeMounts: []api.VolumeMount{
{MountPath: "/test-empty-dir-mnt", Name: "test-empty-dir"},
},
},
}, []api.Volume{
// TODO: Test secret volumes
// TODO: Test hostpath volumes
{Name: "test-empty-dir", VolumeSource: api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{}}},
})
}
@@ -113,6 +118,7 @@ var _ = Describe("Kubelet", func() {
Context("when querying /stats/summary", func() {
It("it should report resource usage through the stats api", func() {
By("Returning stats summary")
resp, err := http.Get(*kubeletAddress + "/stats/summary")
now := time.Now()
Expect(err).To(BeNil(), fmt.Sprintf("Failed to get /stats/summary"))
@@ -124,57 +130,84 @@ var _ = Describe("Kubelet", func() {
err = decoder.Decode(&summary)
Expect(err).To(BeNil(), fmt.Sprintf("Failed to parse /stats/summary to go struct: %+v", resp))
// Verify Misc Stats
By("Having the correct time")
Expect(summary.Time.Time).To(BeTemporally("~", now, 20*time.Second))
// Verify Node Stats are present
By("Having resources for node")
Expect(summary.Node.NodeName).To(Equal(*nodeName))
Expect(summary.Node.CPU.UsageCoreNanoSeconds).NotTo(BeZero())
Expect(summary.Node.Memory.UsageBytes).NotTo(BeZero())
Expect(summary.Node.Memory.WorkingSetBytes).NotTo(BeZero())
Expect(summary.Node.Fs.UsedBytes).NotTo(BeZero())
Expect(summary.Node.Fs.CapacityBytes).NotTo(BeZero())
Expect(summary.Node.Fs.AvailableBytes).NotTo(BeZero())
Expect(summary.Node.CPU.UsageCoreNanoSeconds).NotTo(BeNil())
Expect(*summary.Node.CPU.UsageCoreNanoSeconds).NotTo(BeZero())
Expect(summary.Node.Memory.UsageBytes).NotTo(BeNil())
Expect(*summary.Node.Memory.UsageBytes).NotTo(BeZero())
Expect(summary.Node.Memory.WorkingSetBytes).NotTo(BeNil())
Expect(*summary.Node.Memory.WorkingSetBytes).NotTo(BeZero())
Expect(summary.Node.Fs.AvailableBytes).NotTo(BeNil())
Expect(*summary.Node.Fs.AvailableBytes).NotTo(BeZero())
Expect(summary.Node.Fs.CapacityBytes).NotTo(BeNil())
Expect(*summary.Node.Fs.CapacityBytes).NotTo(BeZero())
Expect(summary.Node.Fs.UsedBytes).NotTo(BeNil())
Expect(*summary.Node.Fs.UsedBytes).NotTo(BeZero())
By("Having resources for kubelet and runtime system containers")
sysContainers := map[string]stats.ContainerStats{}
sysContainersList := []string{}
for _, container := range summary.Node.SystemContainers {
sysContainers[container.Name] = container
sysContainersList = append(sysContainersList, container.Name)
Expect(container.CPU.UsageCoreNanoSeconds).NotTo(BeZero())
// TODO: Test Network
Expect(container.Memory.UsageBytes).NotTo(BeZero())
Expect(container.Memory.WorkingSetBytes).NotTo(BeZero())
Expect(container.Rootfs.CapacityBytes).NotTo(BeZero())
Expect(container.Rootfs.AvailableBytes).NotTo(BeZero())
Expect(container.Logs.CapacityBytes).NotTo(BeZero())
Expect(container.Logs.AvailableBytes).NotTo(BeZero())
ExpectContainerStatsNotEmpty(&container)
}
Expect(sysContainersList).To(ConsistOf("kubelet", "runtime"))
// Verify Pods Stats are present
podsList := []string{}
By("Having resources for pods")
for _, pod := range summary.Pods {
if !strings.HasPrefix(pod.PodRef.Name, statsPrefix) {
// Ignore pods created outside this test
continue
}
// TODO: Test network
podsList = append(podsList, pod.PodRef.Name)
Expect(pod.Containers).To(HaveLen(1))
container := pod.Containers[0]
Expect(container.Name).To(Equal(pod.PodRef.Name + containerSuffix))
Expect(container.CPU.UsageCoreNanoSeconds).NotTo(BeZero())
Expect(container.Memory.UsageBytes).NotTo(BeZero())
Expect(container.Memory.WorkingSetBytes).NotTo(BeZero())
Expect(container.Rootfs.CapacityBytes).NotTo(BeZero())
Expect(container.Rootfs.AvailableBytes).NotTo(BeZero())
Expect(*container.Rootfs.UsedBytes).NotTo(BeZero(), contents)
Expect(container.Logs.CapacityBytes).NotTo(BeZero())
Expect(container.Logs.AvailableBytes).NotTo(BeZero())
Expect(*container.Logs.UsedBytes).NotTo(BeZero(), contents)
ExpectContainerStatsNotEmpty(&container)
// emptydir volume
volumeNames := []string{}
for _, vs := range pod.VolumeStats {
Expect(vs.CapacityBytes).NotTo(BeZero())
Expect(vs.AvailableBytes).NotTo(BeZero())
Expect(vs.UsedBytes).NotTo(BeZero())
if strings.HasPrefix(vs.Name, "default-token-") {
volumeNames = append(volumeNames, "default-token-")
} else {
volumeNames = append(volumeNames, vs.Name)
}
}
Expect(volumeNames).To(ConsistOf("default-token-", "test-empty-dir"))
// fs usage (not for system containers)
Expect(container.Rootfs).NotTo(BeNil(), spew.Sdump(container))
Expect(container.Rootfs.AvailableBytes).NotTo(BeNil(), spew.Sdump(container))
Expect(*container.Rootfs.AvailableBytes).NotTo(BeZero(), spew.Sdump(container))
Expect(container.Rootfs.CapacityBytes).NotTo(BeNil(), spew.Sdump(container))
Expect(*container.Rootfs.CapacityBytes).NotTo(BeZero(), spew.Sdump(container))
Expect(container.Rootfs.UsedBytes).NotTo(BeNil(), spew.Sdump(container))
Expect(*container.Rootfs.UsedBytes).NotTo(BeZero(), spew.Sdump(container))
Expect(container.Logs).NotTo(BeNil(), spew.Sdump(container))
Expect(container.Logs.AvailableBytes).NotTo(BeNil(), spew.Sdump(container))
Expect(*container.Logs.AvailableBytes).NotTo(BeZero(), spew.Sdump(container))
Expect(container.Logs.CapacityBytes).NotTo(BeNil(), spew.Sdump(container))
Expect(*container.Logs.CapacityBytes).NotTo(BeZero(), spew.Sdump(container))
Expect(container.Logs.UsedBytes).NotTo(BeNil(), spew.Sdump(container))
Expect(*container.Logs.UsedBytes).NotTo(BeZero(), spew.Sdump(container))
}
Expect(podsList).To(ConsistOf(podNames))
})
@@ -189,11 +222,25 @@ var _ = Describe("Kubelet", func() {
})
})
func ExpectContainerStatsNotEmpty(container *stats.ContainerStats) {
// TODO: Test Network
Expect(container.CPU).NotTo(BeNil(), spew.Sdump(container))
Expect(container.CPU.UsageCoreNanoSeconds).NotTo(BeNil(), spew.Sdump(container))
Expect(*container.CPU.UsageCoreNanoSeconds).NotTo(BeZero(), spew.Sdump(container))
Expect(container.Memory).NotTo(BeNil(), spew.Sdump(container))
Expect(container.Memory.UsageBytes).NotTo(BeNil(), spew.Sdump(container))
Expect(*container.Memory.UsageBytes).NotTo(BeZero(), spew.Sdump(container))
Expect(container.Memory.WorkingSetBytes).NotTo(BeNil(), spew.Sdump(container))
Expect(*container.Memory.WorkingSetBytes).NotTo(BeZero(), spew.Sdump(container))
}
const (
containerSuffix = "-c"
)
func createPod(cl *client.Client, podName string, containers []api.Container) {
func createPod(cl *client.Client, podName string, containers []api.Container, volumes []api.Volume) {
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: podName,
@@ -205,6 +252,7 @@ func createPod(cl *client.Client, podName string, containers []api.Container) {
// Don't restart the Pod since it is expected to exit
RestartPolicy: api.RestartPolicyNever,
Containers: containers,
Volumes: volumes,
},
}
_, err := cl.Pods(api.NamespaceDefault).Create(pod)