mirror of
https://github.com/containers/podman.git
synced 2025-11-29 09:37:38 +08:00
Add artifact quadlet unit type support
RFE: Add artifact quadlet unit type #25778 Signed-off-by: Odilon Sousa <osousa@redhat.com>
This commit is contained in:
@@ -396,6 +396,8 @@ func generateUnitsInfoMap(units []*parser.UnitFile) map[string]*quadlet.UnitInfo
|
||||
// types), but still breaks the dependency cycle between .volume and .build ([Volume] can
|
||||
// have Image=some.build, and [Build] can have Volume=some.volume:/some-volume)
|
||||
resourceName = quadlet.GetBuiltImageName(unit)
|
||||
case strings.HasSuffix(unit.Filename, ".artifact"):
|
||||
serviceName = quadlet.GetArtifactServiceName(unit)
|
||||
case strings.HasSuffix(unit.Filename, ".pod"):
|
||||
containers = make([]string, 0)
|
||||
// Prefill resouceNames for .pod files.
|
||||
@@ -550,6 +552,9 @@ func process() bool {
|
||||
service, err = quadlet.ConvertImage(unit, unitsInfoMap, isUserFlag)
|
||||
case strings.HasSuffix(unit.Filename, ".build"):
|
||||
service, warnings, err = quadlet.ConvertBuild(unit, unitsInfoMap, isUserFlag)
|
||||
case strings.HasSuffix(unit.Filename, ".artifact"):
|
||||
warnIfAmbiguousName(unit, quadlet.ArtifactGroup)
|
||||
service, err = quadlet.ConvertArtifact(unit, unitsInfoMap, isUserFlag)
|
||||
case strings.HasSuffix(unit.Filename, ".pod"):
|
||||
service, warnings, err = quadlet.ConvertPod(unit, unit.Filename, unitsInfoMap, isUserFlag)
|
||||
default:
|
||||
|
||||
@@ -6,7 +6,7 @@ podman\-systemd.unit - systemd units using Podman Quadlet
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
*name*.container, *name*.volume, *name*.network, *name*.kube *name*.image, *name*.build *name*.pod
|
||||
*name*.container, *name*.volume, *name*.network, *name*.kube *name*.image, *name*.build *name*.pod, *name*.artifact
|
||||
|
||||
### Podman rootful unit search path
|
||||
|
||||
@@ -48,7 +48,7 @@ the [Service] table and [Install] tables pass directly to systemd and are handle
|
||||
See systemd.unit(5) man page for more information.
|
||||
|
||||
The Podman generator reads the search paths above and reads files with the extensions `.container`
|
||||
`.volume`, `.network`, `.build`, `.pod` and `.kube`, and for each file generates a similarly named `.service` file. Be aware that
|
||||
`.volume`, `.network`, `.build`, `.pod`, `.kube`, and `.artifact`, and for each file generates a similarly named `.service` file. Be aware that
|
||||
existing vendor services (i.e., in `/usr/`) are replaced if they have the same name. The generated unit files can
|
||||
be started and managed with `systemctl` like any other systemd service. `systemctl {--user} list-unit-files`
|
||||
lists existing unit files on the system.
|
||||
@@ -104,7 +104,7 @@ Quadlet requires the use of cgroup v2, use `podman info --format {{.Host.Cgroups
|
||||
|
||||
By default, the `Type` field of the `Service` section of the Quadlet file does not need to be set.
|
||||
Quadlet will set it to `notify` for `.container` and `.kube` files,
|
||||
`forking` for `.pod` files, and `oneshot` for `.volume`, `.network`, `.build`, and `.image` files.
|
||||
`forking` for `.pod` files, and `oneshot` for `.volume`, `.network`, `.build`, `.image`, and `.artifact` files.
|
||||
|
||||
However, `Type` may be explicitly set to `oneshot` for `.container` and `.kube` files when no containers are expected
|
||||
to run once `podman` exits.
|
||||
@@ -2091,6 +2091,123 @@ Override the default architecture variant of the container image.
|
||||
|
||||
This is equivalent to the Podman `--variant` option.
|
||||
|
||||
## Artifact units [Artifact]
|
||||
|
||||
### WARNING: Experimental Unit
|
||||
|
||||
This unit is considered experimental and still in development. Inputs, options, and outputs are all subject to change.
|
||||
|
||||
Artifact units are named with a `.artifact` extension and contain a `[Artifact]` section describing
|
||||
the container artifact pull command. The generated service is a one-time command that ensures that the artifact
|
||||
exists on the host, pulling it if needed.
|
||||
|
||||
Using artifact units allows containers to depend on artifacts being automatically pulled. This is
|
||||
particularly useful for managing artifacts that containers need to mount or access, the **Artifact** key is mandatory inside of the [Artifact] unit.
|
||||
|
||||
Valid options for `[Artifact]` are listed below:
|
||||
|
||||
| **[Artifact] options** | **podman artifact pull equivalent** |
|
||||
|---------------------------------------------|--------------------------------------------------------|
|
||||
| Artifact=quay\.io/foobar/artifact:special | podman artifact pull quay\.io/foobar/artifact:special |
|
||||
| AuthFile=/etc/registry/auth\.json | --authfile=/etc/registry/auth\.json |
|
||||
| CertDir=/etc/registry/certs | --cert-dir=/etc/registry/certs |
|
||||
| ContainersConfModule=/etc/nvd\.conf | --module=/etc/nvd\.conf |
|
||||
| Creds=username:password | --creds=username:password |
|
||||
| DecryptionKey=/etc/registry\.key | --decryption-key=/etc/registry\.key |
|
||||
| GlobalArgs=--log-level=debug | --log-level=debug |
|
||||
| PodmanArgs=--pull never | --pull never |
|
||||
| Quiet=true | --quiet |
|
||||
| Retry=5 | --retry=5 |
|
||||
| RetryDelay=10s | --retry-delay=10s |
|
||||
| ServiceName=my-artifact | Set the systemd service name to my-artifact.service |
|
||||
| TLSVerify=false | --tls-verify=false |
|
||||
|
||||
### `Artifact=`
|
||||
|
||||
The artifact to pull from a registry onto the local machine. This is the only required key for artifact units.
|
||||
|
||||
It is required to use a fully qualified artifact name rather than a short name, both for
|
||||
performance and robustness reasons.
|
||||
|
||||
### `AuthFile=`
|
||||
|
||||
Path of the authentication file.
|
||||
|
||||
This is equivalent to the Podman `--authfile` option.
|
||||
|
||||
### `CertDir=`
|
||||
|
||||
Use certificates at path (*.crt, *.cert, *.key) to connect to the registry.
|
||||
|
||||
This is equivalent to the Podman `--cert-dir` option.
|
||||
|
||||
### `ContainersConfModule=`
|
||||
|
||||
Load the specified containers.conf(5) module. Equivalent to the Podman `--module` option.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
### `Creds=`
|
||||
|
||||
The credentials to use when contacting the registry in the format `[username[:password]]`.
|
||||
|
||||
This is equivalent to the Podman `--creds` option.
|
||||
|
||||
### `DecryptionKey=`
|
||||
|
||||
The `[key[:passphrase]]` to be used for decryption of artifacts.
|
||||
|
||||
This is equivalent to the Podman `--decryption-key` option.
|
||||
|
||||
### `GlobalArgs=`
|
||||
|
||||
This key contains a list of arguments passed directly between `podman` and `artifact`
|
||||
in the generated file. It can be used to access Podman features otherwise unsupported by the generator. Since the generator is unaware
|
||||
of what unexpected interactions can be caused by these arguments, it is not recommended to use
|
||||
this option.
|
||||
|
||||
The format of this is a space separated list of arguments, which can optionally be individually
|
||||
escaped to allow inclusion of whitespace and other control characters.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
### `PodmanArgs=`
|
||||
|
||||
This key contains a list of arguments passed directly to the end of the `podman artifact pull` command
|
||||
in the generated file (right before the artifact name in the command line). It can be used to
|
||||
access Podman features otherwise unsupported by the generator. Since the generator is unaware
|
||||
of what unexpected interactions can be caused by these arguments, it is not recommended to use
|
||||
this option.
|
||||
|
||||
The format of this is a space separated list of arguments, which can optionally be individually
|
||||
escaped to allow inclusion of whitespace and other control characters.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
### `Quiet=`
|
||||
|
||||
Suppress output information when pulling artifacts.
|
||||
|
||||
This is equivalent to the Podman `--quiet` option.
|
||||
|
||||
### `Retry=`
|
||||
|
||||
Number of times to retry the artifact pull when a HTTP error occurs. Equivalent to the Podman `--retry` option.
|
||||
|
||||
### `RetryDelay=`
|
||||
|
||||
Delay between retries. Equivalent to the Podman `--retry-delay` option.
|
||||
|
||||
### `ServiceName=`
|
||||
|
||||
The (optional) name of the systemd service. If this is not specified, the default value is the same name as the unit, but with a `-artifact` suffix, i.e. a `$name.artifact` file creates a `$name-artifact.service` systemd service.
|
||||
|
||||
### `TLSVerify=`
|
||||
|
||||
Require HTTPS and verification of certificates when contacting registries.
|
||||
|
||||
This is equivalent to the Podman `--tls-verify` option.
|
||||
|
||||
## Quadlet section [Quadlet]
|
||||
Some quadlet specific configuration is shared between different unit types. Those settings
|
||||
can be configured in the `[Quadlet]` section.
|
||||
@@ -2195,6 +2312,29 @@ IPRange=172.16.0.0/28
|
||||
Label=org.test.Key=value
|
||||
```
|
||||
|
||||
Example `test.artifact` to only pull the artifact using one auth file:
|
||||
```
|
||||
[Artifact]
|
||||
Artifact=quay.io/example/my-artifact:latest
|
||||
AuthFile=/etc/registry/auth.json
|
||||
TLSVerify=false
|
||||
```
|
||||
|
||||
Example usage where a container depends on an artifact:
|
||||
|
||||
`my-artifact.artifact`:
|
||||
```
|
||||
[Artifact]
|
||||
Artifact=quay.io/example/my-config:latest
|
||||
```
|
||||
|
||||
`my-app.container`:
|
||||
```
|
||||
[Container]
|
||||
Image=quay.io/example/my-app:latest
|
||||
Mount=type=artifact,source=my-artifact.artifact,destination=/etc/config
|
||||
```
|
||||
|
||||
Example for Container in a Pod:
|
||||
|
||||
`test.pod`
|
||||
|
||||
@@ -27,6 +27,7 @@ const (
|
||||
UnitDirDistro = "/usr/share/containers/systemd"
|
||||
|
||||
// Names of commonly used systemd/quadlet group names
|
||||
ArtifactGroup = "Artifact"
|
||||
ContainerGroup = "Container"
|
||||
InstallGroup = "Install"
|
||||
KubeGroup = "Kube"
|
||||
@@ -38,6 +39,7 @@ const (
|
||||
ImageGroup = "Image"
|
||||
BuildGroup = "Build"
|
||||
QuadletGroup = "Quadlet"
|
||||
XArtifactGroup = "X-Artifact"
|
||||
XContainerGroup = "X-Container"
|
||||
XKubeGroup = "X-Kube"
|
||||
XNetworkGroup = "X-Network"
|
||||
@@ -61,6 +63,7 @@ const (
|
||||
KeyAllTags = "AllTags"
|
||||
KeyAnnotation = "Annotation"
|
||||
KeyArch = "Arch"
|
||||
KeyArtifact = "Artifact"
|
||||
KeyAuthFile = "AuthFile"
|
||||
KeyAutoUpdate = "AutoUpdate"
|
||||
KeyCertDir = "CertDir"
|
||||
@@ -141,6 +144,7 @@ const (
|
||||
KeyPolicy = "Policy"
|
||||
KeyPublishPort = "PublishPort"
|
||||
KeyPull = "Pull"
|
||||
KeyQuiet = "Quiet"
|
||||
KeyReadOnly = "ReadOnly"
|
||||
KeyReadOnlyTmpfs = "ReadOnlyTmpfs"
|
||||
KeyReloadCmd = "ReloadCmd"
|
||||
@@ -458,6 +462,25 @@ var (
|
||||
KeyVolume: true,
|
||||
},
|
||||
},
|
||||
ArtifactGroup: {
|
||||
GroupName: ArtifactGroup,
|
||||
XGroupName: XArtifactGroup,
|
||||
SupportedKeys: map[string]bool{
|
||||
KeyArtifact: true,
|
||||
KeyAuthFile: true,
|
||||
KeyCertDir: true,
|
||||
KeyContainersConfModule: true,
|
||||
KeyCreds: true,
|
||||
KeyDecryptionKey: true,
|
||||
KeyGlobalArgs: true,
|
||||
KeyPodmanArgs: true,
|
||||
KeyQuiet: true,
|
||||
KeyRetry: true,
|
||||
KeyRetryDelay: true,
|
||||
KeyServiceName: true,
|
||||
KeyTLSVerify: true,
|
||||
},
|
||||
},
|
||||
PodGroup: {
|
||||
GroupName: PodGroup,
|
||||
XGroupName: XPodGroup,
|
||||
@@ -1464,6 +1487,8 @@ func GetUnitServiceName(unit *parser.UnitFile) (string, error) {
|
||||
return GetImageServiceName(unit), nil
|
||||
case strings.HasSuffix(unit.Filename, ".build"):
|
||||
return GetBuildServiceName(unit), nil
|
||||
case strings.HasSuffix(unit.Filename, ".artifact"):
|
||||
return GetArtifactServiceName(unit), nil
|
||||
case strings.HasSuffix(unit.Filename, ".pod"):
|
||||
return GetPodServiceName(unit), nil
|
||||
default:
|
||||
@@ -1495,6 +1520,10 @@ func GetBuildServiceName(podUnit *parser.UnitFile) string {
|
||||
return getServiceName(podUnit, BuildGroup, "-build")
|
||||
}
|
||||
|
||||
func GetArtifactServiceName(podUnit *parser.UnitFile) string {
|
||||
return getServiceName(podUnit, ArtifactGroup, "-artifact")
|
||||
}
|
||||
|
||||
func GetPodServiceName(podUnit *parser.UnitFile) string {
|
||||
return getServiceName(podUnit, PodGroup, "-pod")
|
||||
}
|
||||
@@ -1871,7 +1900,7 @@ func handleStorageSource(quadletUnitFile, serviceUnitFile *parser.UnitFile, sour
|
||||
if source[0] == '/' {
|
||||
// Absolute path
|
||||
serviceUnitFile.Add(UnitGroup, "RequiresMountsFor", source)
|
||||
} else if strings.HasSuffix(source, ".volume") || (checkImage && strings.HasSuffix(source, ".image")) {
|
||||
} else if strings.HasSuffix(source, ".volume") || (checkImage && strings.HasSuffix(source, ".image")) || strings.HasSuffix(source, ".artifact") {
|
||||
sourceUnitInfo, ok := unitsInfoMap[source]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("requested Quadlet source %s was not found", source)
|
||||
@@ -2040,10 +2069,11 @@ func resolveContainerMountParams(containerUnitFile, serviceUnitFile *parser.Unit
|
||||
|
||||
// Source resolution is required only for these types of mounts
|
||||
sourceResultionRequired := map[string]struct{}{
|
||||
"volume": {},
|
||||
"bind": {},
|
||||
"glob": {},
|
||||
"image": {},
|
||||
"volume": {},
|
||||
"bind": {},
|
||||
"glob": {},
|
||||
"image": {},
|
||||
"artifact": {},
|
||||
}
|
||||
if _, ok := sourceResultionRequired[mountType]; !ok {
|
||||
return mount, nil
|
||||
@@ -2297,3 +2327,47 @@ func initServiceUnitFile(quadletUnitFile *parser.UnitFile, isUser bool, unitsInf
|
||||
|
||||
return service, unitInfo, nil
|
||||
}
|
||||
|
||||
func ConvertArtifact(artifact *parser.UnitFile, unitsInfoMap map[string]*UnitInfo, isUser bool) (*parser.UnitFile, error) {
|
||||
service, unitInfo, err := initServiceUnitFile(artifact, isUser, unitsInfoMap, ArtifactGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
artifactName, ok := artifact.Lookup(ArtifactGroup, KeyArtifact)
|
||||
if !ok || len(artifactName) == 0 {
|
||||
return nil, fmt.Errorf("no Artifact key specified")
|
||||
}
|
||||
|
||||
podman := createBasePodmanCommand(artifact, ArtifactGroup)
|
||||
|
||||
podman.add("artifact", "pull")
|
||||
|
||||
stringKeys := map[string]string{
|
||||
KeyAuthFile: "--authfile",
|
||||
KeyCertDir: "--cert-dir",
|
||||
KeyCreds: "--creds",
|
||||
KeyDecryptionKey: "--decryption-key",
|
||||
KeyRetry: "--retry",
|
||||
KeyRetryDelay: "--retry-delay",
|
||||
}
|
||||
lookupAndAddString(artifact, ArtifactGroup, stringKeys, podman)
|
||||
|
||||
boolKeys := map[string]string{
|
||||
KeyQuiet: "--quiet",
|
||||
KeyTLSVerify: "--tls-verify",
|
||||
}
|
||||
lookupAndAddBoolean(artifact, ArtifactGroup, boolKeys, podman)
|
||||
|
||||
handlePodmanArgs(artifact, ArtifactGroup, podman)
|
||||
|
||||
podman.add(artifactName)
|
||||
|
||||
service.AddCmdline(ServiceGroup, "ExecStart", podman.Args)
|
||||
|
||||
defaultOneshotServiceGroup(service, true)
|
||||
|
||||
unitInfo.ResourceName = artifactName
|
||||
|
||||
return service, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ var (
|
||||
// Key: Extension
|
||||
// Value: Processing order for resource naming dependencies
|
||||
SupportedExtensions = map[string]int{
|
||||
".artifact": 1,
|
||||
".container": 4,
|
||||
".volume": 2,
|
||||
".kube": 4,
|
||||
|
||||
24
test/e2e/quadlet/artifact-mount.container
Normal file
24
test/e2e/quadlet/artifact-mount.container
Normal file
@@ -0,0 +1,24 @@
|
||||
## assert-podman-final-args localhost/imagename
|
||||
## assert-podman-args "--name" "systemd-%N"
|
||||
## assert-podman-args "--mount"
|
||||
## assert-podman-args "type=artifact,source=quay.io/libpod/testartifact:20250206-single,destination=/artifacts"
|
||||
## assert-podman-args "--rm"
|
||||
## assert-podman-args "--replace"
|
||||
## assert-podman-args "-d"
|
||||
## assert-podman-args "--cgroups=split"
|
||||
## assert-podman-args "--sdnotify=conmon"
|
||||
## assert-key-is "Unit" "RequiresMountsFor" "%t/containers"
|
||||
## assert-key-is "Service" "KillMode" "mixed"
|
||||
## assert-key-is "Service" "Delegate" "yes"
|
||||
## assert-key-is "Service" "Type" "notify"
|
||||
## assert-key-is "Service" "NotifyAccess" "all"
|
||||
## assert-key-is "Service" "SyslogIdentifier" "%N"
|
||||
## assert-key-is-regex "Service" "ExecStopPost" "-[/S].*/podman rm -v -f -i systemd-%N"
|
||||
## assert-key-is-regex "Service" "ExecStop" ".*/podman rm -v -f -i systemd-%N"
|
||||
## assert-key-is "Service" "Environment" "PODMAN_SYSTEMD_UNIT=%n"
|
||||
## assert-key-is-regex "Unit" "After" "network-online.target|podman-user-wait-network-online.service"
|
||||
## assert-key-is-regex "Unit" "Wants" "network-online.target|podman-user-wait-network-online.service"
|
||||
|
||||
[Container]
|
||||
Image=localhost/imagename
|
||||
Mount=type=artifact,source=quay.io/libpod/testartifact:20250206-single,destination=/artifacts
|
||||
10
test/e2e/quadlet/basic.artifact
Normal file
10
test/e2e/quadlet/basic.artifact
Normal file
@@ -0,0 +1,10 @@
|
||||
## assert-podman-final-args quay.io/libpod/testartifact:20250206-single
|
||||
## assert-podman-args "artifact"
|
||||
## assert-podman-args "pull"
|
||||
## assert-key-is "Service" "Type" "oneshot"
|
||||
## assert-key-is "Service" "RemainAfterExit" "yes"
|
||||
## assert-key-is-regex "Unit" "After" "network-online.target|podman-user-wait-network-online.service"
|
||||
## assert-key-is-regex "Unit" "Wants" "network-online.target|podman-user-wait-network-online.service"
|
||||
|
||||
[Artifact]
|
||||
Artifact=quay.io/libpod/testartifact:20250206-single
|
||||
@@ -9,8 +9,6 @@ Mount=type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared,U=true
|
||||
## assert-podman-args-key-val "--mount" "," "type=volume,source=vol1,destination=/path/in/container,ro=true"
|
||||
Mount=type=volume,source=vol1,destination=/path/in/container,ro=true
|
||||
## assert-podman-args-key-val "--mount" "," "type=volume,source=systemd-basic,destination=/path/in/container,ro=true"
|
||||
## assert-key-is "Unit" "Requires" "basic-volume.service" "basic-image.service"
|
||||
## assert-key-is-regex "Unit" "After" "network-online.target|podman-user-wait-network-online.service" "basic-volume.service" "basic-image.service"
|
||||
Mount=type=volume,source=basic.volume,destination=/path/in/container,ro=true
|
||||
## assert-podman-args-key-val "--mount" "," "type=tmpfs,tmpfs-size=512M,destination=/path/in/container"
|
||||
Mount=type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
||||
@@ -18,6 +16,8 @@ Mount=type=tmpfs,tmpfs-size=512M,destination=/path/in/container
|
||||
Mount=type=image,source=fedora,destination=/fedora-image,rw=true
|
||||
## assert-podman-args-key-val "--mount" "," "type=image,source=localhost/imagename,destination=/fedora-image,rw=true"
|
||||
Mount=type=image,source=basic.image,destination=/fedora-image,rw=true
|
||||
## assert-podman-args-key-val "--mount" "," "type=artifact,source=quay.io/libpod/testartifact:20250206-single,destination=/artifacts"
|
||||
Mount=type=artifact,source=basic.artifact,destination=/artifacts
|
||||
## assert-podman-args-key-val "--mount" "," "type=devpts,destination=/dev/pts"
|
||||
Mount=type=devpts,destination=/dev/pts
|
||||
## assert-podman-args-key-val-regex "--mount" "," "type=bind,source=.*/podman-e2e-.*/subtest-.*/quadlet/path/on/host,destination=/path/in/container"
|
||||
@@ -28,3 +28,5 @@ Mount=type=volume,source=vol1,destination=/path/in/container,ro
|
||||
Mount=type=bind,src=/tmp,\"dst=/path,1\"
|
||||
## assert-podman-args-key-val-regex "--mount" "," "type=bind,source=.*/podman-e2e-.*/subtest-.*/quadlet/src,destination=/dst/,idmap=uids=12-34-1;gids=56-78-1"
|
||||
Mount=type=bind,source=./src/,destination=/dst/,idmap=uids=12-34-1;gids=56-78-1
|
||||
## assert-key-is "Unit" "Requires" "basic-volume.service" "basic-image.service" "basic-artifact.service"
|
||||
## assert-key-is-regex "Unit" "After" "network-online.target|podman-user-wait-network-online.service" "basic-volume.service" "basic-image.service" "basic-artifact.service"
|
||||
|
||||
23
test/e2e/quadlet/options.artifact
Normal file
23
test/e2e/quadlet/options.artifact
Normal file
@@ -0,0 +1,23 @@
|
||||
## assert-podman-final-args quay.io/libpod/testartifact:20250206-single
|
||||
## assert-podman-args "artifact"
|
||||
## assert-podman-args "pull"
|
||||
## assert-podman-args "--authfile"
|
||||
## assert-podman-args "/tmp/auth.json"
|
||||
## assert-podman-args "--cert-dir"
|
||||
## assert-podman-args "/tmp/certs"
|
||||
## assert-podman-args "--quiet"
|
||||
## assert-podman-args "--retry"
|
||||
## assert-podman-args "3"
|
||||
## assert-podman-args "--tls-verify=false"
|
||||
## assert-key-is "Service" "Type" "oneshot"
|
||||
## assert-key-is "Service" "RemainAfterExit" "yes"
|
||||
## assert-key-is-regex "Unit" "After" "network-online.target|podman-user-wait-network-online.service"
|
||||
## assert-key-is-regex "Unit" "Wants" "network-online.target|podman-user-wait-network-online.service"
|
||||
|
||||
[Artifact]
|
||||
Artifact=quay.io/libpod/testartifact:20250206-single
|
||||
AuthFile=/tmp/auth.json
|
||||
CertDir=/tmp/certs
|
||||
Quiet=true
|
||||
Retry=3
|
||||
TLSVerify=false
|
||||
@@ -51,6 +51,8 @@ func calcServiceName(path string) string {
|
||||
service += "-image"
|
||||
case ".build":
|
||||
service += "-build"
|
||||
case ".artifact":
|
||||
service += "-artifact"
|
||||
case ".pod":
|
||||
service += "-pod"
|
||||
}
|
||||
@@ -1076,6 +1078,9 @@ BOGUS=foo
|
||||
Entry("Build - Retry", "retry.build"),
|
||||
Entry("Build - No WorkingDirectory with systemd specifier", "no-workingdirectory-systemd-specifier.build"),
|
||||
|
||||
Entry("Artifact - Basic", "basic.artifact"),
|
||||
Entry("Artifact - Options", "options.artifact"),
|
||||
|
||||
Entry("Pod - Basic", "basic.pod"),
|
||||
Entry("Pod - DNS", "dns.pod"),
|
||||
Entry("Pod - DNS Option", "dns-option.pod"),
|
||||
@@ -1171,13 +1176,14 @@ BOGUS=foo
|
||||
|
||||
runSuccessQuadletTestCase(fileName)
|
||||
},
|
||||
Entry("Container - Mount", "mount.container", []string{"basic.image", "basic.volume"}),
|
||||
Entry("Container - Mount", "mount.container", []string{"basic.image", "basic.volume", "basic.artifact"}),
|
||||
Entry("Container - Quadlet Network", "network.quadlet.container", []string{"basic.network"}),
|
||||
Entry("Container - Quadlet Volume", "volume.container", []string{"basic.volume"}),
|
||||
Entry("Container - Mount overriding service name", "mount.servicename.container", []string{"service-name.volume"}),
|
||||
Entry("Container - Quadlet Network overriding service name", "network.quadlet.servicename.container", []string{"service-name.network"}),
|
||||
Entry("Container - Quadlet Volume overriding service name", "volume.servicename.container", []string{"service-name.volume"}),
|
||||
Entry("Container - Quadlet build with multiple tags", "build.multiple-tags.container", []string{"multiple-tags.build"}),
|
||||
Entry("Container - Artifact Mount", "artifact-mount.container", []string{"basic.artifact"}),
|
||||
Entry("Container - Reuse another container's network", "network.reuse.container", []string{"basic.container"}),
|
||||
Entry("Container - Reuse another named container's network", "network.reuse.name.container", []string{"name.container"}),
|
||||
Entry("Container - Reuse another container's network", "a.network.reuse.container", []string{"basic.container"}),
|
||||
@@ -1186,6 +1192,7 @@ BOGUS=foo
|
||||
"Container - Dependency between quadlet units",
|
||||
"dependent.container",
|
||||
[]string{
|
||||
"basic.artifact",
|
||||
"basic.build",
|
||||
"basic.container",
|
||||
"basic.image",
|
||||
@@ -1239,6 +1246,7 @@ BOGUS=foo
|
||||
"Build - Dependency between quadlet units",
|
||||
"dependent.build",
|
||||
[]string{
|
||||
"basic.artifact",
|
||||
"basic.build",
|
||||
"basic.container",
|
||||
"basic.image",
|
||||
@@ -1258,6 +1266,7 @@ BOGUS=foo
|
||||
"Pod - Dependency between quadlet units",
|
||||
"dependent.pod",
|
||||
[]string{
|
||||
"basic.artifact",
|
||||
"basic.build",
|
||||
"basic.container",
|
||||
"basic.image",
|
||||
@@ -1272,6 +1281,7 @@ BOGUS=foo
|
||||
"Image - Dependency between quadlet units",
|
||||
"dependent.image",
|
||||
[]string{
|
||||
"basic.artifact",
|
||||
"basic.build",
|
||||
"basic.container",
|
||||
"basic.image",
|
||||
@@ -1286,6 +1296,7 @@ BOGUS=foo
|
||||
"Network - Dependency between quadlet units",
|
||||
"dependent.network",
|
||||
[]string{
|
||||
"basic.artifact",
|
||||
"basic.build",
|
||||
"basic.container",
|
||||
"basic.image",
|
||||
|
||||
@@ -1856,6 +1856,71 @@ EOF
|
||||
done < <(parse_table "${dropin_files}")
|
||||
}
|
||||
|
||||
@test "quadlet - artifact" {
|
||||
local quadlet_tmpdir=$PODMAN_TMPDIR/quadlets
|
||||
|
||||
local registry=localhost:${PODMAN_LOGIN_REGISTRY_PORT}
|
||||
local artifact_for_test=$registry/test-artifact:$(random_string)
|
||||
local authfile=$PODMAN_TMPDIR/authfile.json
|
||||
|
||||
# Create a test artifact file
|
||||
local test_artifact_dir=$PODMAN_TMPDIR/test-artifact
|
||||
mkdir -p $test_artifact_dir
|
||||
echo "test artifact content $(random_string)" > $test_artifact_dir/test-file.txt
|
||||
|
||||
# In order to test artifact pull but without possible Network issues,
|
||||
# this test uses an additional registry.
|
||||
# Start the registry and populate the authfile that we can use for the test.
|
||||
start_registry
|
||||
run_podman login --authfile=$authfile \
|
||||
--tls-verify=false \
|
||||
--username ${PODMAN_LOGIN_USER} \
|
||||
--password ${PODMAN_LOGIN_PASS} \
|
||||
$registry
|
||||
|
||||
# Create and push a test artifact to the registry
|
||||
run_podman artifact add $artifact_for_test $test_artifact_dir/test-file.txt
|
||||
run_podman artifact push --tls-verify=false --authfile=$authfile $artifact_for_test
|
||||
|
||||
# Remove the local artifact to make sure it will be pulled again
|
||||
run_podman artifact rm $artifact_for_test
|
||||
|
||||
# Create artifact quadlet file
|
||||
local artifact_file=$PODMAN_TMPDIR/test-artifact.artifact
|
||||
cat >$artifact_file << EOF
|
||||
[Artifact]
|
||||
Artifact=$artifact_for_test
|
||||
AuthFile=$authfile
|
||||
TLSVerify=false
|
||||
EOF
|
||||
|
||||
run_quadlet "$artifact_file"
|
||||
service_setup $QUADLET_SERVICE_NAME
|
||||
|
||||
# Wait for the service to complete (it's a oneshot service)
|
||||
local timeout=30
|
||||
local count=0
|
||||
while [ $count -lt $timeout ]; do
|
||||
run systemctl show --value --property=ActiveState "$QUADLET_SERVICE_NAME"
|
||||
if [ "$output" = "active" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
count=$((count + 1))
|
||||
done
|
||||
|
||||
# Verify artifact was pulled
|
||||
run_podman artifact ls
|
||||
assert $status -eq 0 "Failed to list artifacts"
|
||||
# Extract the repository and tag parts separately since artifact ls shows them in columns
|
||||
local artifact_repo=$(echo "$artifact_for_test" | cut -d: -f1)
|
||||
local artifact_tag=$(echo "$artifact_for_test" | cut -d: -f2)
|
||||
assert "$output" =~ "$artifact_repo.*$artifact_tag" "Artifact should exist after quadlet service runs"
|
||||
|
||||
# Clean up
|
||||
run_podman artifact rm $artifact_for_test
|
||||
}
|
||||
|
||||
# Following issue: https://github.com/containers/podman/issues/24599
|
||||
# Make sure future changes do not break
|
||||
@test "quadlet - build with pull" {
|
||||
|
||||
@@ -126,6 +126,8 @@ quadlet_to_service_name() {
|
||||
suffix="-pod"
|
||||
elif [ "$extension" == "build" ]; then
|
||||
suffix="-build"
|
||||
elif [ "$extension" == "artifact" ]; then
|
||||
suffix="-artifact"
|
||||
fi
|
||||
|
||||
echo "$filename$suffix.service"
|
||||
|
||||
Reference in New Issue
Block a user