Skip to content

Commit 464a497

Browse files
committed
[sbserver] Refactor usageNanoCores be to used for all OSes
Signed-off-by: Maksym Pavlenko <[email protected]>
1 parent 7cd72cc commit 464a497

4 files changed

Lines changed: 149 additions & 150 deletions

File tree

pkg/cri/sbserver/container_stats_list.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ package sbserver
1919
import (
2020
"context"
2121
"fmt"
22+
"time"
2223

2324
"github.com/containerd/containerd/api/services/tasks/v1"
2425
"github.com/containerd/containerd/api/types"
26+
"github.com/containerd/containerd/pkg/cri/store/stats"
2527
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
2628

2729
containerstore "github.com/containerd/containerd/pkg/cri/store/container"
@@ -61,11 +63,85 @@ func (c *criService) toCRIContainerStats(
6163
if err != nil {
6264
return nil, fmt.Errorf("failed to decode container metrics for %q: %w", cntr.ID, err)
6365
}
66+
67+
// this is a calculated value and should be computed for all OSes
68+
nanoUsage, err := c.getUsageNanoCores(cntr.Metadata.ID, false, cs.Cpu.UsageCoreNanoSeconds.Value, time.Unix(0, cs.Cpu.Timestamp))
69+
if err != nil {
70+
return nil, fmt.Errorf("failed to get usage nano cores, containerID: %s: %w", cntr.Metadata.ID, err)
71+
}
72+
cs.Cpu.UsageNanoCores = &runtime.UInt64Value{Value: nanoUsage}
73+
6474
containerStats.Stats = append(containerStats.Stats, cs)
6575
}
6676
return containerStats, nil
6777
}
6878

