From ec21fbaf69fa2e71c2f8f6334d496b80a4dc9e0e Mon Sep 17 00:00:00 2001 From: Celso Henrique Souza Silva Date: Tue, 7 Oct 2025 22:16:19 -0300 Subject: [PATCH 1/2] cmd/podman: add force flag to quadlet install Signed-off-by: Celso Henrique Souza Silva Fixes: #26930 --- cmd/podman/quadlet/install.go | 1 + .../markdown/podman-quadlet-install.1.md | 6 +++ pkg/domain/entities/quadlet.go | 2 + pkg/domain/infra/abi/quadlet.go | 16 +++++--- test/system/253-podman-quadlet.bats | 38 +++++++++++++++++++ 5 files changed, 58 insertions(+), 5 deletions(-) diff --git a/cmd/podman/quadlet/install.go b/cmd/podman/quadlet/install.go index e1a2ea7b44..d49152811e 100644 --- a/cmd/podman/quadlet/install.go +++ b/cmd/podman/quadlet/install.go @@ -36,6 +36,7 @@ podman quadlet install https://github.com/containers/podman/blob/main/test/e2e/q func installFlags(cmd *cobra.Command) { flags := cmd.Flags() flags.BoolVar(&installOptions.ReloadSystemd, "reload-systemd", true, "Reload systemd after installing Quadlets") + flags.BoolVarP(&installOptions.Force, "force", "f", false, "Force the installation even if the quadlet already exists") } func init() { diff --git a/docs/source/markdown/podman-quadlet-install.1.md b/docs/source/markdown/podman-quadlet-install.1.md index ff627e816a..fa470d0d59 100644 --- a/docs/source/markdown/podman-quadlet-install.1.md +++ b/docs/source/markdown/podman-quadlet-install.1.md @@ -22,6 +22,12 @@ Note: In case user wants to install Quadlet application then first path should b ## OPTIONS +#### **--force**, **-f** + +Force the Quadlet installation even if the generated unit file already exists (default false). +In order to enable it users need to manually set the value +of this flag to `true`. This flag is used primarily to update an existing unit. + #### **--reload-systemd** Reload systemd after installing Quadlets (default true). diff --git a/pkg/domain/entities/quadlet.go b/pkg/domain/entities/quadlet.go index 177433232e..0d14f6b6f2 100644 --- a/pkg/domain/entities/quadlet.go +++ b/pkg/domain/entities/quadlet.go @@ -4,6 +4,8 @@ package entities type QuadletInstallOptions struct { // Whether to reload systemd after installation is completed ReloadSystemd bool + // Force the installation even if the quadlet already exists + Force bool } // QuadletInstallReport contains the output of the `quadlet install` command diff --git a/pkg/domain/infra/abi/quadlet.go b/pkg/domain/infra/abi/quadlet.go index 99c4fc40c3..63197994e5 100644 --- a/pkg/domain/infra/abi/quadlet.go +++ b/pkg/domain/infra/abi/quadlet.go @@ -197,7 +197,7 @@ func (ic *ContainerEngine) QuadletInstall(ctx context.Context, pathsOrURLs []str installReport.QuadletErrors[toInstall] = fmt.Errorf("populating temporary file: %w", err) continue } - installedPath, err := ic.installQuadlet(ctx, tmpFile.Name(), quadletFileName, installDir, assetFile, validateQuadletFile) + installedPath, err := ic.installQuadlet(ctx, tmpFile.Name(), quadletFileName, installDir, assetFile, validateQuadletFile, options.Force) if err != nil { installReport.QuadletErrors[toInstall] = err continue @@ -210,7 +210,7 @@ func (ic *ContainerEngine) QuadletInstall(ctx context.Context, pathsOrURLs []str continue } // If toInstall is a single file, execute the original logic - installedPath, err := ic.installQuadlet(ctx, toInstall, "", installDir, assetFile, validateQuadletFile) + installedPath, err := ic.installQuadlet(ctx, toInstall, "", installDir, assetFile, validateQuadletFile, options.Force) if err != nil { installReport.QuadletErrors[toInstall] = err continue @@ -254,7 +254,7 @@ func getFileName(resp *http.Response, fileURL string) (string, error) { // Perform some minimal validation, but not much. // We can't know about a lot of problems without running the Quadlet binary, which we // only want to do once. -func (ic *ContainerEngine) installQuadlet(_ context.Context, path, destName, installDir, assetFile string, isQuadletFile bool) (string, error) { +func (ic *ContainerEngine) installQuadlet(_ context.Context, path, destName, installDir, assetFile string, isQuadletFile, force bool) (string, error) { // First, validate that the source path exists and is a file stat, err := os.Stat(path) if err != nil { @@ -274,9 +274,15 @@ func (ic *ContainerEngine) installQuadlet(_ context.Context, path, destName, ins return "", fmt.Errorf("%q is not a supported Quadlet file type", filepath.Ext(finalPath)) } - file, err := os.OpenFile(finalPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) + var osFlags = os.O_CREATE | os.O_WRONLY + + if !force { + osFlags |= os.O_EXCL + } + + file, err := os.OpenFile(finalPath, osFlags, 0644) if err != nil { - if errors.Is(err, fs.ErrExist) { + if errors.Is(err, fs.ErrExist) && !force { return "", fmt.Errorf("a Quadlet with name %s already exists, refusing to overwrite", filepath.Base(finalPath)) } return "", fmt.Errorf("unable to open file %s: %w", filepath.Base(finalPath), err) diff --git a/test/system/253-podman-quadlet.bats b/test/system/253-podman-quadlet.bats index af4e787500..af9d589809 100644 --- a/test/system/253-podman-quadlet.bats +++ b/test/system/253-podman-quadlet.bats @@ -461,4 +461,42 @@ EOF assert $status -eq 0 "quadlet rm --ignore should succeed even for non-existent quadlets" } +@test "quadlet install --force" { + local install_dir=$(get_quadlet_install_dir) + # Create a test quadlet file + local quadlet_file=$PODMAN_TMPDIR/alpine-quadlet.container + local initial_exec='Exec=sh -c "echo STARTED CONTAINER; trap '\''exit'\'' SIGTERM; while :; do sleep 0.1; done"' + cat > $quadlet_file < $quadlet_file < Date: Wed, 8 Oct 2025 17:47:59 -0300 Subject: [PATCH 2/2] Update docs/source/markdown/podman-quadlet-install.1.md Co-authored-by: Brent Baude Signed-off-by: Celso Henrique <86984438+nothiaki@users.noreply.github.com> --- docs/source/markdown/podman-quadlet-install.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/markdown/podman-quadlet-install.1.md b/docs/source/markdown/podman-quadlet-install.1.md index fa470d0d59..5f00bf6708 100644 --- a/docs/source/markdown/podman-quadlet-install.1.md +++ b/docs/source/markdown/podman-quadlet-install.1.md @@ -25,7 +25,7 @@ Note: In case user wants to install Quadlet application then first path should b #### **--force**, **-f** Force the Quadlet installation even if the generated unit file already exists (default false). -In order to enable it users need to manually set the value +In order to enable it, users need to manually set the value of this flag to `true`. This flag is used primarily to update an existing unit. #### **--reload-systemd**