mirror of
https://github.com/apple/container.git
synced 2026-08-28 11:26:32 +00:00
Expose Command Structs for Plugins (#603)
## Type of Change - [ ] Bug fix - [x] New feature - [ ] Breaking change - [ ] Documentation update ## Motivation and Context Plugins technically exist, but to add shortcuts or to do existing things with functions in `container` requires calling a compiled binary. This pull request aims to remove that hurdle and instability by exposing commands as a new `ContainerCommands ` target. Simply import `ContainerCommands` and you can access almost any command as if it were a native part of the binary. This makes plugin development significantly easier. Closes #609.
This commit is contained in:
@@ -33,6 +33,7 @@ let package = Package(
|
||||
.library(name: "ContainerSandboxService", targets: ["ContainerSandboxService"]),
|
||||
.library(name: "ContainerNetworkService", targets: ["ContainerNetworkService"]),
|
||||
.library(name: "ContainerImagesService", targets: ["ContainerImagesService", "ContainerImagesServiceClient"]),
|
||||
.library(name: "ContainerCommands", targets: ["ContainerCommands"]),
|
||||
.library(name: "ContainerClient", targets: ["ContainerClient"]),
|
||||
.library(name: "ContainerBuild", targets: ["ContainerBuild"]),
|
||||
.library(name: "ContainerLog", targets: ["ContainerLog"]),
|
||||
@@ -65,6 +66,15 @@ let package = Package(
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "container",
|
||||
dependencies: [
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
"ContainerClient",
|
||||
"ContainerCommands",
|
||||
],
|
||||
path: "Sources/ExecutableCLI"
|
||||
),
|
||||
.target(
|
||||
name: "ContainerCommands",
|
||||
dependencies: [
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
.product(name: "Logging", package: "swift-log"),
|
||||
|
||||
@@ -35,12 +35,13 @@ nonisolated(unsafe) var log = {
|
||||
return log
|
||||
}()
|
||||
|
||||
@main
|
||||
struct Application: AsyncParsableCommand {
|
||||
public struct Application: AsyncParsableCommand {
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
static let configuration = CommandConfiguration(
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "container",
|
||||
abstract: "A container platform for macOS",
|
||||
version: ReleaseVersion.singleLine(appName: "container CLI"),
|
||||
@@ -126,7 +127,7 @@ struct Application: AsyncParsableCommand {
|
||||
}
|
||||
}
|
||||
|
||||
static func createPluginLoader() async throws -> PluginLoader {
|
||||
public static func createPluginLoader() async throws -> PluginLoader {
|
||||
let installRoot = CommandLine.executablePathUrl
|
||||
.deletingLastPathComponent()
|
||||
.appendingPathComponent("..")
|
||||
@@ -170,7 +171,7 @@ struct Application: AsyncParsableCommand {
|
||||
)
|
||||
}
|
||||
|
||||
static func handleProcess(io: ProcessIO, process: ClientProcess) async throws -> Int32 {
|
||||
public static func handleProcess(io: ProcessIO, process: ClientProcess) async throws -> Int32 {
|
||||
let signals = AsyncSignalHandler.create(notify: Application.signalSet)
|
||||
return try await withThrowingTaskGroup(of: Int32?.self, returning: Int32.self) { group in
|
||||
let waitAdded = group.addTaskUnlessCancelled {
|
||||
@@ -242,7 +243,7 @@ struct Application: AsyncParsableCommand {
|
||||
}
|
||||
}
|
||||
|
||||
func validate() throws {
|
||||
public func validate() throws {
|
||||
// Not really a "validation", but a cheat to run this before
|
||||
// any of the commands do their business.
|
||||
let debugEnvVar = ProcessInfo.processInfo.environment["CONTAINER_DEBUG"]
|
||||
@@ -308,7 +309,7 @@ extension Application {
|
||||
print(altered)
|
||||
}
|
||||
|
||||
enum ListFormat: String, CaseIterable, ExpressibleByArgument {
|
||||
public enum ListFormat: String, CaseIterable, ExpressibleByArgument {
|
||||
case json
|
||||
case table
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ import NIO
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct BuildCommand: AsyncParsableCommand {
|
||||
public struct BuildCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static var configuration: CommandConfiguration {
|
||||
var config = CommandConfiguration()
|
||||
config.commandName = "build"
|
||||
@@ -38,7 +39,7 @@ extension Application {
|
||||
}
|
||||
|
||||
@Option(name: [.customLong("cpus"), .customShort("c")], help: "Number of CPUs to allocate to the container")
|
||||
public var cpus: Int64 = 2
|
||||
var cpus: Int64 = 2
|
||||
|
||||
@Option(
|
||||
name: [.customLong("memory"), .customShort("m")],
|
||||
@@ -117,7 +118,7 @@ extension Application {
|
||||
@Flag(name: .shortAndLong, help: "Suppress build output")
|
||||
var quiet: Bool = false
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
do {
|
||||
let timeout: Duration = .seconds(300)
|
||||
let progressConfig = try ProgressConfig(
|
||||
@@ -132,16 +133,16 @@ extension Application {
|
||||
|
||||
progress.set(description: "Dialing builder")
|
||||
|
||||
let builder: Builder? = try await withThrowingTaskGroup(of: Builder.self) { group in
|
||||
let builder: Builder? = try await withThrowingTaskGroup(of: Builder.self) { [vsockPort, cpus, memory] group in
|
||||
defer {
|
||||
group.cancelAll()
|
||||
}
|
||||
|
||||
group.addTask {
|
||||
group.addTask { [vsockPort, cpus, memory] in
|
||||
while true {
|
||||
do {
|
||||
let container = try await ClientContainer.get(id: "buildkit")
|
||||
let fh = try await container.dial(self.vsockPort)
|
||||
let fh = try await container.dial(vsockPort)
|
||||
|
||||
let threadGroup: MultiThreadedEventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
|
||||
let b = try Builder(socket: fh, group: threadGroup)
|
||||
@@ -156,8 +157,8 @@ extension Application {
|
||||
progress.set(totalTasks: 3)
|
||||
|
||||
try await BuilderStart.start(
|
||||
cpus: self.cpus,
|
||||
memory: self.memory,
|
||||
cpus: cpus,
|
||||
memory: memory,
|
||||
progressUpdate: progress.handler
|
||||
)
|
||||
|
||||
@@ -255,7 +256,7 @@ extension Application {
|
||||
}
|
||||
return results
|
||||
}()
|
||||
group.addTask { [terminal] in
|
||||
group.addTask { [terminal, buildArg, contextDir, label, noCache, target, quiet, cacheIn, cacheOut] in
|
||||
let config = ContainerBuild.Builder.BuildConfig(
|
||||
buildID: buildID,
|
||||
contentStore: RemoteContentStoreClient(),
|
||||
@@ -341,7 +342,7 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
func validate() throws {
|
||||
public func validate() throws {
|
||||
guard FileManager.default.fileExists(atPath: file) else {
|
||||
throw ValidationError("Dockerfile does not exist at path: \(file)")
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
struct BuilderCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct BuilderCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "builder",
|
||||
abstract: "Manage an image builder instance",
|
||||
subcommands: [
|
||||
|
||||
@@ -20,7 +20,9 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct BuilderDelete: AsyncParsableCommand {
|
||||
public struct BuilderDelete: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static var configuration: CommandConfiguration {
|
||||
var config = CommandConfiguration()
|
||||
config.commandName = "delete"
|
||||
@@ -35,7 +37,7 @@ extension Application {
|
||||
@Flag(name: .shortAndLong, help: "Force delete builder even if it is running")
|
||||
var force = false
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
do {
|
||||
let container = try await ClientContainer.get(id: "buildkit")
|
||||
if container.status != .stopped {
|
||||
|
||||
@@ -27,7 +27,9 @@ import Foundation
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct BuilderStart: AsyncParsableCommand {
|
||||
public struct BuilderStart: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static var configuration: CommandConfiguration {
|
||||
var config = CommandConfiguration()
|
||||
config.commandName = "start"
|
||||
@@ -39,16 +41,16 @@ extension Application {
|
||||
}
|
||||
|
||||
@Option(name: [.customLong("cpus"), .customShort("c")], help: "Number of CPUs to allocate to the container")
|
||||
public var cpus: Int64 = 2
|
||||
var cpus: Int64 = 2
|
||||
|
||||
@Option(
|
||||
name: [.customLong("memory"), .customShort("m")],
|
||||
help:
|
||||
"Amount of memory in bytes, kilobytes (K), megabytes (M), or gigabytes (G) for the container, with MB granularity (for example, 1024K will result in 1MB being allocated for the container)"
|
||||
)
|
||||
public var memory: String = "2048MB"
|
||||
var memory: String = "2048MB"
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let progressConfig = try ProgressConfig(
|
||||
showTasks: true,
|
||||
showItems: true,
|
||||
|
||||
@@ -20,7 +20,9 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct BuilderStatus: AsyncParsableCommand {
|
||||
public struct BuilderStatus: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static var configuration: CommandConfiguration {
|
||||
var config = CommandConfiguration()
|
||||
config.commandName = "status"
|
||||
@@ -34,7 +36,7 @@ extension Application {
|
||||
@Flag(name: .long, help: ArgumentHelp("Display detailed status in json format"))
|
||||
var json: Bool = false
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
do {
|
||||
let container = try await ClientContainer.get(id: "buildkit")
|
||||
if json {
|
||||
|
||||
@@ -20,7 +20,9 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct BuilderStop: AsyncParsableCommand {
|
||||
public struct BuilderStop: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static var configuration: CommandConfiguration {
|
||||
var config = CommandConfiguration()
|
||||
config.commandName = "stop"
|
||||
@@ -31,7 +33,7 @@ extension Application {
|
||||
return config
|
||||
}
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
do {
|
||||
let container = try await ClientContainer.get(id: "buildkit")
|
||||
try await container.stop()
|
||||
|
||||
@@ -21,8 +21,10 @@ import Foundation
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct ContainerCreate: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerCreate: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "create",
|
||||
abstract: "Create a new container")
|
||||
|
||||
@@ -47,7 +49,7 @@ extension Application {
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let progressConfig = try ProgressConfig(
|
||||
showTasks: true,
|
||||
showItems: true,
|
||||
|
||||
@@ -20,8 +20,10 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct ContainerDelete: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerDelete: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "delete",
|
||||
abstract: "Delete one or more containers",
|
||||
aliases: ["rm"])
|
||||
@@ -38,7 +40,7 @@ extension Application {
|
||||
@Argument(help: "Container IDs/names")
|
||||
var containerIDs: [String] = []
|
||||
|
||||
func validate() throws {
|
||||
public func validate() throws {
|
||||
if containerIDs.count == 0 && !all {
|
||||
throw ContainerizationError(.invalidArgument, message: "no containers specified and --all not supplied")
|
||||
}
|
||||
@@ -50,7 +52,7 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
mutating func run() async throws {
|
||||
public mutating func run() async throws {
|
||||
let set = Set<String>(containerIDs)
|
||||
var containers = [ClientContainer]()
|
||||
|
||||
|
||||
@@ -21,8 +21,10 @@ import ContainerizationOS
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct ContainerExec: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerExec: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "exec",
|
||||
abstract: "Run a new command in a running container")
|
||||
|
||||
@@ -38,7 +40,7 @@ extension Application {
|
||||
@Argument(parsing: .captureForPassthrough, help: "New process arguments")
|
||||
var arguments: [String]
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var exitCode: Int32 = 127
|
||||
let container = try await ClientContainer.get(id: containerID)
|
||||
try ensureRunning(container: container)
|
||||
|
||||
@@ -20,8 +20,10 @@ import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
struct ContainerInspect: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerInspect: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "inspect",
|
||||
abstract: "Display information about one or more containers")
|
||||
|
||||
@@ -31,7 +33,7 @@ extension Application {
|
||||
@Argument(help: "Containers to inspect")
|
||||
var containers: [String]
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let objects: [any Codable] = try await ClientContainer.list().filter {
|
||||
containers.contains($0.id)
|
||||
}.map {
|
||||
|
||||
@@ -21,8 +21,10 @@ import ContainerizationOS
|
||||
import Darwin
|
||||
|
||||
extension Application {
|
||||
struct ContainerKill: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerKill: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "kill",
|
||||
abstract: "Kill one or more running containers")
|
||||
|
||||
@@ -38,7 +40,7 @@ extension Application {
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
func validate() throws {
|
||||
public func validate() throws {
|
||||
if containerIDs.count == 0 && !all {
|
||||
throw ContainerizationError(.invalidArgument, message: "no containers specified and --all not supplied")
|
||||
}
|
||||
@@ -47,7 +49,7 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
mutating func run() async throws {
|
||||
public mutating func run() async throws {
|
||||
let set = Set<String>(containerIDs)
|
||||
|
||||
var containers = try await ClientContainer.list().filter { c in
|
||||
|
||||
@@ -22,8 +22,10 @@ import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
struct ContainerList: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerList: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "list",
|
||||
abstract: "List containers",
|
||||
aliases: ["ls"])
|
||||
@@ -40,7 +42,7 @@ extension Application {
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let containers = try await ClientContainer.list()
|
||||
try printContainers(containers: containers, format: format)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ import Dispatch
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct ContainerLogs: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerLogs: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "logs",
|
||||
abstract: "Fetch container stdio or boot logs"
|
||||
)
|
||||
@@ -43,7 +45,7 @@ extension Application {
|
||||
@Argument(help: "Container to fetch logs for")
|
||||
var container: String
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
do {
|
||||
let container = try await ClientContainer.get(id: container)
|
||||
let fhs = try await container.logs()
|
||||
|
||||
@@ -21,8 +21,10 @@ import ContainerizationOS
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct ContainerStart: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerStart: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "start",
|
||||
abstract: "Start a container")
|
||||
|
||||
@@ -38,7 +40,7 @@ extension Application {
|
||||
@Argument(help: "Container's ID")
|
||||
var containerID: String
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var exitCode: Int32 = 127
|
||||
|
||||
let progressConfig = try ProgressConfig(
|
||||
|
||||
@@ -21,8 +21,10 @@ import ContainerizationOS
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct ContainerStop: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerStop: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "stop",
|
||||
abstract: "Stop one or more running containers")
|
||||
|
||||
@@ -41,7 +43,7 @@ extension Application {
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
func validate() throws {
|
||||
public func validate() throws {
|
||||
if containerIDs.count == 0 && !all {
|
||||
throw ContainerizationError(.invalidArgument, message: "no containers specified and --all not supplied")
|
||||
}
|
||||
@@ -51,7 +53,7 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
mutating func run() async throws {
|
||||
public mutating func run() async throws {
|
||||
let set = Set<String>(containerIDs)
|
||||
var containers = [ClientContainer]()
|
||||
if self.all {
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
struct ContainersCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainersCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "containers",
|
||||
abstract: "Manage containers",
|
||||
subcommands: [
|
||||
|
||||
@@ -21,7 +21,7 @@ import Darwin
|
||||
import Foundation
|
||||
|
||||
struct DefaultCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: nil,
|
||||
shouldDisplay: false
|
||||
)
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
struct ImageCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ImageCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "image",
|
||||
abstract: "Manage images",
|
||||
subcommands: [
|
||||
|
||||
@@ -21,8 +21,9 @@ import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
struct ImageInspect: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ImageInspect: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "inspect",
|
||||
abstract: "Display information about one or more images")
|
||||
|
||||
@@ -32,7 +33,7 @@ extension Application {
|
||||
@Argument(help: "Images to inspect")
|
||||
var images: [String]
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var printable = [any Codable]()
|
||||
let result = try await ClientImage.get(names: images)
|
||||
let notFound = result.error
|
||||
|
||||
@@ -23,7 +23,8 @@ import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
struct ListImageOptions: ParsableArguments {
|
||||
public struct ListImageOptions: ParsableArguments {
|
||||
public init() {}
|
||||
@Flag(name: .shortAndLong, help: "Only output the image name")
|
||||
var quiet = false
|
||||
|
||||
@@ -38,15 +39,15 @@ extension Application {
|
||||
}
|
||||
|
||||
struct ListImageImplementation {
|
||||
static private func createHeader() -> [[String]] {
|
||||
static func createHeader() -> [[String]] {
|
||||
[["NAME", "TAG", "DIGEST"]]
|
||||
}
|
||||
|
||||
static private func createVerboseHeader() -> [[String]] {
|
||||
static func createVerboseHeader() -> [[String]] {
|
||||
[["NAME", "TAG", "INDEX DIGEST", "OS", "ARCH", "VARIANT", "SIZE", "CREATED", "MANIFEST DIGEST"]]
|
||||
}
|
||||
|
||||
static private func printImagesVerbose(images: [ClientImage]) async throws {
|
||||
static func printImagesVerbose(images: [ClientImage]) async throws {
|
||||
|
||||
var rows = createVerboseHeader()
|
||||
for image in images {
|
||||
@@ -102,7 +103,7 @@ extension Application {
|
||||
print(formatter.format())
|
||||
}
|
||||
|
||||
static private func printImages(images: [ClientImage], format: ListFormat, options: ListImageOptions) async throws {
|
||||
static func printImages(images: [ClientImage], format: ListFormat, options: ListImageOptions) async throws {
|
||||
var images = images
|
||||
images.sort {
|
||||
$0.reference < $1.reference
|
||||
@@ -160,8 +161,9 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
struct ImageList: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ImageList: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "list",
|
||||
abstract: "List images",
|
||||
aliases: ["ls"])
|
||||
@@ -169,7 +171,7 @@ extension Application {
|
||||
@OptionGroup
|
||||
var options: ListImageOptions
|
||||
|
||||
mutating func run() async throws {
|
||||
public mutating func run() async throws {
|
||||
try ListImageImplementation.validate(options: options)
|
||||
try await ListImageImplementation.listImages(options: options)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,9 @@ import Foundation
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct ImageLoad: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ImageLoad: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "load",
|
||||
abstract: "Load images from an OCI compatible tar archive"
|
||||
)
|
||||
@@ -38,7 +39,7 @@ extension Application {
|
||||
})
|
||||
var input: String
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
guard FileManager.default.fileExists(atPath: input) else {
|
||||
print("File does not exist \(input)")
|
||||
Application.exit(withError: ArgumentParser.ExitCode(1))
|
||||
|
||||
@@ -19,15 +19,16 @@ import ContainerClient
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct ImagePrune: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ImagePrune: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "prune",
|
||||
abstract: "Remove unreferenced and dangling images")
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let (_, size) = try await ClientImage.pruneImages()
|
||||
let formatter = ByteCountFormatter()
|
||||
let freed = formatter.string(fromByteCount: Int64(size))
|
||||
|
||||
@@ -22,8 +22,8 @@ import ContainerizationOCI
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct ImagePull: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ImagePull: AsyncParsableCommand {
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "pull",
|
||||
abstract: "Pull an image"
|
||||
)
|
||||
@@ -55,9 +55,9 @@ extension Application {
|
||||
|
||||
@Argument var reference: String
|
||||
|
||||
init() {}
|
||||
public init() {}
|
||||
|
||||
init(platform: String? = nil, scheme: String = "auto", reference: String, disableProgress: Bool = false) {
|
||||
public init(platform: String? = nil, scheme: String = "auto", reference: String, disableProgress: Bool = false) {
|
||||
self.global = Flags.Global()
|
||||
self.registry = Flags.Registry(scheme: scheme)
|
||||
self.progressFlags = Flags.Progress(disableProgressUpdates: disableProgress)
|
||||
@@ -65,7 +65,7 @@ extension Application {
|
||||
self.reference = reference
|
||||
}
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var p: Platform?
|
||||
if let platform {
|
||||
p = try Platform(from: platform)
|
||||
|
||||
@@ -21,8 +21,9 @@ import ContainerizationOCI
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct ImagePush: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ImagePush: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "push",
|
||||
abstract: "Push an image"
|
||||
)
|
||||
@@ -40,7 +41,7 @@ extension Application {
|
||||
|
||||
@Argument var reference: String
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var p: Platform?
|
||||
if let platform {
|
||||
p = try Platform(from: platform)
|
||||
|
||||
@@ -21,7 +21,9 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct RemoveImageOptions: ParsableArguments {
|
||||
public struct RemoveImageOptions: ParsableArguments {
|
||||
public init() {}
|
||||
|
||||
@Flag(name: .shortAndLong, help: "Remove all images")
|
||||
var all: Bool = false
|
||||
|
||||
@@ -32,7 +34,7 @@ extension Application {
|
||||
var global: Flags.Global
|
||||
}
|
||||
|
||||
struct RemoveImageImplementation {
|
||||
public struct RemoveImageImplementation {
|
||||
static func validate(options: RemoveImageOptions) throws {
|
||||
if options.images.count == 0 && !options.all {
|
||||
throw ContainerizationError(.invalidArgument, message: "no image specified and --all not supplied")
|
||||
@@ -79,20 +81,22 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
struct ImageRemove: AsyncParsableCommand {
|
||||
public struct ImageRemove: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
@OptionGroup
|
||||
var options: RemoveImageOptions
|
||||
|
||||
static let configuration = CommandConfiguration(
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "delete",
|
||||
abstract: "Remove one or more images",
|
||||
aliases: ["rm"])
|
||||
|
||||
func validate() throws {
|
||||
public func validate() throws {
|
||||
try RemoveImageImplementation.validate(options: options)
|
||||
}
|
||||
|
||||
mutating func run() async throws {
|
||||
public mutating func run() async throws {
|
||||
try await RemoveImageImplementation.removeImage(options: options)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,9 @@ import Foundation
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct ImageSave: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ImageSave: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "save",
|
||||
abstract: "Save an image as an OCI compatible tar archive"
|
||||
)
|
||||
@@ -57,7 +58,7 @@ extension Application {
|
||||
|
||||
@Argument var references: [String]
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var p: Platform?
|
||||
if let platform {
|
||||
p = try Platform(from: platform)
|
||||
|
||||
@@ -18,8 +18,9 @@ import ArgumentParser
|
||||
import ContainerClient
|
||||
|
||||
extension Application {
|
||||
struct ImageTag: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ImageTag: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "tag",
|
||||
abstract: "Tag an image")
|
||||
|
||||
@@ -32,7 +33,7 @@ extension Application {
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let existing = try await ClientImage.get(reference: source)
|
||||
let targetReference = try ClientImage.normalizeReference(target)
|
||||
try await existing.tag(new: targetReference)
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
struct NetworkCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct NetworkCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "network",
|
||||
abstract: "Manage container networks",
|
||||
subcommands: [
|
||||
|
||||
@@ -22,8 +22,9 @@ import Foundation
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct NetworkCreate: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct NetworkCreate: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "create",
|
||||
abstract: "Create a new network")
|
||||
|
||||
@@ -36,7 +37,7 @@ extension Application {
|
||||
@Argument(help: "Network name")
|
||||
var name: String
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let parsedLabels = Utility.parseKeyValuePairs(labels)
|
||||
let config = try NetworkConfiguration(id: self.name, mode: .nat, labels: parsedLabels)
|
||||
let state = try await ClientNetwork.create(configuration: config)
|
||||
|
||||
@@ -21,8 +21,9 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct NetworkDelete: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct NetworkDelete: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "delete",
|
||||
abstract: "Delete one or more networks",
|
||||
aliases: ["rm"])
|
||||
@@ -36,7 +37,7 @@ extension Application {
|
||||
@Argument(help: "Network names")
|
||||
var networkNames: [String] = []
|
||||
|
||||
func validate() throws {
|
||||
public func validate() throws {
|
||||
if networkNames.count == 0 && !all {
|
||||
throw ContainerizationError(.invalidArgument, message: "no networks specified and --all not supplied")
|
||||
}
|
||||
@@ -48,7 +49,7 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
mutating func run() async throws {
|
||||
public mutating func run() async throws {
|
||||
let uniqueNetworkNames = Set<String>(networkNames)
|
||||
let networks: [NetworkState]
|
||||
|
||||
|
||||
@@ -21,8 +21,9 @@ import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
struct NetworkInspect: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct NetworkInspect: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "inspect",
|
||||
abstract: "Display information about one or more networks")
|
||||
|
||||
@@ -32,7 +33,7 @@ extension Application {
|
||||
@Argument(help: "Networks to inspect")
|
||||
var networks: [String]
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let objects: [any Codable] = try await ClientNetwork.list().filter {
|
||||
networks.contains($0.id)
|
||||
}.map {
|
||||
|
||||
@@ -22,22 +22,23 @@ import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
struct NetworkList: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct NetworkList: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "list",
|
||||
abstract: "List networks",
|
||||
aliases: ["ls"])
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Flag(name: .shortAndLong, help: "Only output the network name")
|
||||
var quiet = false
|
||||
|
||||
@Option(name: .long, help: "Format of the output")
|
||||
var format: ListFormat = .table
|
||||
|
||||
func run() async throws {
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
public func run() async throws {
|
||||
let networks = try await ClientNetwork.list()
|
||||
try printNetworks(networks: networks, format: format)
|
||||
}
|
||||
@@ -46,7 +47,7 @@ extension Application {
|
||||
[["NETWORK", "STATE", "SUBNET"]]
|
||||
}
|
||||
|
||||
private func printNetworks(networks: [NetworkState], format: ListFormat) throws {
|
||||
func printNetworks(networks: [NetworkState], format: ListFormat) throws {
|
||||
if format == .json {
|
||||
let printables = networks.map {
|
||||
PrintableNetwork($0)
|
||||
@@ -86,13 +87,13 @@ extension NetworkState {
|
||||
}
|
||||
}
|
||||
|
||||
struct PrintableNetwork: Codable {
|
||||
public struct PrintableNetwork: Codable {
|
||||
let id: String
|
||||
let state: String
|
||||
let config: NetworkConfiguration
|
||||
let status: NetworkStatus?
|
||||
|
||||
init(_ network: NetworkState) {
|
||||
public init(_ network: NetworkState) {
|
||||
self.id = network.id
|
||||
self.state = network.state
|
||||
switch network {
|
||||
|
||||
@@ -22,8 +22,9 @@ import ContainerizationOCI
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct Login: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct Login: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
abstract: "Login to a registry"
|
||||
)
|
||||
|
||||
@@ -39,7 +40,7 @@ extension Application {
|
||||
@OptionGroup
|
||||
var registry: Flags.Registry
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var username = self.username
|
||||
var password = ""
|
||||
if passwordStdin {
|
||||
|
||||
@@ -20,8 +20,9 @@ import Containerization
|
||||
import ContainerizationOCI
|
||||
|
||||
extension Application {
|
||||
struct Logout: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct Logout: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
abstract: "Log out from a registry")
|
||||
|
||||
@Argument(help: "Registry server name")
|
||||
@@ -30,7 +31,7 @@ extension Application {
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let keychain = KeychainHelper(id: Constants.keychainID)
|
||||
let r = Reference.resolveDomain(domain: registry)
|
||||
try keychain.delete(domain: r)
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
struct RegistryCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct RegistryCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "registry",
|
||||
abstract: "Manage registry configurations",
|
||||
subcommands: [
|
||||
|
||||
@@ -26,8 +26,9 @@ import NIOPosix
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct ContainerRunCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct ContainerRunCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "run",
|
||||
abstract: "Run a container")
|
||||
|
||||
@@ -55,7 +56,7 @@ extension Application {
|
||||
@Argument(parsing: .captureForPassthrough, help: "Container init process arguments")
|
||||
var arguments: [String] = []
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var exitCode: Int32 = 127
|
||||
let id = Utility.createContainerID(name: self.managementFlags.name)
|
||||
|
||||
@@ -166,13 +167,13 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
struct ProcessIO {
|
||||
public struct ProcessIO: Sendable {
|
||||
let stdin: Pipe?
|
||||
let stdout: Pipe?
|
||||
let stderr: Pipe?
|
||||
var ioTracker: IoTracker?
|
||||
|
||||
struct IoTracker {
|
||||
public struct IoTracker: Sendable {
|
||||
let stream: AsyncStream<Void>
|
||||
let cont: AsyncStream<Void>.Continuation
|
||||
let configuredStreams: Int
|
||||
@@ -333,7 +334,7 @@ struct ProcessIO {
|
||||
}
|
||||
}
|
||||
|
||||
public func wait() async throws {
|
||||
func wait() async throws {
|
||||
guard let ioTracker = self.ioTracker else {
|
||||
return
|
||||
}
|
||||
@@ -355,10 +356,10 @@ struct ProcessIO {
|
||||
}
|
||||
}
|
||||
|
||||
struct OSFile: Sendable {
|
||||
public struct OSFile: Sendable {
|
||||
private let fd: Int32
|
||||
|
||||
enum IOAction: Equatable {
|
||||
public enum IOAction: Equatable {
|
||||
case eof
|
||||
case again
|
||||
case success
|
||||
@@ -366,11 +367,11 @@ struct OSFile: Sendable {
|
||||
case error(_ errno: Int32)
|
||||
}
|
||||
|
||||
init(fd: Int32) {
|
||||
public init(fd: Int32) {
|
||||
self.fd = fd
|
||||
}
|
||||
|
||||
init(handle: FileHandle) {
|
||||
public init(handle: FileHandle) {
|
||||
self.fd = handle.fileDescriptor
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,9 @@ import ContainerizationExtras
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct DNSCreate: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct DNSCreate: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "create",
|
||||
abstract: "Create a local DNS domain for containers (must run as an administrator)"
|
||||
)
|
||||
@@ -30,7 +31,7 @@ extension Application {
|
||||
@Argument(help: "the local domain name")
|
||||
var domainName: String
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let resolver: HostDNSResolver = HostDNSResolver()
|
||||
do {
|
||||
try resolver.createDomain(name: domainName)
|
||||
|
||||
@@ -20,8 +20,9 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct DNSDelete: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct DNSDelete: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "delete",
|
||||
abstract: "Delete a local DNS domain (must run as an administrator)",
|
||||
aliases: ["rm"]
|
||||
@@ -30,7 +31,7 @@ extension Application {
|
||||
@Argument(help: "the local domain name")
|
||||
var domainName: String
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let resolver = HostDNSResolver()
|
||||
do {
|
||||
try resolver.deleteDomain(name: domainName)
|
||||
|
||||
@@ -19,14 +19,15 @@ import ContainerClient
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct DNSList: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct DNSList: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "list",
|
||||
abstract: "List local DNS domains",
|
||||
aliases: ["ls"]
|
||||
)
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let resolver: HostDNSResolver = HostDNSResolver()
|
||||
let domains = resolver.listDomains()
|
||||
print(domains.joined(separator: "\n"))
|
||||
|
||||
@@ -25,8 +25,9 @@ import Foundation
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct KernelSet: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct KernelSet: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "set",
|
||||
abstract: "Set the default kernel"
|
||||
)
|
||||
@@ -46,7 +47,7 @@ extension Application {
|
||||
@Flag(name: .long, help: "Force install of kernel. If a kernel exists with the same name, it will be overwritten.")
|
||||
var force: Bool = false
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
if recommended {
|
||||
let url = DefaultsStore.get(key: .defaultKernelURL)
|
||||
let path = DefaultsStore.get(key: .defaultKernelBinaryPath)
|
||||
@@ -100,7 +101,7 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
public static func downloadAndInstallWithProgressBar(tarRemoteURL: String, kernelFilePath: String, platform: SystemPlatform = .current, force: Bool) async throws {
|
||||
static func downloadAndInstallWithProgressBar(tarRemoteURL: String, kernelFilePath: String, platform: SystemPlatform = .current, force: Bool) async throws {
|
||||
let progressConfig = try ProgressConfig(
|
||||
showTasks: true,
|
||||
totalTasks: 2
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
struct SystemCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct SystemCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "system",
|
||||
abstract: "Manage system components",
|
||||
subcommands: [
|
||||
|
||||
@@ -19,8 +19,9 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct SystemDNS: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct SystemDNS: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "dns",
|
||||
abstract: "Manage local DNS domains",
|
||||
subcommands: [
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
struct SystemKernel: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct SystemKernel: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "kernel",
|
||||
abstract: "Manage the default kernel configuration",
|
||||
subcommands: [
|
||||
|
||||
@@ -22,10 +22,12 @@ import Foundation
|
||||
import OSLog
|
||||
|
||||
extension Application {
|
||||
struct SystemLogs: AsyncParsableCommand {
|
||||
static let subsystem = "com.apple.container"
|
||||
public struct SystemLogs: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
static let configuration = CommandConfiguration(
|
||||
public static let subsystem = "com.apple.container"
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "logs",
|
||||
abstract: "Fetch system logs for `container` services"
|
||||
)
|
||||
@@ -42,7 +44,7 @@ extension Application {
|
||||
@Flag(name: .shortAndLong, help: "Follow log output")
|
||||
var follow: Bool = false
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let process = Process()
|
||||
let sigHandler = AsyncSignalHandler.create(notify: [SIGINT, SIGTERM])
|
||||
|
||||
|
||||
@@ -20,8 +20,10 @@ import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct SystemProperty: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct SystemProperty: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "property",
|
||||
abstract: "Manage system property values",
|
||||
subcommands: [
|
||||
|
||||
@@ -23,8 +23,9 @@ import Foundation
|
||||
import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
struct SystemStart: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct SystemStart: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "start",
|
||||
abstract: "Start `container` services"
|
||||
)
|
||||
@@ -39,7 +40,7 @@ extension Application {
|
||||
name: .long,
|
||||
help: "Path to the installation root directory",
|
||||
transform: { URL(filePath: $0) })
|
||||
public var installRoot = InstallRoot.defaultURL
|
||||
var installRoot = InstallRoot.defaultURL
|
||||
|
||||
@Flag(name: .long, help: "Enable debug logging for the runtime daemon.")
|
||||
var debug = false
|
||||
@@ -49,7 +50,7 @@ extension Application {
|
||||
help: "Specify whether the default kernel should be installed or not. The default behavior is to prompt the user for a response.")
|
||||
var kernelInstall: Bool?
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
// Without the true path to the binary in the plist, `container-apiserver` won't launch properly.
|
||||
// TODO: Use plugin loader for API server.
|
||||
let executableUrl = CommandLine.executablePathUrl
|
||||
|
||||
@@ -22,8 +22,9 @@ import Foundation
|
||||
import Logging
|
||||
|
||||
extension Application {
|
||||
struct SystemStatus: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct SystemStatus: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "status",
|
||||
abstract: "Show the status of `container` services"
|
||||
)
|
||||
@@ -31,7 +32,7 @@ extension Application {
|
||||
@Option(name: .shortAndLong, help: "Launchd prefix for `container` services")
|
||||
var prefix: String = "com.apple.container."
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let isRegistered = try ServiceManager.isRegistered(fullServiceLabel: "\(prefix)apiserver")
|
||||
if !isRegistered {
|
||||
print("apiserver is not running and not registered with launchd")
|
||||
|
||||
@@ -22,11 +22,13 @@ import Foundation
|
||||
import Logging
|
||||
|
||||
extension Application {
|
||||
struct SystemStop: AsyncParsableCommand {
|
||||
public struct SystemStop: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
private static let stopTimeoutSeconds: Int32 = 5
|
||||
private static let shutdownTimeoutSeconds: Int32 = 20
|
||||
|
||||
static let configuration = CommandConfiguration(
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "stop",
|
||||
abstract: "Stop all `container` services"
|
||||
)
|
||||
@@ -34,7 +36,7 @@ extension Application {
|
||||
@Option(name: .shortAndLong, help: "Launchd prefix for `container` services")
|
||||
var prefix: String = "com.apple.container."
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let log = Logger(
|
||||
label: "com.apple.container.cli",
|
||||
factory: { label in
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
struct VolumeCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct VolumeCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "volume",
|
||||
abstract: "Manage container volumes",
|
||||
subcommands: [
|
||||
|
||||
@@ -19,8 +19,9 @@ import ContainerClient
|
||||
import Foundation
|
||||
|
||||
extension Application.VolumeCommand {
|
||||
struct VolumeCreate: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct VolumeCreate: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "create",
|
||||
abstract: "Create a volume"
|
||||
)
|
||||
@@ -37,7 +38,7 @@ extension Application.VolumeCommand {
|
||||
@Argument(help: "Volume name")
|
||||
var name: String
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var parsedDriverOpts = Utility.parseKeyValuePairs(driverOpts)
|
||||
let parsedLabels = Utility.parseKeyValuePairs(labels)
|
||||
|
||||
|
||||
@@ -19,8 +19,9 @@ import ContainerClient
|
||||
import Foundation
|
||||
|
||||
extension Application.VolumeCommand {
|
||||
struct VolumeDelete: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct VolumeDelete: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "delete",
|
||||
abstract: "Remove one or more volumes",
|
||||
aliases: ["rm"]
|
||||
@@ -29,7 +30,7 @@ extension Application.VolumeCommand {
|
||||
@Argument(help: "Volume name(s)")
|
||||
var names: [String]
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
for name in names {
|
||||
try await ClientVolume.delete(name: name)
|
||||
print(name)
|
||||
|
||||
@@ -19,8 +19,9 @@ import ContainerClient
|
||||
import Foundation
|
||||
|
||||
extension Application.VolumeCommand {
|
||||
struct VolumeInspect: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct VolumeInspect: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "inspect",
|
||||
abstract: "Display detailed information on one or more volumes"
|
||||
)
|
||||
@@ -28,7 +29,7 @@ extension Application.VolumeCommand {
|
||||
@Argument(help: "Volume name(s)")
|
||||
var names: [String]
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
var volumes: [Volume] = []
|
||||
|
||||
for name in names {
|
||||
|
||||
@@ -20,8 +20,9 @@ import ContainerizationExtras
|
||||
import Foundation
|
||||
|
||||
extension Application.VolumeCommand {
|
||||
struct VolumeList: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
public struct VolumeList: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "list",
|
||||
abstract: "List volumes",
|
||||
aliases: ["ls"]
|
||||
@@ -33,7 +34,7 @@ extension Application.VolumeCommand {
|
||||
@Option(name: .long, help: "Format of the output")
|
||||
var format: Application.ListFormat = .table
|
||||
|
||||
func run() async throws {
|
||||
public func run() async throws {
|
||||
let volumes = try await ClientVolume.list()
|
||||
try printVolumes(volumes: volumes, format: format)
|
||||
}
|
||||
@@ -42,7 +43,7 @@ extension Application.VolumeCommand {
|
||||
[["NAME", "DRIVER", "OPTIONS"]]
|
||||
}
|
||||
|
||||
private func printVolumes(volumes: [Volume], format: Application.ListFormat) throws {
|
||||
func printVolumes(volumes: [Volume], format: Application.ListFormat) throws {
|
||||
if format == .json {
|
||||
let data = try JSONEncoder().encode(volumes)
|
||||
print(String(data: data, encoding: .utf8)!)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Copyright © 2025 Apple Inc. and the container project authors. All rights reserved.
|
||||
//
|
||||
// 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 ArgumentParser
|
||||
import ContainerClient
|
||||
import ContainerCommands
|
||||
|
||||
@main
|
||||
public struct Executable: AsyncParsableCommand {
|
||||
public init() {}
|
||||
|
||||
@Argument(parsing: .captureForPassthrough)
|
||||
var arguments: [String] = []
|
||||
|
||||
public static let configuration = Application.configuration
|
||||
|
||||
public static func main() async throws {
|
||||
try await Application.main()
|
||||
}
|
||||
|
||||
public func run() async throws {
|
||||
var application = try Application.parse(arguments)
|
||||
try application.validate()
|
||||
try application.run()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user