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
32 changes: 29 additions & 3 deletions Sources/Services/ContainerAPIService/Client/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,16 +251,25 @@ 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]
result = [resolveExecutablePath(entrypoint, workingDir: workingDir)]
hasEntrypointOverride = true
} else if let entrypoint = config?.entrypoint, !entrypoint.isEmpty {
result = entrypoint
var resolved = entrypoint
if let first = entrypoint.first {
resolved[0] = resolveExecutablePath(first, workingDir: workingDir)
}
result = resolved
}

if !arguments.isEmpty {
result.append(contentsOf: arguments)
} else {
if let cmd = config?.cmd, !hasEntrypointOverride, !cmd.isEmpty {
result.append(contentsOf: cmd)
var resolved = cmd
if let first = cmd.first {
resolved[0] = resolveExecutablePath(first, workingDir: workingDir)
}
result.append(contentsOf: resolved)
}
}
return result.count > 0 ? result : nil
Expand Down Expand Up @@ -877,4 +886,21 @@ public struct Parser {
default: return nil
}
}

// MARK: Private

private static func resolveExecutablePath(_ path: String, workingDir: String) -> String {
if path.hasPrefix("/") {
return path
}

if path.hasPrefix("./") || path.hasPrefix("../") {
return URL(fileURLWithPath: workingDir)
.appendingPathComponent(path)
.standardized
.path
}

return path
}
}
148 changes: 148 additions & 0 deletions Tests/ContainerAPIClientTests/ParserTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
// limitations under the License.
//===----------------------------------------------------------------------===//

import ArgumentParser
import ContainerizationError
import ContainerizationExtras
import ContainerizationOCI
import Foundation
import Testing

Expand Down Expand Up @@ -845,4 +847,150 @@ 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 testProcessEntrypointBareCommand() 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 == "python3")
#expect(result.arguments == ["-c", "print('hello')"])
}

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

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

#expect(result.executable == "ls")
#expect(result.arguments == ["-la"])
}

@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"])
}
}