mirror of
https://github.com/apple/container.git
synced 2026-08-24 10:05:43 -05:00
CLI: Forego logging errors on multi-container operations (#1163)
Instead of logging errors, and then additionally throwing an error stating what containers couldn't be stopped/killed/deleted, let's just concatenate the errors and throw the single error.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// An error type that aggregates multiple errors into one.
|
||||
///
|
||||
/// When displayed, each underlying error is printed on its own line.
|
||||
public struct AggregateError: Swift.Error, Sendable {
|
||||
public let errors: [any Error]
|
||||
|
||||
public init(_ errors: [any Error]) {
|
||||
self.errors = errors
|
||||
}
|
||||
}
|
||||
|
||||
extension AggregateError: CustomStringConvertible {
|
||||
public var description: String {
|
||||
errors.map { String(describing: $0) }.joined(separator: "\n")
|
||||
}
|
||||
}
|
||||
@@ -81,11 +81,10 @@ extension Application {
|
||||
}
|
||||
}
|
||||
|
||||
var failed = [String]()
|
||||
var errors: [any Error] = []
|
||||
let force = self.force
|
||||
let all = self.all
|
||||
let logger = log
|
||||
try await withThrowingTaskGroup(of: String?.self) { group in
|
||||
try await withThrowingTaskGroup(of: (any Error)?.self) { group in
|
||||
for container in containers {
|
||||
group.addTask {
|
||||
do {
|
||||
@@ -100,25 +99,20 @@ extension Application {
|
||||
print(container.id)
|
||||
return nil
|
||||
} catch {
|
||||
logger.error("failed to delete container \(container.id): \(error)")
|
||||
return container.id
|
||||
return error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for try await ctr in group {
|
||||
guard let ctr else {
|
||||
continue
|
||||
for try await error in group {
|
||||
if let error {
|
||||
errors.append(error)
|
||||
}
|
||||
failed.append(ctr)
|
||||
}
|
||||
}
|
||||
|
||||
if failed.count > 0 {
|
||||
throw ContainerizationError(
|
||||
.internalError,
|
||||
message: "delete failed for one or more containers: \(failed)"
|
||||
)
|
||||
if !errors.isEmpty {
|
||||
throw AggregateError(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,18 +64,17 @@ extension Application {
|
||||
|
||||
let signalNumber = try Signals.parseSignal(signal)
|
||||
|
||||
var failed: [String] = []
|
||||
var errors: [any Error] = []
|
||||
for container in containers {
|
||||
do {
|
||||
try await client.kill(id: container.id, signal: signalNumber)
|
||||
print(container.id)
|
||||
} catch {
|
||||
log.error("failed to kill container \(container.id): \(error)")
|
||||
failed.append(container.id)
|
||||
errors.append(error)
|
||||
}
|
||||
}
|
||||
if failed.count > 0 {
|
||||
throw ContainerizationError(.internalError, message: "kill failed for one or more containers \(failed.joined(separator: ","))")
|
||||
if !errors.isEmpty {
|
||||
throw AggregateError(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,18 +71,16 @@ extension Application {
|
||||
timeoutInSeconds: self.time,
|
||||
signal: try Signals.parseSignal(self.signal)
|
||||
)
|
||||
let failed = try await Self.stopContainers(client: client, containers: containers, stopOptions: opts, log: log)
|
||||
if failed.count > 0 {
|
||||
throw ContainerizationError(
|
||||
.internalError,
|
||||
message: "stop failed for one or more containers \(failed.joined(separator: ","))"
|
||||
)
|
||||
}
|
||||
try await Self.stopContainers(
|
||||
client: client,
|
||||
containers: containers,
|
||||
stopOptions: opts
|
||||
)
|
||||
}
|
||||
|
||||
static func stopContainers(client: ContainerClient, containers: [ContainerSnapshot], stopOptions: ContainerStopOptions, log: Logger) async throws -> [String] {
|
||||
var failed: [String] = []
|
||||
try await withThrowingTaskGroup(of: ContainerSnapshot?.self) { group in
|
||||
static func stopContainers(client: ContainerClient, containers: [ContainerSnapshot], stopOptions: ContainerStopOptions) async throws {
|
||||
var errors: [any Error] = []
|
||||
await withTaskGroup(of: (any Error)?.self) { group in
|
||||
for container in containers {
|
||||
group.addTask {
|
||||
do {
|
||||
@@ -90,21 +88,21 @@ extension Application {
|
||||
print(container.id)
|
||||
return nil
|
||||
} catch {
|
||||
log.error("failed to stop container \(container.id): \(error)")
|
||||
return container
|
||||
return error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for try await ctr in group {
|
||||
guard let ctr else {
|
||||
continue
|
||||
for await error in group {
|
||||
if let error {
|
||||
errors.append(error)
|
||||
}
|
||||
failed.append(ctr.id)
|
||||
}
|
||||
}
|
||||
|
||||
return failed
|
||||
if !errors.isEmpty {
|
||||
throw AggregateError(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,10 +67,11 @@ extension Application {
|
||||
let containers = try await client.list()
|
||||
let signal = try Signals.parseSignal("SIGTERM")
|
||||
let opts = ContainerStopOptions(timeoutInSeconds: Self.stopTimeoutSeconds, signal: signal)
|
||||
let failed = try await ContainerStop.stopContainers(client: client, containers: containers, stopOptions: opts, log: log)
|
||||
if !failed.isEmpty {
|
||||
log.warning("some containers could not be stopped gracefully", metadata: ["ids": "\(failed)"])
|
||||
}
|
||||
try await ContainerStop.stopContainers(
|
||||
client: client,
|
||||
containers: containers,
|
||||
stopOptions: opts,
|
||||
)
|
||||
} catch {
|
||||
log.warning("failed to stop all containers", metadata: ["error": "\(error)"])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user