mirror of
https://github.com/apple/container.git
synced 2026-07-12 20:47:04 +00:00
356c8d2f88
- Closes #461. - Extract core types into ContainerResources target. - Extract ContainerNetworkServiceClient from ContainerNetworkService. - Relocate sandbox client from ContainerClient to ContainerSandboxServiceClient. - Relocate ContainerClient to ContainerAPIServiceClient. - Common structure from services and clients under Source/Services. Updated project hierarchy: ``` Sources/CAuditToken - audit token access wrapper Sources/CLI - CLI executable Sources/ContainerBuild - builder Sources/ContainerCommands - CLI command implementations Sources/ContainerLog - logging helpers Sources/ContainerPersistence - persistent data and system property helpers Sources/ContainerPlugin - plugin system Sources/ContainerResource - resource (container, image, volume, network) types Sources/ContainerVersion - version helpers Sources/ContainerXPC - XPC helpers Sources/CVersion - injected project version Sources/DNSServer - container DNS resolver Sources/Helpers - service executables Sources/Services/*/Client - service clients Sources/Services/*/Server - service implementations Sources/SocketForwarder - port forwarding Sources/TerminalProgress - progress bar ``` ## Type of Change - [ ] Bug fix - [ ] New feature - [x] Breaking change - [ ] Documentation update ## Motivation and Context The ContainerClient library was a bit of a grab bag. This refactor applies a more sensible project and library structure for resource data types, services, and clients. ## Testing - [x] Tested locally - [x] Added/updated tests - [ ] Added/updated docs
189 lines
7.0 KiB
Swift
189 lines
7.0 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
// Copyright © 2025-2026 Apple Inc. and the container project authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import ContainerAPIClient
|
|
import Containerization
|
|
import ContainerizationArchive
|
|
import ContainerizationOCI
|
|
import Foundation
|
|
import GRPC
|
|
|
|
struct BuildRemoteContentProxy: BuildPipelineHandler {
|
|
let local: ContentStore
|
|
|
|
public init(_ contentStore: ContentStore) throws {
|
|
self.local = contentStore
|
|
}
|
|
|
|
func accept(_ packet: ServerStream) throws -> Bool {
|
|
guard let imageTransfer = packet.getImageTransfer() else {
|
|
return false
|
|
}
|
|
guard imageTransfer.stage() == "content-store" else {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func handle(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ServerStream) async throws {
|
|
guard let imageTransfer = packet.getImageTransfer() else {
|
|
throw Error.imageTransferMissing
|
|
}
|
|
|
|
guard let method = imageTransfer.method() else {
|
|
throw Error.methodMissing
|
|
}
|
|
|
|
switch try ContentStoreMethod(method) {
|
|
case .info:
|
|
try await self.info(sender, imageTransfer, packet.buildID)
|
|
case .readerAt:
|
|
try await self.readerAt(sender, imageTransfer, packet.buildID)
|
|
default:
|
|
throw Error.unknownMethod(method)
|
|
}
|
|
}
|
|
|
|
func info(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer, _ buildID: String) async throws {
|
|
let descriptor = try await local.get(digest: packet.tag)
|
|
let size = try descriptor?.size()
|
|
let transfer = try ImageTransfer(
|
|
id: packet.id,
|
|
digest: packet.tag,
|
|
method: ContentStoreMethod.info.rawValue,
|
|
size: size
|
|
)
|
|
var response = ClientStream()
|
|
response.buildID = buildID
|
|
response.imageTransfer = transfer
|
|
response.packetType = .imageTransfer(transfer)
|
|
sender.yield(response)
|
|
}
|
|
|
|
func readerAt(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer, _ buildID: String) async throws {
|
|
let digest = packet.descriptor.digest
|
|
let offset: UInt64 = packet.offset() ?? 0
|
|
let size: Int = packet.len() ?? 0
|
|
guard let descriptor = try await local.get(digest: digest) else {
|
|
throw Error.contentMissing
|
|
}
|
|
if offset == 0 && size == 0 { // Metadata request
|
|
var transfer = try ImageTransfer(
|
|
id: packet.id,
|
|
digest: packet.tag,
|
|
method: ContentStoreMethod.readerAt.rawValue,
|
|
size: descriptor.size(),
|
|
data: Data()
|
|
)
|
|
transfer.complete = true
|
|
var response = ClientStream()
|
|
response.buildID = buildID
|
|
response.imageTransfer = transfer
|
|
response.packetType = .imageTransfer(transfer)
|
|
sender.yield(response)
|
|
return
|
|
}
|
|
guard let data = try descriptor.data(offset: offset, length: size) else {
|
|
throw Error.invalidOffsetSizeForContent(packet.descriptor.digest, offset, size)
|
|
}
|
|
|
|
let transfer = try ImageTransfer(
|
|
id: packet.id,
|
|
digest: packet.tag,
|
|
method: ContentStoreMethod.readerAt.rawValue,
|
|
size: UInt64(data.count),
|
|
data: data
|
|
)
|
|
var response = ClientStream()
|
|
response.buildID = buildID
|
|
response.imageTransfer = transfer
|
|
response.packetType = .imageTransfer(transfer)
|
|
sender.yield(response)
|
|
}
|
|
|
|
func delete(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer) async throws {
|
|
throw NSError(domain: "RemoteContentProxy", code: 1, userInfo: [NSLocalizedDescriptionKey: "unimplemented method \(ContentStoreMethod.delete)"])
|
|
}
|
|
|
|
func update(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer) async throws {
|
|
throw NSError(domain: "RemoteContentProxy", code: 1, userInfo: [NSLocalizedDescriptionKey: "unimplemented method \(ContentStoreMethod.update)"])
|
|
}
|
|
|
|
func walk(_ sender: AsyncStream<ClientStream>.Continuation, _ packet: ImageTransfer) async throws {
|
|
throw NSError(domain: "RemoteContentProxy", code: 1, userInfo: [NSLocalizedDescriptionKey: "unimplemented method \(ContentStoreMethod.walk)"])
|
|
}
|
|
|
|
enum ContentStoreMethod: String {
|
|
case info = "/containerd.services.content.v1.Content/Info"
|
|
case readerAt = "/containerd.services.content.v1.Content/ReaderAt"
|
|
case delete = "/containerd.services.content.v1.Content/Delete"
|
|
case update = "/containerd.services.content.v1.Content/Update"
|
|
case walk = "/containerd.services.content.v1.Content/Walk"
|
|
|
|
init(_ method: String) throws {
|
|
guard let value = ContentStoreMethod(rawValue: method) else {
|
|
throw Error.unknownMethod(method)
|
|
}
|
|
self = value
|
|
}
|
|
}
|
|
}
|
|
|
|
extension ImageTransfer {
|
|
fileprivate init(id: String, digest: String, method: String, size: UInt64? = nil, data: Data = Data()) throws {
|
|
self.init()
|
|
self.id = id
|
|
self.tag = digest
|
|
self.metadata = [
|
|
"os": "linux",
|
|
"stage": "content-store",
|
|
"method": method,
|
|
]
|
|
if let size {
|
|
self.metadata["size"] = String(size)
|
|
}
|
|
self.complete = true
|
|
self.direction = .into
|
|
self.data = data
|
|
}
|
|
}
|
|
|
|
extension BuildRemoteContentProxy {
|
|
enum Error: Swift.Error, CustomStringConvertible {
|
|
case imageTransferMissing
|
|
case methodMissing
|
|
case contentMissing
|
|
case unknownMethod(String)
|
|
case invalidOffsetSizeForContent(String, UInt64, Int)
|
|
|
|
var description: String {
|
|
switch self {
|
|
case .imageTransferMissing:
|
|
return "imageTransfer is missing"
|
|
case .methodMissing:
|
|
return "method is missing in request"
|
|
case .contentMissing:
|
|
return "content cannot be found"
|
|
case .unknownMethod(let m):
|
|
return "unknown content-store method \(m)"
|
|
case .invalidOffsetSizeForContent(let digest, let offset, let size):
|
|
return "invalid request for content: \(digest) with offset: \(offset) size: \(size)"
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|