Skip to content

Commit d08b2a0

Browse files
mxpvdcantah
authored andcommitted
[sbserver] Refactor usageNanoCores be to used for all OSes
Signed-off-by: Maksym Pavlenko <[email protected]> (cherry picked from commit 464a497) Signed-off-by: Danny Canter <[email protected]>
1 parent 791f042 commit d08b2a0

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
@@ -31,7 +31,6 @@ import (
3131
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
3232

3333
containerstore "github.com/containerd/containerd/pkg/cri/store/container"
34-
"github.com/containerd/containerd/pkg/cri/store/stats"
3534
)
3635

3736
func (c *criService) containerMetrics(
@@ -95,72 +94,6 @@ func (c *criService) containerMetrics(
9594
return &cs, nil
9695
}
9796

98-
func (c *criService) getUsageNanoCores(containerID string, isSandbox bool, currentUsageCoreNanoSeconds uint64, currentTimestamp time.Time) (uint64, error) {
99-
var oldStats *stats.ContainerStats
100-
101-
if isSandbox {
102-
sandbox, err := c.sandboxStore.Get(containerID)
103-
if err != nil {
104-
return 0, fmt.Errorf("failed to get sandbox container: %s: %w", containerID, err)
105-
}
106-
oldStats = sandbox.Stats
107-
} else {
108-
container, err := c.containerStore.Get(containerID)
109-
if err != nil {
110-
return 0, fmt.Errorf("failed to get container ID: %s: %w", containerID, err)
111-
}
112-
oldStats = container.Stats
113-
}
114-
115-
if oldStats == nil {
116-
newStats := &stats.ContainerStats{
117-
UsageCoreNanoSeconds: currentUsageCoreNanoSeconds,
118-
Timestamp: currentTimestamp,
119-
}
120-
if isSandbox {
121-
err := c.sandboxStore.UpdateContainerStats(containerID, newStats)
122-
if err != nil {
123-
return 0, fmt.Errorf("failed to update sandbox stats container ID: %s: %w", containerID, err)
124-
}
125-
} else {
126-
err := c.containerStore.UpdateContainerStats(containerID, newStats)
127-
if err != nil {
128-
return 0, fmt.Errorf("failed to update container stats ID: %s: %w", containerID, err)
129-
}
130-
}
131-
return 0, nil
132-
}
133-
134-
nanoSeconds := currentTimestamp.UnixNano() - oldStats.Timestamp.UnixNano()
135-
136-
// zero or negative interval
137-
if nanoSeconds <= 0 {
138-
return 0, nil
139-
}
140-
141-
newUsageNanoCores := uint64(float64(currentUsageCoreNanoSeconds-oldStats.UsageCoreNanoSeconds) /
142-
float64(nanoSeconds) * float64(time.Second/time.Nanosecond))
143-
144-
newStats := &stats.ContainerStats{
145-
UsageCoreNanoSeconds: currentUsageCoreNanoSeconds,
146-
Timestamp: currentTimestamp,
147-
}
148-
if isSandbox {
149-
err := c.sandboxStore.UpdateContainerStats(containerID, newStats)
150-
if err != nil {
151-
return 0, fmt.Errorf("failed to update sandbox container stats: %s: %w", containerID, err)
152-
}
153-
154-
} else {
155-
err := c.containerStore.UpdateContainerStats(containerID, newStats)
156-
if err != nil {
157-
return 0, fmt.Errorf("failed to update container stats ID: %s: %w", containerID, err)
158-
}
159-
}
160-
161-
return newUsageNanoCores, nil
162-
}
163-
16497
// getWorkingSet calculates workingset memory from cgroup memory stats.
16598
// The caller should make sure memory is not nil.
16699
// workingset = usage - total_inactive_file
@@ -216,32 +149,19 @@ func (c *criService) cpuContainerStats(ID string, isSandbox bool, stats interfac
216149
switch metrics := stats.(type) {
217150
case *v1.Metrics:
218151
if metrics.CPU != nil && metrics.CPU.Usage != nil {
219-
220-
usageNanoCores, err := c.getUsageNanoCores(ID, isSandbox, metrics.CPU.Usage.Total, timestamp)
221-
if err != nil {
222-
return nil, fmt.Errorf("failed to get usage nano cores, containerID: %s: %w", ID, err)
223-
}
224-
225152
return &runtime.CpuUsage{
226153
Timestamp: timestamp.UnixNano(),
227154
UsageCoreNanoSeconds: &runtime.UInt64Value{Value: metrics.CPU.Usage.Total},
228-
UsageNanoCores: &runtime.UInt64Value{Value: usageNanoCores},
229155
}, nil
230156
}
231157
case *v2.Metrics:
232158
if metrics.CPU != nil {
233159
// convert to nano seconds
234160
usageCoreNanoSeconds := metrics.CPU.UsageUsec * 1000
235161

236-
usageNanoCores, err := c.getUsageNanoCores(ID, isSandbox, usageCoreNanoSeconds, timestamp)
237-
if err != nil {
238-
return nil, fmt.Errorf("failed to get usage nano cores, containerID: %s: %w", ID, err)
239-
}
240-
241162
return &runtime.CpuUsage{
242163
Timestamp: timestamp.UnixNano(),
243164
UsageCoreNanoSeconds: &runtime.UInt64Value{Value: usageCoreNanoSeconds},
244-
UsageNanoCores: &runtime.UInt64Value{Value: usageNanoCores},
245165
}, nil
246166
}
247167
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)