@@ -43,65 +43,101 @@ extension Application {
4343 @Argument ( help: " Container IDs " )
4444 var containerIds : [ String ] = [ ]
4545
46+ package struct StopError : Error {
47+ let succeeded : [ String ]
48+ let failed : [ ( String , Error ) ]
49+ }
50+
4651 public func validate( ) throws {
4752 if containerIds. count == 0 && !all {
48- throw ContainerizationError ( . invalidArgument, message: " no containers specified and --all not supplied " )
53+ throw ContainerizationError (
54+ . invalidArgument,
55+ message: " no containers specified and --all not supplied "
56+ )
4957 }
5058 if containerIds. count > 0 && all {
5159 throw ContainerizationError (
52- . invalidArgument, message: " explicitly supplied container IDs conflict with the --all flag " )
60+ . invalidArgument,
61+ message: " explicitly supplied container IDs conflict with the --all flag "
62+ )
5363 }
5464 }
5565
5666 public mutating func run( ) async throws {
57- let set = Set < String > ( containerIds)
5867 var containers = [ ClientContainer] ( )
68+ var allErrors : [ String ] = [ ]
69+
5970 if self . all {
6071 containers = try await ClientContainer . list ( )
6172 } else {
62- containers = try await ClientContainer . list ( ) . filter { c in
63- set. contains ( c. id)
73+ let allContainers = try await ClientContainer . list ( )
74+ let containerMap = Dictionary ( uniqueKeysWithValues: allContainers. map { ( $0. id, $0) } )
75+
76+ for id in containerIds {
77+ if let container = containerMap [ id] {
78+ containers. append ( container)
79+ } else {
80+ allErrors. append ( " Error: No such container: \( id) " )
81+ }
6482 }
6583 }
6684
6785 let opts = ContainerStopOptions (
6886 timeoutInSeconds: self . time,
6987 signal: try Signals . parseSignal ( self . signal)
7088 )
71- let failed = try await Self . stopContainers ( containers: containers, stopOptions: opts)
72- if failed. count > 0 {
73- throw ContainerizationError (
74- . internalError,
75- message: " stop failed for one or more containers \( failed. joined ( separator: " , " ) ) "
76- )
89+
90+ do {
91+ try await Self . stopContainers ( containers: containers, stopOptions: opts)
92+ for container in containers {
93+ print ( container. id)
94+ }
95+ } catch let error as ContainerStop . StopError {
96+ for id in error. succeeded {
97+ print ( id)
98+ }
99+
100+ for (_, err) in error. failed {
101+ allErrors. append ( " Error from APIServer: \( err) " )
102+ }
103+ }
104+
105+ if !allErrors. isEmpty {
106+ var stderr = StandardError ( )
107+ for err in allErrors {
108+ print ( err, to: & stderr)
109+ }
110+ throw ExitCode ( 1 )
77111 }
78112 }
79113
80- static func stopContainers( containers: [ ClientContainer ] , stopOptions: ContainerStopOptions ) async throws -> [ String ] {
81- var failed : [ String ] = [ ]
82- try await withThrowingTaskGroup ( of: ClientContainer ? . self) { group in
114+ static func stopContainers( containers: [ ClientContainer ] , stopOptions: ContainerStopOptions ) async throws {
115+ var succeeded : [ String ] = [ ]
116+ var failed : [ ( String , Error ) ] = [ ]
117+ try await withThrowingTaskGroup ( of: ( String, Error? ) . self) { group in
83118 for container in containers {
84119 group. addTask {
85120 do {
86121 try await container. stop ( opts: stopOptions)
87- print ( container. id)
88- return nil
122+ return ( container. id, nil )
89123 } catch {
90- log. error ( " failed to stop container \( container. id) : \( error) " )
91- return container
124+ return ( container. id, error)
92125 }
93126 }
94127 }
95128
96- for try await ctr in group {
97- guard let ctr else {
98- continue
129+ for try await (id, error) in group {
130+ if let error = error {
131+ failed. append ( ( id, error) )
132+ } else {
133+ succeeded. append ( id)
99134 }
100- failed. append ( ctr. id)
101135 }
102136 }
103137
104- return failed
138+ if !failed. isEmpty {
139+ throw StopError ( succeeded: succeeded, failed: failed)
140+ }
105141 }
106142 }
107143}
0 commit comments