Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 60 additions & 10 deletions Sources/Containerization/ContainerStatistics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,29 @@
/// Statistics for a container.
public struct ContainerStatistics: Sendable {
public var id: String
public var process: ProcessStatistics
public var memory: MemoryStatistics
public var cpu: CPUStatistics
public var blockIO: BlockIOStatistics
public var networks: [NetworkStatistics]
public var process: ProcessStatistics?
public var memory: MemoryStatistics?
public var cpu: CPUStatistics?
public var blockIO: BlockIOStatistics?
public var networks: [NetworkStatistics]?
public var memoryEvents: MemoryEventStatistics?

public init(
id: String,
process: ProcessStatistics,
memory: MemoryStatistics,
cpu: CPUStatistics,
blockIO: BlockIOStatistics,
networks: [NetworkStatistics]
process: ProcessStatistics? = nil,
memory: MemoryStatistics? = nil,
cpu: CPUStatistics? = nil,
blockIO: BlockIOStatistics? = nil,
networks: [NetworkStatistics]? = nil,
memoryEvents: MemoryEventStatistics? = nil
) {
self.id = id
self.process = process
self.memory = memory
self.cpu = cpu
self.blockIO = blockIO
self.networks = networks
self.memoryEvents = memoryEvents
}

/// Process statistics for a container.
Expand Down Expand Up @@ -174,4 +177,51 @@ public struct ContainerStatistics: Sendable {
self.transmittedErrors = transmittedErrors
}
}

/// Memory event counters from cgroup2's memory.events file.
public struct MemoryEventStatistics: Sendable {
/// Number of times the cgroup was reclaimed due to low memory.
public var low: UInt64
/// Number of times the cgroup exceeded its high memory limit.
public var high: UInt64
/// Number of times the cgroup hit its max memory limit.
public var max: UInt64
/// Number of times the cgroup triggered OOM.
public var oom: UInt64
/// Number of processes killed by OOM killer.
public var oomKill: UInt64

public init(low: UInt64, high: UInt64, max: UInt64, oom: UInt64, oomKill: UInt64) {
self.low = low
self.high = high
self.max = max
self.oom = oom
self.oomKill = oomKill
}
}
}

/// Categories of statistics that can be requested.
public struct StatCategory: OptionSet, Sendable {
public let rawValue: Int

public init(rawValue: Int) {
self.rawValue = rawValue
}

/// Process statistics (pids.current, pids.max).
public static let process = StatCategory(rawValue: 1 << 0)
/// Memory usage statistics.
public static let memory = StatCategory(rawValue: 1 << 1)
/// CPU usage statistics.
public static let cpu = StatCategory(rawValue: 1 << 2)
/// Block I/O statistics.
public static let blockIO = StatCategory(rawValue: 1 << 3)
/// Network interface statistics.
public static let network = StatCategory(rawValue: 1 << 4)
/// Memory event counters (OOM kills, pressure events, etc.).
public static let memoryEvents = StatCategory(rawValue: 1 << 5)

/// All available statistics categories.
public static let all: StatCategory = [.process, .memory, .cpu, .blockIO, .network, .memoryEvents]
}
4 changes: 2 additions & 2 deletions Sources/Containerization/LinuxContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -778,12 +778,12 @@ extension LinuxContainer {
}

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

let stats = try await state.vm.withAgent { agent in
let allStats = try await agent.containerStatistics(containerIDs: [self.id])
let allStats = try await agent.containerStatistics(containerIDs: [self.id], categories: categories)
guard let containerStats = allStats.first else {
throw ContainerizationError(
.notFound,
Expand Down
4 changes: 2 additions & 2 deletions Sources/Containerization/LinuxPod.swift
Original file line number Diff line number Diff line change
Expand Up @@ -727,15 +727,15 @@ extension LinuxPod {
}

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

let stats = try await createdState.vm.withAgent { agent in
try await agent.containerStatistics(containerIDs: ids)
try await agent.containerStatistics(containerIDs: ids, categories: categories)
}

return stats
Expand Down
100 changes: 100 additions & 0 deletions Sources/Containerization/SandboxContext/SandboxContext.grpc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtoc
callOptions: CallOptions?
) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>

func getMemoryEvents(
_ request: Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest,
callOptions: CallOptions?
) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest, Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse>

func proxyVsock(
_ request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,
callOptions: CallOptions?
Expand Down Expand Up @@ -531,6 +536,24 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtocol {
)
}

/// Get memory events for a container (OOM kills, memory pressure, etc.).
///
/// - Parameters:
/// - request: Request to send to GetMemoryEvents.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func getMemoryEvents(
_ request: Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest,
callOptions: CallOptions? = nil
) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest, Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse> {
return self.makeUnaryCall(
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.getMemoryEvents.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetMemoryEventsInterceptors() ?? []
)
}

/// Proxy a vsock port to a unix domain socket in the guest, or vice versa.
///
/// - Parameters:
Expand Down Expand Up @@ -869,6 +892,11 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientP
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>

func makeGetMemoryEventsCall(
_ request: Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest, Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse>

func makeProxyVsockCall(
_ request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,
callOptions: CallOptions?
Expand Down Expand Up @@ -1156,6 +1184,18 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtoco
)
}

public func makeGetMemoryEventsCall(
_ request: Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest, Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse> {
return self.makeAsyncUnaryCall(
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.getMemoryEvents.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetMemoryEventsInterceptors() ?? []
)
}

public func makeProxyVsockCall(
_ request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,
callOptions: CallOptions? = nil
Expand Down Expand Up @@ -1519,6 +1559,18 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtoco
)
}

public func getMemoryEvents(
_ request: Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest,
callOptions: CallOptions? = nil
) async throws -> Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse {
return try await self.performAsyncUnaryCall(
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.getMemoryEvents.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeGetMemoryEventsInterceptors() ?? []
)
}

public func proxyVsock(
_ request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,
callOptions: CallOptions? = nil
Expand Down Expand Up @@ -1716,6 +1768,9 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterc
/// - Returns: Interceptors to use when invoking 'containerStatistics'.
func makeContainerStatisticsInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>]

/// - Returns: Interceptors to use when invoking 'getMemoryEvents'.
func makeGetMemoryEventsInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest, Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse>]

