//===----------------------------------------------------------------------===// // 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 Foundation /// PluginConfig details all of the fields to describe and register a plugin. /// A plugin is registered by creating a subdirectory `/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. /// This parameter is used only when `servicesConfig.loadAtBoot` is `true` 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) } }