Skip to content

Commit 5d12165

Browse files
committed
feat: update podman quadlet sub-command
Fixes: #28118 Signed-off-by: axel7083 <42176370+axel7083@users.noreply.github.com>
1 parent 545fe62 commit 5d12165

3 files changed

Lines changed: 68 additions & 51 deletions

File tree

pkg/domain/infra/abi/quadlet.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import (
1515
"path/filepath"
1616
"strings"
1717

18-
"github.com/containers/podman/v6/libpod/define"
19-
"github.com/containers/podman/v6/pkg/domain/entities"
20-
"github.com/containers/podman/v6/pkg/rootless"
21-
"github.com/containers/podman/v6/pkg/systemd"
22-
systemdquadlet "github.com/containers/podman/v6/pkg/systemd/quadlet"
2318
"github.com/sirupsen/logrus"
2419
"go.podman.io/common/pkg/config"
20+
"go.podman.io/podman/v6/libpod/define"
21+
"go.podman.io/podman/v6/pkg/domain/entities"
22+
"go.podman.io/podman/v6/pkg/rootless"
23+
"go.podman.io/podman/v6/pkg/systemd"
24+
systemdquadlet "go.podman.io/podman/v6/pkg/systemd/quadlet"
2525
"go.podman.io/storage/pkg/fileutils"
2626
)
2727

@@ -86,7 +86,7 @@ func (ic *ContainerEngine) QuadletInstall(ctx context.Context, pathsOrURLs []str
8686
}
8787

8888
// If it's a directory, then read all files and add it to paths
89-
entries, err := os.ReadDir(pathsOrURLs[0]) // TODO: make recursive
89+
entries, err := os.ReadDir(pathsOrURLs[0])
9090
if err != nil {
9191
return nil, fmt.Errorf("unable to read Quadlet dir %s: %w", pathsOrURLs[0], err)
9292
}
@@ -622,6 +622,19 @@ func (ic *ContainerEngine) QuadletRemove(ctx context.Context, quadlets []string,
622622
report.Removed = append(report.Removed, unit.Name)
623623
}
624624
}
625+
626+
// clean up application folder when no error
627+
if len(report.Errors) == 0 {
628+
appPath, err := getApplicationPath(quadlet)
629+
if err != nil {
630+
report.Errors[quadlet] = err
631+
} else {
632+
err = os.RemoveAll(appPath)
633+
if err != nil {
634+
report.Errors[quadlet] = err
635+
}
636+
}
637+
}
625638
}
626639
}
627640

pkg/domain/infra/abi/quadlet_utils.go

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import (
1111
"path/filepath"
1212
"strings"
1313

14-
"github.com/containers/podman/v6/libpod/define"
15-
"github.com/containers/podman/v6/pkg/domain/entities"
16-
"github.com/containers/podman/v6/pkg/rootless"
17-
"github.com/containers/podman/v6/pkg/systemd/parser"
18-
systemdquadlet "github.com/containers/podman/v6/pkg/systemd/quadlet"
19-
"github.com/containers/podman/v6/pkg/util"
2014
"github.com/coreos/go-systemd/v22/dbus"
2115
"github.com/sirupsen/logrus"
16+
"go.podman.io/podman/v6/libpod/define"
17+
"go.podman.io/podman/v6/pkg/domain/entities"
18+
"go.podman.io/podman/v6/pkg/rootless"
19+
"go.podman.io/podman/v6/pkg/systemd/parser"
20+
systemdquadlet "go.podman.io/podman/v6/pkg/systemd/quadlet"
21+
"go.podman.io/podman/v6/pkg/util"
2222
)
2323