/// - Returns: Interceptors to use when invoking 'proxyVsock'.
func makeProxyVsockInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>]

Expand Down Expand Up @@ -1771,6 +1826,7 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata {
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.resizeProcess,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.closeProcessStdin,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.containerStatistics,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.getMemoryEvents,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.proxyVsock,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.stopVsockProxy,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipLinkSet,
Expand Down Expand Up @@ -1899,6 +1955,12 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata {
type: GRPCCallType.unary
)

public static let getMemoryEvents = GRPCMethodDescriptor(
name: "GetMemoryEvents",
path: "/com.apple.containerization.sandbox.v3.SandboxContext/GetMemoryEvents",
type: GRPCCallType.unary
)

public static let proxyVsock = GRPCMethodDescriptor(
name: "ProxyVsock",
path: "/com.apple.containerization.sandbox.v3.SandboxContext/ProxyVsock",
Expand Down Expand Up @@ -2025,6 +2087,9 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextProvider: Ca
/// Get statistics for containers.
func containerStatistics(request: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>

/// Get memory events for a container (OOM kills, memory pressure, etc.).
func getMemoryEvents(request: Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse>

/// Proxy a vsock port to a unix domain socket in the guest, or vice versa.
func proxyVsock(request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>

Expand Down Expand Up @@ -2239,6 +2304,15 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextProvider {
userFunction: self.containerStatistics(request:context:)
)

case "GetMemoryEvents":
return UnaryServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest>(),
responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse>(),
interceptors: self.interceptors?.makeGetMemoryEventsInterceptors() ?? [],
userFunction: self.getMemoryEvents(request:context:)
)

case "ProxyVsock":
return UnaryServerHandler(
context: context,
Expand Down Expand Up @@ -2459,6 +2533,12 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvide
context: GRPCAsyncServerCallContext
) async throws -> Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse

/// Get memory events for a container (OOM kills, memory pressure, etc.).
func getMemoryEvents(
request: Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest,
context: GRPCAsyncServerCallContext
) async throws -> Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse

/// Proxy a vsock port to a unix domain socket in the guest, or vice versa.
func proxyVsock(
request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,
Expand Down Expand Up @@ -2710,6 +2790,15 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvider {
wrapping: { try await self.containerStatistics(request: $0, context: $1) }
)

case "GetMemoryEvents":
return GRPCAsyncServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest>(),
responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse>(),
interceptors: self.interceptors?.makeGetMemoryEventsInterceptors() ?? [],
wrapping: { try await self.getMemoryEvents(request: $0, context: $1) }
)

case "ProxyVsock":
return GRPCAsyncServerHandler(
context: context,
Expand Down Expand Up @@ -2884,6 +2973,10 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextServerInterc
/// Defaults to calling `self.makeInterceptors()`.
func makeContainerStatisticsInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>]

/// - Returns: Interceptors to use when handling 'getMemoryEvents'.
/// Defaults to calling `self.makeInterceptors()`.
func makeGetMemoryEventsInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsRequest, Com_Apple_Containerization_Sandbox_V3_GetMemoryEventsResponse>]

/// - Returns: Interceptors to use when handling 'proxyVsock'.
/// Defaults to calling `self.makeInterceptors()`.
func makeProxyVsockInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>]
Expand Down Expand Up @@ -2949,6 +3042,7 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata {
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.resizeProcess,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.closeProcessStdin,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.containerStatistics,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.getMemoryEvents,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.proxyVsock,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.stopVsockProxy,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.ipLinkSet,
Expand Down Expand Up @@ -3077,6 +3171,12 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata {
type: GRPCCallType.unary
)

public static let getMemoryEvents = GRPCMethodDescriptor(
name: "GetMemoryEvents",
path: "/com.apple.containerization.sandbox.v3.SandboxContext/GetMemoryEvents",
type: GRPCCallType.unary
)

public static let proxyVsock = GRPCMethodDescriptor(
name: "ProxyVsock",
path: "/com.apple.containerization.sandbox.v3.SandboxContext/ProxyVsock",
Expand Down
Loading