Skip to content

Commit 88223d8

Browse files
authored
Select alternate data path with container system start --app-root path. (#419)
Closes #418.
1 parent b8965ca commit 88223d8

File tree

20 files changed

+253
-135
lines changed

20 files changed

+253
-135
lines changed

.github/workflows/common.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ jobs:
6868
- name: Test the container project
6969
run: |
7070
launchctl setenv HTTP_PROXY $HTTP_PROXY
71-
make test cleancontent install-kernel integration
71+
APP_ROOT=$(mktemp -d -p "${RUNNER_TEMP}")
72+
trap 'rm -rf "${APP_ROOT}"; echo Removing data directory ${APP_ROOT}' EXIT
73+
echo "Created data directory ${APP_ROOT}"
74+
make APP_ROOT="${APP_ROOT}" test install-kernel integration
7275
env:
7376
DEVELOPER_DIR: "/Applications/Xcode-latest.app/Contents/Developer"
7477

Makefile

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ DSYM_DIR := bin/$(BUILD_CONFIGURATION)/bundle/container-dSYM
2828
DSYM_PATH := bin/$(BUILD_CONFIGURATION)/bundle/container-dSYM.zip
2929
CODESIGN_OPTS ?= --force --sign - --timestamp=none
3030

31+
# Conditionally use a temporary data directory for integration tests
32+
ifeq ($(strip $(APP_ROOT)),)
33+
SYSTEM_START_OPTS :=
34+
else
35+
SYSTEM_START_OPTS := --app-root "$(strip $(APP_ROOT))"
36+
endif
37+
3138
MACOS_VERSION := $(shell sw_vers -productVersion)
3239
MACOS_MAJOR := $(shell echo $(MACOS_VERSION) | cut -d. -f1)
3340

@@ -124,28 +131,30 @@ test:
124131
.PHONY: install-kernel
125132
install-kernel:
126133
@bin/container system stop || true
127-
@bin/container system start --enable-kernel-install
134+
@bin/container system start --enable-kernel-install $(SYSTEM_START_OPTS)
128135

129136
.PHONY: integration
130137
integration: init-block
131138
@echo Ensuring apiserver stopped before the CLI integration tests...
132139
@bin/container system stop && sleep 3 && scripts/ensure-container-stopped.sh
133140
@echo Running the integration tests...
134-
@bin/container system start
135-
@echo "Removing any existing containers"
136-
@bin/container rm --all
137-
@echo "Starting CLI integration tests"
138-
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLINetwork
139-
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunLifecycle
140-
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIExecCommand
141-
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLICreateCommand
142-
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunCommand
143-
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIImagesCommand
144-
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunBase
145-
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIBuildBase
146-
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIVolumes
147-
@echo Ensuring apiserver stopped after the CLI integration tests...
148-
@scripts/ensure-container-stopped.sh
141+
bin/container system start $(SYSTEM_START_OPTS) && \
142+
echo "Starting CLI integration tests" && \
143+
{ \
144+
exit_code=0; \
145+
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLINetwork || exit_code=1 ; \
146+
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunLifecycle || exit_code=1 ; \
147+
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIExecCommand || exit_code=1 ; \
148+
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLICreateCommand || exit_code=1 ; \
149+
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunCommand || exit_code=1 ; \
150+
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIImagesCommand || exit_code=1 ; \
151+
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunBase || exit_code=1 ; \
152+
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIBuildBase || exit_code=1 ; \
153+
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIVolumes || exit_code=1 ; \
154+
echo Ensuring apiserver stopped after the CLI integration tests ; \
155+
scripts/ensure-container-stopped.sh ; \
156+
exit $${exit_code} ; \
157+
}
149158

150159
.PHONY: fmt
151160
fmt: swift-fmt update-licenses

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ let package = Package(
164164
.product(name: "Containerization", package: "containerization"),
165165
"CVersion",
166166
"ContainerLog",
167+
"ContainerPlugin",
167168
"ContainerXPC",
168169
"ContainerImagesService",
169170
],

Sources/APIServer/APIServer.swift

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,11 @@ struct APIServer: AsyncParsableCommand {
4343
@Flag(name: .long, help: "Enable debug logging")
4444
var debug = false
4545

46-
@Option(name: .shortAndLong, help: "Daemon root directory")
47-
var root = Self.appRoot.path
46+
var appRoot = ApplicationRoot.url
4847

49-
static let appRoot: URL = {
50-
FileManager.default.urls(
51-
for: .applicationSupportDirectory,
52-
in: .userDomainMask
53-
).first!
54-
.appendingPathComponent("com.apple.container")
55-
}()
48+
static func releaseVersion() -> String {
49+
(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? get_release_version().map { String(cString: $0) } ?? "0.0.0"
50+
}
5651

5752
func run() async throws {
5853
let commandName = Self.configuration.commandName ?? "container-apiserver"
@@ -64,20 +59,18 @@ struct APIServer: AsyncParsableCommand {
6459

6560
do {
6661
log.info("configuring XPC server")
67-
let root = URL(filePath: root)
6862
var routes = [XPCRoute: XPCServer.RouteHandler]()
6963
let pluginLoader = try initializePluginLoader(log: log)
7064
try await initializePlugins(pluginLoader: pluginLoader, log: log, routes: &routes)
71-
let containersService = try initializeContainerService(root: root, pluginLoader: pluginLoader, log: log, routes: &routes)
65+
let containersService = try initializeContainerService(pluginLoader: pluginLoader, log: log, routes: &routes)
7266
let networkService = try await initializeNetworkService(
73-
root: root,
7467
pluginLoader: pluginLoader,
7568
log: log,
7669
routes: &routes
7770
)
7871
initializeHealthCheckService(log: log, routes: &routes)
7972
try initializeKernelService(log: log, routes: &routes)
80-
try initializeVolumeService(root: root, containersService: containersService, log: log, routes: &routes)
73+
try initializeVolumeService(containersService: containersService, log: log, routes: &routes)
8174

8275
let server = XPCServer(
8376
identifier: "com.apple.container.apiserver",
@@ -134,7 +127,10 @@ struct APIServer: AsyncParsableCommand {
134127
.deletingLastPathComponent()
135128
.appendingPathComponent("..")
136129
.standardized
137-
let pluginsURL = PluginLoader.userPluginsDir(root: installRoot)
130+
log.info("initializing plugin loader", metadata: ["installRoot": "\(installRoot.path(percentEncoded: false))"])
131+
132+
let pluginsURL = PluginLoader.userPluginsDir(installRoot: installRoot)
133+
log.info("detecting user plugins directory", metadata: ["path": "\(pluginsURL.path(percentEncoded: false))"])
138134
var directoryExists: ObjCBool = false
139135
_ = FileManager.default.fileExists(atPath: pluginsURL.path, isDirectory: &directoryExists)
140136
let userPluginsURL = directoryExists.boolValue ? pluginsURL : nil
@@ -161,10 +157,19 @@ struct APIServer: AsyncParsableCommand {
161157
AppBundlePluginFactory(),
162158
]
163159

164-
log.info("PLUGINS: \(pluginDirectories)")
165-
let statePath = PluginLoader.defaultPluginResourcePath(root: Self.appRoot)
160+
for pluginDirectory in pluginDirectories {
161+
log.info("discovered plugin directory", metadata: ["path": "\(pluginDirectory.path(percentEncoded: false))"])
162+
}
163+
164+
let statePath = PluginLoader.defaultPluginResourcePath(root: appRoot)
166165
try FileManager.default.createDirectory(at: statePath, withIntermediateDirectories: true)
167-
return PluginLoader(pluginDirectories: pluginDirectories, pluginFactories: pluginFactories, defaultResourcePath: statePath, log: log)
166+
return PluginLoader(
167+
appRoot: appRoot,
168+
pluginDirectories: pluginDirectories,
169+
pluginFactories: pluginFactories,
170+
defaultResourcePath: statePath,
171+
log: log
172+
)
168173
}
169174

170175
// First load all of the plugins we can find. Then just expose
@@ -188,20 +193,20 @@ struct APIServer: AsyncParsableCommand {
188193
}
189194

190195
private func initializeHealthCheckService(log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) {
191-
let svc = HealthCheckHarness(log: log)
196+
let svc = HealthCheckHarness(appRoot: appRoot, log: log)
192197
routes[XPCRoute.ping] = svc.ping
193198
}
194199

195200
private func initializeKernelService(log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws {
196-
let svc = try KernelService(log: log, appRoot: Self.appRoot)
201+
let svc = try KernelService(log: log, appRoot: appRoot)
197202
let harness = KernelHarness(service: svc, log: log)
198203
routes[XPCRoute.installKernel] = harness.install
199204
routes[XPCRoute.getDefaultKernel] = harness.getDefaultKernel
200205
}
201206

202-
private func initializeContainerService(root: URL, pluginLoader: PluginLoader, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws -> ContainersService {
207+
private func initializeContainerService(pluginLoader: PluginLoader, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws -> ContainersService {
203208
let service = try ContainersService(
204-
root: root,
209+
appRoot: appRoot,
205210
pluginLoader: pluginLoader,
206211
log: log
207212
)
@@ -217,12 +222,11 @@ struct APIServer: AsyncParsableCommand {
217222
}
218223

219224
private func initializeNetworkService(
220-
root: URL,
221225
pluginLoader: PluginLoader,
222226
log: Logger,
223227
routes: inout [XPCRoute: XPCServer.RouteHandler]
224228
) async throws -> NetworksService {
225-
let resourceRoot = root.appendingPathComponent("networks")
229+
let resourceRoot = appRoot.appendingPathComponent("networks")
226230
let service = try await NetworksService(
227231
pluginLoader: pluginLoader,
228232
resourceRoot: resourceRoot,
@@ -245,8 +249,8 @@ struct APIServer: AsyncParsableCommand {
245249
return service
246250
}
247251

248-
private func initializeVolumeService(root: URL, containersService: ContainersService, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws {
249-
let resourceRoot = root.appendingPathComponent("volumes")
252+
private func initializeVolumeService(containersService: ContainersService, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws {
253+
let resourceRoot = appRoot.appendingPathComponent("volumes")
250254
let service = try VolumesService(resourceRoot: resourceRoot, containersService: containersService, log: log)
251255
let harness = VolumesHarness(service: service, log: log)
252256

@@ -255,8 +259,4 @@ struct APIServer: AsyncParsableCommand {
255259
routes[XPCRoute.volumeList] = harness.list
256260
routes[XPCRoute.volumeInspect] = harness.inspect
257261
}
258-
259-
private static func releaseVersion() -> String {
260-
(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? get_release_version().map { String(cString: $0) } ?? "0.0.0"
261-
}
262262
}

Sources/APIServer/Containers/ContainersService.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ actor ContainersService {
5656
}
5757
}
5858

59-
public init(root: URL, pluginLoader: PluginLoader, log: Logger) throws {
60-
let containerRoot = root.appendingPathComponent("containers")
59+
public init(appRoot: URL, pluginLoader: PluginLoader, log: Logger) throws {
60+
let containerRoot = appRoot.appendingPathComponent("containers")
6161
try FileManager.default.createDirectory(at: containerRoot, withIntermediateDirectories: true)
6262
self.containerRoot = containerRoot
6363
self.pluginLoader = pluginLoader
@@ -200,7 +200,7 @@ actor ContainersService {
200200
]
201201
try loader.registerWithLaunchd(
202202
plugin: plugin,
203-
rootURL: path,
203+
pluginStateRoot: path,
204204
args: args,
205205
instanceId: configuration.id
206206
)

Sources/APIServer/HealthCheck/HealthCheckHarness.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,28 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17+
import CVersion
1718
import ContainerClient
1819
import ContainerXPC
1920
import Containerization
21+
import Foundation
2022
import Logging
2123

2224
actor HealthCheckHarness {
25+
private let appRoot: URL
2326
private let log: Logger
2427

25-
public init(log: Logger) {
28+
public init(appRoot: URL, log: Logger) {
29+
self.appRoot = appRoot
2630
self.log = log
2731
}
2832

2933
@Sendable
3034
func ping(_ message: XPCMessage) async -> XPCMessage {
31-
message.reply()
35+
let reply = message.reply()
36+
reply.set(key: .appRoot, value: appRoot.absoluteString)
37+
reply.set(key: .apiServerVersion, value: APIServer.releaseVersion())
38+
reply.set(key: .apiServerCommit, value: get_git_commit().map { String(cString: $0) } ?? "unknown")
39+
return reply
3240
}
3341
}

Sources/APIServer/Networks/NetworksService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ actor NetworksService {
230230

231231
try await pluginLoader.registerWithLaunchd(
232232
plugin: networkPlugin,
233-
rootURL: store.entityUrl(configuration.id),
233+
pluginStateRoot: store.entityUrl(configuration.id),
234234
args: args,
235235
instanceId: configuration.id
236236
)

0 commit comments

Comments
 (0)