Skip to content

Commit bcfaf04

Browse files
committed
cmd/podman: add force flag to quadlet install
Signed-off-by: Celso Henrique Souza Silva <[email protected]> Fixes: #26930
1 parent 77dd165 commit bcfaf04

File tree

5 files changed

+58
-5
lines changed

5 files changed

+58
-5
lines changed

cmd/podman/quadlet/install.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ podman quadlet install https://github.com/containers/podman/blob/main/test/e2e/q
3636
func installFlags(cmd *cobra.Command) {
3737
flags := cmd.Flags()
3838
flags.BoolVar(&installOptions.ReloadSystemd, "reload-systemd", true, "Reload systemd after installing Quadlets")
39+
flags.BoolVarP(&installOptions.Force, "force", "f", false, "Force the installation even if the quadlet already exists")
3940
}
4041

4142
func init() {

docs/source/markdown/podman-quadlet-install.1.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ Reload systemd after installing Quadlets (default true).
2828
In order to disable it users need to manually set the value
2929
of this flag to `false`.
3030

31+
#### **--force**
32+
33+
Force the Quadlet installation even if the generated unit file already exists (default false).
34+
In order to enable it users need to manually set the value
35+
of this flag to `true`. This flag is used primarily to update an existing unit.
36+
3137
## EXAMPLES
3238

3339
Install quadlet from a file.

pkg/domain/entities/quadlet.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ package entities
44
type QuadletInstallOptions struct {
55
// Whether to reload systemd after installation is completed
66
ReloadSystemd bool
7+
// Force the installation even if the quadlet already exists
8+
Force bool
79
}
810

911
// QuadletInstallReport contains the output of the `quadlet install` command

pkg/domain/infra/abi/quadlet.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ func (ic *ContainerEngine) QuadletInstall(ctx context.Context, pathsOrURLs []str
197197
installReport.QuadletErrors[toInstall] = fmt.Errorf("populating temporary file: %w", err)
198198
continue
199199
}
200-
installedPath, err := ic.installQuadlet(ctx, tmpFile.Name(), quadletFileName, installDir, assetFile, validateQuadletFile)
200+
installedPath, err := ic.installQuadlet(ctx, tmpFile.Name(), quadletFileName, installDir, assetFile, validateQuadletFile, options.Force)
201201
if err != nil {
202202
installReport.QuadletErrors[toInstall] = err
203203
continue
@@ -210,7 +210,7 @@ func (ic *ContainerEngine) QuadletInstall(ctx context.Context, pathsOrURLs []str
210210
continue
211211
}
212212
// If toInstall is a single file, execute the original logic
213-
installedPath, err := ic.installQuadlet(ctx, toInstall, "", installDir, assetFile, validateQuadletFile)
213+
installedPath, err := ic.installQuadlet(ctx, toInstall, "", installDir, assetFile, validateQuadletFile, options.Force)
214214
if err != nil {
215215
installReport.QuadletErrors[toInstall] = err
216216
continue
@@ -254,7 +254,7 @@ func getFileName(resp *http.Response, fileURL string) (string, error) {
254254
// Perform some minimal validation, but not much.
255255
// We can't know about a lot of problems without running the Quadlet binary, which we
256256
// only want to do once.
257-
func (ic *ContainerEngine) installQuadlet(_ context.Context, path, destName, installDir, assetFile string, isQuadletFile bool) (string, error) {
257+
func (ic *ContainerEngine) installQuadlet(_ context.Context, path, destName, installDir, assetFile string, isQuadletFile, force bool) (string, error) {
258258
// First, validate that the source path exists and is a file
259259
stat, err := os.Stat(path)
260260
if err != nil {
@@ -274,9 +274,15 @@ func (ic *ContainerEngine) installQuadlet(_ context.Context, path, destName, ins
274274
return "", fmt.Errorf("%q is not a supported Quadlet file type", filepath.Ext(finalPath))
275275
}
276276

277-
file, err := os.OpenFile(finalPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644)
277+
var osFlags int = os.O_CREATE | os.O_WRONLY
278+
279+
if !force {
280+
osFlags |= os.O_EXCL
281+
}
282+
283+
file, err := os.OpenFile(finalPath, osFlags, 0644)
278284
if err != nil {
279-
if errors.Is(err, fs.ErrExist) {
285+
if errors.Is(err, fs.ErrExist) && !force {
280286
return "", fmt.Errorf("a Quadlet with name %s already exists, refusing to overwrite", filepath.Base(finalPath))
281287
}
282288
return "", fmt.Errorf("unable to open file %s: %w", filepath.Base(finalPath), err)

test/system/253-podman-quadlet.bats

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,4 +461,42 @@ EOF
461461
assert $status -eq 0 "quadlet rm --ignore should succeed even for non-existent quadlets"
462462
}
463463

464+
@test "quadlet install --force" {
465+
local install_dir=$(get_quadlet_install_dir)
466+
# Create a test quadlet file
467+
local quadlet_file=$PODMAN_TMPDIR/alpine-quadlet.container
468+
local initial_exec='Exec=sh -c "echo STARTED CONTAINER; trap '\''exit'\'' SIGTERM; while :; do sleep 0.1; done"'
469+
cat > $quadlet_file <<EOF
470+
[Container]
471+
Image=$IMAGE
472+
$initial_exec
473+
EOF
474+
# Test quadlet install
475+
run_podman quadlet install $quadlet_file
476+
# Verify install output contains the quadlet name on a single line
477+
assert "$output" =~ "alpine-quadlet.container" "install output should contain quadlet name"
478+
479+
# Without force should fail
480+
run_podman quadlet install $quadlet_file
481+
assert "$output" =~ "refusing to overwrite" "reinstall without --force must fail with the overwrite error message"
482+
483+
cat > $quadlet_file <<EOF
484+
[Container]
485+
Image=$IMAGE
486+
Exec=sh -c "echo STARTED CONTAINER UPDATED; trap 'exit' SIGTERM; while :; do sleep 0.1; done"
487+
EOF
488+
# With force should pass and update quadlet
489+
run_podman quadlet install --force $quadlet_file
490+
491+
# Verify install output contains the quadlet name on a single line
492+
assert "$output" =~ "alpine-quadlet.container" "install output should contain quadlet name"
493+
494+
run_podman quadlet print alpine-quadlet.container
495+
496+
assert "$output" !~ "$initial_exec" "Printed content must not show the initial version"
497+
498+
# Clean up
499+
run_podman quadlet rm alpine-quadlet.container
500+
}
501+
464502
# vim: filetype=sh

0 commit comments

Comments
 (0)