refactor(config): centralize DBX_* env-var resolution

Env vars were read in three layers — cli.EnvVars on flags, os.Getenv
in CLI actions, and the podman provider — leaving library users no
single override point. Funnelling every DBX_* through pkg/config gives
consumers one injection point, lets the container manager receive its
configuration instead of sniffing for it, and fixes the create flags
whose Usage advertised a default the env-set value was already
shadowing.

Signed-off-by: Luca Di Maio <luca.dimaio1@gmail.com>
This commit is contained in:
Luca Di Maio
2026-06-25 09:07:25 +02:00
parent 9ab96ee1e0
commit bdc2576f2c
12 changed files with 234 additions and 110 deletions
+25 -24
View File
@@ -17,6 +17,19 @@ import (
//nolint:funlen // function length is acceptable for CLI command definition
func newCreateCommand(cfg *config.Values) *cli.Command {
imageDefault := cfg.DefaultContainerImage
if cfg.ContainerImage != "" {
imageDefault = cfg.ContainerImage
}
nameDefault := cfg.DefaultContainerName
if cfg.ContainerName != "" {
nameDefault = cfg.ContainerName
}
hostnameDefault := cfg.ContainerHostname
if hostnameDefault == "" {
hostnameDefault = defaultHostname()
}
return &cli.Command{
Name: "create",
@@ -40,37 +53,30 @@ Examples:
&cli.StringFlag{
Name: "image",
Aliases: []string{"i"},
Sources: cli.EnvVars("DBX_CONTAINER_IMAGE"),
Usage: fmt.Sprintf(
"image to use for the container (default: %s)",
cfg.DefaultContainerImage,
),
// No Value default: leaving it empty lets makeContainerImage's
// "no clone & no image" branch select cfg.DefaultContainerImage,
// which is what triggers the default-name fallback. Setting
// Value here would defeat that check (shell parity).
Value: cfg.ContainerImage,
Usage: fmt.Sprintf("image to use for the container (default: %s)", imageDefault),
},
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Sources: cli.EnvVars("DBX_CONTAINER_NAME"),
Usage: fmt.Sprintf("name for the distrobox (default: %s)", cfg.DefaultContainerName),
Value: cfg.ContainerName,
Usage: fmt.Sprintf("name for the distrobox (default: %s)", nameDefault),
},
&cli.StringFlag{
Name: "hostname",
Sources: cli.EnvVars("DBX_CONTAINER_HOSTNAME"),
Usage: fmt.Sprintf("hostname for the distrobox (default: %s)", defaultHostname()),
Name: "hostname",
Value: cfg.ContainerHostname,
Usage: fmt.Sprintf("hostname for the distrobox (default: %s)", hostnameDefault),
},
&cli.BoolFlag{
Name: "pull",
Aliases: []string{"p"},
Sources: cli.EnvVars("DBX_CONTAINER_ALWAYS_PULL"),
Value: cfg.ContainerAlwaysPull,
Usage: "pull the image even if it exists locally (implies --yes)",
},
&cli.BoolFlag{
Name: "yes",
Aliases: []string{"Y"},
Sources: cli.EnvVars("DBX_NON_INTERACTIVE"),
Value: cfg.NonInteractive,
Usage: "non-interactive, pull images without asking",
},
&cli.StringFlag{
@@ -83,7 +89,7 @@ of the same environment.`,
&cli.StringFlag{
Name: "home",
Aliases: []string{"H"},
Sources: cli.EnvVars("DBX_CONTAINER_CUSTOM_HOME"),
Value: cfg.ContainerCustomHome,
Usage: "select a custom HOME directory for the container. Useful to avoid host's home littering with temp files.",
},
&cli.StringSliceFlag{
@@ -191,18 +197,13 @@ func createAction(ctx context.Context, cmd *cli.Command, cfg *config.Values) err
return errors.New("container manager not found in context")
}
// DBX_CONTAINER_GENERATE_ENTRY=0 disables entry generation (shell parity);
// DBX_CONTAINER_HOME_PREFIX seeds a per-box custom home when no --home is set.
generateEntry := !cmd.Bool("no-entry")
if v := os.Getenv("DBX_CONTAINER_GENERATE_ENTRY"); v == "0" || v == "false" {
generateEntry = false
}
generateEntry := cfg.GenerateEntry && !cmd.Bool("no-entry")
opts := commands.CreateOptions{
ContainerImage: cmd.String("image"),
ContainerName: cmd.String("name"),
ContainerHostname: cmd.String("hostname"),
ContainerHomePrefix: os.Getenv("DBX_CONTAINER_HOME_PREFIX"),
ContainerHomePrefix: cfg.ContainerHomePrefix,
ContainerClone: cmd.String("clone"),
UnshareNetNs: cmd.Bool("unshare-netns") || cmd.Bool("unshare-all"),
UnshareDevsys: cmd.Bool("unshare-devsys") || cmd.Bool("unshare-all"),
+4 -4
View File
@@ -23,7 +23,7 @@ func newEnterCommand(cfg *config.Values) *cli.Command {
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Sources: cli.EnvVars("DBX_CONTAINER_NAME"),
Value: cfg.ContainerName,
Usage: "name for the distrobox",
},
&cli.BoolFlag{
@@ -41,7 +41,7 @@ func newEnterCommand(cfg *config.Values) *cli.Command {
&cli.BoolFlag{
Name: "clean-path",
Aliases: []string{"c"},
Sources: cli.EnvVars("DBX_CONTAINER_CLEAN_PATH"),
Value: cfg.CleanPath,
Usage: "reset PATH inside the container to FHS standard",
},
&cli.StringFlag{
@@ -52,7 +52,7 @@ func newEnterCommand(cfg *config.Values) *cli.Command {
&cli.BoolFlag{
Name: "yes",
Aliases: []string{"y"},
Sources: cli.EnvVars("DBX_NON_INTERACTIVE"),
Value: cfg.NonInteractive,
Usage: "non-interactive, do not ask questions",
},
&cli.BoolFlag{
@@ -63,7 +63,7 @@ func newEnterCommand(cfg *config.Values) *cli.Command {
&cli.BoolFlag{
Name: "no-workdir",
Aliases: []string{"nw"},
Sources: cli.EnvVars("DBX_SKIP_WORKDIR"),
Value: cfg.SkipWorkDir,
Usage: "always start the container from container's home directory",
},
},
+4 -4
View File
@@ -33,13 +33,13 @@ func newRmCommand(cfg *config.Values) *cli.Command {
&cli.BoolFlag{
Name: "yes",
Aliases: []string{"Y"},
Sources: cli.EnvVars("DBX_NON_INTERACTIVE"),
Value: cfg.NonInteractive,
Usage: "non-interactive mode",
},
&cli.BoolFlag{
Name: "rm-home",
Sources: cli.EnvVars("DBX_CONTAINER_RM_CUSTOM_HOME"),
Usage: "Remove container's home directory",
Name: "rm-home",
Value: cfg.RmCustomHome,
Usage: "Remove container's home directory",
},
},
+5 -3
View File
@@ -268,6 +268,7 @@ func withContainerManager(cfg *config.Values, cmd *cli.Command) *cli.Command {
c.String("sudo-command"),
c.Bool("verbose"),
c.Bool("root") || os.Getuid() == 0,
cfg.UsernsNoLimit,
)
if err != nil {
return nil, err
@@ -283,6 +284,7 @@ func buildContainerManager(
sudoCommand string,
verbose bool,
root bool,
usernsNoLimit bool,
) (containermanager.ContainerManager, error) {
errPrinter := ui.NewPrinter(os.Stderr, true)
@@ -290,11 +292,11 @@ func buildContainerManager(
case "docker":
return providers.NewDocker(root, sudoCommand, verbose), nil
case "podman":
return providers.NewPodman(root, sudoCommand, verbose), nil
return providers.NewPodman(root, sudoCommand, verbose, usernsNoLimit), nil
case "podman-launcher":
return providers.NewPodmanLauncher(root, sudoCommand, verbose), nil
return providers.NewPodmanLauncher(root, sudoCommand, verbose, usernsNoLimit), nil
case "autodetect", "":
cm, err := providers.NewAutoDetect(root, sudoCommand, verbose)
cm, err := providers.NewAutoDetect(root, sudoCommand, verbose, usernsNoLimit)
if err != nil {
if errors.Is(err, providers.ErrNoContainerManager) {
printMissingContainerManager(errPrinter)
+8 -7
View File
@@ -35,7 +35,7 @@ Examples:
&cli.BoolFlag{
Name: "yes",
Aliases: []string{"Y"},
Sources: cli.EnvVars("DBX_NON_INTERACTIVE"),
Value: cfg.NonInteractive,
Usage: "non-interactive, stop without asking",
},
},
@@ -55,12 +55,13 @@ func stopAction(ctx context.Context, cmd *cli.Command, cfg *config.Values) error
nonInteractive := cmd.Bool("yes")
containerNames := cmd.Args().Slice()
// Shell distrobox-stop seeds container_name from DBX_CONTAINER_NAME
// (distrobox-stop:90, 200-202) when no positional and not --all.
if !all && len(containerNames) == 0 {
if envName := os.Getenv("DBX_CONTAINER_NAME"); envName != "" {
containerNames = []string{envName}
}
// Mirror shell distrobox-stop:90,200-202: when no positional and not --all,
// fall back to the env-set container name. The env value comes from
// cfg.ContainerName (resolved by pkg/config from DBX_CONTAINER_NAME); we
// only consult it here, never read the env directly. An unset env leaves
// containerNames empty so StopCommand applies its default-name fallback.
if !all && len(containerNames) == 0 && cfg.ContainerName != "" {
containerNames = []string{cfg.ContainerName}
}
options := &commands.StopOptions{
+84 -3
View File
@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"gopkg.in/ini.v1"
)
@@ -14,6 +15,22 @@ type Values struct {
Verbose bool
DefaultContainerImage string
DefaultContainerName string
// Env-set values; empty when the corresponding DBX_* var is not exported.
// Consumers prefer these over the Default* counterparts when non-empty,
// matching the shell's `[ -n "$DBX_X" ] && var=$DBX_X` pattern.
ContainerImage string
ContainerName string
ContainerHostname string
ContainerCustomHome string
ContainerHomePrefix string
ContainerAlwaysPull bool
NonInteractive bool
GenerateEntry bool
CleanPath bool
SkipWorkDir bool
UsernsNoLimit bool
RmCustomHome bool
}
func defaultsMap() map[string]string {
@@ -22,8 +39,15 @@ func defaultsMap() map[string]string {
"sudo_program": "sudo",
"verbose": "false",
// container_image Fedora toolbox is a sensitive default
"container_image": "registry.fedoraproject.org/fedora-toolbox:latest",
"container_name": "my-distrobox",
"container_image": "registry.fedoraproject.org/fedora-toolbox:latest",
"container_name": "my-distrobox",
"container_always_pull": "false",
"non_interactive": "false",
"container_generate_entry": "true",
"container_clean_path": "false",
"container_skip_workdir": "false",
"userns_nolimit": "false",
"rm_home": "false",
}
}
@@ -60,11 +84,32 @@ func toStruct(configMap map[string]string) *Values {
Verbose: toBool(configMap["verbose"]),
DefaultContainerImage: configMap["container_image"],
DefaultContainerName: configMap["container_name"],
ContainerImage: configMap["container_image_env"],
ContainerName: configMap["container_name_env"],
ContainerHostname: configMap["container_hostname"],
ContainerCustomHome: configMap["container_user_custom_home"],
ContainerHomePrefix: configMap["container_home_prefix"],
ContainerAlwaysPull: toBool(configMap["container_always_pull"]),
NonInteractive: toBool(configMap["non_interactive"]),
GenerateEntry: toBool(configMap["container_generate_entry"]),
CleanPath: toBool(configMap["container_clean_path"]),
SkipWorkDir: toBool(configMap["container_skip_workdir"]),
UsernsNoLimit: toBool(configMap["userns_nolimit"]),
RmCustomHome: toBool(configMap["rm_home"]),
}
}
// toBool recognizes the values the shell distrobox treats as truthy
// (`1`, `true`, `yes`, `on`, case-insensitive). Anything else, including the
// empty string, is false. The previous narrower `value == "true"` silently
// ignored `DBX_VERBOSE=1` and similar — this fixes it.
func toBool(value string) bool {
return value == "true"
switch strings.ToLower(strings.TrimSpace(value)) {
case "true", "1", "yes", "on":
return true
default:
return false
}
}
// getConfigFilePaths returns a list of configuration file paths in order of priority.
@@ -157,6 +202,42 @@ func readEnv() map[string]string {
if value, exists := os.LookupEnv("DBX_VERBOSE"); exists {
envConfig["verbose"] = value
}
if value, exists := os.LookupEnv("DBX_CONTAINER_IMAGE"); exists {
envConfig["container_image_env"] = value
}
if value, exists := os.LookupEnv("DBX_CONTAINER_NAME"); exists {
envConfig["container_name_env"] = value
}
if value, exists := os.LookupEnv("DBX_CONTAINER_HOSTNAME"); exists {
envConfig["container_hostname"] = value
}
if value, exists := os.LookupEnv("DBX_CONTAINER_CUSTOM_HOME"); exists {
envConfig["container_user_custom_home"] = value
}
if value, exists := os.LookupEnv("DBX_CONTAINER_HOME_PREFIX"); exists {
envConfig["container_home_prefix"] = value
}
if value, exists := os.LookupEnv("DBX_CONTAINER_ALWAYS_PULL"); exists {
envConfig["container_always_pull"] = value
}
if value, exists := os.LookupEnv("DBX_NON_INTERACTIVE"); exists {
envConfig["non_interactive"] = value
}
if value, exists := os.LookupEnv("DBX_CONTAINER_GENERATE_ENTRY"); exists {
envConfig["container_generate_entry"] = value
}
if value, exists := os.LookupEnv("DBX_CONTAINER_CLEAN_PATH"); exists {
envConfig["container_clean_path"] = value
}
if value, exists := os.LookupEnv("DBX_SKIP_WORKDIR"); exists {
envConfig["container_skip_workdir"] = value
}
if value, exists := os.LookupEnv("DBX_USERNS_NOLIMIT"); exists {
envConfig["userns_nolimit"] = value
}
if value, exists := os.LookupEnv("DBX_CONTAINER_RM_CUSTOM_HOME"); exists {
envConfig["rm_home"] = value
}
return envConfig
}
+59 -5
View File
@@ -11,11 +11,23 @@ import (
func TestToStruct(t *testing.T) {
input := map[string]string{
"container_manager": "docker",
"sudo_program": "doas",
"verbose": "true",
"container_image": "ubuntu:latest",
"container_name": "mybox",
"container_manager": "docker",
"sudo_program": "doas",
"verbose": "true",
"container_image": "ubuntu:latest",
"container_name": "mybox",
"container_image_env": "alpine:3.21",
"container_name_env": "envbox",
"container_hostname": "myhost",
"container_user_custom_home": "/tmp/home",
"container_home_prefix": "/data/boxes",
"container_always_pull": "1",
"non_interactive": "yes",
"container_generate_entry": "false",
"container_clean_path": "on",
"container_skip_workdir": "true",
"userns_nolimit": "1",
"rm_home": "yes",
}
cfg := toStruct(input)
@@ -23,8 +35,23 @@ func TestToStruct(t *testing.T) {
assert.Equal(t, "docker", cfg.ContainerManagerType)
assert.Equal(t, "doas", cfg.SudoProgram)
assert.True(t, cfg.Verbose)
// Default* keep distrobox.conf semantics; *_env keys are env-only and
// stay separate so the create command can distinguish "user has set X"
// from "X is the hardcoded default".
assert.Equal(t, "ubuntu:latest", cfg.DefaultContainerImage)
assert.Equal(t, "mybox", cfg.DefaultContainerName)
assert.Equal(t, "alpine:3.21", cfg.ContainerImage)
assert.Equal(t, "envbox", cfg.ContainerName)
assert.Equal(t, "myhost", cfg.ContainerHostname)
assert.Equal(t, "/tmp/home", cfg.ContainerCustomHome)
assert.Equal(t, "/data/boxes", cfg.ContainerHomePrefix)
assert.True(t, cfg.ContainerAlwaysPull)
assert.True(t, cfg.NonInteractive)
assert.False(t, cfg.GenerateEntry)
assert.True(t, cfg.CleanPath)
assert.True(t, cfg.SkipWorkDir)
assert.True(t, cfg.UsernsNoLimit)
assert.True(t, cfg.RmCustomHome)
}
func TestToStruct_MissingKeys(t *testing.T) {
@@ -35,6 +62,33 @@ func TestToStruct_MissingKeys(t *testing.T) {
assert.False(t, cfg.Verbose)
assert.Empty(t, cfg.DefaultContainerImage)
assert.Empty(t, cfg.DefaultContainerName)
assert.Empty(t, cfg.ContainerImage)
assert.Empty(t, cfg.ContainerName)
assert.Empty(t, cfg.ContainerHostname)
assert.Empty(t, cfg.ContainerCustomHome)
assert.Empty(t, cfg.ContainerHomePrefix)
assert.False(t, cfg.ContainerAlwaysPull)
assert.False(t, cfg.NonInteractive)
assert.False(t, cfg.GenerateEntry)
assert.False(t, cfg.CleanPath)
assert.False(t, cfg.SkipWorkDir)
assert.False(t, cfg.UsernsNoLimit)
assert.False(t, cfg.RmCustomHome)
}
// toBool must accept every truthy form the shell does (`1`, `true`, `yes`,
// `on`) — case-insensitive, surrounding whitespace ignored — and reject
// everything else, including the empty string. Anchors the
// `DBX_VERBOSE=1`/`DBX_USERNS_NOLIMIT=1` fix-in-passing.
func TestToBool(t *testing.T) {
truthy := []string{"1", "true", "TRUE", "True", "yes", "Yes", "on", "ON", " 1 ", "\ttrue\n"}
for _, v := range truthy {
assert.Truef(t, toBool(v), "toBool(%q) should be true", v)
}
falsy := []string{"", "0", "false", "FALSE", "no", "off", "nope", "garbage"}
for _, v := range falsy {
assert.Falsef(t, toBool(v), "toBool(%q) should be false", v)
}
}
func TestMergeConfigMaps(t *testing.T) {
+3 -3
View File
@@ -12,12 +12,12 @@ var ErrNoContainerManager = errors.New("no container manager found")
// NewAutoDetect returns a ContainerManager for the first available container runtime.
// Priority order: podman > podman-launcher > docker.
func NewAutoDetect(root bool, sudoCommand string, verbose bool) (containermanager.ContainerManager, error) {
func NewAutoDetect(root bool, sudoCommand string, verbose, usernsNoLimit bool) (containermanager.ContainerManager, error) {
if _, err := exec.LookPath("podman"); err == nil {
return NewPodman(root, sudoCommand, verbose), nil
return NewPodman(root, sudoCommand, verbose, usernsNoLimit), nil
}
if _, err := exec.LookPath("podman-launcher"); err == nil {
return NewPodmanLauncher(root, sudoCommand, verbose), nil
return NewPodmanLauncher(root, sudoCommand, verbose, usernsNoLimit), nil
}
if _, err := exec.LookPath("docker"); err == nil {
return NewDocker(root, sudoCommand, verbose), nil
@@ -54,7 +54,7 @@ func TestNewAutoDetect(t *testing.T) {
}
t.Setenv("PATH", dir)
cm, err := providers.NewAutoDetect(false, "sudo", false)
cm, err := providers.NewAutoDetect(false, "sudo", false, false)
if tt.expectError {
require.Error(t, err)
assert.Nil(t, cm)
@@ -8,7 +8,7 @@ import (
)
func TestPodman_CloneAsRoot_PreservesFieldsAndFlipsRoot(t *testing.T) {
original := newPodman(podmanCommandPodman, false, "doas", true)
original := newPodman(podmanCommandPodman, false, "doas", true, true)
cloned := original.CloneAsRoot()
@@ -21,12 +21,13 @@ func TestPodman_CloneAsRoot_PreservesFieldsAndFlipsRoot(t *testing.T) {
assert.Equal(t, original.command, clone.command)
assert.Equal(t, original.sudoCommand, clone.sudoCommand)
assert.Equal(t, original.verbose, clone.verbose)
assert.Equal(t, original.usernsNoLimit, clone.usernsNoLimit, "CloneAsRoot must preserve usernsNoLimit")
assert.False(t, original.root, "original should remain non-root")
}
func TestPodman_CloneAsRoot_AlreadyRootStillReturnsCopy(t *testing.T) {
original := newPodman(podmanCommandLauncher, true, "sudo", false)
original := newPodman(podmanCommandLauncher, true, "sudo", false, false)
cloned := original.CloneAsRoot()
+19 -25
View File
@@ -21,10 +21,11 @@ import (
)
type Podman struct {
command podmanCommand
root bool
sudoCommand string
verbose bool
command podmanCommand
root bool
sudoCommand string
verbose bool
usernsNoLimit bool
}
// podmanCommand represents the executable name for the Podman provider.
@@ -37,21 +38,22 @@ const (
var _ containermanager.ContainerManager = &Podman{}
func newPodman(command podmanCommand, root bool, sudoCommand string, verbose bool) *Podman {
func newPodman(command podmanCommand, root bool, sudoCommand string, verbose, usernsNoLimit bool) *Podman {
return &Podman{
command: command,
sudoCommand: sudoCommand,
root: root,
verbose: verbose,
command: command,
sudoCommand: sudoCommand,
root: root,
verbose: verbose,
usernsNoLimit: usernsNoLimit,
}
}
func NewPodman(root bool, sudoCommand string, verbose bool) *Podman {
return newPodman(podmanCommandPodman, root, sudoCommand, verbose)
func NewPodman(root bool, sudoCommand string, verbose, usernsNoLimit bool) *Podman {
return newPodman(podmanCommandPodman, root, sudoCommand, verbose, usernsNoLimit)
}
func NewPodmanLauncher(root bool, sudoCommand string, verbose bool) *Podman {
return newPodman(podmanCommandLauncher, root, sudoCommand, verbose)
func NewPodmanLauncher(root bool, sudoCommand string, verbose, usernsNoLimit bool) *Podman {
return newPodman(podmanCommandLauncher, root, sudoCommand, verbose, usernsNoLimit)
}
func (p *Podman) CloneAsRoot() containermanager.ContainerManager {
@@ -408,11 +410,11 @@ func (p *Podman) makeCreateCommand(
options = append(options, "--systemd=always")
}
// Use keep-id only if going rootless. DBX_USERNS_NOLIMIT (set to a non-zero
// value) drops the :size cap, matching the shell (distrobox-create:962-977).
// Use keep-id only if going rootless. The usernsNoLimit struct field
// (resolved from cfg.UsernsNoLimit / DBX_USERNS_NOLIMIT in the config
// layer) drops the :size cap, matching the shell (distrobox-create:962-977).
if !p.root {
usernsNoLimit := usernsNoLimitEnabled()
if !usernsNoLimit && (dryRun || p.supportsKeepIDSize(ctx, containerImage)) {
if !p.usernsNoLimit && (dryRun || p.supportsKeepIDSize(ctx, containerImage)) {
options = append(options, "--userns", "keep-id:size=65536")
} else {
options = append(options, "--userns", "keep-id")
@@ -682,14 +684,6 @@ func commandExists(cmd string) bool {
return err == nil
}
// usernsNoLimitEnabled reports whether DBX_USERNS_NOLIMIT requests dropping the
// keep-id:size cap (any non-empty, non-"0" value), mirroring the shell's
// userns_nolimit (distrobox-create:159,962-977).
func usernsNoLimitEnabled() bool {
v := os.Getenv("DBX_USERNS_NOLIMIT")
return v != "" && v != "0" && v != "false"
}
// supportsKeepIDSize tests whether podman supports the keep-id:size= userns option
// by attempting a quick container run. Older podman versions do not support the size suboption.
func (p *Podman) supportsKeepIDSize(ctx context.Context, image string) bool {
@@ -16,21 +16,11 @@ import (
"github.com/89luca89/distrobox/pkg/ui"
)
// DBX_USERNS_NOLIMIT (non-zero) drops the keep-id:size cap.
func TestUsernsNoLimitEnabled(t *testing.T) {
cases := map[string]bool{"": false, "0": false, "false": false, "1": true, "true": true, "yes": true}
for v, want := range cases {
t.Setenv("DBX_USERNS_NOLIMIT", v)
if got := usernsNoLimitEnabled(); got != want {
t.Errorf("DBX_USERNS_NOLIMIT=%q: got %v, want %v", v, got, want)
}
}
}
// with DBX_USERNS_NOLIMIT set, rootless create uses plain keep-id (no :size).
// with usernsNoLimit set, rootless create uses plain keep-id (no :size).
// The env var (DBX_USERNS_NOLIMIT) is resolved by pkg/config and surfaces
// here only as the constructor argument.
func TestPodman_makeCreateCommandUsernsNoLimit(t *testing.T) {
t.Setenv("DBX_USERNS_NOLIMIT", "1")
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, true)
userEnv := &userenv.UserEnvironment{User: "user", UserID: "1000", GroupID: "1000", Home: "/home/user", Shell: "/bin/sh"}
cmd := podman.makeCreateCommand(
@@ -51,7 +41,7 @@ func TestPodman_makeCreateCommandUsernsNoLimit(t *testing.T) {
}
func TestPodman_makeCreateCommand(t *testing.T) {
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, false)
userEnv := &userenv.UserEnvironment{
User: "user",
@@ -137,7 +127,7 @@ func TestPodman_makeCreateCommand(t *testing.T) {
func TestPodman_makeCreateCommandRootful(t *testing.T) {
// Test rootful mode - should NOT have --userns keep-id
podman := NewPodman(true, "sudo", false)
podman := NewPodman(true, "sudo", false, false)
userEnv := &userenv.UserEnvironment{
User: "user",
@@ -187,7 +177,7 @@ func TestPodman_makeCreateCommandRootful(t *testing.T) {
func TestPodman_makeCreateCommandNoInit(t *testing.T) {
// Test without init - should NOT have --systemd=always
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, false)
userEnv := &userenv.UserEnvironment{
User: "user",
@@ -237,7 +227,7 @@ func TestPodman_makeCreateCommandWithCrun(t *testing.T) {
// This test checks that if crun exists, --runtime=crun is added
// Note: This test will pass or fail depending on whether crun is installed
// on the test system. In a real scenario, you might want to mock commandExists()
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, false)
userEnv := &userenv.UserEnvironment{
User: "user",
@@ -285,7 +275,7 @@ func TestPodman_makeCreateCommandWithCrun(t *testing.T) {
}
func TestPodman_makeCreateCommandWithPlatform(t *testing.T) {
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, false)
userEnv := &userenv.UserEnvironment{
User: "user",
@@ -328,7 +318,7 @@ func TestPodman_makeCreateCommandWithPlatform(t *testing.T) {
}
func TestPodman_makeCreateCommandWithCustomHome(t *testing.T) {
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, false)
userEnv := &userenv.UserEnvironment{
User: "user",
@@ -381,7 +371,7 @@ func TestPodman_makeCreateCommandWithCustomHome(t *testing.T) {
}
func TestPodman_makeCreateCommandWithAdditionalFlags(t *testing.T) {
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, false)
userEnv := &userenv.UserEnvironment{
User: "user",
@@ -430,7 +420,7 @@ func TestPodman_makeCreateCommandWithAdditionalFlags(t *testing.T) {
}
func TestPodman_makeCreateCommandWithAdditionalPackages(t *testing.T) {
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, false)
userEnv := &userenv.UserEnvironment{
User: "user",
@@ -537,7 +527,7 @@ func TestPodman_makeCreateCommandUnshareOptions(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, false)
userEnv := &userenv.UserEnvironment{
User: "user",
@@ -598,10 +588,10 @@ func TestPodman_makeCreateCommandUnshareOptions(t *testing.T) {
}
func TestPodman_Name(t *testing.T) {
podman := NewPodman(false, "sudo", false)
podman := NewPodman(false, "sudo", false, false)
assert.Equal(t, "podman", podman.Name())
launcher := NewPodmanLauncher(false, "sudo", false)
launcher := NewPodmanLauncher(false, "sudo", false, false)
assert.Equal(t, "podman-launcher", launcher.Name())
}
@@ -678,12 +668,12 @@ func TestPodman_runUsesCorrectBinary(t *testing.T) {
}{
{
name: "NewPodman uses podman binary",
constructor: func() *Podman { return NewPodman(false, "sudo", false) },
constructor: func() *Podman { return NewPodman(false, "sudo", false, false) },
expectedPrefix: "podman ",
},
{
name: "NewPodmanLauncher uses podman-launcher binary",
constructor: func() *Podman { return NewPodmanLauncher(false, "sudo", false) },
constructor: func() *Podman { return NewPodmanLauncher(false, "sudo", false, false) },
expectedPrefix: "podman-launcher ",
},
}
@@ -728,7 +718,7 @@ func TestPodmanEnterPropagatesStartError(t *testing.T) {
t.Setenv("FAKE_START_EXIT", "9")
t.Setenv("FAKE_START_STDERR", "start failed")
err := NewPodman(false, "sudo", false).Enter(
err := NewPodman(false, "sudo", false, false).Enter(
t.Context(),
containermanager.EnterOptions{
ContainerName: "box",
@@ -749,7 +739,7 @@ func TestPodmanEnterPropagatesExecError(t *testing.T) {
t.Setenv("FAKE_EXEC_EXIT", "7")
t.Setenv("FAKE_EXEC_STDERR", "exec failed")
err := NewPodman(false, "sudo", false).Enter(
err := NewPodman(false, "sudo", false, false).Enter(
t.Context(),
containermanager.EnterOptions{
ContainerName: "box",