-
Notifications
You must be signed in to change notification settings - Fork 743
Fix CI integration test coldstart issues. #1230
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -55,7 +55,7 @@ extension Application.NetworkCommand { | |||||||||||||||
| // Note: This failure may occur due to a race condition between the network/ | ||||||||||||||||
| // container collection above and a container run command that attaches to a | ||||||||||||||||
| // network listed in the networksToPrune collection. | ||||||||||||||||
| log.error("Failed to prune network \(network.id): \(error)") | ||||||||||||||||
| log.error("failed to prune network", metadata: ["id": "\(network.id)", "error": "\(error)"]) | ||||||||||||||||
|
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.
Suggested change
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 reasonable. Let me do it in the follow-up (log collection PR) so we can start unblocking the PR backlog. |
||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
|
|
||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,7 +58,7 @@ extension Application { | |
| do { | ||
| var args = ["log"] | ||
| args.append(self.follow ? "stream" : "show") | ||
| args.append(contentsOf: ["--info", "--debug"]) | ||
| args.append(contentsOf: ["--info", logOptions.debug ? "--debug" : nil].compactMap { $0 }) | ||
|
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. Now we only pass debug into |
||
| if !self.follow { | ||
| args.append(contentsOf: ["--last", last]) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ import ArgumentParser | |
| import ContainerAPIClient | ||
| import ContainerPersistence | ||
| import ContainerPlugin | ||
| import ContainerXPC | ||
| import ContainerizationError | ||
| import Foundation | ||
| import TerminalProgress | ||
|
|
@@ -48,9 +49,15 @@ extension Application { | |
| var kernelInstall: Bool? | ||
|
|
||
| @Option( | ||
| name: .long, | ||
| help: "Number of seconds to wait for API service to become responsive") | ||
| var timeout: Double = 10.0 | ||
| help: "Number of seconds to wait for API service to become responsive", | ||
| transform: { | ||
| guard let timeoutSeconds = Double($0) else { | ||
| throw ValidationError("Invalid timeout value: \($0)") | ||
| } | ||
| return .seconds(timeoutSeconds) | ||
| } | ||
| ) | ||
| var timeout: Duration = XPCClient.xpcRegistrationTimeout | ||
|
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. Key point here is that we use the common timeout value now. |
||
|
|
||
| @OptionGroup | ||
| public var logOptions: Flags.Logging | ||
|
|
@@ -98,7 +105,7 @@ extension Application { | |
| // Now ping our friendly daemon. Fail if we don't get a response. | ||
| do { | ||
| print("Verifying apiserver is running...") | ||
| _ = try await ClientHealthCheck.ping(timeout: .seconds(timeout)) | ||
| _ = try await ClientHealthCheck.ping(timeout: timeout) | ||
| } catch { | ||
| throw ContainerizationError( | ||
| .internalError, | ||
|
|
@@ -124,7 +131,7 @@ extension Application { | |
| do { | ||
| try await pullCommand.run() | ||
| } catch { | ||
| log.error("failed to install base container filesystem: \(error)") | ||
| log.error("failed to install base container filesystem", metadata: ["error": "\(error)"]) | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -206,7 +206,8 @@ extension PluginLoader { | |
| plugin: Plugin, | ||
| pluginStateRoot: URL? = nil, | ||
| args: [String]? = nil, | ||
| instanceId: String? = nil | ||
| instanceId: String? = nil, | ||
| debug: Bool = false, | ||
| ) throws { | ||
| // We only care about loading plugins that have a service | ||
| // to expose; otherwise, they may just be CLI commands. | ||
|
|
@@ -223,9 +224,10 @@ extension PluginLoader { | |
| env[ApplicationRoot.environmentName] = appRoot.path(percentEncoded: false) | ||
| env[InstallRoot.environmentName] = installRoot.path(percentEncoded: false) | ||
|
|
||
| let processedArgs = (args ?? ["start"]) + (debug ? ["--debug"] : []) | ||
|
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 the main change, adds The implication is that all plugins need to implement a |
||
| let plist = LaunchPlist( | ||
| label: id, | ||
| arguments: [plugin.binaryURL.path] + (args ?? ["start"]) + serviceConfig.defaultArguments, | ||
| arguments: [plugin.binaryURL.path] + processedArgs + serviceConfig.defaultArguments, | ||
| environment: env, | ||
| limitLoadToSessionType: [.Aqua, .Background, .System], | ||
| runAtLoad: serviceConfig.runAtLoad, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,13 @@ import ContainerizationError | |
| import Foundation | ||
|
|
||
| public final class XPCClient: Sendable { | ||
| /// The maximum amount of time to wait for a request to a recently | ||
| /// registered XPC service. Once a service has launched, XPC | ||
| /// requests only have milliseconds of overhead, but in some instances, | ||
| /// macOS can take 5 seconds (or considerably longer) to launch a | ||
| /// service after it has been registered. | ||
| public static let xpcRegistrationTimeout: Duration = .seconds(60) | ||
|
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. Our common timeout value, chosen to work with our slow service startup in CI. |
||
|
|
||
| private nonisolated(unsafe) let connection: xpc_connection_t | ||
| private let q: DispatchQueue? | ||
| private let service: String | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,22 +46,22 @@ extension APIServer { | |
| func run() async throws { | ||
| let commandName = Self.configuration.commandName ?? "container-apiserver" | ||
| let log = APIServer.setupLogger(debug: debug) | ||
| log.info("starting \(commandName)") | ||
| log.info("starting helper", metadata: ["name": "\(commandName)"]) | ||
|
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 pattern is used for startup/shutdown for all services. |
||
| defer { | ||
| log.info("stopping \(commandName)") | ||
| log.info("stopping helper", metadata: ["name": "\(commandName)"]) | ||
| } | ||
|
|
||
| do { | ||
| log.info("configuring XPC server") | ||
| var routes = [XPCRoute: XPCServer.RouteHandler]() | ||
| let pluginLoader = try initializePluginLoader(log: log) | ||
| try await initializePlugins(pluginLoader: pluginLoader, log: log, routes: &routes) | ||
| let containersService = try initializeContainerService( | ||
| let containersService = try initializeContainersService( | ||
|
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. Just some name nitpickery for "containers service" and "networks service". |
||
| pluginLoader: pluginLoader, | ||
| log: log, | ||
| routes: &routes | ||
| ) | ||
| let networkService = try await initializeNetworkService( | ||
| let networkService = try await initializeNetworksService( | ||
| pluginLoader: pluginLoader, | ||
| containersService: containersService, | ||
| log: log, | ||
|
|
@@ -131,7 +131,7 @@ extension APIServer { | |
| */ | ||
| } | ||
| } catch { | ||
| log.error("\(commandName) failed", metadata: ["error": "\(error)"]) | ||
| log.error("helper failed", metadata: ["name": "\(commandName)", "error": "\(error)"]) | ||
| APIServer.exit(withError: error) | ||
| } | ||
| } | ||
|
|
@@ -222,13 +222,14 @@ extension APIServer { | |
| routes[XPCRoute.getDefaultKernel] = harness.getDefaultKernel | ||
| } | ||
|
|
||
| private func initializeContainerService(pluginLoader: PluginLoader, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws -> ContainersService { | ||
| log.info("initializing container service") | ||
| private func initializeContainersService(pluginLoader: PluginLoader, log: Logger, routes: inout [XPCRoute: XPCServer.RouteHandler]) throws -> ContainersService { | ||
| log.info("initializing containers service") | ||
|
|
||
| let service = try ContainersService( | ||
| appRoot: appRoot, | ||
| pluginLoader: pluginLoader, | ||
| log: log | ||
| log: log, | ||
| debugHelpers: debug | ||
|
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. Propagate debug settings from API server to any runtime helpers we create. |
||
| ) | ||
| let harness = ContainersHarness(service: service, log: log) | ||
|
|
||
|
|
@@ -250,20 +251,21 @@ extension APIServer { | |
| return service | ||
| } | ||
|
|
||
| private func initializeNetworkService( | ||
| private func initializeNetworksService( | ||
| pluginLoader: PluginLoader, | ||
| containersService: ContainersService, | ||
| log: Logger, | ||
| routes: inout [XPCRoute: XPCServer.RouteHandler] | ||
| ) async throws -> NetworksService { | ||
| log.info("initializing network service") | ||
| log.info("initializing networks service") | ||
|
|
||
| let resourceRoot = appRoot.appendingPathComponent("networks") | ||
| let service = try await NetworksService( | ||
| pluginLoader: pluginLoader, | ||
| resourceRoot: resourceRoot, | ||
| containersService: containersService, | ||
| log: log | ||
| log: log, | ||
| debugHelpers: debug | ||
|
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. Propagate debug settings from API server to any network helpers we create. |
||
| ) | ||
|
|
||
| let defaultNetwork = try await service.list() | ||
|
|
||
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.
Don't do this anymore since we'll want to add other options (--log-root) soon. Make it so we can accumulate startup options.