Skip to content
Merged
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
5 changes: 4 additions & 1 deletion .github/workflows/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ jobs:
- name: Test the container project
run: |
launchctl setenv HTTP_PROXY $HTTP_PROXY
make test cleancontent install-kernel integration
APP_ROOT=$(mktemp -d -p "${RUNNER_TEMP}")
trap 'rm -rf "${APP_ROOT}"; echo Removing data directory ${APP_ROOT}' EXIT
echo "Created data directory ${APP_ROOT}"
make APP_ROOT="${APP_ROOT}" test install-kernel integration
env:
DEVELOPER_DIR: "/Applications/Xcode-latest.app/Contents/Developer"

Expand Down
41 changes: 25 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ DSYM_DIR := bin/$(BUILD_CONFIGURATION)/bundle/container-dSYM
DSYM_PATH := bin/$(BUILD_CONFIGURATION)/bundle/container-dSYM.zip
CODESIGN_OPTS ?= --force --sign - --timestamp=none

# Conditionally use a temporary data directory for integration tests
ifeq ($(strip $(APP_ROOT)),)
SYSTEM_START_OPTS :=
else
SYSTEM_START_OPTS := --app-root "$(strip $(APP_ROOT))"
endif

MACOS_VERSION := $(shell sw_vers -productVersion)
MACOS_MAJOR := $(shell echo $(MACOS_VERSION) | cut -d. -f1)

Expand Down Expand Up @@ -124,28 +131,30 @@ test:
.PHONY: install-kernel
install-kernel:
@bin/container system stop || true
@bin/container system start --enable-kernel-install
@bin/container system start --enable-kernel-install $(SYSTEM_START_OPTS)

.PHONY: integration
integration: init-block
@echo Ensuring apiserver stopped before the CLI integration tests...
@bin/container system stop && sleep 3 && scripts/ensure-container-stopped.sh
@echo Running the integration tests...
@bin/container system start
@echo "Removing any existing containers"
@bin/container rm --all
@echo "Starting CLI integration tests"
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLINetwork
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunLifecycle
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIExecCommand
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLICreateCommand
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunCommand
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIImagesCommand
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunBase
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIBuildBase
@$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIVolumes
@echo Ensuring apiserver stopped after the CLI integration tests...
@scripts/ensure-container-stopped.sh
bin/container system start $(SYSTEM_START_OPTS) && \
echo "Starting CLI integration tests" && \
{ \
exit_code=0; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLINetwork || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunLifecycle || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIExecCommand || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLICreateCommand || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunCommand || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIImagesCommand || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIRunBase || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIBuildBase || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) --filter TestCLIVolumes || exit_code=1 ; \
echo Ensuring apiserver stopped after the CLI integration tests ; \
scripts/ensure-container-stopped.sh ; \
exit $${exit_code} ; \
}

