Skip to content
Open
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
6 changes: 3 additions & 3 deletions Sources/Containerization/Agent/Vminitd.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ extension Vminitd: VirtualMachineAgent {

try await setenv(key: "PATH", value: Self.defaultPath)

let mounts: [ContainerizationOCI.Mount] = [
let mounts: [OCIMount] = [
.init(type: "sysfs", source: "sysfs", destination: "/sys"),
.init(type: "tmpfs", source: "tmpfs", destination: "/tmp"),
.init(type: "devpts", source: "devpts", destination: "/dev/pts", options: ["gid=5", "mode=620", "ptmxmode=666"]),
Expand All @@ -67,7 +67,7 @@ extension Vminitd: VirtualMachineAgent {
}

/// Mount a filesystem in the sandbox's environment.
public func mount(_ mount: ContainerizationOCI.Mount) async throws {
public func mount(_ mount: OCIMount) async throws {
_ = try await client.mount(
.with {
$0.type = mount.type
Expand Down Expand Up @@ -102,7 +102,7 @@ extension Vminitd: VirtualMachineAgent {
stdinPort: UInt32?,
stdoutPort: UInt32?,
stderrPort: UInt32?,
configuration: ContainerizationOCI.Spec,
configuration: OCISpec,
options: Data?
) async throws {
let enc = JSONEncoder()
Expand Down
16 changes: 8 additions & 8 deletions Sources/Containerization/Image/Image.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ public struct Image: Sendable {
/// The string reference of the image.
public let reference: String
/// The descriptor identifying the image.
public let descriptor: Descriptor
public let descriptor: OCIDescriptor
/// The digest for the image.
public var digest: String { descriptor.digest }
/// The media type of the image.
public var mediaType: String { descriptor.mediaType }

public init(reference: String, descriptor: Descriptor) {
public init(reference: String, descriptor: OCIDescriptor) {
self.reference = reference
self.descriptor = descriptor
}
}

/// The descriptor for the image.
public var descriptor: Descriptor { description.descriptor }
public var descriptor: OCIDescriptor { description.descriptor }
/// The digest of the image.
public var digest: String { description.digest }
/// The media type of the image.
Expand All @@ -57,15 +57,15 @@ public struct Image: Sendable {
}

/// Returns the underlying OCI index for the image.
public func index() async throws -> Index {
public func index() async throws -> OCIIndex {
guard let content: Content = try await contentStore.get(digest: digest) else {
throw ContainerizationError(.notFound, message: "Content with digest \(digest)")
}
return try content.decode()
}

/// Returns the manifest for the specified platform.
public func manifest(for platform: Platform) async throws -> Manifest {
public func manifest(for platform: OCIPlatform) async throws -> OCIManifest {
let index = try await self.index()
let desc = index.manifests.first { desc in
desc.platform == platform
Expand All @@ -81,7 +81,7 @@ public struct Image: Sendable {

/// Returns the descriptor for the given platform. If it does not exist
/// will throw a ContainerizationError with the code set to .invalidArgument.
public func descriptor(for platform: Platform) async throws -> Descriptor {
public func descriptor(for platform: OCIPlatform) async throws -> OCIDescriptor {
let index = try await self.index()
let desc = index.manifests.first { $0.platform == platform }
guard let desc else {
Expand All @@ -91,7 +91,7 @@ public struct Image: Sendable {
}

/// Returns the OCI config for the specified platform.
public func config(for platform: Platform) async throws -> ContainerizationOCI.Image {
public func config(for platform: OCIPlatform) async throws -> OCIImage {
let manifest = try await self.manifest(for: platform)
let desc = manifest.config
guard let content: Content = try await contentStore.get(digest: desc.digest) else {
Expand All @@ -106,7 +106,7 @@ public struct Image: Sendable {
let index = try await self.index()
for manifest in index.manifests {
referenced.append(manifest.digest.trimmingDigestPrefix)
guard let m: Manifest = try? await contentStore.get(digest: manifest.digest) else {
guard let m: OCIManifest = try? await contentStore.get(digest: manifest.digest) else {
// If the requested digest does not exist or is not a manifest. Skip.
// Its safe to skip processing this digest as it wont have any child layers.
continue
Expand Down
32 changes: 16 additions & 16 deletions Sources/Containerization/Image/ImageStore/ImageStore+Export.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ extension ImageStore {
}

@discardableResult
internal func export(index: Descriptor, platforms: (Platform) -> Bool) async throws -> Descriptor {
var pushQueue: [[Descriptor]] = []
var current: [Descriptor] = [index]
internal func export(index: OCIDescriptor, platforms: (OCIPlatform) -> Bool) async throws -> OCIDescriptor {
var pushQueue: [[OCIDescriptor]] = []
var current: [OCIDescriptor] = [index]
while !current.isEmpty {
let children = try await self.getChildren(descs: current)
let matches = try filterPlatforms(matcher: platforms, children).uniqued { $0.digest }
Expand Down Expand Up @@ -78,16 +78,16 @@ extension ImageStore {
// Lastly, we need to construct and push a new index, since we may
// have pushed content only for specific platforms.
let digest = SHA256.hash(data: localIndexData)
let descriptor = Descriptor(
mediaType: MediaTypes.index,
let descriptor = OCIDescriptor(
mediaType: OCIMediaTypes.index,
digest: digest.digestString,
size: Int64(localIndexData.count))
let stream = ReadStream(data: localIndexData)
try await self.pushContent(descriptor: descriptor, stream: stream)
return descriptor
}

private func updatePushProgress(pushQueue: [[Descriptor]], localIndexData: Data) async {
private func updatePushProgress(pushQueue: [[OCIDescriptor]], localIndexData: Data) async {
for layerGroup in pushQueue {
for desc in layerGroup {
await progress?([
Expand All @@ -102,13 +102,13 @@ extension ImageStore {
])
}

private func createIndex(from index: Descriptor, matching: (Platform) -> Bool) async throws -> Data {
private func createIndex(from index: OCIDescriptor, matching: (OCIPlatform) -> Bool) async throws -> Data {
guard let content = try await self.contentStore.get(digest: index.digest) else {
throw ContainerizationError(.notFound, message: "Content with digest \(index.digest)")
}
var idx: Index = try content.decode()
var idx: OCIIndex = try content.decode()
let manifests = idx.manifests
var matchedManifests: [Descriptor] = []
var matchedManifests: [OCIDescriptor] = []
var skippedPlatforms = false
for manifest in manifests {
guard let p = manifest.platform else {
Expand All @@ -127,7 +127,7 @@ extension ImageStore {
return try JSONEncoder().encode(idx)
}

private func pushContent(descriptor: Descriptor, stream: ReadStream) async throws {
private func pushContent(descriptor: OCIDescriptor, stream: ReadStream) async throws {
do {
let generator = {
try stream.reset()
Expand All @@ -151,19 +151,19 @@ extension ImageStore {
}
}

private func getChildren(descs: [Descriptor]) async throws -> [Descriptor] {
var out: [Descriptor] = []
private func getChildren(descs: [OCIDescriptor]) async throws -> [OCIDescriptor] {
var out: [OCIDescriptor] = []
for desc in descs {
let mediaType = desc.mediaType
guard let content = try await self.contentStore.get(digest: desc.digest) else {
throw ContainerizationError(.notFound, message: "Content with digest \(desc.digest)")
}
switch mediaType {
case MediaTypes.index, MediaTypes.dockerManifestList:
let index: Index = try content.decode()
case OCIMediaTypes.index, OCIMediaTypes.dockerManifestList:
let index: OCIIndex = try content.decode()
out.append(contentsOf: index.manifests)
case MediaTypes.imageManifest, MediaTypes.dockerManifest:
let manifest: Manifest = try content.decode()
case OCIMediaTypes.imageManifest, OCIMediaTypes.dockerManifest:
let manifest: OCIManifest = try content.decode()
out.append(manifest.config)
out.append(contentsOf: manifest.layers)
default:
Expand Down
56 changes: 27 additions & 29 deletions Sources/Containerization/Image/ImageStore/ImageStore+Import.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

//

import ContainerizationError
import ContainerizationExtras
import ContainerizationOCI
Expand All @@ -40,7 +38,7 @@ extension ImageStore {
}

/// Pull the required image layers for the provided descriptor and platform(s) into the given directory using the provided client. Returns a descriptor to the Index manifest.
internal func `import`(root: Descriptor, matcher: (ContainerizationOCI.Platform) -> Bool) async throws -> Descriptor {
internal func `import`(root: OCIDescriptor, matcher: (OCIPlatform) -> Bool) async throws -> OCIDescriptor {
var toProcess = [root]
while !toProcess.isEmpty {
// Count the total number of blobs and their size
Expand All @@ -61,14 +59,14 @@ extension ImageStore {
toProcess = filtered.uniqued { $0.digest }
}

guard root.mediaType != MediaTypes.dockerManifestList && root.mediaType != MediaTypes.index else {
guard root.mediaType != OCIMediaTypes.dockerManifestList && root.mediaType != OCIMediaTypes.index else {
return root
}

// Create an index for the root descriptor and write it to the content store
let index = try await self.createIndex(for: root)
// In cases where the root descriptor pointed to `MediaTypes.imageManifest`
// Or `MediaTypes.dockerManifest`, it is required that we check the supported platform
// In cases where the root descriptor pointed to `OCIMediaTypes.imageManifest`
// Or `OCIMediaTypes.dockerManifest`, it is required that we check the supported platform
// matches the platforms we were asked to pull. This can be done only after we created
// the Index.
let supportedPlatforms = index.manifests.compactMap { $0.platform }
Expand All @@ -77,13 +75,13 @@ extension ImageStore {
}
let writer = try ContentWriter(for: self.ingestDir)
let result = try writer.create(from: index)
return Descriptor(
mediaType: MediaTypes.index,
return OCIDescriptor(
mediaType: OCIMediaTypes.index,
digest: result.digest.digestString,
size: Int64(result.size))
}

private func getManifestContent<T: Sendable & Codable>(descriptor: Descriptor) async throws -> T {
private func getManifestContent<T: Sendable & Codable>(descriptor: OCIDescriptor) async throws -> T {
do {
if let content = try await self.contentStore.get(digest: descriptor.digest.trimmingDigestPrefix) {
return try content.decode()
Expand All @@ -97,16 +95,16 @@ extension ImageStore {
}
}

private func walk(_ descriptors: [Descriptor]) async throws -> [Descriptor] {
var out: [Descriptor] = []
private func walk(_ descriptors: [OCIDescriptor]) async throws -> [OCIDescriptor] {
var out: [OCIDescriptor] = []
for desc in descriptors {
let mediaType = desc.mediaType
switch mediaType {
case MediaTypes.index, MediaTypes.dockerManifestList:
let index: Index = try await self.getManifestContent(descriptor: desc)
case OCIMediaTypes.index, OCIMediaTypes.dockerManifestList:
let index: OCIIndex = try await self.getManifestContent(descriptor: desc)
out.append(contentsOf: index.manifests)
case MediaTypes.imageManifest, MediaTypes.dockerManifest:
let manifest: Manifest = try await self.getManifestContent(descriptor: desc)
case OCIMediaTypes.imageManifest, OCIMediaTypes.dockerManifest:
let manifest: OCIManifest = try await self.getManifestContent(descriptor: desc)
out.append(manifest.config)
out.append(contentsOf: manifest.layers)
default:
Expand All @@ -117,7 +115,7 @@ extension ImageStore {
return out
}

private func fetchAll(_ descriptors: [Descriptor]) async throws {
private func fetchAll(_ descriptors: [OCIDescriptor]) async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
var iterator = descriptors.makeIterator()
for _ in 0..<8 {
Expand All @@ -137,7 +135,7 @@ extension ImageStore {
}
}

private func fetch(_ descriptor: Descriptor) async throws {
private func fetch(_ descriptor: OCIDescriptor) async throws {
if let found = try await self.contentStore.get(digest: descriptor.digest) {
try FileManager.default.copyItem(at: found.path, to: ingestDir.appendingPathComponent(descriptor.digest.trimmingDigestPrefix))
await progress?([
Expand All @@ -160,7 +158,7 @@ extension ImageStore {
])
}

private func fetchBlob(_ descriptor: Descriptor) async throws {
private func fetchBlob(_ descriptor: OCIDescriptor) async throws {
let id = UUID().uuidString
let fm = FileManager.default
let tempFile = ingestDir.appendingPathComponent(id)
Expand All @@ -179,7 +177,7 @@ extension ImageStore {
}

@discardableResult
private func fetchData(_ descriptor: Descriptor) async throws -> Data {
private func fetchData(_ descriptor: OCIDescriptor) async throws -> Data {
let data = try await client.fetchData(name: name, descriptor: descriptor)
let writer = try ContentWriter(for: ingestDir)
let result = try writer.write(data)
Expand All @@ -195,11 +193,11 @@ extension ImageStore {
return data
}

private func createIndex(for root: Descriptor) async throws -> Index {
private func createIndex(for root: OCIDescriptor) async throws -> OCIIndex {
switch root.mediaType {
case MediaTypes.index, MediaTypes.dockerManifestList:
case OCIMediaTypes.index, OCIMediaTypes.dockerManifestList:
return try await self.getManifestContent(descriptor: root)
case MediaTypes.imageManifest, MediaTypes.dockerManifest:
case OCIMediaTypes.imageManifest, OCIMediaTypes.dockerManifest:
let supportedPlatforms = try await getSupportedPlatforms(for: root)
guard supportedPlatforms.count == 1 else {
throw ContainerizationError(
Expand All @@ -211,20 +209,20 @@ extension ImageStore {
let platform = supportedPlatforms.first!
var root = root
root.platform = platform
let index = ContainerizationOCI.Index(
let index = OCIIndex(
schemaVersion: 2, manifests: [root],
annotations: [
// indicate that this is a synthesized index which is not directly user facing
AnnotationKeys.containerizationIndexIndirect: "true"
OCIAnnotationKeys.containerizationIndexIndirect: "true"
])
return index
default:
throw ContainerizationError(.internalError, message: "Failed to create index for descriptor \(root.digest), media type \(root.mediaType)")
}
}

private func getSupportedPlatforms(for root: Descriptor) async throws -> [ContainerizationOCI.Platform] {
var supportedPlatforms: [ContainerizationOCI.Platform] = []
private func getSupportedPlatforms(for root: OCIDescriptor) async throws -> [OCIPlatform] {
var supportedPlatforms: [OCIPlatform] = []
var toProcess = [root]
while !toProcess.isEmpty {
let children = try await self.walk(toProcess)
Expand All @@ -234,9 +232,9 @@ extension ImageStore {
continue
}
switch child.mediaType {
case MediaTypes.imageConfig, MediaTypes.dockerImageConfig:
let config: ContainerizationOCI.Image = try await self.getManifestContent(descriptor: child)
let p = ContainerizationOCI.Platform(
case OCIMediaTypes.imageConfig, OCIMediaTypes.dockerImageConfig:
let config: OCIImage = try await self.getManifestContent(descriptor: child)
let p = OCIPlatform(
arch: config.architecture, os: config.os, osFeatures: config.osFeatures, variant: config.variant
)
supportedPlatforms.append(p)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ extension ImageStore {
/// - platform: An optional parameter to indicate the platform to be saved for the images.
/// Defaults to `nil` signifying that layers for all supported platforms by the images will be saved.
///
public func save(references: [String], out: URL, platform: Platform? = nil) async throws {
public func save(references: [String], out: URL, platform: OCIPlatform? = nil) async throws {
let matcher = createPlatformMatcher(for: platform)
let fileManager = FileManager.default
let tempDir = fileManager.uniqueTemporaryDirectory()
Expand All @@ -41,14 +41,14 @@ extension ImageStore {
var toSave: [Image] = []
for reference in references {
let image = try await self.get(reference: reference)
let allowedMediaTypes = [MediaTypes.dockerManifestList, MediaTypes.index]
let allowedMediaTypes = [OCIMediaTypes.dockerManifestList, OCIMediaTypes.index]
guard allowedMediaTypes.contains(image.mediaType) else {
throw ContainerizationError(.internalError, message: "Cannot save image \(image.reference) with Index media type \(image.mediaType)")
}
toSave.append(image)
}
let client = try LocalOCILayoutClient(root: out)
var saved: [Descriptor] = []
var saved: [OCIDescriptor] = []

for image in toSave {
let ref = try Reference.parse(image.reference)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ extension ImageStore {
internal actor ReferenceManager: Sendable {
private let path: URL

private typealias State = [String: Descriptor]
private typealias State = [String: OCIDescriptor]
private var images: State

public init(path: URL) throws {
Expand Down
Loading