Skip to content

Commit b2a9946

Browse files
committed
Allow filtering container statistics
It's possible a user doesn't want the full stats list, and only wants cpu/mem etc. This plumbs through the ability to filter to only what is requested. This, while we're already here, adds in memory.event output to the stats list. For that specifically, I think eventually we may want a streaming variant of this so you can get alerted of changes in the file immediately instead of polling/one off reads, but this is useful for now.
1 parent fc5399e commit b2a9946

12 files changed

Lines changed: 615 additions & 127 deletions

File tree

Sources/Containerization/ContainerStatistics.swift

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,29 @@
1717
/// Statistics for a container.
1818
public struct ContainerStatistics: Sendable {
1919
public var id: String
20-
public var process: ProcessStatistics
21-
public var memory: MemoryStatistics
22-
public var cpu: CPUStatistics
23-
public var blockIO: BlockIOStatistics
24-
public var networks: [NetworkStatistics]
20+
public var process: ProcessStatistics?
21+
public var memory: MemoryStatistics?
22+
public var cpu: CPUStatistics?
23+
public var blockIO: BlockIOStatistics?
24+
public var networks: [NetworkStatistics]?
25+
public var memoryEvents: MemoryEventStatistics?
2526

2627
public init(
2728
id: String,
28-
process: ProcessStatistics,
29-
memory: MemoryStatistics,
30-
cpu: CPUStatistics,
31-
blockIO: BlockIOStatistics,
32-
networks: [NetworkStatistics]
29+
process: ProcessStatistics? = nil,
30+
memory: MemoryStatistics? = nil,
31+
cpu: CPUStatistics? = nil,
32+
blockIO: BlockIOStatistics? = nil,
33+
networks: [NetworkStatistics]? = nil,
34+
memoryEvents: MemoryEventStatistics? = nil
3335
) {
3436
self.id = id
3537
self.process = process
3638
self.memory = memory
3739
self.cpu = cpu
3840
self.blockIO = blockIO
3941
self.networks = networks
42+
self.memoryEvents = memoryEvents
4043
}
4144

4245
/// Process statistics for a container.
@@ -174,4 +177,51 @@ public struct ContainerStatistics: Sendable {
174177
self.transmittedErrors = transmittedErrors
175178
}
176179
}
180+
181+
/// Memory event counters from cgroup2's memory.events file.
182+
public struct MemoryEventStatistics: Sendable {
183+
/// Number of times the cgroup was reclaimed due to low memory.
184+
public var low: UInt64
185+
/// Number of times the cgroup exceeded its high memory limit.
186+
public var high: UInt64
187+
/// Number of times the cgroup hit its max memory limit.
188+
public var max: UInt64
189+
/// Number of times the cgroup triggered OOM.
190+
public var oom: UInt64
191+
/// Number of processes killed by OOM killer.
192+
public var oomKill: UInt64
193+
194+
public init(low: UInt64, high: UInt64, max: UInt64, oom: UInt64, oomKill: UInt64) {
195+
self.low = low
196+
self.high = high
197+
self.max = max
198+
self.oom = oom
199+
self.oomKill = oomKill
200+
}
201+
}
202+
}
203+
204+
/// Categories of statistics that can be requested.
205+
public struct StatCategory: OptionSet, Sendable {
206+
public let rawValue: Int
207+
208+
public init(rawValue: Int) {
209+
self.rawValue = rawValue
210+
}
211+
212+
/// Process statistics (pids.current, pids.max).
213+
public static let process = StatCategory(rawValue: 1 << 0)
214+
/// Memory usage statistics.
215+
public static let memory = StatCategory(rawValue: 1 << 1)
216+
/// CPU usage statistics.
217+
public static let cpu = StatCategory(rawValue: 1 << 2)
218+
/// Block I/O statistics.
219+
public static let blockIO = StatCategory(rawValue: 1 << 3)
220+
/// Network interface statistics.
221+
public static let network = StatCategory(rawValue: 1 << 4)
222+
/// Memory event counters (OOM kills, pressure events, etc.).
223+
public static let memoryEvents = StatCategory(rawValue: 1 << 5)
224+
225+
/// All available statistics categories.
226+
public static let all: StatCategory = [.process, .memory, .cpu, .blockIO, .network, .memoryEvents]
177227
}

Sources/Containerization/LinuxContainer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,12 +778,12 @@ extension LinuxContainer {
778778
}
779779

780780
/// Get statistics for the container.
781-
public func statistics() async throws -> ContainerStatistics {
781+
public func statistics(categories: StatCategory = .all) async throws -> ContainerStatistics {
782782
try await self.state.withLock {
783783
let state = try $0.startedState("statistics")
784784

785785
let stats = try await state.vm.withAgent { agent in
786-
let allStats = try await agent.containerStatistics(containerIDs: [self.id])
786+
let allStats = try await agent.containerStatistics(containerIDs: [self.id], categories: categories)
787787
guard let containerStats = allStats.first else {
788788
throw ContainerizationError(
789789
.notFound,

Sources/Containerization/LinuxPod.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -727,15 +727,15 @@ extension LinuxPod {
727727
}
728728

729729
/// Get statistics for containers in the pod.
730-
public func statistics(containerIDs: [String]? = nil) async throws -> [ContainerStatistics] {
730+
public func statistics(containerIDs: [String]? = nil, categories: StatCategory = .all) async throws -> [ContainerStatistics] {
731731
let (createdState, ids) = try await self.state.withLock { state in
732732
let createdState = try state.phase.createdState("statistics")
733733
let ids = containerIDs ?? Array(state.containers.keys)
734734
return (createdState, ids)
735735
}
736736

737737
let stats = try await createdState.vm.withAgent { agent in
738-
try await agent.containerStatistics(containerIDs: ids)
738+
try await agent.containerStatistics(containerIDs: ids, categories: categories)
739739
}
740740

741741
return stats

0 commit comments

Comments
 (0)