-
Notifications
You must be signed in to change notification settings - Fork 656
Use labels instead of id to discriminate the builtin network. #1123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // Copyright © 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 | ||
|
|
||
| /// Common properties for all managed resources. | ||
| public protocol ManagedResource: Identifiable, Sendable, Codable { | ||
| /// A 64 byte hexadecimal string, assigned by the system, that uniquely | ||
| /// identifies the resource. | ||
| var id: String { get } | ||
|
|
||
| /// A user assigned name that shall be unique within the namespace of | ||
| /// the resource category. If the user does not assign a name, this value | ||
| /// shall be the same as the system-assigned identifier. | ||
| var name: String { get } | ||
|
|
||
| /// The time at which the system created the resource. | ||
| var creationDate: Date { get } | ||
|
|
||
| /// Key-value properties for the resource. The user and system may both | ||
| /// make use of labels to read and write annotations or other metadata. | ||
| /// A good practice is to use | ||
| var labels: [String: String] { get } | ||
|
|
||
| /// Generates a unique resource ID value. | ||
| static func generateId() -> String | ||
|
|
||
| /// Returns true only if the specified resource name is syntactically valid. | ||
| static func nameValid(_ name: String) -> Bool | ||
| } | ||
|
|
||
| extension ManagedResource { | ||
| /// Generate a random identifier that has the format of an ASCII SHA-256 hash. | ||
| public static func randomId() -> String { | ||
| (0..<2) | ||
| .map { _ in UInt128.random(in: 0...UInt128.max) } | ||
| .map { String($0, radix: 16).padding(toLength: 32, withPad: "0", startingAt: 0) } | ||
| .joined() | ||
| } | ||
| } | ||
|
|
||
| // FIXME: This moves to ManagedResource and/or a ResourceLabels typealias eventually. | ||
| extension [String: String] { | ||
| public var isBuiltin: Bool { self.contains { $0 == ResourceLabelKeys.role && $1 == ResourceRoleValues.builtin } } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // Copyright © 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. | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice clean API! Could we add some usage examples in the doc comments? It would help clarify when to use builtin vs builder and what other roles we might support in the future |
||
| /// System-defined keys for resource labels. | ||
| public struct ResourceLabelKeys { | ||
| /// Indicates a owner of a resource managed by a plugin. | ||
| public static let plugin = "com.apple.container.plugin" | ||
|
|
||
| /// Indicates a resource with a reserved or dedicated purpose. | ||
| public static let role = "com.apple.container.resource.role" | ||
| } | ||
|
|
||
| /// System-defined values for resource the resource role label. | ||
| public struct ResourceRoleValues { | ||
| /// Indicates a container that can build images. | ||
| public static let builder = "builder" | ||
|
|
||
| /// Indicates a system-created resource that cannot be deleted by the user. | ||
| public static let builtin = "builtin" | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -66,7 +66,17 @@ public actor NetworksService { | |
| self.networkPlugin = networkPlugin | ||
|
|
||
| let configurations = try await store.list() | ||
| for configuration in configurations { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This migration logic will run on every startup even after all networks are migrated. Are we considereing adding a one-time migration flag or checking a schema version? Running this check for every network on every boot seems wasteful once everyone's migrated.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a very inexpensive operation; there's no need to pursue optimizations here. A one-time migration flag requires extra effort from the user which means more support burden for us. We're not investing effort in schema versioning at this point as the API and on-disk schema are very fluid and we expect to be breaking things for a little while longer.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
make sense, thanks for explaining! Given the schema is still evolving, keeping it simple is definitely the right decision here. |
||
| for var configuration in configurations { | ||
| // Ensure the network with id "default" is marked as builtin. | ||
| if configuration.id == ClientNetwork.defaultNetworkName { | ||
| let role = configuration.labels[ResourceLabelKeys.role] | ||
| if role == nil || role != ResourceRoleValues.builtin { | ||
| configuration.labels[ResourceLabelKeys.role] = ResourceRoleValues.builtin | ||
| try await store.update(configuration) | ||
| } | ||
| } | ||
|
|
||
| // Start up the network. | ||
| do { | ||
| try await registerService(configuration: configuration) | ||
| } catch { | ||
|
|
@@ -186,17 +196,17 @@ public actor NetworksService { | |
| "id": "\(id)" | ||
| ]) | ||
|
|
||
| // basic sanity checks on network itself | ||
| if id == ClientNetwork.defaultNetworkName { | ||
| throw ContainerizationError(.invalidArgument, message: "cannot delete system subnet \(ClientNetwork.defaultNetworkName)") | ||
| } | ||
|
|
||
| guard let networkState = networkStates[id] else { | ||
| throw ContainerizationError(.notFound, message: "no network for id \(id)") | ||
| } | ||
|
|
||
| // basic sanity checks on network itself | ||
| if networkState.isBuiltin { | ||
| throw ContainerizationError(.invalidArgument, message: "cannot delete builtin network: \(id)") | ||
| } | ||
|
|
||
| guard case .running = networkState else { | ||
| throw ContainerizationError(.invalidState, message: "cannot delete subnet \(id) in state \(networkState.state)") | ||
| throw ContainerizationError(.invalidState, message: "cannot delete network \(id) in state \(networkState.state)") | ||
| } | ||
|
|
||
| // prevent container operations while we atomically check and delete | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This extension on [String: String] feels a bit too broad - it adds isBuiltin to every dictionary in the codebase. Could we create a ResourceLabels typealias or wrapper type instead? Something like:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see the FIXME?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I saw i thought in this pr if we are implementing we can do it at same time. prolly wont have to do it later. if you are thinking we can implement later.