Skip to content
Closed
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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,9 @@ define RUN_INTEGRATION
echo "==> Warmup pass" && \
$(SWIFT) test $(INTEGRATION_SWIFT_EXTRA) -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter "$(WARMUP_FILTER)" && \
echo "==> Concurrent pass (width=$(PARALLEL_WIDTH))" && \
$(SWIFT) test $(INTEGRATION_SWIFT_EXTRA) -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --experimental-maximum-parallelization-width $(PARALLEL_WIDTH) --filter "$(CONCURRENT_FILTER)" && \
$(SWIFT) test $(INTEGRATION_SWIFT_EXTRA) -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --parallel --num-workers $(PARALLEL_WIDTH) --filter "$(CONCURRENT_FILTER)" && \
Comment thread
lakshitsoni26 marked this conversation as resolved.
echo "==> Global pass (serial)" && \
$(SWIFT) test $(INTEGRATION_SWIFT_EXTRA) -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --experimental-maximum-parallelization-width 1 --filter "$(SERIAL_FILTER)" ; \
$(SWIFT) test $(INTEGRATION_SWIFT_EXTRA) -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --no-parallel --filter "$(SERIAL_FILTER)" ; \
exit_code=$$? ; \
$(INTEGRATION_POST_TEST) \
echo Ensuring apiserver stopped after the CLI integration tests ; \
Expand Down
16 changes: 1 addition & 15 deletions Sources/ContainerBuild/BuildFSSync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,6 @@ actor BuildFSSync: BuildPipelineHandler {
let entry = DirEntry(url: url, isDirectory: url.hasDirectoryPath, relativePath: relPath)
entries[parentPath, default: []].insert(entry)

if url.isSymlink {
let target: URL = url.resolvingSymlinksInPath()
if self.contextDir.parentOf(target) {
let relPath = try target.relativeChildPath(to: self.contextDir)
let entry = DirEntry(url: target, isDirectory: target.hasDirectoryPath, relativePath: relPath)
let parentPath: String = try target.deletingLastPathComponent().relativeChildPath(to: self.contextDir)
entries[parentPath, default: []].insert(entry)
}
}
}