2424
type QuadletFilter func(q *entities.ListQuadlet) bool
@@ -49,7 +49,6 @@ func generateQuadletFilters(filters []string) (QuadletFilter, error) {
4949
// Create filter functions
5050
filterFuncs := make([]QuadletFilter, 0, len(filters))
5151
filterMap := make(map[string][]string)
52-
// TODO: Add filter for app names.
5352
for _, f := range filters {
5453
fname, filter, hasFilter := strings.Cut(f, "=")
5554
if !hasFilter {
@@ -229,3 +228,29 @@ func getQuadletServiceName(quadletPath string) (string, error) {
229228
}
230229
return serviceName + ".service", nil
231230
}
231+
232+
func getApplicationPath(app string) (string, error) {
233+
// Get the root paths of all quadlets available to the current user
234+
quadletDirs := systemdquadlet.GetUnitDirs(rootless.IsRootless(), false)
235+
236+
// for every quadlet dir, let's get the quadlets
237+
for _, dir := range quadletDirs {
238+
// Avoiding using filepath.join with "app", as it may lead to
239+
// path traversal attack
240+
files, err := os.ReadDir(dir)
241+
if errors.Is(err, fs.ErrNotExist) {
242+
continue
243+
}
244+
245+
if err != nil {
246+
return "", err
247+
}
248+
249+
for _, file := range files {
250+
if file.IsDir() && file.Name() == app {
251+
return filepath.Join(dir, file.Name()), nil
252+
}
253+
}
254+
}
255+
return "", fmt.Errorf("application %s not found", app)
256+
}

test/system/254-podman-quadlet-multi.bats

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ port=3000
300300
EOF
301301

302302
# Install the directory
303-
run_podman quadlet install "$app_dir"
303+
run_podman quadlet install "$app_dir" --application=$app_name
304304

305305
# Verify all quadlets were installed (2 individual + 3 from .quadlets file = 5 total)
306306
assert "$output" =~ "${frontend_name}.container" "install output should contain ${frontend_name}.container"
@@ -313,23 +313,12 @@ EOF
313313
assert "${#lines[@]}" -eq 6 "install output should contain exactly six lines"
314314

315315
# Verify all files exist on disk
316-
[[ -f "$install_dir/${frontend_name}.container" ]] || die "${frontend_name}.container should exist on disk"
317-
[[ -f "$install_dir/${data_name}.volume" ]] || die "${data_name}.volume should exist on disk"
318-
[[ -f "$install_dir/${api_name}.container" ]] || die "${api_name}.container should exist on disk"
319-
[[ -f "$install_dir/${cache_name}.volume" ]] || die "${cache_name}.volume should exist on disk"
320-
[[ -f "$install_dir/${network_name}.network" ]] || die "${network_name}.network should exist on disk"
321-
[[ -f "$install_dir/app.conf" ]] || die "app.conf should exist on disk"
322-
323-
# Check that the .app file was created (all files are part of one application)
324-
[[ -f "$install_dir/.${app_name}.app" ]] || die ".${app_name}.app file should exist"
325-
326-
# Verify the .app file contains all quadlet names
327-
run cat "$install_dir/.${app_name}.app"
328-
assert "$output" =~ "${frontend_name}.container" ".app file should contain ${frontend_name}.container"
329-
assert "$output" =~ "${data_name}.volume" ".app file should contain ${data_name}.volume"
330-
assert "$output" =~ "${api_name}.container" ".app file should contain ${api_name}.container"
331-
assert "$output" =~ "${cache_name}.volume" ".app file should contain ${cache_name}.volume"
332-
assert "$output" =~ "${network_name}.network" ".app file should contain ${network_name}.network"
316+
[[ -f "$install_dir/$app_name/${frontend_name}.container" ]] || die "${frontend_name}.container should exist on disk"
317+
[[ -f "$install_dir/$app_name/${data_name}.volume" ]] || die "${data_name}.volume should exist on disk"
318+
[[ -f "$install_dir/$app_name/${api_name}.container" ]] || die "${api_name}.container should exist on disk"
319+
[[ -f "$install_dir/$app_name/${cache_name}.volume" ]] || die "${cache_name}.volume should exist on disk"
320+
[[ -f "$install_dir/$app_name/${network_name}.network" ]] || die "${network_name}.network should exist on disk"
321+
[[ -f "$install_dir/$app_name/app.conf" ]] || die "app.conf should exist on disk"
333322

334323
# Test quadlet list to verify all quadlets show the same app name
335324
run_podman quadlet list
@@ -339,28 +328,21 @@ EOF
339328
local cache_line=$(echo "$output" | grep "${cache_name}.volume")
340329
local network_line=$(echo "$output" | grep "${network_name}.network")
341330

342-
# All lines should contain the same app name (.${app_name}.app)
343-
assert "$frontend_line" =~ "\\.${app_name}\\.app" "${frontend_name} should show .${app_name}.app as app"
344-
assert "$data_line" =~ "\\.${app_name}\\.app" "${data_name} should show .${app_name}.app as app"
345-
assert "$api_line" =~ "\\.${app_name}\\.app" "${api_name} should show .${app_name}.app as app"
346-
assert "$cache_line" =~ "\\.${app_name}\\.app" "${cache_name} should show .${app_name}.app as app"
347-
assert "$network_line" =~ "\\.${app_name}\\.app" "${network_name} should show .${app_name}.app as app"
348-
349331
# Verify content of individual quadlet files
350-
run cat "$install_dir/${frontend_name}.container"
332+
run cat "$install_dir/$app_name/${frontend_name}.container"
351333
assert "$output" =~ "\\[Container\\]" "frontend container file should contain [Container] section"
352334
assert "$output" =~ "ContainerName=frontend-app-" "frontend container file should contain correct name prefix"
353335

354-
run cat "$install_dir/${api_name}.container"
336+
run cat "$install_dir/$app_name/${api_name}.container"
355337
assert "$output" =~ "\\[Container\\]" "api-server container file should contain [Container] section"
356338
assert "$output" =~ "ContainerName=api-server-" "api-server container file should contain correct name prefix"
357339

358-
run cat "$install_dir/${network_name}.network"
340+
run cat "$install_dir/$app_name/${network_name}.network"
359341
assert "$output" =~ "\\[Network\\]" "network file should contain [Network] section"
360342
assert "$output" =~ "Subnet=192.168.1.0/24" "network file should contain correct subnet"
361343

362344
# Test that removing one quadlet removes the entire application
363-
run_podman quadlet rm ${frontend_name}.container
345+
run_podman quadlet rm $app_name --recursive
364346

365347
# All quadlets should be removed since they're part of the same app
366348
run_podman quadlet list
@@ -370,14 +352,11 @@ EOF
370352
assert "$output" !~ "${cache_name}.volume" "${cache_name}.volume should also be removed as part of same app"
371353
assert "$output" !~ "${network_name}.network" "${network_name}.network should also be removed as part of same app"
372354

373-
# The .app file should also be removed
374-
[[ ! -f "$install_dir/.${app_name}.app" ]] || die ".${app_name}.app file should be removed"
375-
376355
# All individual files should be removed
377-
[[ ! -f "$install_dir/${frontend_name}.container" ]] || die "${frontend_name}.container should be removed"
378-
[[ ! -f "$install_dir/${data_name}.volume" ]] || die "${data_name}.volume should be removed"
379-
[[ ! -f "$install_dir/${api_name}.container" ]] || die "${api_name}.container should be removed"
380-
[[ ! -f "$install_dir/${cache_name}.volume" ]] || die "${cache_name}.volume should be removed"
381-
[[ ! -f "$install_dir/${network_name}.network" ]] || die "${network_name}.network should be removed"
382-
[[ ! -f "$install_dir/app.conf" ]] || die "app.conf should be removed"
356+
[[ ! -f "$install_dir/$app_name/${frontend_name}.container" ]] || die "${frontend_name}.container should be removed"
357+
[[ ! -f "$install_dir/$app_name/${data_name}.volume" ]] || die "${data_name}.volume should be removed"
358+
[[ ! -f "$install_dir/$app_name/${api_name}.container" ]] || die "${api_name}.container should be removed"
359+
[[ ! -f "$install_dir/$app_name/${cache_name}.volume" ]] || die "${cache_name}.volume should be removed"
360+
[[ ! -f "$install_dir/$app_name/${network_name}.network" ]] || die "${network_name}.network should be removed"
361+
[[ ! -f "$install_dir/$app_name/app.conf" ]] || die "app.conf should be removed"
383362
}

0 commit comments

Comments
 (0)