Skip to content

Commit a0af6f1

Browse files
ttys3TomasLudvik
andcommitted
fix: improve container running checks with custom container_name support
- Rename getIPForRunningService to getIPForContainer to accept containerName directly - Rename waitUntilServiceIsRunning to waitUntilContainerIsRunning - Add status tracking for better error messages on timeout - Check if container already exists before starting - Support custom container_name when checking container status - Update stopOldStuff in ComposeUp to respect custom container_name (consistency with ComposeDown) This fixes timeout issues where containers started correctly but checks failed because they were looking for the wrong container name. Co-authored-by: TomasLudvik <5638367+TomasLudvik@users.noreply.github.com>
1 parent b9c07d0 commit a0af6f1

1 file changed

Lines changed: 43 additions & 24 deletions

File tree

Sources/Container-Compose/Commands/ComposeUp.swift

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
143143
}
144144

145145
// Stop Services
146-
try await stopOldStuff(services.map({ $0.serviceName }), remove: true)
146+
try await stopOldStuff(services, remove: true)
147147

148148
// Process top-level networks
149149
// This creates named networks defined in the docker-compose.yml
@@ -186,11 +186,7 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
186186
fatalError("unreachable")
187187
}
188188

189-
private func getIPForRunningService(_ serviceName: String) async throws -> String? {
190-
guard let projectName else { return nil }
191-
192-
let containerName = "\(projectName)-\(serviceName)"
193-
189+
private func getIPForContainer(_ containerName: String) async throws -> String? {
194190
let container = try await ClientContainer.get(id: containerName)
195191
let ip = container.networks.first?.ipv4Address.address.description
196192

@@ -202,36 +198,47 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
202198
/// - containerName: The exact name of the container (e.g. "Assignment-Manager-API-db").
203199
/// - timeout: Max seconds to wait before failing.
204200
/// - interval: How often to poll (in seconds).
205-
/// - Returns: `true` if the container reached "running" state within the timeout.
206-
private func waitUntilServiceIsRunning(_ serviceName: String, timeout: TimeInterval = 30, interval: TimeInterval = 0.5) async throws {
207-
guard let projectName else { return }
208-
let containerName = "\(projectName)-\(serviceName)"
209-
201+
private func waitUntilContainerIsRunning(_ containerName: String, timeout: TimeInterval = 30, interval: TimeInterval = 0.5) async throws {
210202
let deadline = Date().addingTimeInterval(timeout)
203+
var lastStatusDescription: String?
211204

212205
while Date() < deadline {
213-
let container = try? await ClientContainer.get(id: containerName)
214-
if container?.status == .running {
215-
return
206+
do {
207+
let container = try await ClientContainer.get(id: containerName)
208+
lastStatusDescription = "\(container.status)"
209+
if container.status == .running {
210+
print("Container '\(containerName)' is now running.")
211+
return
212+
}
213+
} catch {
214+
// Container doesn't exist yet, keep polling
216215
}
217216

218217
try await Task.sleep(nanoseconds: UInt64(interval * 1_000_000_000))
219218
}
220219

220+
let statusMessage = lastStatusDescription.map { "Last status: \($0)" } ?? "Container was never found"
221221
throw NSError(
222222
domain: "ContainerWait", code: 1,
223223
userInfo: [
224-
NSLocalizedDescriptionKey: "Timed out waiting for container '\(containerName)' to be running."
224+
NSLocalizedDescriptionKey: "Timed out waiting for container '\(containerName)' to be running. \(statusMessage)"
225225
])
226226
}
227227

228-
private func stopOldStuff(_ services: [String], remove: Bool) async throws {
228+
private func stopOldStuff(_ services: [(serviceName: String, service: Service)], remove: Bool) async throws {
229229
guard let projectName else { return }
230-
let containers = services.map { "\(projectName)-\($0)" }
231230

232-
for container in containers {
233-
print("Stopping container: \(container)")
234-
guard let container = try? await ClientContainer.get(id: container) else { continue }
231+
for (serviceName, service) in services {
232+
// Respect explicit container_name, otherwise use default pattern
233+
let containerName: String
234+
if let explicitContainerName = service.container_name {
235+
containerName = explicitContainerName
236+
} else {
237+
containerName = "\(projectName)-\(serviceName)"
238+
}
239+
240+
print("Stopping container: \(containerName)")
241+
guard let container = try? await ClientContainer.get(id: containerName) else { continue }
235242

236243
do {
237244
try await container.stop()
@@ -250,8 +257,8 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
250257

251258
// MARK: Compose Top Level Functions
252259

253-
private mutating func updateEnvironmentWithServiceIP(_ serviceName: String) async throws {
254-
let ip = try await getIPForRunningService(serviceName)
260+
private mutating func updateEnvironmentWithServiceIP(_ serviceName: String, containerName: String) async throws {
261+
let ip = try await getIPForContainer(containerName)
255262
self.containerIps[serviceName] = ip
256263
for (key, value) in environmentVariables.map({ ($0, $1) }) where value == serviceName {
257264
self.environmentVariables[key] = ip ?? value
@@ -554,6 +561,18 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
554561

555562
self.containerConsoleColors[serviceName] = serviceColor
556563

564+
// Check if container already exists
565+
if let existingContainer = try? await ClientContainer.get(id: containerName) {
566+
if existingContainer.status == .running {
567+
print("Container '\(containerName)' is already running.")
568+
try await updateEnvironmentWithServiceIP(serviceName, containerName: containerName)
569+
return
570+
} else {
571+
print("Error: Container '\(containerName)' already exists with status: \(existingContainer.status).")
572+
return
573+
}
574+
}
575+
557576
Task { [self, serviceColor] in
558577
@Sendable
559578
func handleOutput(_ output: String) {
@@ -568,8 +587,8 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
568587
}
569588

570589
do {
571-
try await waitUntilServiceIsRunning(serviceName)
572-
try await updateEnvironmentWithServiceIP(serviceName)
590+
try await waitUntilContainerIsRunning(containerName)
591+
try await updateEnvironmentWithServiceIP(serviceName, containerName: containerName)
573592
} catch {
574593
print(error)
575594
}

0 commit comments

Comments
 (0)