.PHONY: fmt
fmt: swift-fmt update-licenses
Expand Down
1 change: 1 addition & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ let package = Package(
.product(name: "Containerization", package: "containerization"),
"CVersion",
"ContainerLog",
"ContainerPlugin",
"ContainerXPC",
"ContainerImagesService",
],
Expand Down
58 changes: 29 additions & 29 deletions Sources/APIServer/APIServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,11 @@ struct APIServer: AsyncParsableCommand {
@Flag(name: .long, help: "Enable debug logging")
var debug = false

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

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

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

do {
log.info("configuring XPC server")
let root = URL(filePath: root)
var routes = [XPCRoute: XPCServer.RouteHandler]()
let pluginLoader = try initializePluginLoader(log: log)
try await initializePlugins(pluginLoader: pluginLoader, log: log, routes: &routes)
let containersService = try initializeContainerService(root: root, pluginLoader: pluginLoader, log: log, routes: &routes)
let containersService = try initializeContainerService(pluginLoader: pluginLoader, log: log, routes: &routes)
let networkService = try await initializeNetworkService(
root: root,
pluginLoader: pluginLoader,
log: log,
routes: &routes
)
initializeHealthCheckService(log: log, routes: &routes)
try initializeKernelService(log: log, routes: &routes)
try initializeVolumeService(root: root, containersService: containersService, log: log, routes: &routes)
try initializeVolumeService(containersService: containersService, log: log, routes: &routes)

let server = XPCServer(
identifier: "com.apple.container.apiserver",
Expand Down Expand Up @@ -134,7 +127,10 @@ struct APIServer: AsyncParsableCommand {
.deletingLastPathComponent()
.appendingPathComponent("..")
.standardized
let pluginsURL = PluginLoader.userPluginsDir(root: installRoot)
log.info("initializing plugin loader", metadata: ["installRoot": "\(installRoot.path(percentEncoded: false))"])

let pluginsURL = PluginLoader.userPluginsDir(installRoot: installRoot)
log.info("detecting user plugins directory", metadata: ["path": "\(pluginsURL.path(percentEncoded: false))"])
var directoryExists: ObjCBool = false
_ = FileManager.default.fileExists(atPath: pluginsURL.path, isDirectory: &directoryExists)
let userPluginsURL = directoryExists.boolValue ? pluginsURL : nil
Expand All @@ -161,10 +157,19 @@ struct APIServer: AsyncParsableCommand {
AppBundlePluginFactory(),
]

log.info("PLUGINS: \(pluginDirectories)")
let statePath = PluginLoader.defaultPluginResourcePath(root: Self.appRoot)
for pluginDirectory in pluginDirectories {
log.info("discovered plugin directory", metadata: ["path": "\(pluginDirectory.path(percentEncoded: false))"])
}

let statePath = PluginLoader.defaultPluginResourcePath(root: appRoot)
try FileManager.default.createDirectory(at: statePath, withIntermediateDirectories: true)
return PluginLoader(pluginDirectories: pluginDirectories, pluginFactories: pluginFactories, defaultResourcePath: statePath, log: log)
return PluginLoader(
appRoot: appRoot,
pluginDirectories: pluginDirectories,
pluginFactories: pluginFactories,
defaultResourcePath: statePath,
log: log
)
}

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

private func initializeHealthCheckService(log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) {
let svc = HealthCheckHarness(log: log)
let svc = HealthCheckHarness(appRoot: appRoot, log: log)
routes[XPCRoute.ping] = svc.ping
}

private func initializeKernelService(log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws {
let svc = try KernelService(log: log, appRoot: Self.appRoot)
let svc = try KernelService(log: log, appRoot: appRoot)
let harness = KernelHarness(service: svc, log: log)
routes[XPCRoute.installKernel] = harness.install
routes[XPCRoute.getDefaultKernel] = harness.getDefaultKernel
}

private func initializeContainerService(root: URL, pluginLoader: PluginLoader, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws -> ContainersService {
private func initializeContainerService(pluginLoader: PluginLoader, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws -> ContainersService {
let service = try ContainersService(
root: root,
appRoot: appRoot,
pluginLoader: pluginLoader,
log: log
)
Expand All @@ -217,12 +222,11 @@ struct APIServer: AsyncParsableCommand {
}

private func initializeNetworkService(
root: URL,
pluginLoader: PluginLoader,
log: Logger,
routes: inout [XPCRoute: XPCServer.RouteHandler]
) async throws -> NetworksService {
let resourceRoot = root.appendingPathComponent("networks")
let resourceRoot = appRoot.appendingPathComponent("networks")
let service = try await NetworksService(
pluginLoader: pluginLoader,
resourceRoot: resourceRoot,
Expand All @@ -245,8 +249,8 @@ struct APIServer: AsyncParsableCommand {
return service
}

private func initializeVolumeService(root: URL, containersService: ContainersService, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws {
let resourceRoot = root.appendingPathComponent("volumes")
private func initializeVolumeService(containersService: ContainersService, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws {
let resourceRoot = appRoot.appendingPathComponent("volumes")
let service = try VolumesService(resourceRoot: resourceRoot, containersService: containersService, log: log)
let harness = VolumesHarness(service: service, log: log)

Expand All @@ -255,8 +259,4 @@ struct APIServer: AsyncParsableCommand {
routes[XPCRoute.volumeList] = harness.list
routes[XPCRoute.volumeInspect] = harness.inspect
}

private static func releaseVersion() -> String {
(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? get_release_version().map { String(cString: $0) } ?? "0.0.0"
}
}
6 changes: 3 additions & 3 deletions Sources/APIServer/Containers/ContainersService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ actor ContainersService {
}
}

public init(root: URL, pluginLoader: PluginLoader, log: Logger) throws {
let containerRoot = root.appendingPathComponent("containers")
public init(appRoot: URL, pluginLoader: PluginLoader, log: Logger) throws {
let containerRoot = appRoot.appendingPathComponent("containers")
try FileManager.default.createDirectory(at: containerRoot, withIntermediateDirectories: true)
self.containerRoot = containerRoot
self.pluginLoader = pluginLoader
Expand Down Expand Up @@ -200,7 +200,7 @@ actor ContainersService {
]
try loader.registerWithLaunchd(
plugin: plugin,
rootURL: path,
pluginStateRoot: path,
args: args,
instanceId: configuration.id
)
Expand Down
12 changes: 10 additions & 2 deletions Sources/APIServer/HealthCheck/HealthCheckHarness.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,28 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

import CVersion
import ContainerClient
import ContainerXPC
import Containerization
import Foundation
import Logging

actor HealthCheckHarness {
private let appRoot: URL
private let log: Logger

public init(log: Logger) {
public init(appRoot: URL, log: Logger) {
self.appRoot = appRoot
self.log = log
}

@Sendable
func ping(_ message: XPCMessage) async -> XPCMessage {
message.reply()
let reply = message.reply()
reply.set(key: .appRoot, value: appRoot.absoluteString)
reply.set(key: .apiServerVersion, value: APIServer.releaseVersion())
reply.set(key: .apiServerCommit, value: get_git_commit().map { String(cString: $0) } ?? "unknown")
return reply
}
}
2 changes: 1 addition & 1 deletion Sources/APIServer/Networks/NetworksService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ actor NetworksService {

try await pluginLoader.registerWithLaunchd(
plugin: networkPlugin,
rootURL: store.entityUrl(configuration.id),
pluginStateRoot: store.entityUrl(configuration.id),
args: args,
instanceId: configuration.id
)
Expand Down
Loading