Skip to content

Commit 80e84a9

Browse files
authored
Add filesystem notification (FSNotify) support (#294)
Addresses apple/container#141, where containers don't receive filesystem events on mounted volumes, preventing incremental rebuilds and other file-watching features. This PR implements the guest-side components for FSNotify. Host-side implementation in the container repo will complete the pipeline. Summary: - Add gRPC protocol definitions for filesystem event notifications - Implement guest-side event handler that generates Linux inotify events - Add CLI testing tool (`cctl fsnotify`) and integration test infrastructure
1 parent bec1008 commit 80e84a9

File tree

13 files changed

+1028
-8
lines changed

13 files changed

+1028
-8
lines changed

Sources/Containerization/SandboxContext/SandboxContext.grpc.swift

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtoc
168168
_ request: Com_Apple_Containerization_Sandbox_V3_KillRequest,
169169
callOptions: CallOptions?
170170
) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse>
171+
172+
func notifyFileSystemEvent(
173+
callOptions: CallOptions?,
174+
handler: @escaping (Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse) -> Void
175+
) -> BidirectionalStreamingCall<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest, Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse>
171176
}
172177

173178
extension Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtocol {
@@ -661,6 +666,27 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtocol {
661666
interceptors: self.interceptors?.makeKillInterceptors() ?? []
662667
)
663668
}
669+
670+
/// Notify guest of filesystem events from host.
671+
///
672+
/// Callers should use the `send` method on the returned object to send messages
673+
/// to the server. The caller should send an `.end` after the final message has been sent.
674+
///
675+
/// - Parameters:
676+
/// - callOptions: Call options.
677+
/// - handler: A closure called when each response is received from the server.
678+
/// - Returns: A `ClientStreamingCall` with futures for the metadata and status.
679+
public func notifyFileSystemEvent(
680+
callOptions: CallOptions? = nil,
681+
handler: @escaping (Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse) -> Void
682+
) -> BidirectionalStreamingCall<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest, Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse> {
683+
return self.makeBidirectionalStreamingCall(
684+
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.notifyFileSystemEvent.path,
685+
callOptions: callOptions ?? self.defaultCallOptions,
686+
interceptors: self.interceptors?.makeNotifyFileSystemEventInterceptors() ?? [],
687+
handler: handler
688+
)
689+
}
664690
}
665691

666692
@available(*, deprecated)
@@ -860,6 +886,10 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientP
860886
_ request: Com_Apple_Containerization_Sandbox_V3_KillRequest,
861887
callOptions: CallOptions?
862888
) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse>
889+
890+
func makeNotifyFileSystemEventCall(
891+
callOptions: CallOptions?
892+
) -> GRPCAsyncBidirectionalStreamingCall<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest, Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse>
863893
}
864894

865895
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@@ -1195,6 +1225,16 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtoco
11951225
interceptors: self.interceptors?.makeKillInterceptors() ?? []
11961226
)
11971227
}
1228+
1229+
public func makeNotifyFileSystemEventCall(
1230+
callOptions: CallOptions? = nil
1231+
) -> GRPCAsyncBidirectionalStreamingCall<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest, Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse> {
1232+
return self.makeAsyncBidirectionalStreamingCall(
1233+
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.notifyFileSystemEvent.path,
1234+
callOptions: callOptions ?? self.defaultCallOptions,
1235+
interceptors: self.interceptors?.makeNotifyFileSystemEventInterceptors() ?? []
1236+
)
1237+
}
11981238
}
11991239

12001240
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@@ -1522,6 +1562,30 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtoco
15221562
interceptors: self.interceptors?.makeKillInterceptors() ?? []
15231563
)
15241564
}
1565+
1566+
public func notifyFileSystemEvent<RequestStream>(
1567+
_ requests: RequestStream,
1568+
callOptions: CallOptions? = nil
1569+
) -> GRPCAsyncResponseStream<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse> where RequestStream: Sequence, RequestStream.Element == Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest {
1570+
return self.performAsyncBidirectionalStreamingCall(
1571+
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.notifyFileSystemEvent.path,
1572+
requests: requests,
1573+
callOptions: callOptions ?? self.defaultCallOptions,
1574+
interceptors: self.interceptors?.makeNotifyFileSystemEventInterceptors() ?? []
1575+
)
1576+
}
1577+
1578+
public func notifyFileSystemEvent<RequestStream>(
1579+
_ requests: RequestStream,
1580+
callOptions: CallOptions? = nil
1581+
) -> GRPCAsyncResponseStream<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse> where RequestStream: AsyncSequence & Sendable, RequestStream.Element == Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest {
1582+
return self.performAsyncBidirectionalStreamingCall(
1583+
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.notifyFileSystemEvent.path,
1584+
requests: requests,
1585+
callOptions: callOptions ?? self.defaultCallOptions,
1586+
interceptors: self.interceptors?.makeNotifyFileSystemEventInterceptors() ?? []
1587+
)
1588+
}
15251589
}
15261590

15271591
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@@ -1623,6 +1687,9 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterc
16231687

