Skip to content

Conversation

@hdm
Copy link
Contributor

@hdm hdm commented Oct 18, 2025

This PR enables bloodhound-cli to use Podman as a backup if Docker is not available. The main driver is recent license changes to Docker Desktop that restrict its use within commercial environments. Podman must be configured in Docker compatibility mode for it to be a full drop-in replacement for bloodhound-cli.

Summary by CodeRabbit

  • New Features
    • Added Podman support as an automatic fallback when Docker is not available, allowing either runtime to be used.
  • Documentation
    • Updated README and changelog to note Podman compatibility and configuration requirements.
  • Tests
    • Updated tests to accept either Docker or Podman being present and adjusted assertions accordingly.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 18, 2025

Walkthrough

The init logic now falls back to Podman when Docker is missing, stores the chosen runtime in a dockerCmd variable, and uses that variable for engine/compose checks and related messaging; tests, README, and changelog updated accordingly.

Changes

Cohort / File(s) Summary
Runtime initialization
cmd/internal/docker.go
Adds Podman fallback detection: if docker is absent, checks for podman, sets dockerCmd to the selected binary, logs a notice for Podman, and fatally errors if neither is found. Replaces hardcoded docker invocations with dockerCmd and updates daemon/access error messages.
Command execution
cmd/internal/utils.go
Adjusts RunCmd logic to prepend compose when the runtime name is "docker" or "podman". Minor import reordering (xdg moved within the import block; no semantic change).
Test updates
cmd/internal/utils_test.go
Updates test assertions to accept either docker or podman in PATH and reorders an import; assertion style/message adjusted.
Docs & metadata
CHANGELOG.md, README.md
Adds changelog entry for v0.1.9 noting Podman support and updates README to mention Podman compatibility mode and Compose v2 compatibility.

Sequence Diagram

sequenceDiagram
    autonumber
    participant Init as Initialization
    participant CheckDocker as Check `docker`
    participant CheckPodman as Check `podman`
    participant Config as Config (set `dockerCmd`)
    participant Runtime as Runtime (engine/compose)

    Init->>CheckDocker: look for `docker` in PATH
    alt docker found
        CheckDocker-->>Config: set `dockerCmd = "docker"`
    else docker not found
        Init->>CheckPodman: look for `podman` in PATH
        alt podman found
            CheckPodman-->>Config: set `dockerCmd = "podman"`
            Note right of Init: log notice about Podman
        else neither found
            Init->>Init: fatal error (no container runtime)
        end
    end
    Config->>Runtime: use `dockerCmd` for engine & compose checks
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 I sniff the path, a tiny plan in paw,

When Docker naps, Podman takes the law,
A gentle hop, the runtime now shared,
Compose and I — compatibility paired,
Hooray, the CLI keeps hopping onward!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Fallback to Podman if Docker is not installed" directly and accurately reflects the main objective of the changeset. The core changes across docker.go, utils.go, and related test files all implement the Podman fallback mechanism when Docker is unavailable, which is precisely what the title communicates. The title is concise, specific, and clear enough that a developer reviewing the commit history would immediately understand the primary change without ambiguity.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92c9ad4 and e43e9cf.