var fileOrder = [String]()
Expand Down Expand Up @@ -335,12 +326,7 @@ actor BuildFSSync: BuildPipelineHandler {

init(path: URL, contextDir: URL) throws {
if path.isSymlink {
let target: URL = path.resolvingSymlinksInPath()
if contextDir.parentOf(target) {
self.target = target.relativePathFrom(from: path)
} else {
self.target = target.cleanPath
}
self.target = try FileManager.default.destinationOfSymbolicLink(atPath: path.cleanPath)
} else {
self.target = ""
}
Expand Down
4 changes: 3 additions & 1 deletion Sources/ContainerBuild/Builder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,9 @@ public struct Builder: Sendable {

switch type {
case "oci", "tar", "local":
break // ignore destination
if let destination = destination {
components.append("dest=\(destination.path(percentEncoded: false))")
}
default:
throw Builder.Error.invalidExport(rawValue, "unsupported output type")
}
Expand Down
19 changes: 13 additions & 6 deletions Sources/ContainerBuild/URL+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,16 @@ extension URL {
self.path.fs_cleaned
}

private func stripPrivatePrefix(_ path: String) -> String {
Comment thread
lakshitsoni26 marked this conversation as resolved.
if path.hasPrefix("/private/") {
return String(path.dropFirst(8))
}
return path
}

func parentOf(_ url: URL) -> Bool {
let parentPath = self.absoluteURL.cleanPath
let childPath = url.absoluteURL.cleanPath
let parentPath = stripPrivatePrefix(self.absoluteURL.cleanPath)
let childPath = stripPrivatePrefix(url.absoluteURL.cleanPath)

guard parentPath.fs_isAbsolute else {
return true
Expand All @@ -74,15 +81,15 @@ extension URL {
throw BuildFSSync.Error.pathIsNotChild(cleanPath, context.cleanPath)
}

let ctxParts = context.cleanPath.fs_components
let selfParts = cleanPath.fs_components
let ctxParts = stripPrivatePrefix(context.absoluteURL.cleanPath).fs_components
let selfParts = stripPrivatePrefix(self.absoluteURL.cleanPath).fs_components

return selfParts.dropFirst(ctxParts.count).joined(separator: "/")
}

func relativePathFrom(from base: URL) -> String {
let destParts = cleanPath.fs_components
let baseParts = base.cleanPath.fs_components
let destParts = stripPrivatePrefix(self.absoluteURL.cleanPath).fs_components
let baseParts = stripPrivatePrefix(base.absoluteURL.cleanPath).fs_components

let common = zip(destParts, baseParts).prefix { $0 == $1 }.count
guard common > 0 else { return cleanPath }
Expand Down
9 changes: 7 additions & 2 deletions Sources/ContainerCommands/BuildCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,15 @@ extension Application {
}
try fileHandle.close()
buildFileData = try Data(contentsOf: URL(filePath: tempFile.path()))

let contextIgnoreURL = URL(fileURLWithPath: contextDir).appendingPathComponent(".dockerignore")
ignoreFileData = try? Data(contentsOf: contextIgnoreURL)
} else {
let ignoreFileURL = URL(filePath: dockerfile + ".dockerignore")
let specificIgnoreURL = URL(filePath: dockerfile + ".dockerignore")
let contextIgnoreURL = URL(fileURLWithPath: contextDir).appendingPathComponent(".dockerignore")

buildFileData = try Data(contentsOf: URL(filePath: dockerfile))
ignoreFileData = try? Data(contentsOf: ignoreFileURL)
ignoreFileData = (try? Data(contentsOf: specificIgnoreURL)) ?? (try? Data(contentsOf: contextIgnoreURL))
}

// BUG: See https://github.com/apple/container/issues/735.
Expand Down
78 changes: 47 additions & 31 deletions Sources/ContainerXPC/XPCClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,45 +95,61 @@ extension XPCClient {
XPCClientSession(client: self)
}

private actor ContinuationResolver {
Comment thread
lakshitsoni26 marked this conversation as resolved.
var continuation: CheckedContinuation<XPCMessage, Error>?
init(_ continuation: CheckedContinuation<XPCMessage, Error>) {
self.continuation = continuation
}
func resume(returning value: XPCMessage) {
continuation?.resume(returning: value)
continuation = nil
}
func resume(throwing error: Error) {
continuation?.resume(throwing: error)
continuation = nil
}
}

/// Send the provided message to the service.
@discardableResult
public func send(_ message: XPCMessage, responseTimeout: Duration? = nil) async throws -> XPCMessage {
try await withThrowingTaskGroup(of: XPCMessage.self, returning: XPCMessage.self) { group in
if let responseTimeout {
group.addTask {
try await Task.sleep(for: responseTimeout)
let route = message.string(key: XPCMessage.routeKey) ?? "nil"
throw ContainerizationError(
.internalError,
message: "XPC timeout for request to \(self.service)/\(route)"
)
try await withCheckedThrowingContinuation { cont in
let resolver = ContinuationResolver(cont)

let timeoutTask = responseTimeout.map { timeout in
Task {
do {
try await Task.sleep(for: timeout)
let route = message.string(key: XPCMessage.routeKey) ?? "nil"
await resolver.resume(throwing: ContainerizationError(
.internalError,
message: "XPC timeout for request to \(self.service)/\(route)"
))
} catch {
// Task was cancelled before timeout elapsed
}
}
}

group.addTask {
try await withCheckedThrowingContinuation { cont in
xpc_connection_send_message_with_reply(self.connection, message.underlying, nil) { reply in
do {
let message = try self.parseReply(reply)
cont.resume(returning: message)
} catch {
cont.resume(throwing: error)
}

xpc_connection_send_message_with_reply(self.connection, message.underlying, nil) { reply in
timeoutTask?.cancel()

let parsedResult: Result<XPCMessage, Error>
do {
let parsedMessage = try self.parseReply(reply)
parsedResult = .success(parsedMessage)
} catch {
parsedResult = .failure(error)
}
Task {
switch parsedResult {
case .success(let parsedMessage):
await resolver.resume(returning: parsedMessage)
case .failure(let error):
await resolver.resume(throwing: error)
}
}
}

let response = try await group.next()
// once one task has finished, cancel the rest.
group.cancelAll()
// we don't really care about the second error here
// as it's most likely a `CancellationError`.
try? await group.waitForAll()

guard let response else {
throw ContainerizationError(.invalidState, message: "failed to receive XPC response")
}
return response
}
}

Expand Down