79+
func (c *criService) getUsageNanoCores(containerID string, isSandbox bool, currentUsageCoreNanoSeconds uint64, currentTimestamp time.Time) (uint64, error) {
80+
var oldStats *stats.ContainerStats
81+
82+
if isSandbox {
83+
sandbox, err := c.sandboxStore.Get(containerID)
84+
if err != nil {
85+
return 0, fmt.Errorf("failed to get sandbox container: %s: %w", containerID, err)
86+
}
87+
oldStats = sandbox.Stats
88+
} else {
89+
container, err := c.containerStore.Get(containerID)
90+
if err != nil {
91+
return 0, fmt.Errorf("failed to get container ID: %s: %w", containerID, err)
92+
}
93+
oldStats = container.Stats
94+
}
95+
96+
if oldStats == nil {
97+
newStats := &stats.ContainerStats{
98+
UsageCoreNanoSeconds: currentUsageCoreNanoSeconds,
99+
Timestamp: currentTimestamp,
100+
}
101+
if isSandbox {
102+
err := c.sandboxStore.UpdateContainerStats(containerID, newStats)
103+
if err != nil {
104+
return 0, fmt.Errorf("failed to update sandbox stats container ID: %s: %w", containerID, err)
105+
}
106+
} else {
107+
err := c.containerStore.UpdateContainerStats(containerID, newStats)
108+
if err != nil {
109+
return 0, fmt.Errorf("failed to update container stats ID: %s: %w", containerID, err)
110+
}
111+
}
112+
return 0, nil
113+
}
114+
115+
nanoSeconds := currentTimestamp.UnixNano() - oldStats.Timestamp.UnixNano()
116+
117+
// zero or negative interval
118+
if nanoSeconds <= 0 {
119+
return 0, nil
120+
}
121+
122+
newUsageNanoCores := uint64(float64(currentUsageCoreNanoSeconds-oldStats.UsageCoreNanoSeconds) /
123+
float64(nanoSeconds) * float64(time.Second/time.Nanosecond))
124+
125+
newStats := &stats.ContainerStats{
126+
UsageCoreNanoSeconds: currentUsageCoreNanoSeconds,
127+
Timestamp: currentTimestamp,
128+
}
129+
if isSandbox {
130+
err := c.sandboxStore.UpdateContainerStats(containerID, newStats)
131+
if err != nil {
132+
return 0, fmt.Errorf("failed to update sandbox container stats: %s: %w", containerID, err)
133+
}
134+
135+
} else {
136+
err := c.containerStore.UpdateContainerStats(containerID, newStats)
137+
if err != nil {
138+
return 0, fmt.Errorf("failed to update container stats ID: %s: %w", containerID, err)
139+
}
140+
}
141+
142+
return newUsageNanoCores, nil
143+
}
144+
69145
func (c *criService) normalizeContainerStatsFilter(filter *runtime.ContainerStatsFilter) {
70146
if cntr, err := c.containerStore.Get(filter.GetId()); err == nil {
71147
filter.Id = cntr.ID

pkg/cri/sbserver/container_stats_list_linux.go

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
2929

3030
containerstore "github.com/containerd/containerd/pkg/cri/store/container"
31-
"github.com/containerd/containerd/pkg/cri/store/stats"
3231
)
3332

3433
func (c *criService) containerMetrics(
@@ -81,72 +80,6 @@ func (c *criService) containerMetrics(
8180
return &cs, nil
8281
}
8382

84-
func (c *criService) getUsageNanoCores(containerID string, isSandbox bool, currentUsageCoreNanoSeconds uint64, currentTimestamp time.Time) (uint64, error) {
85-
var oldStats *stats.ContainerStats
86-
87-
if isSandbox {
88-
sandbox, err := c.sandboxStore.Get(containerID)
89-
if err != nil {
90-
return 0, fmt.Errorf("failed to get sandbox container: %s: %w", containerID, err)
91-
}
92-
oldStats = sandbox.Stats
93-
} else {
94-
container, err := c.containerStore.Get(containerID)
95-
if err != nil {
96-
return 0, fmt.Errorf("failed to get container ID: %s: %w", containerID, err)
97-
}
98-
oldStats = container.Stats
99-
}
100-
101-
if oldStats == nil {
102-
newStats := &stats.ContainerStats{
103-
UsageCoreNanoSeconds: currentUsageCoreNanoSeconds,
104-
Timestamp: currentTimestamp,
105-
}
106-
if isSandbox {
107-
err := c.sandboxStore.UpdateContainerStats(containerID, newStats)
108-
if err != nil {
109-
return 0, fmt.Errorf("failed to update sandbox stats container ID: %s: %w", containerID, err)
110-
}
111-
} else {
112-
err := c.containerStore.UpdateContainerStats(containerID, newStats)
113-
if err != nil {
114-
return 0, fmt.Errorf("failed to update container stats ID: %s: %w", containerID, err)
115-
}
116-
}
117-
return 0, nil
118-
}
119-
120-
nanoSeconds := currentTimestamp.UnixNano() - oldStats.Timestamp.UnixNano()
121-
122-
// zero or negative interval
123-
if nanoSeconds <= 0 {
124-
return 0, nil
125-
}
126-
127-
newUsageNanoCores := uint64(float64(currentUsageCoreNanoSeconds-oldStats.UsageCoreNanoSeconds) /
128-
float64(nanoSeconds) * float64(time.Second/time.Nanosecond))
129-
130-
newStats := &stats.ContainerStats{
131-
UsageCoreNanoSeconds: currentUsageCoreNanoSeconds,
132-
Timestamp: currentTimestamp,
133-
}
134-
if isSandbox {
135-
err := c.sandboxStore.UpdateContainerStats(containerID, newStats)
136-
if err != nil {
137-
return 0, fmt.Errorf("failed to update sandbox container stats: %s: %w", containerID, err)
138-
}
139-
140-
} else {
141-
err := c.containerStore.UpdateContainerStats(containerID, newStats)
142-
if err != nil {
143-
return 0, fmt.Errorf("failed to update container stats ID: %s: %w", containerID, err)
144-
}
145-
}
146-
147-
return newUsageNanoCores, nil
148-
}
149-
15083
// getWorkingSet calculates workingset memory from cgroup memory stats.
15184
// The caller should make sure memory is not nil.
15285
// workingset = usage - total_inactive_file
@@ -202,32 +135,19 @@ func (c *criService) cpuContainerStats(ID string, isSandbox bool, stats interfac
202135
switch metrics := stats.(type) {
203136
case *v1.Metrics:
204137
if metrics.CPU != nil && metrics.CPU.Usage != nil {
205-
206-
usageNanoCores, err := c.getUsageNanoCores(ID, isSandbox, metrics.CPU.Usage.Total, timestamp)
207-
if err != nil {
208-
return nil, fmt.Errorf("failed to get usage nano cores, containerID: %s: %w", ID, err)
209-
}
210-
211138
return &runtime.CpuUsage{
212139
Timestamp: timestamp.UnixNano(),
213140
UsageCoreNanoSeconds: &runtime.UInt64Value{Value: metrics.CPU.Usage.Total},
214-
UsageNanoCores: &runtime.UInt64Value{Value: usageNanoCores},
215141
}, nil
216142
}
217143
case *v2.Metrics:
218144
if metrics.CPU != nil {
219145
// convert to nano seconds
220146
usageCoreNanoSeconds := metrics.CPU.UsageUsec * 1000
221147

222-
usageNanoCores, err := c.getUsageNanoCores(ID, isSandbox, usageCoreNanoSeconds, timestamp)
223-
if err != nil {
224-
return nil, fmt.Errorf("failed to get usage nano cores, containerID: %s: %w", ID, err)
225-
}
226-
227148
return &runtime.CpuUsage{
228149
Timestamp: timestamp.UnixNano(),
229150
UsageCoreNanoSeconds: &runtime.UInt64Value{Value: usageCoreNanoSeconds},
230-
UsageNanoCores: &runtime.UInt64Value{Value: usageNanoCores},
231151
}, nil
232152
}
233153
default:

pkg/cri/sbserver/container_stats_list_linux_test.go

Lines changed: 0 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323

2424
v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
2525
v2 "github.com/containerd/cgroups/v3/cgroup2/stats"
26-
containerstore "github.com/containerd/containerd/pkg/cri/store/container"
2726
"github.com/stretchr/testify/assert"
2827
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
2928
)
@@ -156,75 +155,6 @@ func TestGetAvailableBytesV2(t *testing.T) {
156155
}
157156
}
158157

159-
func TestContainerMetricsCPU(t *testing.T) {
160-
c := newTestCRIService()
161-
timestamp := time.Now()
162-
secondAfterTimeStamp := timestamp.Add(time.Second)
163-
ID := "ID"
164-
165-
for desc, test := range map[string]struct {
166-
firstMetrics interface{}
167-
secondMetrics interface{}
168-
expectedFirst *runtime.CpuUsage
169-
expectedSecond *runtime.CpuUsage
170-
}{
171-
"v1 metrics": {
172-
firstMetrics: &v1.Metrics{
173-
CPU: &v1.CPUStat{
174-
Usage: &v1.CPUUsage{
175-
Total: 50,
176-
},
177-
},
178-
},
179-
secondMetrics: &v1.Metrics{
180-
CPU: &v1.CPUStat{
181-
Usage: &v1.CPUUsage{
182-
Total: 500,
183-
},
184-
},
185-
},
186-
expectedFirst: &runtime.CpuUsage{
187-
Timestamp: timestamp.UnixNano(),
188-
UsageCoreNanoSeconds: &runtime.UInt64Value{Value: 50},
189-
UsageNanoCores: &runtime.UInt64Value{Value: 0},
190-
},
191-
expectedSecond: &runtime.CpuUsage{
192-
Timestamp: secondAfterTimeStamp.UnixNano(),
193-
UsageCoreNanoSeconds: &runtime.UInt64Value{Value: 500},
194-
UsageNanoCores: &runtime.UInt64Value{Value: 450},
195-
},
196-
},
197-
} {
198-
t.Run(desc, func(t *testing.T) {
199-
container, err := containerstore.NewContainer(
200-
containerstore.Metadata{ID: ID},
201-
)
202-
assert.NoError(t, err)
203-
assert.Nil(t, container.Stats)
204-
err = c.containerStore.Add(container)
205-
assert.NoError(t, err)
206-
207-
cpuUsage, err := c.cpuContainerStats(ID, false, test.firstMetrics, timestamp)
208-
assert.NoError(t, err)
209-
210-
container, err = c.containerStore.Get(ID)
211-
assert.NoError(t, err)
212-
assert.NotNil(t, container.Stats)
213-
214-
assert.Equal(t, test.expectedFirst, cpuUsage)
215-
216-
cpuUsage, err = c.cpuContainerStats(ID, false, test.secondMetrics, secondAfterTimeStamp)
217-
assert.NoError(t, err)
218-
assert.Equal(t, test.expectedSecond, cpuUsage)
219-
220-
container, err = c.containerStore.Get(ID)
221-
assert.NoError(t, err)
222-
assert.NotNil(t, container.Stats)
223-
})
224-
}
225-
226-
}
227-
228158
func TestContainerMetricsMemory(t *testing.T) {
229159
c := newTestCRIService()
230160
timestamp := time.Now()
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package sbserver
18+
19+
import (
20+
"testing"
21+
"time"
22+
23+
containerstore "github.com/containerd/containerd/pkg/cri/store/container"
24+
"github.com/stretchr/testify/assert"
25+
)
26+
27+
func TestContainerMetricsCPUNanoCoreUsage(t *testing.T) {
28+
c := newTestCRIService()
29+
timestamp := time.Now()
30+
secondAfterTimeStamp := timestamp.Add(time.Second)
31+
ID := "ID"
32+
33+
for desc, test := range map[string]struct {
34+
firstCPUValue uint64
35+
secondCPUValue uint64
36+
expectedNanoCoreUsageFirst uint64
37+
expectedNanoCoreUsageSecond uint64
38+
}{
39+
"metrics": {
40+
firstCPUValue: 50,
41+
secondCPUValue: 500,
42+
expectedNanoCoreUsageFirst: 0,
43+
expectedNanoCoreUsageSecond: 450,
44+
},
45+
} {
46+
t.Run(desc, func(t *testing.T) {
47+
container, err := containerstore.NewContainer(
48+
containerstore.Metadata{ID: ID},
49+
)
50+
assert.NoError(t, err)
51+
assert.Nil(t, container.Stats)
52+
err = c.containerStore.Add(container)
53+
assert.NoError(t, err)
54+
55+
cpuUsage, err := c.getUsageNanoCores(ID, false, test.firstCPUValue, timestamp)
56+
assert.NoError(t, err)
57+
58+
container, err = c.containerStore.Get(ID)
59+
assert.NoError(t, err)
60+
assert.NotNil(t, container.Stats)
61+
62+
assert.Equal(t, test.expectedNanoCoreUsageFirst, cpuUsage)
63+
64+
cpuUsage, err = c.getUsageNanoCores(ID, false, test.secondCPUValue, secondAfterTimeStamp)
65+
assert.NoError(t, err)
66+
assert.Equal(t, test.expectedNanoCoreUsageSecond, cpuUsage)
67+
68+
container, err = c.containerStore.Get(ID)
69+
assert.NoError(t, err)
70+
assert.NotNil(t, container.Stats)
71+
})
72+
}
73+
}

0 commit comments

Comments
 (0)