mirror of
https://github.com/apple/container.git
synced 2026-09-20 22:55:40 +00:00
Cleans up network and volume subcommands. (#661)
- Part of #515. - Order options alphabetically. - Use consistent delete logic for network and volume delete. ## Motivation and Context See #515
This commit is contained in:
@@ -18,7 +18,6 @@ import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
public struct NetworkCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "network",
|
||||
abstract: "Manage container networks",
|
||||
@@ -30,5 +29,7 @@ extension Application {
|
||||
],
|
||||
aliases: ["n"]
|
||||
)
|
||||
|
||||
public init() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,20 +23,21 @@ import TerminalProgress
|
||||
|
||||
extension Application {
|
||||
public struct NetworkCreate: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "create",
|
||||
abstract: "Create a new network")
|
||||
|
||||
@Option(name: .customLong("label"), help: "Set metadata for a network")
|
||||
var labels: [String] = []
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Option(name: .customLong("label"), help: "Set metadata on a network")
|
||||
var labels: [String] = []
|
||||
|
||||
@Argument(help: "Network name")
|
||||
var name: String
|
||||
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
let parsedLabels = Utility.parseKeyValuePairs(labels)
|
||||
let config = try NetworkConfiguration(id: self.name, mode: .nat, labels: parsedLabels)
|
||||
|
||||
@@ -22,21 +22,22 @@ import Foundation
|
||||
|
||||
extension Application {
|
||||
public struct NetworkDelete: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "delete",
|
||||
abstract: "Delete one or more networks",
|
||||
aliases: ["rm"])
|
||||
|
||||
@Flag(name: .shortAndLong, help: "Delete all networks")
|
||||
var all = false
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Flag(name: .shortAndLong, help: "Remove all networks")
|
||||
var all = false
|
||||
|
||||
@Argument(help: "Network names")
|
||||
var networkNames: [String] = []
|
||||
|
||||
public init() {}
|
||||
|
||||
public func validate() throws {
|
||||
if networkNames.count == 0 && !all {
|
||||
throw ContainerizationError(.invalidArgument, message: "no networks specified and --all not supplied")
|
||||
|
||||
@@ -22,16 +22,17 @@ import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
public struct NetworkInspect: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "inspect",
|
||||
abstract: "Display information about one or more networks")
|
||||
|
||||
@Argument(help: "Networks to inspect")
|
||||
var networks: [String]
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Argument(help: "Networks to inspect")
|
||||
var networks: [String]
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
let objects: [any Codable] = try await ClientNetwork.list().filter {
|
||||
|
||||
@@ -23,21 +23,22 @@ import SwiftProtobuf
|
||||
|
||||
extension Application {
|
||||
public struct NetworkList: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "list",
|
||||
abstract: "List networks",
|
||||
aliases: ["ls"])
|
||||
|
||||
@Flag(name: .shortAndLong, help: "Only output the network name")
|
||||
var quiet = false
|
||||
|
||||
@Option(name: .long, help: "Format of the output")
|
||||
var format: ListFormat = .table
|
||||
|
||||
@Flag(name: .shortAndLong, help: "Only output the network name")
|
||||
var quiet = false
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
let networks = try await ClientNetwork.list()
|
||||
try printNetworks(networks: networks, format: format)
|
||||
|
||||
@@ -18,7 +18,6 @@ import ArgumentParser
|
||||
|
||||
extension Application {
|
||||
public struct VolumeCommand: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "volume",
|
||||
abstract: "Manage container volumes",
|
||||
@@ -30,5 +29,7 @@ extension Application {
|
||||
],
|
||||
aliases: ["v"]
|
||||
)
|
||||
|
||||
public init() {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,24 +20,28 @@ import Foundation
|
||||
|
||||
extension Application.VolumeCommand {
|
||||
public struct VolumeCreate: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "create",
|
||||
abstract: "Create a volume"
|
||||
)
|
||||
|
||||
@Option(name: .customShort("s"), help: "Size of the volume (default: 512GB). Examples: 1G, 512MB, 2T")
|
||||
var size: String?
|
||||
@Option(name: .customLong("label"), help: "Set metadata for a volume")
|
||||
var labels: [String] = []
|
||||
|
||||
@Option(name: .customLong("opt"), help: "Set driver specific options")
|
||||
var driverOpts: [String] = []
|
||||
|
||||
@Option(name: .customLong("label"), help: "Set metadata on a volume")
|
||||
var labels: [String] = []
|
||||
@Option(name: .short, help: "Size of the volume in bytes, with optional K, M, G, T, or P suffix")
|
||||
var size: String?
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Argument(help: "Volume name")
|
||||
var name: String
|
||||
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
var parsedDriverOpts = Utility.parseKeyValuePairs(driverOpts)
|
||||
let parsedLabels = Utility.parseKeyValuePairs(labels)
|
||||
|
||||
@@ -16,24 +16,83 @@
|
||||
|
||||
import ArgumentParser
|
||||
import ContainerClient
|
||||
import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application.VolumeCommand {
|
||||
public struct VolumeDelete: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "delete",
|
||||
abstract: "Remove one or more volumes",
|
||||
abstract: "Delete one or more volumes",
|
||||
aliases: ["rm"]
|
||||
)
|
||||
|
||||
@Argument(help: "Volume name(s)")
|
||||
var names: [String]
|
||||
@Flag(name: .shortAndLong, help: "Delete all volumes")
|
||||
var all = false
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Argument(help: "Volume names")
|
||||
var names: [String] = []
|
||||
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
for name in names {
|
||||
try await ClientVolume.delete(name: name)
|
||||
print(name)
|
||||
let uniqueVolumeNames = Set<String>(names)
|
||||
let volumes: [Volume]
|
||||
|
||||
if all {
|
||||
volumes = try await ClientVolume.list()
|
||||
} else {
|
||||
volumes = try await ClientVolume.list()
|
||||
.filter { v in
|
||||
uniqueVolumeNames.contains(v.id)
|
||||
}
|
||||
|
||||
// If one of the volumes requested isn't present lets throw. We don't need to do
|
||||
// this for --all as --all should be perfectly usable with no volumes to remove,
|
||||
// otherwise it'd be quite clunky.
|
||||
if volumes.count != uniqueVolumeNames.count {
|
||||
let missing = uniqueVolumeNames.filter { id in
|
||||
!volumes.contains { v in
|
||||
v.id == id
|
||||
}
|
||||
}
|
||||
throw ContainerizationError(
|
||||
.notFound,
|
||||
message: "failed to delete one or more volumes: \(missing)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var failed = [String]()
|
||||
try await withThrowingTaskGroup(of: Volume?.self) { group in
|
||||
for volume in volumes {
|
||||
group.addTask {
|
||||
do {
|
||||
// delete atomically disables the IP allocator, then deletes
|
||||
// the allocator disable fails if any IPs are still in use
|
||||
try await ClientVolume.delete(name: volume.id)
|
||||
print(volume.id)
|
||||
return nil
|
||||
} catch {
|
||||
log.error("failed to delete volume \(volume.id): \(error)")
|
||||
return volume
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for try await volume in group {
|
||||
guard let volume else {
|
||||
continue
|
||||
}
|
||||
failed.append(volume.id)
|
||||
}
|
||||
}
|
||||
|
||||
if failed.count > 0 {
|
||||
throw ContainerizationError(.internalError, message: "delete failed for one or more volumes: \(failed)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,15 +20,19 @@ import Foundation
|
||||
|
||||
extension Application.VolumeCommand {
|
||||
public struct VolumeInspect: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "inspect",
|
||||
abstract: "Display detailed information on one or more volumes"
|
||||
abstract: "Display information about one or more volumes"
|
||||
)
|
||||
|
||||
@Argument(help: "Volume name(s)")
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Argument(help: "Volume names")
|
||||
var names: [String]
|
||||
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
var volumes: [Volume] = []
|
||||
|
||||
|
||||
@@ -21,19 +21,23 @@ import Foundation
|
||||
|
||||
extension Application.VolumeCommand {
|
||||
public struct VolumeList: AsyncParsableCommand {
|
||||
public init() {}
|
||||
public static let configuration = CommandConfiguration(
|
||||
commandName: "list",
|
||||
abstract: "List volumes",
|
||||
aliases: ["ls"]
|
||||
)
|
||||
|
||||
@Flag(name: .shortAndLong, help: "Only display volume names")
|
||||
var quiet: Bool = false
|
||||
|
||||
@Option(name: .long, help: "Format of the output")
|
||||
var format: Application.ListFormat = .table
|
||||
|
||||
@Flag(name: .shortAndLong, help: "Only output the volume name")
|
||||
var quiet: Bool = false
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
public init() {}
|
||||
|
||||
public func run() async throws {
|
||||
let volumes = try await ClientVolume.list()
|
||||
try printVolumes(volumes: volumes, format: format)
|
||||
|
||||
Reference in New Issue
Block a user