mirror of
https://github.com/apple/container.git
synced 2026-07-12 20:47:04 +00:00
9032d73d07
## Type of Change - [x] New feature - [x] Breaking change ## Motivation and Context This PR changes the behavior of the `defaultArguments` field in the service plugin config. Previously, `defaultArguments` was functioning as a way to indicate how to start a plugin in the event that the plugin is loaded at boot (such as for the `container-core-images plugin`). However, we now follow a convention where all plugins have a "start" command that is used when launching the plugin, so this `defaultArguments` field wasn't really providing much. Instead, there are use cases where we may want to set default values to pass to a plugin. This PR repurposes the `defaultArguments` field for those use cases. As an example use case, there are scenarios where someone may want to use the AllocationOnlyVmnetNetwork even when running on macOS 26+. This PR adds the ability to pass in a command line option to the vmnet network plugin to specify that request. Combined with the `defaultArguments` plugin config change, a user may choose to set that field to ["--variant", "allocationOnly"] in the `container-network-vmnet-config.json` to use AllocationOnlyVmnetNetwork by default for all networks. ## Testing - [x] Tested locally
117 lines
5.3 KiB
Swift
117 lines
5.3 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
// Copyright © 2025-2026 Apple Inc. and the container project authors.
|
|
//
|
|
// 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 Foundation
|
|
|
|
/// PluginConfig details all of the fields to describe and register a plugin.
|
|
/// A plugin is registered by creating a subdirectory `<application-root>/user-plugins`,
|
|
/// where the name of the subdirectory is the name of the plugin, and then placing a
|
|
/// file named `config.json` inside with the schema below.
|
|
/// If `services` is filled in then there MUST be a binary named matching the plugin name
|
|
/// in a `bin` subdirectory inside the same directory as the `config.json`.
|
|
/// An example of a valid plugin directory structure would be
|
|
/// $ tree foobar
|
|
/// foobar
|
|
/// ├── bin
|
|
/// │ └── foobar
|
|
/// └── config.json
|
|
public struct PluginConfig: Sendable, Codable {
|
|
/// Categories of services that can be offered through plugins.
|
|
public enum DaemonPluginType: String, Sendable, Codable {
|
|
/// A runtime plugin provides an XPC API through which the lifecycle
|
|
/// of a **single** container can be managed.
|
|
/// A runtime daemon plugin would typically also have a counterpart
|
|
/// CLI plugin which knows how to talk to the API exposed by the runtime plugin.
|
|
/// The API server ensures that a single instance of the plugin is configured
|
|
/// for a given container such that the client can communicate with it given an instance id.
|
|
case runtime
|
|
/// A network plugin provides an XPC API through which IP address allocations on a given
|
|
/// network can be managed. The API server ensures that a single instance
|
|
/// of this plugin is configured for a given network. Similar to the runtime plugin, it typically
|
|
/// would be accompanied by a CLI plugin that knows how to communicate with the XPC API
|
|
/// given an instance id.
|
|
case network
|
|
/// A core plugin provides an XPC API to manage a given type of resource.
|
|
/// The API server ensures that there exist only a single running instance
|
|
/// of this plugin type. A core plugin can be thought of a singleton whose lifecycle
|
|
/// is tied to that of the API server. Core plugins can be used to expand the base functionality
|
|
/// provided by the API server. As with the other plugin types, it maybe associated with a client
|
|
/// side plugin that communicates with the XPC service exposed by the daemon plugin.
|
|
case core
|
|
/// Reserved for future use. Currently there is no difference between a core and auxiliary daemon plugin.
|
|
case auxiliary
|
|
}
|
|
|
|
// An XPC service that the plugin publishes.
|
|
public struct Service: Sendable, Codable {
|
|
/// The type of the service the daemon is exposing.
|
|
/// One plugin can expose multiple services of different types.
|
|
///
|
|
/// The plugin MUST expose a MachService at
|
|
/// `com.apple.container.{type}.{name}.[{id}]` for
|
|
/// each service that it exposes.
|
|
public let type: DaemonPluginType
|
|
/// Optional description of this service.
|
|
public let description: String?
|
|
}
|
|
|
|
/// Descriptor for the services that the plugin offers.
|
|
public struct ServicesConfig: Sendable, Codable {
|
|
/// Load the plugin into launchd when the API server starts.
|
|
public let loadAtBoot: Bool
|
|
/// Launch the plugin binary as soon as it loads into launchd.
|
|
public let runAtLoad: Bool
|
|
/// The service types that the plugin provides.
|
|
public let services: [Service]
|
|
/// An optional parameter that include any command line arguments
|
|
/// that must be passed to the plugin binary when it is loaded.
|
|
public let defaultArguments: [String]
|
|
}
|
|
|
|
/// Short description of the plugin surface. This will be displayed as the
|
|
/// help-text for CLI plugins, and will be returned in API calls to view loaded
|
|
/// plugins from the daemon.
|
|
public let abstract: String
|
|
|
|
/// Author of the plugin. This is solely metadata.
|
|
public let author: String?
|
|
|
|
/// Services configuration. Specify nil for a CLI plugin, and an empty array for
|
|
/// that does not publish any XPC services.
|
|
public let servicesConfig: ServicesConfig?
|
|
}
|
|
|
|
extension PluginConfig {
|
|
public var isCLI: Bool { self.servicesConfig == nil }
|
|
}
|
|
|
|
extension PluginConfig {
|
|
public init?(configURL: URL) throws {
|
|
let fm = FileManager.default
|
|
if !fm.fileExists(atPath: configURL.path) {
|
|
return nil
|
|
}
|
|
|
|
guard let data = fm.contents(atPath: configURL.path) else {
|
|
return nil
|
|
}
|
|
|
|
let decoder: JSONDecoder = JSONDecoder()
|
|
self = try decoder.decode(PluginConfig.self, from: data)
|
|
}
|
|
}
|