mirror of
https://github.com/apple/container.git
synced 2026-08-24 02:24:19 -05:00
93 lines
4.2 KiB
Swift
93 lines
4.2 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
// Copyright © 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 ContainerPlugin
|
|
import Containerization
|
|
import ContainerizationArchive
|
|
import CryptoKit
|
|
import Foundation
|
|
|
|
/// Fixture helpers for `TestCLIKernelSetSerial`.
|
|
///
|
|
/// `container system kernel set` always requires a kernel to already be
|
|
/// installed for the integration suite to run at all (`system start
|
|
/// --enable-kernel-install` guarantees this). Rather than downloading the real
|
|
/// ~570MB kata-static release tarball in every test, these helpers capture the
|
|
/// bytes of whatever kernel is already installed and repackage them into a
|
|
/// tar with the same internal layout (a real binary plus the `vmlinux.container`
|
|
/// symlink member kata's release tarballs ship) so the real install/extract/
|
|
/// digest-verify code paths still get exercised, without any network access.
|
|
public struct KernelFixture {
|
|
public init() {}
|
|
private let kernelsDirectory = URL(fileURLWithPath: ApplicationRoot.pathname).appendingPathComponent("kernels")
|
|
|
|
private var defaultKernelSymlink: URL {
|
|
kernelsDirectory.appendingPathComponent("default.kernel-\(SystemPlatform.linuxArm.architecture.rawValue)")
|
|
}
|
|
|
|
/// Copies the bytes of the currently-installed default kernel to `destination`.
|
|
public func captureInstalledBinary(to destination: URL) throws {
|
|
let resolved = defaultKernelSymlink.resolvingSymlinksInPath()
|
|
guard FileManager.default.fileExists(atPath: resolved.path) else {
|
|
throw CommandError.executionFailed("no default kernel installed at \(resolved.path)")
|
|
}
|
|
try FileManager.default.copyItem(at: resolved, to: destination)
|
|
}
|
|
|
|
/// Writes a tar at `tarPath` containing `binary`'s bytes at `binaryArchivePath`,
|
|
/// plus a `vmlinux.container` symlink alongside it pointing at the binary's
|
|
/// filename — mirroring the layout `KernelService.extractFile`'s
|
|
/// symlink-following branch expects.
|
|
///
|
|
/// Returns the tar's own `sha256:<hex>` digest, since `container system
|
|
/// kernel set --digest` verifies the archive's digest, not the extracted
|
|
/// binary's.
|
|
@discardableResult
|
|
public func writeTar(binary: URL, binaryArchivePath: String, to tarPath: URL) throws -> String {
|
|
let binaryData = try Data(contentsOf: binary)
|
|
|
|
let writer = try ArchiveWriter(format: .ustar, filter: .none, file: tarPath)
|
|
let fileEntry = WriteEntry()
|
|
fileEntry.path = binaryArchivePath
|
|
fileEntry.fileType = .regular
|
|
fileEntry.permissions = 0o644
|
|
fileEntry.size = Int64(binaryData.count)
|
|
try writer.writeEntry(entry: fileEntry, data: binaryData)
|
|
|
|
let symlinkEntry = WriteEntry()
|
|
symlinkEntry.path =
|
|
URL(filePath: binaryArchivePath)
|
|
.deletingLastPathComponent()
|
|
.appending(path: "vmlinux.container")
|
|
.relativePath
|
|
symlinkEntry.fileType = .symbolicLink
|
|
symlinkEntry.symlinkTarget = URL(filePath: binaryArchivePath).lastPathComponent
|
|
symlinkEntry.permissions = 0o644
|
|
try writer.writeEntry(entry: symlinkEntry, data: nil)
|
|
|
|
try writer.finishEncoding()
|
|
|
|
var hasher = SHA256()
|
|
let handle = try FileHandle(forReadingFrom: tarPath)
|
|
defer { try? handle.close() }
|
|
while let chunk = try handle.read(upToCount: 4 * 1024 * 1024), !chunk.isEmpty {
|
|
hasher.update(data: chunk)
|
|
}
|
|
let hex = hasher.finalize().map { String(format: "%02x", $0) }.joined()
|
|
return "sha256:\(hex)"
|
|
}
|
|
}
|