Skip to content
Open
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
35 changes: 32 additions & 3 deletions Sources/ContainerClient/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,45 @@ public struct Parser {
var hasEntrypointOverride: Bool = false
// ensure the entrypoint is honored if it has been explicitly set by the user
if let entrypoint = managementFlags.entrypoint, !entrypoint.isEmpty {
result = [entrypoint]
if entrypoint.hasPrefix("/") {
result = [entrypoint]
} else {
let resolved = URL(fileURLWithPath: workingDir)
.appendingPathComponent(entrypoint)
.standardized
.path
result = [resolved]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't cover all cases. Please add handling for bare commands:

% container run --rm -w / --entrypoint ls alpine
Error: internalError: "failed to find target executable /ls"

% docker run --rm -w / --entrypoint ls alpine   
bin
dev
etc

hasEntrypointOverride = true
} else if let entrypoint = config?.entrypoint, !entrypoint.isEmpty {
result = entrypoint
if let first = entrypoint.first, !first.hasPrefix("/") {
var resolved = entrypoint
resolved[0] =
URL(fileURLWithPath: workingDir)
.appendingPathComponent(first)
.standardized
.path
result = resolved
} else {
result = entrypoint
}
}

if !arguments.isEmpty {
result.append(contentsOf: arguments)
} else {
if let cmd = config?.cmd, !hasEntrypointOverride, !cmd.isEmpty {
result.append(contentsOf: cmd)
if let first = cmd.first, !first.hasPrefix("/") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're repeating the same code in three places - extract method?

var resolved = cmd
resolved[0] =
URL(fileURLWithPath: workingDir)
.appendingPathComponent(first)
.standardized
.path
result.append(contentsOf: resolved)
} else {
result.append(contentsOf: cmd)
}
}
}
return result.count > 0 ? result : nil
Expand Down
132 changes: 132 additions & 0 deletions Tests/ContainerClientTests/ParserTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

import ArgumentParser
import ContainerizationError
import ContainerizationOCI
import Foundation
import Testing

Expand Down Expand Up @@ -698,4 +700,134 @@ struct ParserTest {
return error.description.contains("invalid property format")
}
}

@Test
func testProcessEntrypointRelativePathWithDotSlash() throws {
let processFlags = try Flags.Process.parse(["--cwd", "/usr/local/cargo/bin"])
let managementFlags = try Flags.Management.parse(["--entrypoint", "./rustc"])

let result = try Parser.process(
arguments: ["--version"],
processFlags: processFlags,
managementFlags: managementFlags,
config: nil
)

#expect(result.executable == "/usr/local/cargo/bin/rustc")
#expect(result.arguments == ["--version"])
#expect(result.workingDirectory == "/usr/local/cargo/bin")
}

@Test
func testProcessEntrypointRelativePathWithoutPrefix() throws {
let processFlags = try Flags.Process.parse(["--cwd", "/usr/bin"])
let managementFlags = try Flags.Management.parse(["--entrypoint", "python3"])

let result = try Parser.process(
arguments: ["-c", "print('hello')"],
processFlags: processFlags,
managementFlags: managementFlags,
config: nil
)

#expect(result.executable == "/usr/bin/python3")
#expect(result.arguments == ["-c", "print('hello')"])
}

@Test
func testProcessEntrypointAbsolutePathUnchanged() throws {
let processFlags = try Flags.Process.parse(["--cwd", "/home/user"])
let managementFlags = try Flags.Management.parse(["--entrypoint", "/bin/bash"])

let result = try Parser.process(
arguments: ["-c", "echo hello"],
processFlags: processFlags,
managementFlags: managementFlags,
config: nil
)

#expect(result.executable == "/bin/bash")
#expect(result.arguments == ["-c", "echo hello"])
}

@Test
func testProcessEntrypointRelativePathNormalization() throws {
let processFlags = try Flags.Process.parse(["--cwd", "/usr/local/bin"])
let managementFlags = try Flags.Management.parse(["--entrypoint", "../lib/node"])

let result = try Parser.process(
arguments: ["--version"],
processFlags: processFlags,
managementFlags: managementFlags,
config: nil
)

#expect(result.executable == "/usr/local/lib/node")
}

@Test
func testProcessEntrypointRelativePathWithDefaultWorkdir() throws {
let processFlags = try Flags.Process.parse([])
let managementFlags = try Flags.Management.parse(["--entrypoint", "./app"])

let result = try Parser.process(
arguments: [],
processFlags: processFlags,
managementFlags: managementFlags,
config: nil
)

#expect(result.executable == "/app")
}

@Test
func testProcessEntrypointRelativePathWithComplexPath() throws {
let processFlags = try Flags.Process.parse(["--cwd", "/home/user/project"])
let managementFlags = try Flags.Management.parse(["--entrypoint", "./bin/../scripts/./run.sh"])

let result = try Parser.process(
arguments: [],
processFlags: processFlags,
managementFlags: managementFlags,
config: nil
)

#expect(result.executable == "/home/user/project/scripts/run.sh")
}

@Test
func testProcessRelativeEntrypointInImageConfig() throws {
let config = ContainerizationOCI.ImageConfig(
entrypoint: ["./start.sh"],
workingDir: "/app"
)

let result = try Parser.process(
arguments: [],
processFlags: try Flags.Process.parse([]),
managementFlags: try Flags.Management.parse([]),
config: config
)

#expect(result.executable == "/app/start.sh")
}

@Test
func testProcessRelativeCmdInImageConfig() throws {
let config = ContainerizationOCI.ImageConfig(
entrypoint: nil,
cmd: ["./run.py", "--fast"],
workingDir: "/scripts"
)

let result = try Parser.process(
arguments: [],
processFlags: try Flags.Process.parse([]),
managementFlags: try Flags.Management.parse([]),
config: config
)

#expect(result.executable == "/scripts/run.py")
#expect(result.arguments == ["--fast"])
}
}