mirror of
https://github.com/apple/container.git
synced 2026-09-27 10:05:42 +00:00
Replace scattered defaults subcommands with system property. (#604)
Common subcommands for all defaults. - Closes #384. - Replaces `registry default` and `system dns default` subcommands with `system property`. - Users can use `system property ls` to see details about each supported default value. - `system property set` implements reasonable validation for all properties. - NOTE: Probing of the registry for `registry default set` was removed, which means users will find out about a botched setting when pulling or pushing. - Updates docs. ## Type of Change - [ ] Bug fix - [x] New feature - [x] Breaking change - [x] Documentation update ## Motivation and Context See #384. ## Testing - [x] Tested locally - [x] Added/updated tests - [x] Added/updated docs
This commit is contained in:
@@ -24,7 +24,6 @@ extension Application {
|
||||
subcommands: [
|
||||
Login.self,
|
||||
Logout.self,
|
||||
RegistryDefault.self,
|
||||
],
|
||||
aliases: ["r"]
|
||||
)
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerPersistence
|
||||
import ContainerizationError
|
||||
import ContainerizationOCI
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct RegistryDefault: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "default",
|
||||
abstract: "Manage the default image registry",
|
||||
subcommands: [
|
||||
DefaultSetCommand.self,
|
||||
DefaultUnsetCommand.self,
|
||||
DefaultInspectCommand.self,
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
struct DefaultSetCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "set",
|
||||
abstract: "Set the default registry"
|
||||
)
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@OptionGroup
|
||||
var registry: Flags.Registry
|
||||
|
||||
@Argument
|
||||
var host: String
|
||||
|
||||
func run() async throws {
|
||||
let scheme = try RequestScheme(registry.scheme).schemeFor(host: host)
|
||||
|
||||
let _url = "\(scheme)://\(host)"
|
||||
guard let url = URL(string: _url), let domain = url.host() else {
|
||||
throw ContainerizationError(.invalidArgument, message: "Cannot convert \(_url) to URL")
|
||||
}
|
||||
let resolvedDomain = Reference.resolveDomain(domain: domain)
|
||||
let client = RegistryClient(host: resolvedDomain, scheme: scheme.rawValue, port: url.port)
|
||||
do {
|
||||
try await client.ping()
|
||||
} catch let err as RegistryClient.Error {
|
||||
switch err {
|
||||
case .invalidStatus(url: _, .unauthorized, _), .invalidStatus(url: _, .forbidden, _):
|
||||
break
|
||||
default:
|
||||
throw err
|
||||
}
|
||||
}
|
||||
DefaultsStore.set(value: host, key: .defaultRegistryDomain)
|
||||
print("Set default registry to \(host)")
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultUnsetCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "unset",
|
||||
abstract: "Unset the default registry",
|
||||
aliases: ["clear"]
|
||||
)
|
||||
|
||||
func run() async throws {
|
||||
DefaultsStore.unset(key: .defaultRegistryDomain)
|
||||
print("Unset the default registry domain")
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultInspectCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "inspect",
|
||||
abstract: "Display the default registry domain"
|
||||
)
|
||||
|
||||
func run() async throws {
|
||||
print(DefaultsStore.get(key: .defaultRegistryDomain))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerPersistence
|
||||
|
||||
extension Application {
|
||||
struct DNSDefault: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "default",
|
||||
abstract: "Set or unset the default local DNS domain",
|
||||
subcommands: [
|
||||
DefaultSetCommand.self,
|
||||
DefaultUnsetCommand.self,
|
||||
DefaultInspectCommand.self,
|
||||
]
|
||||
)
|
||||
|
||||
struct DefaultSetCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "set",
|
||||
abstract: "Set the default local DNS domain"
|
||||
|
||||
)
|
||||
|
||||
@Argument(help: "the default `--domain-name` to use for the `create` or `run` command")
|
||||
var domainName: String
|
||||
|
||||
func run() async throws {
|
||||
DefaultsStore.set(value: domainName, key: .defaultDNSDomain)
|
||||
print(domainName)
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultUnsetCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "unset",
|
||||
abstract: "Unset the default local DNS domain",
|
||||
aliases: ["clear"]
|
||||
)
|
||||
|
||||
func run() async throws {
|
||||
DefaultsStore.unset(key: .defaultDNSDomain)
|
||||
print("Unset the default local DNS domain")
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultInspectCommand: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "inspect",
|
||||
abstract: "Display the default local DNS domain"
|
||||
)
|
||||
|
||||
func run() async throws {
|
||||
print(DefaultsStore.getOptional(key: .defaultDNSDomain) ?? "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerPersistence
|
||||
import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct PropertyClear: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "clear",
|
||||
abstract: "Clear a property value"
|
||||
)
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Argument(help: "the property ID")
|
||||
var id: String
|
||||
|
||||
func run() async throws {
|
||||
guard let key = DefaultsStore.Keys(rawValue: id) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid property ID: \(id)")
|
||||
}
|
||||
|
||||
DefaultsStore.unset(key: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerPersistence
|
||||
import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct PropertyGet: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "get",
|
||||
abstract: "Retrieve a property value"
|
||||
)
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Argument(help: "the property ID")
|
||||
var id: String
|
||||
|
||||
func run() async throws {
|
||||
let value = DefaultsStore.allValues()
|
||||
.filter { id == $0.id }
|
||||
.first
|
||||
guard let value else {
|
||||
throw ContainerizationError(.invalidArgument, message: "property ID \(id) not found")
|
||||
}
|
||||
|
||||
guard let val = value.value?.description else {
|
||||
return
|
||||
}
|
||||
|
||||
print(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerPersistence
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct PropertyList: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "list",
|
||||
abstract: "List system properties",
|
||||
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
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
func run() async throws {
|
||||
let vals = DefaultsStore.allValues()
|
||||
try printValues(vals, format: format)
|
||||
}
|
||||
|
||||
private func createHeader() -> [[String]] {
|
||||
[["ID", "TYPE", "VALUE", "DESCRIPTION"]]
|
||||
}
|
||||
|
||||
private func printValues(_ vals: [DefaultsStoreValue], format: ListFormat) throws {
|
||||
if format == .json {
|
||||
let data = try JSONEncoder().encode(vals)
|
||||
print(String(data: data, encoding: .utf8)!)
|
||||
return
|
||||
}
|
||||
|
||||
if self.quiet {
|
||||
vals.forEach {
|
||||
print($0.id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var rows = createHeader()
|
||||
for property in vals {
|
||||
rows.append(property.asRow)
|
||||
}
|
||||
|
||||
let formatter = TableOutput(rows: rows)
|
||||
print(formatter.format())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DefaultsStoreValue {
|
||||
var asRow: [String] {
|
||||
[id, String(describing: type), value?.description.elided(to: 40) ?? "*undefined*", description]
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func elided(to maxCount: Int) -> String {
|
||||
let ellipsis = "..."
|
||||
guard self.count > maxCount else {
|
||||
return self
|
||||
}
|
||||
|
||||
if maxCount < ellipsis.count {
|
||||
return ellipsis
|
||||
}
|
||||
|
||||
let prefixCount = maxCount - ellipsis.count
|
||||
return self.prefix(prefixCount) + ellipsis
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerPersistence
|
||||
import ContainerizationError
|
||||
import ContainerizationExtras
|
||||
import ContainerizationOCI
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct PropertySet: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "set",
|
||||
abstract: "Set a property value"
|
||||
)
|
||||
|
||||
@OptionGroup
|
||||
var global: Flags.Global
|
||||
|
||||
@Argument(help: "the property ID")
|
||||
var id: String
|
||||
|
||||
@Argument(help: "the property value")
|
||||
var value: String
|
||||
|
||||
func run() async throws {
|
||||
guard let key = DefaultsStore.Keys(rawValue: id) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid property ID: \(id)")
|
||||
}
|
||||
|
||||
switch key {
|
||||
case .buildRosetta:
|
||||
guard let boolValue = Parser.parseBool(string: value) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid boolean value: \(value)")
|
||||
}
|
||||
DefaultsStore.setBool(value: boolValue, key: key)
|
||||
case .defaultDNSDomain, .defaultRegistryDomain:
|
||||
guard Parser.isValidDomainName(value) else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid domain name: \(value)")
|
||||
}
|
||||
DefaultsStore.set(value: value, key: key)
|
||||
case .defaultBuilderImage, .defaultInitImage:
|
||||
guard (try? Reference.parse(value)) != nil else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid image reference: \(value)")
|
||||
}
|
||||
DefaultsStore.set(value: value, key: key)
|
||||
case .defaultKernelBinaryPath:
|
||||
DefaultsStore.set(value: value, key: key)
|
||||
case .defaultKernelURL:
|
||||
guard URL(string: value) != nil else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid URL: \(value)")
|
||||
}
|
||||
DefaultsStore.set(value: value, key: key)
|
||||
return
|
||||
case .defaultSubnet:
|
||||
guard (try? CIDRAddress(value)) != nil else {
|
||||
throw ContainerizationError(.invalidArgument, message: "invalid CIDRv4 address: \(value)")
|
||||
}
|
||||
DefaultsStore.set(value: value, key: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,12 @@ extension Application {
|
||||
abstract: "Manage system components",
|
||||
subcommands: [
|
||||
SystemDNS.self,
|
||||
SystemLogs.self,
|
||||
SystemStart.self,
|
||||
SystemStop.self,
|
||||
SystemStatus.self,
|
||||
SystemKernel.self,
|
||||
SystemLogs.self,
|
||||
SystemProperty.self,
|
||||
SystemStart.self,
|
||||
SystemStatus.self,
|
||||
SystemStop.self,
|
||||
],
|
||||
aliases: ["s"]
|
||||
)
|
||||
|
||||
@@ -27,7 +27,6 @@ extension Application {
|
||||
DNSCreate.self,
|
||||
DNSDelete.self,
|
||||
DNSList.self,
|
||||
DNSDefault.self,
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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 ContainerPersistence
|
||||
import ContainerizationError
|
||||
import Foundation
|
||||
|
||||
extension Application {
|
||||
struct SystemProperty: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "property",
|
||||
abstract: "Manage system property values",
|
||||
subcommands: [
|
||||
PropertyClear.self,
|
||||
PropertyGet.self,
|
||||
PropertyList.self,
|
||||
PropertySet.self,
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -640,4 +640,32 @@ public struct Parser {
|
||||
"invalid publish-socket format \(socketText). Expected: host_path:container_path")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: DNS
|
||||
|
||||
public static func isValidDomainName(_ name: String) -> Bool {
|
||||
guard !name.isEmpty && name.count <= 255 else {
|
||||
return false
|
||||
}
|
||||
return name.components(separatedBy: ".").allSatisfy { Self.isValidDomainNameLabel($0) }
|
||||
}
|
||||
|
||||
public static func isValidDomainNameLabel(_ label: String) -> Bool {
|
||||
guard !label.isEmpty && label.count <= 63 else {
|
||||
return false
|
||||
}
|
||||
let pattern = #/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/#
|
||||
return !label.ranges(of: pattern).isEmpty
|
||||
}
|
||||
|
||||
// MARK: Miscellaneous
|
||||
|
||||
public static func parseBool(string: String) -> Bool? {
|
||||
let lower = string.lowercased()
|
||||
switch lower {
|
||||
case "true", "t": return true
|
||||
case "false", "f": return false
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ public enum DefaultsStore {
|
||||
private static let userDefaultDomain = "com.apple.container.defaults"
|
||||
|
||||
public enum Keys: String {
|
||||
case defaultBuilderImage = "image.builder"
|
||||
case defaultDNSDomain = "dns.domain"
|
||||
case defaultRegistryDomain = "registry.domain"
|
||||
case defaultInitImage = "image.init"
|
||||
case defaultKernelURL = "kernel.url"
|
||||
case defaultKernelBinaryPath = "kernel.binaryPath"
|
||||
case defaultSubnet = "network.subnet"
|
||||
case buildRosetta = "build.rosetta"
|
||||
case defaultDNSDomain = "dns.domain"
|
||||
case defaultBuilderImage = "image.builder"
|
||||
case defaultInitImage = "image.init"
|
||||
case defaultKernelBinaryPath = "kernel.binaryPath"
|
||||
case defaultKernelURL = "kernel.url"
|
||||
case defaultSubnet = "network.subnet"
|
||||
case defaultRegistryDomain = "registry.domain"
|
||||
}
|
||||
|
||||
public static func set(value: String, key: DefaultsStore.Keys) {
|
||||
@@ -58,6 +58,23 @@ public enum DefaultsStore {
|
||||
return udSuite.bool(forKey: key.rawValue)
|
||||
}
|
||||
|
||||
public static func allValues() -> [DefaultsStoreValue] {
|
||||
let allKeys: [(Self.Keys, (Self.Keys) -> Any?)] = [
|
||||
(.buildRosetta, { Self.getBool(key: $0) }),
|
||||
(.defaultBuilderImage, { Self.get(key: $0) }),
|
||||
(.defaultInitImage, { Self.get(key: $0) }),
|
||||
(.defaultKernelBinaryPath, { Self.get(key: $0) }),
|
||||
(.defaultKernelURL, { Self.get(key: $0) }),
|
||||
(.defaultSubnet, { Self.getOptional(key: $0) }),
|
||||
(.defaultDNSDomain, { Self.getOptional(key: $0) }),
|
||||
(.defaultRegistryDomain, { Self.get(key: $0) }),
|
||||
]
|
||||
return
|
||||
allKeys
|
||||
.map { DefaultsStoreValue(id: $0.rawValue, description: $0.summary, value: $1($0) as? (Encodable & CustomStringConvertible), type: $0.type) }
|
||||
.sorted(by: { $0.id < $1.id })
|
||||
}
|
||||
|
||||
private static var udSuite: UserDefaults {
|
||||
guard let ud = UserDefaults.init(suiteName: self.userDefaultDomain) else {
|
||||
fatalError("Failed to initialize UserDefaults for domain \(self.userDefaultDomain)")
|
||||
@@ -66,31 +83,98 @@ public enum DefaultsStore {
|
||||
}
|
||||
}
|
||||
|
||||
public struct DefaultsStoreValue: Identifiable, CustomStringConvertible, Encodable {
|
||||
public let id: String
|
||||
public let description: String
|
||||
public let value: (Encodable & CustomStringConvertible)?
|
||||
public let type: Any.Type
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(description, forKey: .description)
|
||||
|
||||
if let value = value {
|
||||
try container.encode(value, forKey: .value)
|
||||
} else {
|
||||
try container.encodeNil(forKey: .value)
|
||||
}
|
||||
|
||||
try container.encode(String(describing: type), forKey: .type)
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id, description, value, type
|
||||
}
|
||||
}
|
||||
|
||||
extension DefaultsStore.Keys {
|
||||
public var summary: String {
|
||||
switch self {
|
||||
case .buildRosetta:
|
||||
return "Build amd64 images on arm64 using Rosetta, instead of QEMU."
|
||||
case .defaultDNSDomain:
|
||||
return "If defined, the local DNS domain to use for containers with unqualified names."
|
||||
case .defaultBuilderImage:
|
||||
return "The image reference for the utility container that `container build` uses."
|
||||
case .defaultInitImage:
|
||||
return "The image reference for the default initial filesystem image."
|
||||
case .defaultKernelBinaryPath:
|
||||
return "If the kernel URL is for an archive, the archive member pathname for the kernel file."
|
||||
case .defaultKernelURL:
|
||||
return "The URL for the kernel file to install, or the URL for an archive containing the kernel file."
|
||||
case .defaultSubnet:
|
||||
return "Default subnet for IP allocation (used on macOS 15 only)."
|
||||
case .defaultRegistryDomain:
|
||||
return "The default registry to use for image references that do not specify a registry."
|
||||
}
|
||||
}
|
||||
|
||||
public var type: Any.Type {
|
||||
switch self {
|
||||
case .buildRosetta:
|
||||
return Bool.self
|
||||
case .defaultDNSDomain:
|
||||
return String.self
|
||||
case .defaultBuilderImage:
|
||||
return String.self
|
||||
case .defaultInitImage:
|
||||
return String.self
|
||||
case .defaultKernelBinaryPath:
|
||||
return String.self
|
||||
case .defaultKernelURL:
|
||||
return String.self
|
||||
case .defaultSubnet:
|
||||
return String.self
|
||||
case .defaultRegistryDomain:
|
||||
return String.self
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var defaultValue: String {
|
||||
switch self {
|
||||
case .defaultKernelURL:
|
||||
return "https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz"
|
||||
case .defaultKernelBinaryPath:
|
||||
return "opt/kata/share/kata-containers/vmlinux-6.12.28-153"
|
||||
case .buildRosetta:
|
||||
// This is a boolean key, not used with the string get() method
|
||||
return "true"
|
||||
case .defaultDNSDomain:
|
||||
return "test"
|
||||
case .defaultBuilderImage:
|
||||
let tag = String(cString: get_container_builder_shim_version())
|
||||
return "ghcr.io/apple/container-builder-shim/builder:\(tag)"
|
||||
case .defaultDNSDomain:
|
||||
return "test"
|
||||
case .defaultRegistryDomain:
|
||||
return "docker.io"
|
||||
case .defaultInitImage:
|
||||
let tag = String(cString: get_swift_containerization_version())
|
||||
guard tag != "latest" else {
|
||||
return "vminit:latest"
|
||||
}
|
||||
return "ghcr.io/apple/containerization/vminit:\(tag)"
|
||||
case .defaultKernelBinaryPath:
|
||||
return "opt/kata/share/kata-containers/vmlinux-6.12.28-153"
|
||||
case .defaultKernelURL:
|
||||
return "https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz"
|
||||
case .defaultSubnet:
|
||||
return "192.168.64.1/24"
|
||||
case .buildRosetta:
|
||||
// This is a boolean key, not used with the string get() method
|
||||
return "true"
|
||||
case .defaultRegistryDomain:
|
||||
return "docker.io"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user