📒 Files selected for processing (3)
  • CHANGELOG.md (1 hunks)
  • README.md (1 hunks)
  • cmd/internal/docker.go (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/internal/docker.go
🔇 Additional comments (1)
README.md (1)

13-13: Well-placed and accurate Podman documentation.

The added line correctly documents the new Podman fallback support and appropriately emphasizes the Docker compatibility mode requirement, which is essential for users to understand. The link to Podman documentation is relevant and helpful. Good visibility in the README immediately following the introduction.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/internal/docker.go (1)

84-96: Critical: docker-compose fallback breaks Podman support

The docker-compose script fallback at Line 91 will override dockerCmd even when it was set to "podman", breaking the Podman fallback feature entirely. The logic needs to be restructured to handle this properly.

Based on the PR description mentioning "Podman must be configured in Docker compatibility mode", it seems the compose plugin is expected. Consider one of these approaches:

Option 1: Only allow docker-compose fallback when using Docker

 	// Check for the ``compose`` plugin as our first choice
 	_, composeErr := RunBasicCmd(dockerCmd, []string{"compose", "version"})
 	if composeErr != nil {
-		fmt.Println("[+] The `compose` plugin is not installed, so we'll try the deprecated `docker-compose` script")
-		composeScriptExists := CheckPath("docker-compose")
-		if composeScriptExists {
-			fmt.Println("[+] The `docker-compose` script is installed, so we'll use that instead")
-			dockerCmd = "docker-compose"
-		} else {
-			fmt.Println("[+] The `docker-compose` script is also not installed or not in the PATH")
-			log.Fatalln("Docker Compose is not installed, so please install it and try again: https://docs.docker.com/compose/install/")
+		if dockerCmd == "docker" {
+			fmt.Println("[+] The `compose` plugin is not installed, so we'll try the deprecated `docker-compose` script")
+			composeScriptExists := CheckPath("docker-compose")
+			if composeScriptExists {
+				fmt.Println("[+] The `docker-compose` script is installed, so we'll use that instead")
+				dockerCmd = "docker-compose"
+			} else {
+				fmt.Println("[+] The `docker-compose` script is also not installed or not in the PATH")
+				log.Fatalln("Docker Compose is not installed, so please install it and try again: https://docs.docker.com/compose/install/")
+			}
+		} else {
+			// Podman requires the compose plugin
+			log.Fatalln("Podman requires the compose plugin. Please install podman-compose or configure Podman with Docker compatibility: https://podman.io/")
 		}
 	}

Option 2: Check for podman-compose when using Podman
This would require checking if podman-compose exists as a separate tool when dockerCmd is "podman".

🧹 Nitpick comments (1)
cmd/internal/utils_test.go (1)

16-20: Consider renaming variable for clarity

The test logic correctly validates that either docker or podman is available in PATH. The variable name dockerFound is slightly misleading since it might actually be finding podman. Consider renaming to runtimeFound or containerRuntimeFound for better clarity.

-	dockerFound := CheckPath("docker")
-	if !dockerFound {
-		dockerFound = CheckPath("podman")
+	runtimeFound := CheckPath("docker")
+	if !runtimeFound {
+		runtimeFound = CheckPath("podman")
 	}
-	assert.True(t, dockerFound, "Expected `CheckPath()` to find `docker` or `podman` in `$PATH`")
+	assert.True(t, runtimeFound, "Expected `CheckPath()` to find `docker` or `podman` in `$PATH`")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 379053a and 92c9ad4.

📒 Files selected for processing (3)
  • cmd/internal/docker.go (3 hunks)
  • cmd/internal/utils.go (2 hunks)
  • cmd/internal/utils_test.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
cmd/internal/docker.go (1)
cmd/internal/utils.go (2)
  • CheckPath (152-155)
  • RunBasicCmd (159-163)
cmd/internal/utils_test.go (1)
cmd/internal/utils.go (1)
  • CheckPath (152-155)
🔇 Additional comments (8)
cmd/internal/utils.go (2)

18-19: LGTM: Import reorganization

The import block reorganization is a formatting-only change with no semantic impact.


168-169: LGTM: Podman support in RunCmd

The extension to handle both "docker" and "podman" is correct and necessary for the Podman fallback feature. Both runtimes use the compose subcommand in the same way.

cmd/internal/utils_test.go (1)

6-7: LGTM: Import reorganization

The import block reorganization is a formatting-only change with no semantic impact.

cmd/internal/docker.go (5)

10-12: LGTM: Import reorganization

The import block reorganization is a formatting-only change with no semantic impact.


24-24: LGTM: Documentation improvement

Good addition documenting the Podman fallback behavior for the dockerCmd variable.


79-79: LGTM: Correct usage of dockerCmd variable

Good use of the dockerCmd variable instead of hardcoded "docker", enabling Podman support.


334-358: Verify Podman compatibility with Docker client library

The GetRunning() function also uses the Docker client library. Same verification and documentation considerations apply as mentioned for FetchLogs().


289-331: Confirm Podman Docker compatibility setup is documented

The code correctly uses client.FromEnv and client.WithAPIVersionNegotiation(), which are compatible with Podman's Docker API layer when properly configured. However, Podman's Docker-API compatibility layer requires podman system service to be enabled and is not 100% feature-complete.

This code will work with Podman, but users need explicit setup instructions. Add documentation or an error message clarifying the Podman configuration requirement—particularly that the Docker-compatible socket must be accessible (either via /var/run/docker.sock or DOCKER_HOST environment variable).

@chrismaddalena
Copy link
Member

Thanks fr the submission! I reviewed the changes and made some adjustments to log messages and the README for a release. I'll merge this for v0.1.9.

@chrismaddalena chrismaddalena self-assigned this Oct 21, 2025
@hdm
Copy link
Contributor Author

hdm commented Oct 21, 2025

Thanks @chrismaddalena!

@chrismaddalena chrismaddalena merged commit 6e19941 into SpecterOps:main Oct 21, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants