Make summary timestamps more granular

pull/6/head
Tim St. Clair 2016-02-10 15:41:57 -08:00
parent 86a7a9534a
commit 7a54f94cf4
3 changed files with 45 additions and 16 deletions

View File

@ -100,10 +100,6 @@ func (sb *summaryBuilder) build() (*Summary, error) {
if !found { if !found {
return nil, fmt.Errorf("Missing stats for root container") return nil, fmt.Errorf("Missing stats for root container")
} }
cstat, found := sb.latestContainerStats(&rootInfo)
if !found {
return nil, fmt.Errorf("Missing stats for root container")
}
rootStats := sb.containerInfoV2ToStats("", &rootInfo) rootStats := sb.containerInfoV2ToStats("", &rootInfo)
nodeStats := NodeStats{ nodeStats := NodeStats{
@ -130,7 +126,6 @@ func (sb *summaryBuilder) build() (*Summary, error) {
} }
summary := Summary{ summary := Summary{
Time: unversioned.NewTime(cstat.Timestamp),
Node: nodeStats, Node: nodeStats,
Pods: sb.buildSummaryPods(), Pods: sb.buildSummaryPods(),
} }
@ -250,15 +245,17 @@ func (sb *summaryBuilder) containerInfoV2ToStats(
name string, name string,
info *cadvisorapiv2.ContainerInfo) ContainerStats { info *cadvisorapiv2.ContainerInfo) ContainerStats {
stats := ContainerStats{ stats := ContainerStats{
Name: name,
StartTime: unversioned.NewTime(info.Spec.CreationTime), StartTime: unversioned.NewTime(info.Spec.CreationTime),
Name: name,
} }
cstat, found := sb.latestContainerStats(info) cstat, found := sb.latestContainerStats(info)
if !found { if !found {
return stats return stats
} }
if info.Spec.HasCpu { if info.Spec.HasCpu {
cpuStats := CPUStats{} cpuStats := CPUStats{
Time: unversioned.NewTime(cstat.Timestamp),
}
if cstat.CpuInst != nil { if cstat.CpuInst != nil {
cpuStats.UsageNanoCores = &cstat.CpuInst.Usage.Total cpuStats.UsageNanoCores = &cstat.CpuInst.Usage.Total
} }
@ -271,6 +268,7 @@ func (sb *summaryBuilder) containerInfoV2ToStats(
pageFaults := cstat.Memory.ContainerData.Pgfault pageFaults := cstat.Memory.ContainerData.Pgfault
majorPageFaults := cstat.Memory.ContainerData.Pgmajfault majorPageFaults := cstat.Memory.ContainerData.Pgmajfault
stats.Memory = &MemoryStats{ stats.Memory = &MemoryStats{
Time: unversioned.NewTime(cstat.Timestamp),
UsageBytes: &cstat.Memory.Usage, UsageBytes: &cstat.Memory.Usage,
WorkingSetBytes: &cstat.Memory.WorkingSet, WorkingSetBytes: &cstat.Memory.WorkingSet,
PageFaults: &pageFaults, PageFaults: &pageFaults,
@ -304,6 +302,7 @@ func (sb *summaryBuilder) containerInfoV2ToNetworkStats(info *cadvisorapiv2.Cont
txErrors += inter.TxErrors txErrors += inter.TxErrors
} }
return &NetworkStats{ return &NetworkStats{
Time: unversioned.NewTime(cstat.Timestamp),
RxBytes: &rxBytes, RxBytes: &rxBytes,
RxErrors: &rxErrors, RxErrors: &rxErrors,
TxBytes: &txBytes, TxBytes: &txBytes,
@ -353,6 +352,7 @@ func (sb *summaryBuilder) containerInfoV2ToUserDefinedMetrics(info *cadvisorapiv
for _, specVal := range udmMap { for _, specVal := range udmMap {
udm = append(udm, UserDefinedMetric{ udm = append(udm, UserDefinedMetric{
UserDefinedMetricDescriptor: specVal.ref, UserDefinedMetricDescriptor: specVal.ref,
Time: unversioned.NewTime(specVal.time),
Value: specVal.value, Value: specVal.value,
}) })
} }

View File

@ -26,6 +26,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/kubelet/cm" "k8s.io/kubernetes/pkg/kubelet/cm"
"k8s.io/kubernetes/pkg/kubelet/leaky" "k8s.io/kubernetes/pkg/kubelet/leaky"
) )
@ -44,6 +45,11 @@ const (
offsetNetTxErrors offsetNetTxErrors
) )
var (
timestamp = time.Now()
creationTime = timestamp.Add(-5 * time.Minute)
)
func TestBuildSummary(t *testing.T) { func TestBuildSummary(t *testing.T) {
node := api.Node{} node := api.Node{}
node.Name = "FooNode" node.Name = "FooNode"
@ -111,6 +117,7 @@ func TestBuildSummary(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
nodeStats := summary.Node nodeStats := summary.Node
assert.Equal(t, "FooNode", nodeStats.NodeName) assert.Equal(t, "FooNode", nodeStats.NodeName)
assert.EqualValues(t, testTime(creationTime, seedRoot).Unix(), nodeStats.StartTime.Time.Unix())
checkCPUStats(t, "Node", seedRoot, nodeStats.CPU) checkCPUStats(t, "Node", seedRoot, nodeStats.CPU)
checkMemoryStats(t, "Node", seedRoot, nodeStats.Memory) checkMemoryStats(t, "Node", seedRoot, nodeStats.Memory)
checkNetworkStats(t, "Node", seedRoot, nodeStats.Network) checkNetworkStats(t, "Node", seedRoot, nodeStats.Network)
@ -126,6 +133,7 @@ func TestBuildSummary(t *testing.T) {
if !found { if !found {
t.Errorf("Unknown SystemContainer: %q", name) t.Errorf("Unknown SystemContainer: %q", name)
} }
assert.EqualValues(t, testTime(creationTime, seed).Unix(), sys.StartTime.Time.Unix(), name+".StartTime")
checkCPUStats(t, name, seed, sys.CPU) checkCPUStats(t, name, seed, sys.CPU)
checkMemoryStats(t, name, seed, sys.Memory) checkMemoryStats(t, name, seed, sys.Memory)
} }
@ -145,13 +153,16 @@ func TestBuildSummary(t *testing.T) {
indexCon[con.Name] = con indexCon[con.Name] = con
} }
con := indexCon[cName00] con := indexCon[cName00]
assert.EqualValues(t, testTime(creationTime, seedPod0Container0).Unix(), con.StartTime.Time.Unix())
checkCPUStats(t, "container", seedPod0Container0, con.CPU) checkCPUStats(t, "container", seedPod0Container0, con.CPU)
checkMemoryStats(t, "container", seedPod0Container0, con.Memory) checkMemoryStats(t, "container", seedPod0Container0, con.Memory)
con = indexCon[cName01] con = indexCon[cName01]
assert.EqualValues(t, testTime(creationTime, seedPod0Container1).Unix(), con.StartTime.Time.Unix())
checkCPUStats(t, "container", seedPod0Container1, con.CPU) checkCPUStats(t, "container", seedPod0Container1, con.CPU)
checkMemoryStats(t, "container", seedPod0Container1, con.Memory) checkMemoryStats(t, "container", seedPod0Container1, con.Memory)
assert.EqualValues(t, testTime(creationTime, seedPod0Infra).Unix(), ps.StartTime.Time.Unix())
checkNetworkStats(t, "Pod", seedPod0Infra, ps.Network) checkNetworkStats(t, "Pod", seedPod0Infra, ps.Network)
// Validate Pod1 Results // Validate Pod1 Results
@ -231,6 +242,7 @@ func summaryTestContainerInfo(seed int, podName string, podNamespace string, con
} }
} }
spec := v2.ContainerSpec{ spec := v2.ContainerSpec{
CreationTime: testTime(creationTime, seed),
HasCpu: true, HasCpu: true,
HasMemory: true, HasMemory: true,
HasNetwork: true, HasNetwork: true,
@ -239,8 +251,9 @@ func summaryTestContainerInfo(seed int, podName string, podNamespace string, con
} }
stats := v2.ContainerStats{ stats := v2.ContainerStats{
Cpu: &v1.CpuStats{}, Timestamp: testTime(timestamp, seed),
CpuInst: &v2.CpuInstStats{}, Cpu: &v1.CpuStats{},
CpuInst: &v2.CpuInstStats{},
Memory: &v1.MemoryStats{ Memory: &v1.MemoryStats{
Usage: uint64(seed + offsetMemUsageBytes), Usage: uint64(seed + offsetMemUsageBytes),
WorkingSet: uint64(seed + offsetMemWorkingSetBytes), WorkingSet: uint64(seed + offsetMemWorkingSetBytes),
@ -267,7 +280,12 @@ func summaryTestContainerInfo(seed int, podName string, podNamespace string, con
} }
} }
func testTime(base time.Time, seed int) time.Time {
return base.Add(time.Duration(seed) * time.Second)
}
func checkNetworkStats(t *testing.T, label string, seed int, stats *NetworkStats) { func checkNetworkStats(t *testing.T, label string, seed int, stats *NetworkStats) {
assert.EqualValues(t, testTime(timestamp, seed).Unix(), stats.Time.Time.Unix(), label+".Net.Time")
assert.EqualValues(t, seed+offsetNetRxBytes, *stats.RxBytes, label+".Net.RxBytes") assert.EqualValues(t, seed+offsetNetRxBytes, *stats.RxBytes, label+".Net.RxBytes")
assert.EqualValues(t, seed+offsetNetRxErrors, *stats.RxErrors, label+".Net.RxErrors") assert.EqualValues(t, seed+offsetNetRxErrors, *stats.RxErrors, label+".Net.RxErrors")
assert.EqualValues(t, seed+offsetNetTxBytes, *stats.TxBytes, label+".Net.TxBytes") assert.EqualValues(t, seed+offsetNetTxBytes, *stats.TxBytes, label+".Net.TxBytes")
@ -275,11 +293,13 @@ func checkNetworkStats(t *testing.T, label string, seed int, stats *NetworkStats
} }
func checkCPUStats(t *testing.T, label string, seed int, stats *CPUStats) { func checkCPUStats(t *testing.T, label string, seed int, stats *CPUStats) {
assert.EqualValues(t, testTime(timestamp, seed).Unix(), stats.Time.Time.Unix(), label+".CPU.Time")
assert.EqualValues(t, seed+offsetCPUUsageCores, *stats.UsageNanoCores, label+".CPU.UsageCores") assert.EqualValues(t, seed+offsetCPUUsageCores, *stats.UsageNanoCores, label+".CPU.UsageCores")
assert.EqualValues(t, seed+offsetCPUUsageCoreSeconds, *stats.UsageCoreNanoSeconds, label+".CPU.UsageCoreSeconds") assert.EqualValues(t, seed+offsetCPUUsageCoreSeconds, *stats.UsageCoreNanoSeconds, label+".CPU.UsageCoreSeconds")
} }
func checkMemoryStats(t *testing.T, label string, seed int, stats *MemoryStats) { func checkMemoryStats(t *testing.T, label string, seed int, stats *MemoryStats) {
assert.EqualValues(t, testTime(timestamp, seed).Unix(), stats.Time.Time.Unix(), label+".Mem.Time")
assert.EqualValues(t, seed+offsetMemUsageBytes, *stats.UsageBytes, label+".Mem.UsageBytes") assert.EqualValues(t, seed+offsetMemUsageBytes, *stats.UsageBytes, label+".Mem.UsageBytes")
assert.EqualValues(t, seed+offsetMemWorkingSetBytes, *stats.WorkingSetBytes, label+".Mem.WorkingSetBytes") assert.EqualValues(t, seed+offsetMemWorkingSetBytes, *stats.WorkingSetBytes, label+".Mem.WorkingSetBytes")
assert.EqualValues(t, seed+offsetMemPageFaults, *stats.PageFaults, label+".Mem.PageFaults") assert.EqualValues(t, seed+offsetMemPageFaults, *stats.PageFaults, label+".Mem.PageFaults")
@ -301,24 +321,26 @@ func TestCustomMetrics(t *testing.T) {
Units: "count", Units: "count",
}, },
} }
timestamp1 := time.Now()
timestamp2 := time.Now().Add(time.Minute)
metrics := map[string][]v1.MetricVal{ metrics := map[string][]v1.MetricVal{
"qos": { "qos": {
{ {
Timestamp: time.Now(), Timestamp: timestamp1,
IntValue: 10, IntValue: 10,
}, },
{ {
Timestamp: time.Now().Add(time.Minute), Timestamp: timestamp2,
IntValue: 100, IntValue: 100,
}, },
}, },
"cpuLoad": { "cpuLoad": {
{ {
Timestamp: time.Now(), Timestamp: timestamp1,
FloatValue: 1.2, FloatValue: 1.2,
}, },
{ {
Timestamp: time.Now().Add(time.Minute), Timestamp: timestamp2,
FloatValue: 2.1, FloatValue: 2.1,
}, },
}, },
@ -341,6 +363,7 @@ func TestCustomMetrics(t *testing.T) {
Type: MetricGauge, Type: MetricGauge,
Units: "per second", Units: "per second",
}, },
Time: unversioned.NewTime(timestamp2),
Value: 100, Value: 100,
}, },
UserDefinedMetric{ UserDefinedMetric{
@ -349,6 +372,7 @@ func TestCustomMetrics(t *testing.T) {
Type: MetricCumulative, Type: MetricCumulative,
Units: "count", Units: "count",
}, },
Time: unversioned.NewTime(timestamp2),
Value: 2.1, Value: 2.1,
}) })
} }

View File

@ -22,9 +22,6 @@ import (
// Summary is a top-level container for holding NodeStats and PodStats. // Summary is a top-level container for holding NodeStats and PodStats.
type Summary struct { type Summary struct {
// The time the most recent data included in this summary was collect at, rounded to the nearest
// second.
Time unversioned.Time `json:"time"`
// Overall node stats. // Overall node stats.
Node NodeStats `json:"node"` Node NodeStats `json:"node"`
// Per-pod stats. // Per-pod stats.
@ -104,6 +101,8 @@ type PodReference struct {
// NetworkStats contains data about network resources. // NetworkStats contains data about network resources.
type NetworkStats struct { type NetworkStats struct {
// The time at which these stats were updated.
Time unversioned.Time `json:"time"`
// Cumulative count of bytes received. // Cumulative count of bytes received.
RxBytes *uint64 `json:"rxBytes,omitempty"` RxBytes *uint64 `json:"rxBytes,omitempty"`
// Cumulative count of receive errors encountered. // Cumulative count of receive errors encountered.
@ -116,6 +115,8 @@ type NetworkStats struct {
// CPUStats contains data about CPU usage. // CPUStats contains data about CPU usage.
type CPUStats struct { type CPUStats struct {
// The time at which these stats were updated.
Time unversioned.Time `json:"time"`
// Total CPU usage (sum of all cores) averaged over the sample window. // Total CPU usage (sum of all cores) averaged over the sample window.
// The "core" unit can be interpreted as CPU core-nanoseconds per second. // The "core" unit can be interpreted as CPU core-nanoseconds per second.
UsageNanoCores *uint64 `json:"usageNanoCores,omitempty"` UsageNanoCores *uint64 `json:"usageNanoCores,omitempty"`
@ -125,6 +126,8 @@ type CPUStats struct {
// MemoryStats contains data about memory usage. // MemoryStats contains data about memory usage.
type MemoryStats struct { type MemoryStats struct {
// The time at which these stats were updated.
Time unversioned.Time `json:"time"`
// Total memory in use. This includes all memory regardless of when it was accessed. // Total memory in use. This includes all memory regardless of when it was accessed.
UsageBytes *uint64 `json:"usageBytes,omitempty"` UsageBytes *uint64 `json:"usageBytes,omitempty"`
// The amount of working set memory. This includes recently accessed memory, // The amount of working set memory. This includes recently accessed memory,
@ -188,6 +191,8 @@ type UserDefinedMetricDescriptor struct {
// UserDefinedMetric represents a metric defined and generate by users. // UserDefinedMetric represents a metric defined and generate by users.
type UserDefinedMetric struct { type UserDefinedMetric struct {
UserDefinedMetricDescriptor `json:",inline"` UserDefinedMetricDescriptor `json:",inline"`
// The time at which these stats were updated.
Time unversioned.Time `json:"time"`
// Value of the metric. Float64s have 53 bit precision. // Value of the metric. Float64s have 53 bit precision.
// We do not forsee any metrics exceeding that value. // We do not forsee any metrics exceeding that value.
Value float64 `json:"value"` Value float64 `json:"value"`