chore: import upstream snapshot with attribution
container project - merge build / Invoke build (push) Successful in 1s
container project - merge build / Invoke build (push) Successful in 1s
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
import TOML
|
||||
|
||||
/// 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.toml` (or fall back to the legacy `config.json` file) 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.toml`. An example of a valid plugin directory structure would be
|
||||
/// $ tree foobar
|
||||
/// foobar
|
||||
/// ├── bin
|
||||
/// │ └── foobar
|
||||
/// └── config.toml (`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?
|
||||
|
||||
public init(abstract: String, author: String?, servicesConfig: ServicesConfig?) {
|
||||
self.abstract = abstract
|
||||
self.author = author
|
||||
self.servicesConfig = servicesConfig
|
||||
}
|
||||
}
|
||||
|
||||
extension PluginConfig {
|
||||
public var isCLI: Bool { self.servicesConfig == nil }
|
||||
}
|
||||
|
||||
extension PluginConfig {
|
||||
/// Initialize from a config file, selecting the decoder based on file extension.
|
||||
/// Supports `.toml` (via TOMLDecoder) and `.json` (via JSONDecoder).
|
||||
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
|
||||
}
|
||||
|
||||
switch configURL.pathExtension {
|
||||
case "toml":
|
||||
guard let content = String(data: data, encoding: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
self = try TOMLDecoder().decode(PluginConfig.self, from: content)
|
||||
case "json":
|
||||
self = try JSONDecoder().decode(PluginConfig.self, from: data)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user