Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 ${TMP_DIR}' 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
37 changes: 21 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,26 @@ 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" ; \
$(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

.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.formatted())
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