mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-10-22 06:59:03 +00:00
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:
@@ -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)
|
||||
|
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package e2e_node
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// RetryFn represents a retryable test condition. It returns an error if the condition is not met
|
||||
// otherwise returns nil for success.
|
||||
type RetryFn func() error
|
||||
|
||||
// Retry retries the RetryFn for a maximum of maxWait time. The wait duration is waited between
|
||||
// retries. If the success condition is not met in maxWait time, the list of encountered errors
|
||||
// is returned. If successful returns an empty list.
|
||||
// Example:
|
||||
// Expect(Retry(time.Minute*1, time.Second*2, func() error {
|
||||
// if success {
|
||||
// return nil
|
||||
// } else {
|
||||
// return errors.New("Failed")
|
||||
// }
|
||||
// }).To(BeNil(), fmt.Sprintf("Failed"))
|
||||
func Retry(maxWait time.Duration, wait time.Duration, retry RetryFn) []error {
|
||||
errs := []error{}
|
||||
for start := time.Now(); time.Now().Before(start.Add(maxWait)); {
|
||||
if err := retry(); err != nil {
|
||||
errs = append(errs, err)
|
||||
} else {
|
||||
return []error{}
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
Reference in New Issue
Block a user