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
7 changes: 7 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ let package = Package(
"ContainerBuild"
]
),
.testTarget(
name: "ContainerCommandsTests",
dependencies: [
"ContainerCommands",
"ContainerResource",
]
),
.executableTarget(
name: "container-apiserver",
dependencies: [
Expand Down
5 changes: 0 additions & 5 deletions Sources/ContainerCommands/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,6 @@ extension Application {
print(altered)
}

public enum ListFormat: String, CaseIterable, ExpressibleByArgument {
case json
case table
}

func isTranslated() throws -> Bool {
do {
return try Sysctl.byName("sysctl.proc_translated") == 1
Expand Down
73 changes: 33 additions & 40 deletions Sources/ContainerCommands/Builder/BuilderStatus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,60 +45,53 @@ extension Application {
do {
let client = ContainerClient()
let container = try await client.get(id: "buildkit")
try printContainers(containers: [container], format: format)

if format == .json {
try emit(renderJSON([PrintableContainer(container)]))
return
}

if quiet && container.status != .running {
return
}

emit(renderList([PrintableBuilder(container)], quiet: quiet))
} catch {
if error is ContainerizationError {
if (error as? ContainerizationError)?.code == .notFound && !quiet {
if let czError = error as? ContainerizationError, czError.code == .notFound {
if !quiet {
print("builder is not running")
return
}
}
throw error
}
}
}
}

private func createHeader() -> [[String]] {
[["ID", "IMAGE", "STATE", "ADDR", "CPUS", "MEMORY"]]
}

private func printContainers(containers: [ContainerSnapshot], format: ListFormat) throws {
if format == .json {
let printables = containers.map {
PrintableContainer($0)
}
let data = try JSONEncoder().encode(printables)
print(String(decoding: data, as: UTF8.self))

return
}

if self.quiet {
containers
.filter { $0.status == .running }
.forEach { print($0.id) }
return
}
private struct PrintableBuilder: ListDisplayable {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason we can't define a ListDisplayable extension on the bulk of or regular types instead of defining completely separate types that conform to that protocol? It feels like there might be a bit more boilerplate as it currently stands.

let snapshot: ContainerSnapshot

var rows = createHeader()
for container in containers {
rows.append(container.asRow)
}
init(_ snapshot: ContainerSnapshot) {
self.snapshot = snapshot
}

let formatter = TableOutput(rows: rows)
print(formatter.format())
}
static var tableHeader: [String] {
["ID", "IMAGE", "STATE", "ADDR", "CPUS", "MEMORY"]
}
}

extension ContainerSnapshot {
fileprivate var asRow: [String] {
var tableRow: [String] {
[
self.id,
self.configuration.image.reference,
self.status.rawValue,
self.networks.compactMap { $0.ipv4Address.description }.joined(separator: ","),
"\(self.configuration.resources.cpus)",
"\(self.configuration.resources.memoryInBytes / (1024 * 1024)) MB",
snapshot.id,
snapshot.configuration.image.reference,
snapshot.status.rawValue,
snapshot.networks.map { $0.ipv4Address.description }.joined(separator: ","),
"\(snapshot.configuration.resources.cpus)",
"\(snapshot.configuration.resources.memoryInBytes / (1024 * 1024)) MB",
]
}

var quietValue: String {
snapshot.id
}
}
4 changes: 2 additions & 2 deletions Sources/ContainerCommands/Container/ContainerInspect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ extension Application {

public func run() async throws {
let client = ContainerClient()
let objects: [any Codable] = try await client.list().filter {
let containers = try await client.list().filter {
containerIds.contains($0.id)
}.map {
PrintableContainer($0)
}
print(try objects.jsonArray())
try emit(renderJSON(containers))
}
}
}
54 changes: 19 additions & 35 deletions Sources/ContainerCommands/Container/ContainerList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,59 +46,43 @@ extension Application {
let client = ContainerClient()
let filters = self.all ? ContainerListFilters.all : ContainerListFilters(status: .running)
let containers = try await client.list(filters: filters)
try printContainers(containers: containers, format: format)
}

private func createHeader() -> [[String]] {
[["ID", "IMAGE", "OS", "ARCH", "STATE", "ADDR", "CPUS", "MEMORY", "STARTED"]]
}
let items = containers.map { PrintableContainer($0) }

private func printContainers(containers: [ContainerSnapshot], format: ListFormat) throws {
if format == .json {
let printables = containers.map {
PrintableContainer($0)
}
let data = try JSONEncoder().encode(printables)
print(String(decoding: data, as: UTF8.self))

return
}

if self.quiet {
containers.forEach {
print($0.id)
}
try emit(renderJSON(items))
return
}

var rows = createHeader()
for container in containers {
rows.append(container.asRow)
}

let formatter = TableOutput(rows: rows)
print(formatter.format())
emit(renderList(items, quiet: quiet))
}
}
}

extension ContainerSnapshot {
fileprivate var asRow: [String] {
extension PrintableContainer: ListDisplayable {
static var tableHeader: [String] {
["ID", "IMAGE", "OS", "ARCH", "STATE", "ADDR", "CPUS", "MEMORY", "STARTED"]
}

var tableRow: [String] {
[
self.id,
self.configuration.id,
self.configuration.image.reference,
self.platform.os,
self.platform.architecture,
self.configuration.platform.os,
self.configuration.platform.architecture,
self.status.rawValue,
self.networks.compactMap { $0.ipv4Address.description }.joined(separator: ","),
self.networks.map { $0.ipv4Address.description }.joined(separator: ","),
"\(self.configuration.resources.cpus)",
"\(self.configuration.resources.memoryInBytes / (1024 * 1024)) MB",
self.startedDate.map { ISO8601DateFormatter().string(from: $0) } ?? "",
self.startedDate?.ISO8601Format() ?? "",
]
}

var quietValue: String {
self.configuration.id
}
}

struct PrintableContainer: Codable {
struct PrintableContainer: Codable, Sendable {
let status: RuntimeStatus
let configuration: ContainerConfiguration
let networks: [Attachment]
Expand Down
3 changes: 1 addition & 2 deletions Sources/ContainerCommands/Container/ContainerStats.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,7 @@ extension Application {

if format == .json {
let jsonStats = statsData.map { $0.stats2 }
let data = try JSONEncoder().encode(jsonStats)
print(String(decoding: data, as: UTF8.self))
try emit(renderJSON(jsonStats))
return
}

Expand Down
5 changes: 3 additions & 2 deletions Sources/ContainerCommands/Image/ImageInspect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import ArgumentParser
import ContainerAPIClient
import ContainerLog
import ContainerResource
import ContainerizationError
import Foundation
import Logging
Expand All @@ -42,7 +43,7 @@ extension Application {
}

public func run() async throws {
var printable = [any Codable]()
var printable: [ImageDetail] = []
var succeededImages: [String] = []
var allErrors: [(String, Error)] = []

Expand All @@ -59,7 +60,7 @@ extension Application {
}

if !printable.isEmpty {
print(try printable.jsonArray())
try emit(renderJSON(printable))
}

if !allErrors.isEmpty {
Expand Down
Loading
Loading