mirror of
https://github.com/apple/container.git
synced 2026-09-10 17:55:38 +00:00
add ability to export initfs as block (#268)
This refactors the rootfs command to produce an image and have the ability to create an ext4 formatted block of the init filesystem directly from the command. closes #220 Signed-off-by: crosbymichael <michael_crosby@apple.com>
This commit is contained in:
@@ -57,7 +57,12 @@ containerization:
|
||||
init: containerization vminitd
|
||||
@echo Creating init.ext4...
|
||||
@rm -f bin/init.rootfs.tar.gz bin/init.block
|
||||
@./bin/cctl rootfs create --vminitd vminitd/bin/vminitd --labels org.opencontainers.image.source=https://github.com/apple/containerization --vmexec vminitd/bin/vmexec bin/init.rootfs.tar.gz vminit:latest
|
||||
@./bin/cctl rootfs create \
|
||||
--vminitd vminitd/bin/vminitd \
|
||||
--labels org.opencontainers.image.source=https://github.com/apple/containerization \
|
||||
--vmexec vminitd/bin/vmexec \
|
||||
--image vminit:latest \
|
||||
bin/init.rootfs.tar.gz
|
||||
|
||||
.PHONY: cross-prep
|
||||
cross-prep:
|
||||
|
||||
@@ -32,13 +32,56 @@ public struct EXT4Unpacker: Unpacker {
|
||||
self.blockSizeInBytes = blockSizeInBytes
|
||||
}
|
||||
|
||||
public func unpack(_ image: Image, for platform: Platform, at path: URL, progress: ProgressHandler? = nil) async throws -> Mount {
|
||||
#if os(macOS)
|
||||
/// Performs the unpacking of a tar archive into a filesystem.
|
||||
/// - Parameters:
|
||||
/// - archive: The archive to unpack.
|
||||
/// - compression: The compression to use when unpacking the image.
|
||||
/// - path: The path to the filesystem that will be created.
|
||||
public func unpack(
|
||||
archive: URL,
|
||||
compression: ContainerizationArchive.Filter,
|
||||
at path: URL
|
||||
) throws {
|
||||
let cleanedPath = try prepareUnpackPath(path: path)
|
||||
let filesystem = try EXT4.Formatter(
|
||||
FilePath(cleanedPath),
|
||||
minDiskSize: blockSizeInBytes
|
||||
)
|
||||
defer { try? filesystem.close() }
|
||||
|
||||
try filesystem.unpack(
|
||||
source: archive,
|
||||
format: .paxRestricted,
|
||||
compression: compression,
|
||||
progress: nil
|
||||
)
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Returns a `Mount` point after unpacking the image into a filesystem.
|
||||
/// - Parameters:
|
||||
/// - image: The image to unpack.
|
||||
/// - platform: The platform content to unpack.
|
||||
/// - path: The path to the directory where the filesystem will be created.
|
||||
/// - progress: The progress handler to invoke as the unpacking progresses.
|
||||
public func unpack(
|
||||
_ image: Image,
|
||||
for platform: Platform,
|
||||
at path: URL,
|
||||
progress: ProgressHandler? = nil
|
||||
) async throws -> Mount {
|
||||
#if !os(macOS)
|
||||
throw ContainerizationError(.unsupported, message: "Cannot unpack an image on current platform")
|
||||
#else
|
||||
let blockPath = try prepareUnpackPath(path: path)
|
||||
let cleanedPath = try prepareUnpackPath(path: path)
|
||||
let manifest = try await image.manifest(for: platform)
|
||||
let filesystem = try EXT4.Formatter(FilePath(path), minDiskSize: blockSizeInBytes)
|
||||
let filesystem = try EXT4.Formatter(
|
||||
FilePath(
|
||||
cleanedPath
|
||||
),
|
||||
minDiskSize: blockSizeInBytes
|
||||
)
|
||||
defer { try? filesystem.close() }
|
||||
|
||||
for layer in manifest.layers {
|
||||
@@ -64,7 +107,7 @@ public struct EXT4Unpacker: Unpacker {
|
||||
|
||||
return .block(
|
||||
format: "ext4",
|
||||
source: blockPath,
|
||||
source: cleanedPath,
|
||||
destination: "/",
|
||||
options: []
|
||||
)
|
||||
|
||||
@@ -46,9 +46,14 @@ extension Application {
|
||||
@Option(name: .long, help: "Labels to add to the built image of the form <key1>=<value1>, [<key2>=<value2>,...]")
|
||||
var labels: [String] = []
|
||||
|
||||
@Argument var rootfsPath: String
|
||||
@Option(name: .customLong("image"), help: "The name of the image to produce.")
|
||||
var imageName: String?
|
||||
|
||||
@Argument var tag: String
|
||||
@Option(name: .customLong("ext4"), help: "The path to an ext4 image to create.")
|
||||
var ext4File: String?
|
||||
|
||||
// The path where the intermediate tar archive is created.
|
||||
@Argument var tarPath: String
|
||||
|
||||
private static let directories = [
|
||||
"bin",
|
||||
@@ -63,19 +68,51 @@ extension Application {
|
||||
]
|
||||
|
||||
func run() async throws {
|
||||
try await writeArchive()
|
||||
let p = try Platform(from: platformString)
|
||||
let rootfs = URL(filePath: rootfsPath)
|
||||
let labels = Application.parseKeyValuePairs(from: labels)
|
||||
_ = try await InitImage.create(
|
||||
reference: tag, rootfs: rootfs,
|
||||
platform: p, labels: labels,
|
||||
imageStore: Application.imageStore,
|
||||
contentStore: Application.contentStore)
|
||||
let path = URL(filePath: self.tarPath)
|
||||
try await writeArchive(path: path)
|
||||
|
||||
if let image = self.imageName {
|
||||
print("creating initfs image \(image)...")
|
||||
try await outputImage(
|
||||
path: path,
|
||||
reference: image
|
||||
)
|
||||
}
|
||||
|
||||
if let ext4Path = self.ext4File {
|
||||
print("creating initfs ext4 image at \(ext4Path)...")
|
||||
try await outputExt4(
|
||||
archive: path,
|
||||
to: URL(filePath: ext4Path)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func writeArchive() async throws {
|
||||
let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: URL(filePath: rootfsPath))
|
||||
private func outputExt4(archive: URL, to path: URL) async throws {
|
||||
let unpacker = EXT4Unpacker(blockSizeInBytes: 256.mib())
|
||||
|
||||
try unpacker.unpack(archive: archive, compression: .gzip, at: path)
|
||||
}
|
||||
|
||||
private func outputImage(path: URL, reference: String) async throws {
|
||||
let p = try Platform(from: platformString)
|
||||
let labels = Application.parseKeyValuePairs(from: labels)
|
||||
_ = try await InitImage.create(
|
||||
reference: reference,
|
||||
rootfs: path,
|
||||
platform: p,
|
||||
labels: labels,
|
||||
imageStore: Application.imageStore,
|
||||
contentStore: Application.contentStore
|
||||
)
|
||||
}
|
||||
|
||||
private func writeArchive(path: URL) async throws {
|
||||
let writer = try ArchiveWriter(
|
||||
format: .pax,
|
||||
filter: .gzip,
|
||||
file: path,
|
||||
)
|
||||
let ts = Date()
|
||||
let entry = WriteEntry()
|
||||
entry.permissions = 0o755
|
||||
@@ -84,6 +121,7 @@ extension Application {
|
||||
entry.group = 0
|
||||
entry.owner = 0
|
||||
entry.fileType = .directory
|
||||
|
||||
// create the initial directory structure.
|
||||
for dir in Self.directories {
|
||||
entry.path = dir
|
||||
|
||||
Reference in New Issue
Block a user