Skip to content

Commit cfcc333

Browse files
authored
Merge branch 'main' into fix-debug-system-start
2 parents b1e6b77 + c9f81ca commit cfcc333

17 files changed

Lines changed: 406 additions & 19 deletions

File tree

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,11 @@ integration: init-block
188188
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIRunCommand2 || exit_code=1 ; \
189189
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIRunCommand3 || exit_code=1 ; \
190190
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIPruneCommand || exit_code=1 ; \
191+
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIRegistry || exit_code=1 ; \
191192
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIStatsCommand || exit_code=1 ; \
192193
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIImagesCommand || exit_code=1 ; \
193194
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIRunBase || exit_code=1 ; \
195+
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIRunInitImage || exit_code=1 ; \
194196
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIBuildBase || exit_code=1 ; \
195197
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIVolumes || exit_code=1 ; \
196198
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIKernelSet || exit_code=1 ; \

Sources/ContainerCommands/Container/ContainerCreate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ extension Application {
8484

8585
let options = ContainerCreateOptions(autoRemove: managementFlags.remove)
8686
let client = ContainerClient()
87-
try await client.create(configuration: ck.0, options: options, kernel: ck.1)
87+
try await client.create(configuration: ck.0, options: options, kernel: ck.1, initImage: ck.2)
8888

8989
if !self.managementFlags.cidfile.isEmpty {
9090
let path = self.managementFlags.cidfile

Sources/ContainerCommands/Container/ContainerRun.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ extension Application {
113113
try await client.create(
114114
configuration: ck.0,
115115
options: options,
116-
kernel: ck.1
116+
kernel: ck.1,
117+
initImage: ck.2
117118
)
118119

119120
let detach = self.managementFlags.detach

Sources/ContainerCommands/Registry/RegistryCommand.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ extension Application {
2323
commandName: "registry",
2424
abstract: "Manage registry logins",
2525
subcommands: [
26-
Login.self,
27-
Logout.self,
26+
RegistryLogin.self,
27+
RegistryLogout.self,
28+
RegistryList.self,
2829
],
2930
aliases: ["r"]
3031
)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//===----------------------------------------------------------------------===//
2+
// Copyright © 2026 Apple Inc. and the container project authors.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// https://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
//===----------------------------------------------------------------------===//
16+
17+
import ArgumentParser
18+
import ContainerAPIClient
19+
import ContainerizationOCI
20+
import ContainerizationOS
21+
import Foundation
22+
23+
extension Application {
24+
public struct RegistryList: AsyncLoggableCommand {
25+
@OptionGroup
26+
public var logOptions: Flags.Logging
27+
28+
@Option(name: .long, help: "Format of the output")
29+
var format: ListFormat = .table
30+
31+
@Flag(name: .shortAndLong, help: "Only output the registry name")
32+
var quiet = false
33+
34+
public init() {}
35+
public static let configuration = CommandConfiguration(
36+
commandName: "list",
37+
abstract: "List image registry logins",
38+
aliases: ["ls"])
39+
40+
public func run() async throws {
41+
let keychain = KeychainHelper(securityDomain: Constants.keychainID)
42+
let registries = try keychain.list()
43+
try printRegistries(registries: registries, format: format)
44+
}
45+
46+
private func createHeader() -> [[String]] {
47+
[["HOSTNAME", "USERNAME", "MODIFIED", "CREATED"]]
48+
}
49+
50+
private func printRegistries(registries: [RegistryInfo], format: ListFormat) throws {
51+
if format == .json {
52+
let printables = registries.map {
53+
PrintableRegistry($0)
54+
}
55+
let data = try JSONEncoder().encode(printables)
56+
print(String(decoding: data, as: UTF8.self))
57+
58+
return
59+
}
60+
61+
if self.quiet {
62+
registries.forEach {
63+
print($0.hostname)
64+
}
65+
return
66+
}
67+
68+
var rows = createHeader()
69+
for registry in registries {
70+
rows.append(registry.asRow)
71+
}
72+
73+
let formatter = TableOutput(rows: rows)
74+
print(formatter.format())
75+
}
76+
}
77+
}
78+
extension RegistryInfo {
79+
fileprivate var asRow: [String] {
80+
[
81+
self.hostname,
82+
self.username,
83+
self.modifiedDate.ISO8601Format(),
84+
self.createdDate.ISO8601Format(),
85+
]
86+
}
87+
}
88+
struct PrintableRegistry: Codable {
89+
let hostname: String
90+
let username: String
91+
let modifiedDate: Date
92+
let createdDate: Date
93+
94+
init(_ registry: RegistryInfo) {
95+
self.hostname = registry.hostname
96+
self.username = registry.username
97+
self.modifiedDate = registry.modifiedDate
98+
self.createdDate = registry.createdDate
99+
}
100+
}

Sources/ContainerCommands/Registry/Login.swift renamed to Sources/ContainerCommands/Registry/RegistryLogin.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//===----------------------------------------------------------------------===//
2-
// Copyright © 2025-2026 Apple Inc. and the container project authors.
2+
// Copyright © 2026 Apple Inc. and the container project authors.
33
//
44
// Licensed under the Apache License, Version 2.0 (the "License");
55
// you may not use this file except in compliance with the License.
@@ -22,9 +22,10 @@ import ContainerizationOCI
2222
import Foundation
2323

2424
extension Application {
25-
public struct Login: AsyncLoggableCommand {
25+
public struct RegistryLogin: AsyncLoggableCommand {
2626
public init() {}
2727
public static let configuration = CommandConfiguration(
28+
commandName: "login",
2829
abstract: "Log in to a registry"
2930
)
3031

Sources/ContainerCommands/Registry/Logout.swift renamed to Sources/ContainerCommands/Registry/RegistryLogout.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//===----------------------------------------------------------------------===//
2-
// Copyright © 2025-2026 Apple Inc. and the container project authors.
2+
// Copyright © 2026 Apple Inc. and the container project authors.
33
//
44
// Licensed under the Apache License, Version 2.0 (the "License");
55
// you may not use this file except in compliance with the License.
@@ -20,10 +20,12 @@ import Containerization
2020
import ContainerizationOCI
2121

2222
extension Application {
23-
public struct Logout: AsyncLoggableCommand {
23+
public struct RegistryLogout: AsyncLoggableCommand {
2424
public init() {}
2525
public static let configuration = CommandConfiguration(
26-
abstract: "Log out from a registry")
26+
commandName: "logout",
27+
abstract: "Log out from a registry"
28+
)
2729

2830
@OptionGroup
2931
public var logOptions: Flags.Logging

Sources/Services/ContainerAPIService/Client/ContainerClient.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ public struct ContainerClient: Sendable {
4848
public func create(
4949
configuration: ContainerConfiguration,
5050
options: ContainerCreateOptions = .default,
51-
kernel: Kernel
51+
kernel: Kernel,
52+
initImage: String? = nil
5253
) async throws {
5354
do {
5455
let request = XPCMessage(route: .containerCreate)
@@ -60,6 +61,10 @@ public struct ContainerClient: Sendable {
6061
request.set(key: .kernel, value: kdata)
6162
request.set(key: .containerOptions, value: odata)
6263

64+
if let initImage {
65+
request.set(key: .initImage, value: initImage)
66+
}
67+
6368
try await xpcSend(message: request)
6469
} catch {
6570
throw ContainerizationError(

Sources/Services/ContainerAPIService/Client/Flags.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ public struct Flags {
158158
)
159159
public var kernel: String?
160160

161+
@Option(
162+
name: .long,
163+
help: .init("Use a custom init image instead of the default", valueName: "image")
164+
)
165+
public var initImage: String?
166+
161167
@Option(name: [.short, .customLong("label")], help: "Add a key=value label to the container")
162168
public var labels: [String] = []
163169

Sources/Services/ContainerAPIService/Client/Utility.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public struct Utility {
8484
imageFetch: Flags.ImageFetch,
8585
progressUpdate: @escaping ProgressUpdateHandler,
8686
log: Logger
87-
) async throws -> (ContainerConfiguration, Kernel) {
87+
) async throws -> (ContainerConfiguration, Kernel, String?) {
8888
var requestedPlatform = Parser.platform(os: management.os, arch: management.arch)
8989
// Prefer --platform
9090
if let platform = management.platform {
@@ -129,8 +129,9 @@ public struct Utility {
129129
.setItemsName("blobs"),
130130
])
131131
let fetchInitTask = await taskManager.startTask()
132+
let initImageRef = management.initImage ?? ClientImage.initImageRef
132133
let initImage = try await ClientImage.fetch(
133-
reference: ClientImage.initImageRef, platform: .current, scheme: scheme,
134+
reference: initImageRef, platform: .current, scheme: scheme,
134135
progressUpdate: ProgressTaskCoordinator.handler(for: fetchInitTask, from: progressUpdate),
135136
maxConcurrentDownloads: imageFetch.maxConcurrentDownloads)
136137

@@ -252,7 +253,7 @@ public struct Utility {
252253
config.runtimeHandler = runtime
253254
}
254255

255-
return (config, kernel)
256+
return (config, kernel, management.initImage)
256257
}
257258

258259
static func getAttachmentConfigurations(

0 commit comments

Comments
 (0)