Skip to content

Commit 07e2484

Browse files
committed
added env based routing for container managers
1 parent 8426e1b commit 07e2484

2 files changed

Lines changed: 52 additions & 12 deletions

File tree

buildtools/cli.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,29 +1044,44 @@ func goCmdVerification(c *cli.Context) (string, error) {
10441044
return configFilePath, nil
10451045
}
10461046

1047+
// containerManagerEnvVar lets users force the container manager used by 'jf docker'
1048+
// subcommands, bypassing auto-detection. Accepted values (case-insensitive): "docker", "podman".
1049+
const containerManagerEnvVar = "JFROG_CLI_CONTAINER_MANAGER"
1050+
10471051
// podmanDetector is indirected through a package-level variable so tests can
10481052
// replace the real 'docker version' probe with a deterministic stub.
1049-
var podmanDetector = isPodmanBackedDockerCli
1053+
var podmanDetector = dockerIsPodman
10501054

10511055
// resolveContainerManagerType returns the container manager to use when running 'jf docker' subcommands.
10521056
//
1053-
// If the local 'docker' binary reports Podman in its version output (i.e. the podman-docker shim
1054-
// or native podman aliased as docker), treat it as Podman so 'jf docker ...' works transparently
1055-
// for Podman users without daemon-socket access. Otherwise default to Docker.
1057+
// Resolution order:
1058+
// 1. Explicit override via the JFROG_CLI_CONTAINER_MANAGER env var ("docker" or "podman").
1059+
// 2. Auto-detection: if the local 'docker' binary reports Podman in its version output
1060+
// (i.e. the podman-docker shim or native podman aliased as docker), treat it as Podman
1061+
// so 'jf docker ...' works transparently for Podman users without daemon-socket access.
1062+
// 3. Default: Docker.
10561063
//
10571064
// Detection is intentionally conservative: only a positive "Podman" signal from 'docker version'
10581065
// switches behavior. Real Docker installations are unaffected.
10591066
func resolveContainerManagerType() containerutils.ContainerManagerType {
1067+
switch strings.ToLower(strings.TrimSpace(os.Getenv(containerManagerEnvVar))) {
1068+
case "podman":
1069+
log.Debug(containerManagerEnvVar + "=podman. Routing 'jf docker' subcommands through Podman.")
1070+
return containerutils.Podman
1071+
case "docker":
1072+
log.Debug(containerManagerEnvVar + "=docker. Routing 'jf docker' subcommands through Docker.")
1073+
return containerutils.DockerClient
1074+
}
10601075
if podmanDetector() {
10611076
log.Debug("Detected Podman-backed 'docker' CLI. Routing 'jf docker' subcommands through Podman.")
10621077
return containerutils.Podman
10631078
}
10641079
return containerutils.DockerClient
10651080
}
10661081

1067-
// isPodmanBackedDockerCli returns true if the local 'docker' binary is actually Podman
1082+
// dockerIsPodman returns true if the local 'docker' binary is actually Podman
10681083
// (either via the podman-docker shim or an alias). Any error or missing binary returns false.
1069-
func isPodmanBackedDockerCli() bool {
1084+
func dockerIsPodman() bool {
10701085
cmd := exec.Command("docker", "version")
10711086
out, err := cmd.CombinedOutput()
10721087
if err != nil {

buildtools/cli_test.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package buildtools
22

33
import (
44
"errors"
5+
"os"
56
"testing"
67

78
containerutils "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/ocicontainer"
@@ -304,23 +305,47 @@ func overrideCommandAction(commands []cli.Command, name string, action cli.Actio
304305
return newCommands
305306
}
306307

307-
// TestResolveContainerManagerType verifies the dispatcher routes to Podman
308-
// only when the detector reports a Podman-backed 'docker' CLI.
308+
// TestResolveContainerManagerType verifies the resolution order:
309+
// 1. JFROG_CLI_CONTAINER_MANAGER env override (docker/podman, case-insensitive).
310+
// 2. Auto-detection via the Podman probe.
311+
// 3. Default to Docker.
309312
func TestResolveContainerManagerType(t *testing.T) {
310313
tests := []struct {
311314
name string
315+
envValue string // "" means unset
316+
envUnset bool
312317
podmanFound bool
313318
want containerutils.ContainerManagerType
314319
}{
315-
{name: "podman detected -> route to podman", podmanFound: true, want: containerutils.Podman},
316-
{name: "no podman detected -> default docker", podmanFound: false, want: containerutils.DockerClient},
320+
{name: "env=podman forces podman even when detector says docker", envValue: "podman", want: containerutils.Podman},
321+
{name: "env=docker forces docker even when detector says podman", envValue: "docker", podmanFound: true, want: containerutils.DockerClient},
322+
{name: "env=PODMAN is case-insensitive", envValue: "PODMAN", want: containerutils.Podman},
323+
{name: "env= with whitespace is trimmed", envValue: " podman ", want: containerutils.Podman},
324+
{name: "unknown env value falls through to detector (podman)", envValue: "rkt", podmanFound: true, want: containerutils.Podman},
325+
{name: "unknown env value falls through to detector (docker)", envValue: "rkt", podmanFound: false, want: containerutils.DockerClient},
326+
{name: "unset env + detector finds podman -> podman", envUnset: true, podmanFound: true, want: containerutils.Podman},
327+
{name: "unset env + detector finds docker -> docker", envUnset: true, podmanFound: false, want: containerutils.DockerClient},
317328
}
318329

319330
for _, tc := range tests {
320331
t.Run(tc.name, func(t *testing.T) {
321-
original := podmanDetector
332+
originalEnv, envWasSet := os.LookupEnv(containerManagerEnvVar)
333+
if tc.envUnset {
334+
require.NoError(t, os.Unsetenv(containerManagerEnvVar))
335+
} else {
336+
require.NoError(t, os.Setenv(containerManagerEnvVar, tc.envValue))
337+
}
338+
defer func() {
339+
if envWasSet {
340+
_ = os.Setenv(containerManagerEnvVar, originalEnv)
341+
} else {
342+
_ = os.Unsetenv(containerManagerEnvVar)
343+
}
344+
}()
345+
346+
originalDetector := podmanDetector
322347
podmanDetector = func() bool { return tc.podmanFound }
323-
defer func() { podmanDetector = original }()
348+
defer func() { podmanDetector = originalDetector }()
324349

325350
assert.Equal(t, tc.want, resolveContainerManagerType())
326351
})

0 commit comments

Comments
 (0)