-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add support for podman quadlet
#26330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: flouthoc The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
[NON-BLOCKING] Packit jobs failed. @containers/packit-build please check. Everyone else, feel free to ignore. |
Why is the quadlet binary bloated by this? the quadlet binary size really needs to be small and avoid any unnecessary imports. |
pkg/systemd/quadlet/unitdirs.go
Outdated
var ( | ||
Errorf func(string, ...interface{}) = logrus.Errorf | ||
Debugf func(string, ...interface{}) = logrus.Debugf | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should not use logrus in quadlet, that is causing the bloat i guess
9cee402
to
c7c1da9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments/questions
cmd/podman/quadlet/remove.go
Outdated
if err != nil { | ||
return err | ||
} | ||
|
||
return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just return err
if err != nil { | |
return err | |
} | |
return nil | |
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
"github.com/containers/podman/v5/pkg/domain/entities" | ||
) | ||
|
||
var errNotImplemented = errors.New("not implemented for the remote Podman client") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it just for this PR, or is it not intended to be implemented for remote?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not as of now, as discussed with @mheon , our priority is to get basic feature in, in future we can explore implementation of API for this. But overall I am still working on this PR by the time I undraft this PR i hope to resolve all your comments, thanks for the feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we'll put it in Q3 prioritization to finish the commands by adding missing things (multi-file, better integration with bootc LBIs, etc)
pkg/domain/infra/abi/quadlet.go
Outdated
installDir := systemdquadlet.UnitDirAdmin | ||
if rootless.IsRootless() { | ||
// Install just for the user in question. | ||
quadletRootlessDirs := systemdquadlet.GetUnitDirs(true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure systemdquadlet.GetUnitDirs
acts the way you expect it to. The list of directories is sorted to the order in which the directories were added. So, IIUC the code here it will break some assumptions you have here:
/etc/containers/systemd/users
might be the only one that fits your check. But, this directory is for root to put quadlets for all users. Yes, usually the write check will fail, but nothing guaranties it- Because you don't break from the loop when
foundAdminDir = true
, if there are directories under/etc/containers/systemd/users/$UID
e.g./etc/containers/systemd/users/aaa
and/etc/containers/systemd/users/bbb
, the Quadlets will be installed in/etc/containers/systemd/users/bbb
. It might even go further in if of you have/etc/containers/systemd/users/bbb/z
- Not sure this is all
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fixed.
a2b08f8
to
ae01986
Compare
} | ||
return nil | ||
}, | ||
Example: `podman quadlet install /path/to/myquadlet.container |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: what would be the recommend way of installing a kube quadlet? since the YAML would not be copied with this command?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this is an even bigger questions. Many .container
units come with a configuration file stored locally and mounted into the container. For example, see: https://github.com/containers/appstore/tree/main/quadlet/minio-prometheus. prometheus.yml
must be copied along with prometheus.container
.
Furthermore, if you look at the examples, you can see that in many cases, more than one file is needed (units and others). Can the install
command support installing a directory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Furthermore, if you look at the examples, you can see that in many cases, more than one file is needed (units and others). Can the install command support installing a directory?
Yes it supports multiplefile but I think we need to start consuming entire directory as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it supports multiplefile but I think we need to start consuming entire directory as well.
Yes, having to list all the files does not make sense for the user. Plus, as I wrote not all of them will be Quadlet files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we copy resources, we may want to ensure they don't already exists, and have a --force
flag, to allow overwriting existing files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added patch to install from directory in new commit.
If we copy resources, we may want to ensure they don't already exists, and have a --force flag, to allow overwriting existing files
Will add --force
in a follow up PR once this gets merged in order to keep review simpler and not to added too much to matt's original patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One general issue that keep repeating in my comments is about non-Quadlet files. Not supporting them creates an issue
pkg/domain/infra/abi/quadlet.go
Outdated
quadletExtension := getFileExtension(r) | ||
if quadletExtension == "" { | ||
quadletExtension = ".container" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? What if this is configuration file that also need to be copied?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
pkg/domain/infra/abi/quadlet.go
Outdated
continue | ||
} | ||
filePath := filepath.Join(toInstall, file.Name()) | ||
if !systemdquadlet.IsExtSupported(filePath) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this skip non-Qualet files? E.g. configuration files that are mounted into the container?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added support for non-quadlet files.
pkg/domain/infra/abi/quadlet.go
Outdated
continue | ||
} | ||
for _, file := range files { | ||
if file.IsDir() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why skip subdirectories? Why are we not just copying the directory. I think we are overcomplicating this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
pkg/domain/infra/abi/quadlet.go
Outdated
// really has to parse everything), but we should be able to output in a machine-readable format | ||
// (JSON, maybe?) so we can easily associated error to quadlet here, and return better | ||
// results. | ||
var validateErr error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why we are doing this.
But if we are, first, I think the user should be aware of it and also maybe have a way to skip the check. Second, doesn't it make more sense to prepare everything in a temporary directory, run the dry-run on it and only if the dry-run passes copy to the real target?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that work if the quadlet has dependencies on already-installed quadlets?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have removed this check for now, already too much in this PR.
pkg/domain/infra/abi/quadlet.go
Outdated
continue | ||
} | ||
|
||
report.Status = fmt.Sprintf("%s/%s", unitStatus.ActiveState, unitStatus.SubState) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this line to an else
section of the previous if
. This way you can remove the continue
and combine the duplicated two lines
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
return string(contents), nil | ||
} | ||
|
||
func (ic *ContainerEngine) QuadletRemove(ctx context.Context, quadlets []string, options entities.QuadletRemoveOptions) (*entities.QuadletRemoveReport, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand how the user can remove non-Quadlet files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this feature and also added test.
} | ||
|
||
// Reload systemd, if necessary/requested. | ||
if needReload { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, needReload
is not set according to QuadletRemoveOptions
so if the user requests it it won't run. Second, the doc does not mention the reload will happen regardless to what they asked.
@@ -1865,4 +1865,164 @@ EOF | |||
|
|||
run_podman rmi -i $image_tag | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Install and remove tests should also include non-Quadlet files that are mounted into the container.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, new tests below have these.
@@ -1865,4 +1865,164 @@ EOF | |||
|
|||
run_podman rmi -i $image_tag | |||
} | |||
|
|||
@test "quadlet verb - install, list, rm" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't these tests be in a separate file? quadlet
and podman quadlet
are not the same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought quadlet
and quadlet verb
are togather part of similar big feature, since helpers functions are being shared therefore I added them in same file. These are only few new tests and I have added quadlet verb
as suffix to differentiate them. If these extend to more test I will move them to a new file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rather they be in a separate file. It's relevant when running the tests locally. I don't see a reason to run the quadlet verb
tests when making changes to quadlet
We don't support I propose a method like this |
Using a hidden file SGTM, but that user interface seems pretty bad. Should we restrict |
That sounds good to me and much cleaner. What do others think @ygalblum @Luap99 ? |
Installing a single Quadlet file will generate errors in daemon reload if other units are missing. For example, if you install the |
Problem with directory is, if a directory contains two Or in case of a directory we can just associate all |
Maybe when it's a directory, the identifier shouldn't be at a Quadlet unit level, but an application or deployment (find better name) level. |
I think quadlet list should still show all quadlets independentaly but with another column if they belong to common app and deleting a quadlet from app, should remove entire So it both should remove app
|
e4d2fb5
to
690c3ae
Compare
This adds `podman quadlet list`, `podman quadlet install`, `podman quadlet rm` and `podman quadlet print`. Signed-off-by: Matt Heon <[email protected]> Co-authored-by: flouthoc <[email protected]> Signed-off-by: flouthoc <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still need to review the abi
. But, I wanted to put in these comments
var err error | ||
switch { | ||
case cmd.Flag("format").Changed: | ||
rpt, err = rpt.Parse(report.OriginUser, format) | ||
default: | ||
rpt, err = rpt.Parse(report.OriginPodman, format) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var err error | |
switch { | |
case cmd.Flag("format").Changed: | |
rpt, err = rpt.Parse(report.OriginUser, format) | |
default: | |
rpt, err = rpt.Parse(report.OriginPodman, format) | |
} | |
origin := report.OriginPodman | |
if cmd.Flag("format").Changed { | |
origin = report.OriginUser | |
} | |
rpt, err := rpt.Parse(origin, format) |
Remove one or more installed Quadlets from the current user. Following command also takes application name | ||
as input and removes all the Quadlets which belongs to that specific application. | ||
|
||
Note: If quadlet belongs to an application then removing that specific quadlet will remove entire application. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear from reading this and the install page what defines an application
@@ -1865,4 +1865,164 @@ EOF | |||
|
|||
run_podman rmi -i $image_tag | |||
} | |||
|
|||
@test "quadlet verb - install, list, rm" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rather they be in a separate file. It's relevant when running the tests locally. I don't see a reason to run the quadlet verb
tests when making changes to quadlet
|
||
# Test quadlet install | ||
run_podman quadlet install $quadlet_file | ||
assert $status -eq 0 "quadlet install should succeed" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK run_podman
already asserts the value of status
. If first parameter is numeric, it will treat it as the expected value and start the command from the second parameter. Otherwise, it will expect 0.
This is relevant throughout this test
assert "$output" =~ "Mount=type=bind,source=./test.txt,destination=/test.txt:z" "print should show volume mount configuration" | ||
|
||
# Test quadlet rm | ||
run_podman quadlet rm mount-test.container |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this call expected to also remove the "$install_dir/test.txt"? If yes, it should be verified. If not, aren't we leaving leftovers?
LogDriver=passthrough | ||
EOF | ||
# Clean all quadlets | ||
run_podman quadlet rm --all -f |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I run this locally, it will remove all my Quadlet files.
# bats test_tags=distro-integration | ||
@test "quadlet verb - install multiple quadlets from directory and remove by application" { | ||
# Create a temporary directory for multiple quadlet files | ||
local app_name="test-app-$(random_string)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use safe_name
instead of random_string
(throughout)
# bats test_tags=distro-integration | ||
@test "quadlet verb - install multiple files from directory" { | ||
# Create a directory for multiple quadlet files | ||
local quadlet_dir=$PODMAN_TMPDIR/quadlet-multi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Always use safe_name
to allow parallelism
|
||
## DESCRIPTION | ||
|
||
Install a Quadlet file or an application (which may include multiple Quadlet files) for the current user. You can specify Quadlet files as local files or web URLs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need more details on how applications work here - it's not very clear
|
||
| Filter | Description | | ||
|------------|--------------------------------------------------------------------------------------------------| | ||
| name | Filter by quadlet name. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a TODO somewhere to add an Application filter to this?
|
||
// AppendStringToFile appends the given text to the specified file. | ||
// If the file does not exist, it will be created with 0644 permissions. | ||
func appendStringToFile(filePath, text string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like this should be in pkg/util?
return err | ||
} | ||
|
||
// deleteFilesFromAsset reads .<name>.asset, deletes listed files, then deletes the asset file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this deletes everything it feels like this should be named deleteAsset
instead
| install | [podman-quadlet-install(1)](podman-quadlet-install.1.md) | Install a quadlet file or quadlet application | | ||
| list | [podman-quadlet-list(1)](podman-quadlet-list.1.md) | List installed quadlets | | ||
| print | [podman-quadlet-print(1)](podman-quadlet-print.1.md) | Display the contents of a quadlet | | ||
| rm | [podman-quadlet-rm(1)](podman-quadlet-rm.1.md) | Removes an installed quadlet | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like at some point we need a quadlet inspect
that prints more details. Particularly about the app the quadlet is part of. But that is followon work.
quadlet := quadlets[i] | ||
quadletPath, err := getQuadletByName(quadlet) | ||
if options.All { | ||
quadletPath = quadlet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem right. Why are we even doing getQuadletByName
when options.All
is set?
Does this PR introduce a user-facing change?