16241688
/// - Returns: Interceptors to use when invoking 'kill'.
16251689
func makeKillInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse>]
1690+
1691+
/// - Returns: Interceptors to use when invoking 'notifyFileSystemEvent'.
1692+
func makeNotifyFileSystemEventInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest, Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse>]
16261693
}
16271694

16281695
public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata {
@@ -1657,6 +1724,7 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata {
16571724
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.configureHosts,
16581725
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sync,
16591726
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.kill,
1727+
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.notifyFileSystemEvent,
16601728
]
16611729
)
16621730

@@ -1822,6 +1890,12 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata {
18221890
path: "/com.apple.containerization.sandbox.v3.SandboxContext/Kill",
18231891
type: GRPCCallType.unary
18241892
)
1893+
1894+
public static let notifyFileSystemEvent = GRPCMethodDescriptor(
1895+
name: "NotifyFileSystemEvent",
1896+
path: "/com.apple.containerization.sandbox.v3.SandboxContext/NotifyFileSystemEvent",
1897+
type: GRPCCallType.bidirectionalStreaming
1898+
)
18251899
}
18261900
}
18271901

@@ -1912,6 +1986,9 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextProvider: Ca
19121986

19131987
/// Send a signal to a process via the PID.
19141988
func kill(request: Com_Apple_Containerization_Sandbox_V3_KillRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_KillResponse>
1989+
1990+
/// Notify guest of filesystem events from host.
1991+
func notifyFileSystemEvent(context: StreamingResponseCallContext<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse>) -> EventLoopFuture<(StreamEvent<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest>) -> Void>
19151992
}
19161993

19171994
extension Com_Apple_Containerization_Sandbox_V3_SandboxContextProvider {
@@ -2169,6 +2246,15 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextProvider {
21692246
userFunction: self.kill(request:context:)
21702247
)
21712248

2249+
case "NotifyFileSystemEvent":
2250+
return BidirectionalStreamingServerHandler(
2251+
context: context,
2252+
requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest>(),
2253+
responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse>(),
2254+
interceptors: self.interceptors?.makeNotifyFileSystemEventInterceptors() ?? [],
2255+
observerFactory: self.notifyFileSystemEvent(context:)
2256+
)
2257+
21722258
default:
21732259
return nil
21742260
}
@@ -2345,6 +2431,13 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvide
23452431
request: Com_Apple_Containerization_Sandbox_V3_KillRequest,
23462432
context: GRPCAsyncServerCallContext
23472433
) async throws -> Com_Apple_Containerization_Sandbox_V3_KillResponse
2434+
2435+
/// Notify guest of filesystem events from host.
2436+
func notifyFileSystemEvent(
2437+
requestStream: GRPCAsyncRequestStream<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest>,
2438+
responseStream: GRPCAsyncResponseStreamWriter<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse>,
2439+
context: GRPCAsyncServerCallContext
2440+
) async throws
23482441
}
23492442

23502443
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
@@ -2609,6 +2702,15 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvider {
26092702
wrapping: { try await self.kill(request: $0, context: $1) }
26102703
)
26112704

2705+
case "NotifyFileSystemEvent":
2706+
return GRPCAsyncServerHandler(
2707+
context: context,
2708+
requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest>(),
2709+
responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse>(),
2710+
interceptors: self.interceptors?.makeNotifyFileSystemEventInterceptors() ?? [],
2711+
wrapping: { try await self.notifyFileSystemEvent(requestStream: $0, responseStream: $1, context: $2) }
2712+
)
2713+
26122714
default:
26132715
return nil
26142716
}
@@ -2724,6 +2826,10 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextServerInterc
27242826
/// - Returns: Interceptors to use when handling 'kill'.
27252827
/// Defaults to calling `self.makeInterceptors()`.
27262828
func makeKillInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse>]
2829+
2830+
/// - Returns: Interceptors to use when handling 'notifyFileSystemEvent'.
2831+
/// Defaults to calling `self.makeInterceptors()`.
2832+
func makeNotifyFileSystemEventInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventRequest, Com_Apple_Containerization_Sandbox_V3_NotifyFileSystemEventResponse>]
27272833
}
27282834

27292835
public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata {
@@ -2758,6 +2864,7 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata {
27582864
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.configureHosts,
27592865
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.sync,
27602866
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.kill,
2867+
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.notifyFileSystemEvent,
27612868
]
27622869
)
27632870

@@ -2923,5 +3030,11 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata {
29233030
path: "/com.apple.containerization.sandbox.v3.SandboxContext/Kill",
29243031
type: GRPCCallType.unary
29253032
)
3033+
3034+
public static let notifyFileSystemEvent = GRPCMethodDescriptor(
3035+
name: "NotifyFileSystemEvent",
3036+
path: "/com.apple.containerization.sandbox.v3.SandboxContext/NotifyFileSystemEvent",
3037+
type: GRPCCallType.bidirectionalStreaming
3038+
)
29263039
}
29273040
}

0 commit comments

Comments
 (0)