Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Sources/Container-Compose/Commands/ComposeUp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,14 @@ public struct ComposeUp: AsyncParsableCommand, @unchecked Sendable {
runCommandArgs.append("--read-only")
}

// Add resource limits
if let cpus = service.deploy?.resources?.limits?.cpus {
runCommandArgs.append(contentsOf: ["--cpus", cpus])
}
if let memory = service.deploy?.resources?.limits?.memory {
runCommandArgs.append(contentsOf: ["--memory", memory])
}

// Handle service-level configs (note: still only parsing/logging, not attaching)
if let serviceConfigs = service.configs {
print(
Expand Down
33 changes: 33 additions & 0 deletions Tests/Container-Compose-DynamicTests/ComposeUpTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,39 @@ struct ComposeUpTests {
#expect(webContainer.status == .running)
#expect(dbContainer.status == .running)
}

@Test("Test container created with non-default CPU and memory limits")
func testCpuAndMemoryLimits() async throws {
let yaml = """
version: "3.8"
services:
app:
image: nginx:alpine
deploy:
resources:
limits:
cpus: "1"
memory: "512MB"
"""

let tempLocation = URL.temporaryDirectory.appending(path: "Container-Compose_Tests_\(UUID().uuidString)/docker-compose.yaml")
try? FileManager.default.createDirectory(at: tempLocation.deletingLastPathComponent(), withIntermediateDirectories: true)
try yaml.write(to: tempLocation, atomically: false, encoding: .utf8)
let folderName = tempLocation.deletingLastPathComponent().lastPathComponent

var composeUp = try ComposeUp.parse(["-d", "--cwd", tempLocation.deletingLastPathComponent().path(percentEncoded: false)])
try await composeUp.run()

let containers = try await ClientContainer.list()
.filter { $0.configuration.id.contains(folderName) }

guard let appContainer = containers.first(where: { $0.configuration.id == "\(folderName)-app" }) else {
throw Errors.containerNotFound
}

#expect(appContainer.configuration.resources.cpus == 1)
#expect(appContainer.configuration.resources.memoryInBytes == 512.mib())
}

enum Errors: Error {
case containerNotFound
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,27 @@ struct DockerComposeParsingTests {

#expect(compose.services["app"]??.platform == "linux/amd64")
}

@Test("Parse deploy resources limits (cpus and memory)")
func parseComposeWithDeployResources() throws {
let yaml = """
version: '3.8'
services:
app:
image: alpine:latest
deploy:
resources:
limits:
cpus: "0.5"
memory: "512M"
"""

let decoder = YAMLDecoder()
let compose = try decoder.decode(DockerCompose.self, from: yaml)

#expect(compose.services["app"]??.deploy?.resources?.limits?.cpus == "0.5")
#expect(compose.services["app"]??.deploy?.resources?.limits?.memory == "512M")
}

@Test("Service must have image or build - should fail without either")
func serviceRequiresImageOrBuild() throws {
Expand Down