mirror of
https://github.com/containers/podman.git
synced 2025-08-06 11:32:07 +08:00
Quadlet: Add support for .build files
.build files allow to build an image via Quadlet. The keys from a .build file are translated to arguments of a `podman build` command by Quadlet. Minimal keys for .build files are `ImageTag=` and a context directory, see `SetWorkingDirectory=`, or a `File=` pointing to a Containerfile. After sorting .build files into the Quadlet dependency order, there remains a possible dependency cycle issue between .volume and .build files: A .volume can have `Image=some.build`, and a .build can have `Volume=some.volume:/some/volume`. We solve this dependency cycle by prefilling resourceNames with all image names from .build files before converting all the unit files. This results in an issue for the test suite though: For .volume's depending on *.image or *.build, we need to copy these additional dependencies to the test's quadletDir, otherwise the test will fail. This is necessary, because `handleImageSource()` actually needs to know the image name defined in the referenced *.{build,image} file. It cannot fall back on the default names, as it is done for networks or volumes, for example. Signed-off-by: Johannes Maibaum <jmaibaum@gmail.com>
This commit is contained in:
@ -49,12 +49,13 @@ var (
|
||||
// Key: Extension
|
||||
// Value: Processing order for resource naming dependencies
|
||||
supportedExtensions = map[string]int{
|
||||
".container": 3,
|
||||
".container": 4,
|
||||
".volume": 2,
|
||||
".kube": 3,
|
||||
".kube": 4,
|
||||
".network": 2,
|
||||
".image": 1,
|
||||
".pod": 4,
|
||||
".build": 3,
|
||||
".pod": 5,
|
||||
}
|
||||
)
|
||||
|
||||
@ -474,7 +475,7 @@ func warnIfAmbiguousName(unit *parser.UnitFile, group string) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if strings.HasSuffix(imageName, ".image") {
|
||||
if strings.HasSuffix(imageName, ".build") || strings.HasSuffix(imageName, ".image") {
|
||||
return
|
||||
}
|
||||
if !isUnambiguousName(imageName) {
|
||||
@ -499,6 +500,19 @@ func generatePodsInfoMap(units []*parser.UnitFile) map[string]*quadlet.PodInfo {
|
||||
return podsInfoMap
|
||||
}
|
||||
|
||||
func prefillBuiltImageNames(units []*parser.UnitFile, resourceNames map[string]string) {
|
||||
for _, unit := range units {
|
||||
if !strings.HasSuffix(unit.Filename, ".build") {
|
||||
continue
|
||||
}
|
||||
|
||||
imageName := quadlet.GetBuiltImageName(unit)
|
||||
if len(imageName) > 0 {
|
||||
resourceNames[unit.Filename] = imageName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := process(); err != nil {
|
||||
Logf("%s", err.Error())
|
||||
@ -600,6 +614,12 @@ func process() error {
|
||||
// A map of network/volume unit file-names, against their calculated names, as needed by Podman.
|
||||
var resourceNames = make(map[string]string)
|
||||
|
||||
// Prefill resouceNames for .build files. This is significantly less complex than
|
||||
// pre-computing all resourceNames for all Quadlet types (which is rather complex for a few
|
||||
// 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)
|
||||
prefillBuiltImageNames(units, resourceNames)
|
||||
|
||||
for _, unit := range units {
|
||||
var service *parser.UnitFile
|
||||
var name string
|
||||
@ -619,6 +639,8 @@ func process() error {
|
||||
case strings.HasSuffix(unit.Filename, ".image"):
|
||||
warnIfAmbiguousName(unit, quadlet.ImageGroup)
|
||||
service, name, err = quadlet.ConvertImage(unit)
|
||||
case strings.HasSuffix(unit.Filename, ".build"):
|
||||
service, name, err = quadlet.ConvertBuild(unit, resourceNames)
|
||||
case strings.HasSuffix(unit.Filename, ".pod"):
|
||||
service, err = quadlet.ConvertPod(unit, unit.Filename, podsInfoMap, resourceNames)
|
||||
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*.pod
|
||||
*name*.container, *name*.volume, *name*.network, *name*.kube *name*.image, *name*.build *name*.pod
|
||||
|
||||
### Podman rootful unit search path
|
||||
|
||||
@ -30,7 +30,7 @@ Symbolic links below the search paths are not supported.
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Podman supports starting containers (and creating volumes) via systemd by using a
|
||||
Podman supports building, and starting containers (and creating volumes) via systemd by using a
|
||||
[systemd generator](https://www.freedesktop.org/software/systemd/man/systemd.generator.html).
|
||||
These files are read during boot (and when `systemctl daemon-reload` is run) and generate
|
||||
corresponding regular systemd service unit files. Both system and user systemd units are supported.
|
||||
@ -39,7 +39,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`, `.pod` and `.kube`, and for each file generates a similarly named `.service` file. Be aware that
|
||||
`.volume`, `.network`, `.build`, `.pod` and `.kube`, 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.
|
||||
@ -65,7 +65,7 @@ session gets started. For unit files placed in subdirectories within
|
||||
/etc/containers/systemd/user/${UID}/ and the other user unit search paths,
|
||||
Quadlet will recursively search and run the unit files present in these subdirectories.
|
||||
|
||||
Note: When a Quadlet is starting, Podman often pulls one more container images which may take a considerable amount of time.
|
||||
Note: When a Quadlet is starting, Podman often pulls or builds one more container images which may take a considerable amount of time.
|
||||
Systemd defaults service start time to 90 seconds, or fails the service. Pre-pulling the image or extending
|
||||
the systemd timeout time for the service using the *TimeoutStartSec* Service option can fix the problem.
|
||||
|
||||
@ -82,7 +82,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` and `.image` files.
|
||||
`forking` for `.pod` files, and `oneshot` for `.volume`, `.network`, `.build`, and `.image` files.
|
||||
|
||||
However, `Type` may be explicitly set to `oneshot` for `.container` and `.kube` files when no containers are expected
|
||||
to run once `podman` exits.
|
||||
@ -1324,6 +1324,251 @@ The (optional) name of the Podman volume. If this is not specified, the default
|
||||
`systemd-%N` is used, which is the same as the unit name but with a `systemd-` prefix to avoid
|
||||
conflicts with user-managed volumes.
|
||||
|
||||
## Build units [Build]
|
||||
|
||||
Build files are named with a `.build` extension and contain a section `[Build]` describing the image
|
||||
build command. The generated service is a one-time command that ensures that the image is built on
|
||||
the host from a supplied Containerfile and context directory. Subsequent (re-)starts of the
|
||||
generated built service will usually finish quickly, as image layer caching will skip unchanged
|
||||
build steps.
|
||||
|
||||
A minimal `.build` unit needs at least the `ImageTag=` key, and either of `File=` or
|
||||
`SetWorkingDirectory=` keys.
|
||||
|
||||
Using build units allows containers and volumes to depend on images being built locally. This can be
|
||||
interesting for creating container images not available on container registries, or for local
|
||||
testing and development.
|
||||
|
||||
Valid options for `[Build]` are listed below:
|
||||
|
||||
| **[Build] options** | **podman build equivalent** |
|
||||
|-------------------------------------|---------------------------------------------|
|
||||
| Annotation=annotation=value | --annotation=annotation=value |
|
||||
| Arch=aarch64 | --arch=aarch64 |
|
||||
| AuthFile=/etc/registry/auth\.json | --authfile=/etc/registry/auth\.json |
|
||||
| ContainersConfModule=/etc/nvd\.conf | --module=/etc/nvd\.conf |
|
||||
| DNS=192.168.55.1 | --dns=192.168.55.1 |
|
||||
| DNSOption=ndots:1 | --dns-option=ndots:1 |
|
||||
| DNSSearch=foo.com | --dns-search=foo.com |
|
||||
| Environment=foo=bar | --env foo=bar |
|
||||
| File=/path/to/Containerfile | --file=/path/to/Containerfile |
|
||||
| ForceRM=false | --force-rm=false |
|
||||
| GlobalArgs=--log-level=debug | --log-level=debug |
|
||||
| GroupAdd=keep-groups | --group-add=keep-groups |
|
||||
| ImageTag=localhost/imagename | --tag=localhost/imagename |
|
||||
| Label=label | --label=label |
|
||||
| Network=host | --network=host |
|
||||
| PodmanArgs=--add-host foobar | --add-host foobar |
|
||||
| Pull=never | --pull=never |
|
||||
| Secret=secret | --secret=id=mysecret,src=path |
|
||||
| SetWorkingDirectory=unit | Set `WorkingDirectory` of systemd unit file |
|
||||
| Target=my-app | --target=my-app |
|
||||
| TLSVerify=false | --tls-verify=false |
|
||||
| Variant=arm/v7 | --variant=arm/v7 |
|
||||
| Volume=/source:/dest | --volume /source:/dest |
|
||||
|
||||
### `Annotation=`
|
||||
|
||||
Add an image *annotation* (e.g. annotation=*value*) to the image metadata. Can be used multiple
|
||||
times.
|
||||
|
||||
This is equivalant to the `--annotation` option of `podman build`.
|
||||
|
||||
### `Arch=`
|
||||
|
||||
Override the architecture, defaults to hosts', of the image to be built.
|
||||
|
||||
This is equivalent to the `--arch` option of `podman build`.
|
||||
|
||||
### `AuthFile=`
|
||||
|
||||
Path of the authentication file.
|
||||
|
||||
This is equivalent to the `--authfile` option of `podman build`.
|
||||
|
||||
### `ContainersConfModule=`
|
||||
|
||||
Load the specified containers.conf(5) module. Equivalent to the Podman `--module` option.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
### `DNS=`
|
||||
|
||||
Set network-scoped DNS resolver/nameserver for the build container.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
This is equivalent to the `--dns` option of `podman build`.
|
||||
|
||||
### `DNSOption=`
|
||||
|
||||
Set custom DNS options.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
This is equivalent to the `--dns-option` option of `podman build`.
|
||||
|
||||
### `DNSSearch=`
|
||||
|
||||
Set custom DNS search domains. Use **DNSSearch=.** to remove the search domain.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
This is equivalent to the `--dns-search` option of `podman build`.
|
||||
|
||||
### `Environment=`
|
||||
|
||||
Add a value (e.g. env=*value*) to the built image. This uses the same format as [services in
|
||||
systemd](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Environment=) and can be
|
||||
listed multiple times.
|
||||
|
||||
### `File=`
|
||||
|
||||
Specifies a Containerfile which contains instructions for building the image. A URL starting with
|
||||
`http(s)://` allows you to specify a remote Containerfile to be downloaded. Note that for a given
|
||||
relative path to a Containerfile, or when using a `http(s)://` URL, you also must set
|
||||
`SetWorkingDirectory=` in order for `podman build` to find a valid context directory for the
|
||||
resources specified in the Containerfile.
|
||||
|
||||
Note that setting a `File=` field is mandatory for a `.build` file, unless `SetWorkingDirectory` (or
|
||||
a `WorkingDirectory` in the `Service` group) has also been set.
|
||||
|
||||
This is equivalent to the `--file` option of `podman build`.
|
||||
|
||||
### `ForceRM=`
|
||||
|
||||
Always remove intermediate containers after a build, even if the build fails (default true).
|
||||
|
||||
This is equivalent to the `--force-rm` option of `podman build`.
|
||||
|
||||
### `GlobalArgs=`
|
||||
|
||||
This key contains a list of arguments passed directly between `podman` and `build` 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.
|
||||
|
||||
### `GroupAdd=`
|
||||
|
||||
Assign additional groups to the primary user running within the container process. Also supports the
|
||||
`keep-groups` special flag.
|
||||
|
||||
This is equivalent to the `--group-add` option of `podman build`.
|
||||
|
||||
### `ImageTag=`
|
||||
|
||||
Specifies the name which is assigned to the resulting image if the build process completes
|
||||
successfully.
|
||||
|
||||
This is equivalent to the `--tag` option of `podman build`.
|
||||
|
||||
### `Label=`
|
||||
|
||||
Add an image *label* (e.g. label=*value*) to the image metadata. Can be used multiple times.
|
||||
|
||||
This is equivalent to the `--label` option of `podman build`.
|
||||
|
||||
### `Network=`
|
||||
|
||||
Sets the configuration for network namespaces when handling RUN instructions. This has the same
|
||||
format as the `--network` option to `podman build`. For example, use `host` to use the host network,
|
||||
or `none` to not set up networking.
|
||||
|
||||
As a special case, if the `name` of the network ends with `.network`, Quadlet will look for the
|
||||
corresponding `.network` Quadlet unit. If found, Quadlet will use the name of the Network set in the
|
||||
Unit, otherwise, `systemd-$name` is used. The generated systemd service contains a dependency on the
|
||||
service unit generated for that `.network` unit, or on `$name-network.service` if the `.network`
|
||||
unit is not found.
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
### `PodmanArgs=`
|
||||
|
||||
This key contains a list of arguments passed directly to the end of the `podman build` command
|
||||
in the generated file (right before the image 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.
|
||||
|
||||
### `Pull=`
|
||||
|
||||
Set the image pull policy.
|
||||
|
||||
This is equivalent to the `--pull` option of `podman build`.
|
||||
|
||||
### `Secret=`
|
||||
|
||||
Pass secret information used in Containerfile build stages in a safe way.
|
||||
|
||||
This is equivalent to the `--secret` option of `podman build` and generally has the form
|
||||
`secret[,opt=opt ...]`.
|
||||
|
||||
### `SetWorkingDirectory=`
|
||||
|
||||
Provide context (a working directory) to `podman build`. Supported values are a path, a URL, or the
|
||||
special keys `file` or `unit` to set the context directory to the parent directory of the file from
|
||||
the `File=` key or to that of the Quadlet `.build` unit file, respectively. This allows Quadlet to
|
||||
resolve relative paths.
|
||||
|
||||
When using one of the special keys (`file` or `unit`), the `WorkingDirectory` field of the `Service`
|
||||
group of the Systemd service unit will also be set to accordingly. Alternatively, users can
|
||||
explicitly set the `WorkingDirectory` field of the `Service` group in the `.build` file. Please note
|
||||
that if the `WorkingDirectory` field of the `Service` group is set by the user, Quadlet will not
|
||||
overwrite it even if `SetWorkingDirectory` is set to `file` or `unit`.
|
||||
|
||||
By providing a URL to `SetWorkingDirectory=` you can instruct `podman build` to clone a Git
|
||||
repository or download an archive file extracted to a temporary location by `podman build` as build
|
||||
context. Note that in this case, the `WorkingDirectory` of the Systemd service unit is left
|
||||
untouched by Quadlet.
|
||||
|
||||
Note that providing context directory is mandatory for a `.build` file, unless a `File=` key has
|
||||
also been provided.
|
||||
|
||||
### `Target=`
|
||||
|
||||
Set the target build stage to build. Commands in the Containerfile after the target stage are
|
||||
skipped.
|
||||
|
||||
This is equivalent to the `--target` option of `podman build`.
|
||||
|
||||
### `TLSVerify=`
|
||||
|
||||
Require HTTPS and verification of certificates when contacting registries.
|
||||
|
||||
This is equivalent to the `--tls-verify` option of `podman build`.
|
||||
|
||||
### `Variant=`
|
||||
|
||||
Override the default architecture variant of the container image to be built.
|
||||
|
||||
This is equivalent to the `--variant` option of `podman build`.
|
||||
|
||||
### `Volume=`
|
||||
|
||||
Mount a volume to containers when executing RUN instructions during the build. This is equivalent to
|
||||
the `--volume` option of `podman build`, and generally has the form
|
||||
`[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]`.
|
||||
|
||||
If `SOURCE-VOLUME` starts with `.`, Quadlet resolves the path relative to the location of the unit file.
|
||||
|
||||
As a special case, if `SOURCE-VOLUME` ends with `.volume`, Quadlet will look for the corresponding
|
||||
`.volume` Quadlet unit. If found, Quadlet will use the name of the Volume set in the Unit,
|
||||
otherwise, `systemd-$name` is used. The generated systemd service contains a dependency on the
|
||||
service unit generated for that `.volume` unit, or on `$name-volume.service` if the `.volume` unit
|
||||
is not found
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
## Image units [Image]
|
||||
|
||||
Image files are named with a `.image` extension and contain a section `[Image]` describing the
|
||||
@ -1510,6 +1755,26 @@ Yaml=/opt/k8s/deployment.yml
|
||||
WantedBy=multi-user.target default.target
|
||||
```
|
||||
|
||||
Example for locally built image to be used in a container:
|
||||
|
||||
`test.build`
|
||||
```
|
||||
[Build]
|
||||
# Tag the image to be built
|
||||
ImageTag=localhost/imagename
|
||||
|
||||
# Set the working directory to the path of the unit file,
|
||||
# expecting to find a Containerfile/Dockerfile
|
||||
# + other files needed to build the image
|
||||
SetWorkingDirectory=unit
|
||||
```
|
||||
|
||||
`test.container`
|
||||
```
|
||||
[Container]
|
||||
Image=test.build
|
||||
```
|
||||
|
||||
Example `test.volume`:
|
||||
|
||||
```
|
||||
|
@ -35,12 +35,14 @@ const (
|
||||
UnitGroup = "Unit"
|
||||
VolumeGroup = "Volume"
|
||||
ImageGroup = "Image"
|
||||
BuildGroup = "Build"
|
||||
XContainerGroup = "X-Container"
|
||||
XKubeGroup = "X-Kube"
|
||||
XNetworkGroup = "X-Network"
|
||||
XPodGroup = "X-Pod"
|
||||
XVolumeGroup = "X-Volume"
|
||||
XImageGroup = "X-Image"
|
||||
XBuildGroup = "X-Build"
|
||||
)
|
||||
|
||||
// Systemd Unit file keys
|
||||
@ -78,6 +80,8 @@ const (
|
||||
KeyExec = "Exec"
|
||||
KeyExitCodePropagation = "ExitCodePropagation"
|
||||
KeyExposeHostPort = "ExposeHostPort"
|
||||
KeyFile = "File"
|
||||
KeyForceRM = "ForceRM"
|
||||
KeyGateway = "Gateway"
|
||||
KeyGIDMap = "GIDMap"
|
||||
KeyGlobalArgs = "GlobalArgs"
|
||||
@ -142,6 +146,7 @@ const (
|
||||
KeySubnet = "Subnet"
|
||||
KeySubUIDMap = "SubUIDMap"
|
||||
KeySysctl = "Sysctl"
|
||||
KeyTarget = "Target"
|
||||
KeyTimezone = "Timezone"
|
||||
KeyTLSVerify = "TLSVerify"
|
||||
KeyTmpfs = "Tmpfs"
|
||||
@ -165,6 +170,7 @@ type PodInfo struct {
|
||||
}
|
||||
|
||||
var (
|
||||
URL = regexp.Delayed(`^((https?)|(git)://)|(github\.com/).+$`)
|
||||
validPortRange = regexp.Delayed(`\d+(-\d+)?(/udp|/tcp)?$`)
|
||||
|
||||
// Supported keys in "Container" group
|
||||
@ -323,6 +329,33 @@ var (
|
||||
KeyVariant: true,
|
||||
}
|
||||
|
||||
// Supported keys in "Build" group
|
||||
supportedBuildKeys = map[string]bool{
|
||||
KeyAnnotation: true,
|
||||
KeyArch: true,
|
||||
KeyAuthFile: true,
|
||||
KeyContainersConfModule: true,
|
||||
KeyDNS: true,
|
||||
KeyDNSOption: true,
|
||||
KeyDNSSearch: true,
|
||||
KeyEnvironment: true,
|
||||
KeyFile: true,
|
||||
KeyForceRM: true,
|
||||
KeyGlobalArgs: true,
|
||||
KeyGroupAdd: true,
|
||||
KeyImageTag: true,
|
||||
KeyLabel: true,
|
||||
KeyNetwork: true,
|
||||
KeyPodmanArgs: true,
|
||||
KeyPull: true,
|
||||
KeySecret: true,
|
||||
KeySetWorkingDirectory: true,
|
||||
KeyTarget: true,
|
||||
KeyTLSVerify: true,
|
||||
KeyVariant: true,
|
||||
KeyVolume: true,
|
||||
}
|
||||
|
||||
supportedPodKeys = map[string]bool{
|
||||
KeyContainersConfModule: true,
|
||||
KeyGlobalArgs: true,
|
||||
@ -345,6 +378,10 @@ func replaceExtension(name string, extension string, extraPrefix string, extraSu
|
||||
return extraPrefix + baseName + extraSuffix + extension
|
||||
}
|
||||
|
||||
func isURL(urlCandidate string) bool {
|
||||
return URL.MatchString(urlCandidate)
|
||||
}
|
||||
|
||||
func isPortRange(port string) bool {
|
||||
return validPortRange.MatchString(port)
|
||||
}
|
||||
@ -1178,7 +1215,7 @@ func ConvertKube(kube *parser.UnitFile, names map[string]string, isUser bool) (*
|
||||
execStop.add(yamlPath)
|
||||
service.AddCmdline(ServiceGroup, "ExecStopPost", execStop.Args)
|
||||
|
||||
err = handleSetWorkingDirectory(kube, service)
|
||||
_, err = handleSetWorkingDirectory(kube, service, KubeGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1264,6 +1301,157 @@ func ConvertImage(image *parser.UnitFile) (*parser.UnitFile, string, error) {
|
||||
return service, imageName, nil
|
||||
}
|
||||
|
||||
func ConvertBuild(build *parser.UnitFile, names map[string]string) (*parser.UnitFile, string, error) {
|
||||
service := build.Dup()
|
||||
service.Filename = replaceExtension(build.Filename, ".service", "", "-build")
|
||||
|
||||
// Add a dependency on network-online.target so the image pull does not happen
|
||||
// before network is ready
|
||||
// https://github.com/containers/podman/issues/21873
|
||||
// Prepend the lines, so the user-provided values
|
||||
// override the default ones.
|
||||
service.PrependUnitLine(UnitGroup, "After", "network-online.target")
|
||||
service.PrependUnitLine(UnitGroup, "Wants", "network-online.target")
|
||||
|
||||
/* Rename old Build group to X-Build so that systemd ignores it */
|
||||
service.RenameGroup(BuildGroup, XBuildGroup)
|
||||
|
||||
// Need the containers filesystem mounted to start podman
|
||||
service.Add(UnitGroup, "RequiresMountsFor", "%t/containers")
|
||||
|
||||
if build.Path != "" {
|
||||
service.Add(UnitGroup, "SourcePath", build.Path)
|
||||
}
|
||||
|
||||
if err := checkForUnknownKeys(build, BuildGroup, supportedBuildKeys); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
podman := createBasePodmanCommand(build, BuildGroup)
|
||||
podman.add("build")
|
||||
|
||||
stringKeys := map[string]string{
|
||||
KeyArch: "--arch",
|
||||
KeyAuthFile: "--authfile",
|
||||
KeyPull: "--pull",
|
||||
KeyTarget: "--target",
|
||||
KeyVariant: "--variant",
|
||||
}
|
||||
|
||||
boolKeys := map[string]string{
|
||||
KeyTLSVerify: "--tls-verify",
|
||||
KeyForceRM: "--force-rm",
|
||||
}
|
||||
|
||||
for key, flag := range stringKeys {
|
||||
lookupAndAddString(build, BuildGroup, key, flag, podman)
|
||||
}
|
||||
|
||||
for key, flag := range boolKeys {
|
||||
lookupAndAddBoolean(build, BuildGroup, key, flag, podman)
|
||||
}
|
||||
|
||||
annotations := build.LookupAllKeyVal(BuildGroup, KeyAnnotation)
|
||||
podman.addAnnotations(annotations)
|
||||
|
||||
dns := build.LookupAll(BuildGroup, KeyDNS)
|
||||
for _, ipAddr := range dns {
|
||||
podman.addf("--dns=%s", ipAddr)
|
||||
}
|
||||
|
||||
dnsOptions := build.LookupAll(BuildGroup, KeyDNSOption)
|
||||
for _, dnsOption := range dnsOptions {
|
||||
podman.addf("--dns-option=%s", dnsOption)
|
||||
}
|
||||
|
||||
dnsSearches := build.LookupAll(BuildGroup, KeyDNSSearch)
|
||||
for _, dnsSearch := range dnsSearches {
|
||||
podman.addf("--dns-search=%s", dnsSearch)
|
||||
}
|
||||
|
||||
podmanEnv := build.LookupAllKeyVal(BuildGroup, KeyEnvironment)
|
||||
podman.addEnv(podmanEnv)
|
||||
|
||||
groupsAdd := build.LookupAll(BuildGroup, KeyGroupAdd)
|
||||
for _, groupAdd := range groupsAdd {
|
||||
if len(groupAdd) > 0 {
|
||||
podman.addf("--group-add=%s", groupAdd)
|
||||
}
|
||||
}
|
||||
|
||||
labels := build.LookupAllKeyVal(BuildGroup, KeyLabel)
|
||||
podman.addLabels(labels)
|
||||
|
||||
builtImageName, ok := names[build.Filename]
|
||||
if !ok {
|
||||
return nil, "", fmt.Errorf("no ImageTag key specified")
|
||||
}
|
||||
|
||||
podman.addf("--tag=%s", builtImageName)
|
||||
|
||||
addNetworks(build, BuildGroup, service, names, podman)
|
||||
|
||||
secrets := build.LookupAllArgs(BuildGroup, KeySecret)
|
||||
for _, secret := range secrets {
|
||||
podman.add("--secret", secret)
|
||||
}
|
||||
|
||||
if err := addVolumes(build, service, BuildGroup, names, podman); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
// In order to build an image locally, we need either a File key pointing directly at a
|
||||
// Containerfile, or we need a context or WorkingDirectory containing all required files.
|
||||
// SetWorkingDirectory= can also be a path, a URL to either a Containerfile, a Git repo, or
|
||||
// an archive.
|
||||
context, err := handleSetWorkingDirectory(build, service, BuildGroup)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
workingDirectory, okWD := service.Lookup(ServiceGroup, ServiceKeyWorkingDirectory)
|
||||
filePath, okFile := build.Lookup(BuildGroup, KeyFile)
|
||||
if (!okWD || len(workingDirectory) == 0) && (!okFile || len(filePath) == 0) && len(context) == 0 {
|
||||
return nil, "", fmt.Errorf("neither SetWorkingDirectory, nor File key specified")
|
||||
}
|
||||
|
||||
if len(filePath) > 0 {
|
||||
podman.addf("--file=%s", filePath)
|
||||
}
|
||||
|
||||
handlePodmanArgs(build, BuildGroup, podman)
|
||||
|
||||
// Context or WorkingDirectory has to be last argument
|
||||
if len(context) > 0 {
|
||||
podman.add(context)
|
||||
} else if !filepath.IsAbs(filePath) && !isURL(filePath) {
|
||||
// Special handling for relative filePaths
|
||||
if len(workingDirectory) == 0 {
|
||||
return nil, "", fmt.Errorf("relative path in File key requires SetWorkingDirectory key to be set")
|
||||
}
|
||||
podman.add(workingDirectory)
|
||||
}
|
||||
|
||||
service.AddCmdline(ServiceGroup, "ExecStart", podman.Args)
|
||||
|
||||
service.Setv(ServiceGroup,
|
||||
"Type", "oneshot",
|
||||
"RemainAfterExit", "yes",
|
||||
|
||||
// The default syslog identifier is the exec basename (podman)
|
||||
// which isn't very useful here
|
||||
"SyslogIdentifier", "%N")
|
||||
|
||||
return service, builtImageName, nil
|
||||
}
|
||||
|
||||
func GetBuiltImageName(buildUnit *parser.UnitFile) string {
|
||||
if builtImageName, ok := buildUnit.Lookup(BuildGroup, KeyImageTag); ok {
|
||||
return builtImageName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetPodServiceName(podUnit *parser.UnitFile) string {
|
||||
return replaceExtension(podUnit.Filename, "", "", "-pod")
|
||||
}
|
||||
@ -1686,39 +1874,67 @@ func handlePodmanArgs(unitFile *parser.UnitFile, groupName string, podman *Podma
|
||||
}
|
||||
}
|
||||
|
||||
func handleSetWorkingDirectory(kube, serviceUnitFile *parser.UnitFile) error {
|
||||
// If WorkingDirectory is already set in the Service section do not change it
|
||||
workingDir, ok := kube.Lookup(ServiceGroup, ServiceKeyWorkingDirectory)
|
||||
if ok && len(workingDir) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
setWorkingDirectory, ok := kube.Lookup(KubeGroup, KeySetWorkingDirectory)
|
||||
func handleSetWorkingDirectory(quadletUnitFile, serviceUnitFile *parser.UnitFile, quadletGroup string) (string, error) {
|
||||
setWorkingDirectory, ok := quadletUnitFile.Lookup(quadletGroup, KeySetWorkingDirectory)
|
||||
if !ok || len(setWorkingDirectory) == 0 {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var relativeToFile string
|
||||
var context string
|
||||
switch strings.ToLower(setWorkingDirectory) {
|
||||
case "yaml":
|
||||
relativeToFile, ok = kube.Lookup(KubeGroup, KeyYaml)
|
||||
if !ok {
|
||||
return fmt.Errorf("no Yaml key specified")
|
||||
}
|
||||
case "unit":
|
||||
relativeToFile = kube.Path
|
||||
default:
|
||||
return fmt.Errorf("unsupported value for %s: %s ", ServiceKeyWorkingDirectory, setWorkingDirectory)
|
||||
if quadletGroup != KubeGroup {
|
||||
return "", fmt.Errorf("SetWorkingDirectory=%s is only supported in .kube files", setWorkingDirectory)
|
||||
}
|
||||
|
||||
fileInWorkingDir, err := getAbsolutePath(kube, relativeToFile)
|
||||
relativeToFile, ok = quadletUnitFile.Lookup(quadletGroup, KeyYaml)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("no Yaml key specified")
|
||||
}
|
||||
case "file":
|
||||
if quadletGroup != BuildGroup {
|
||||
return "", fmt.Errorf("SetWorkingDirectory=%s is only supported in .build files", setWorkingDirectory)
|
||||
}
|
||||
|
||||
relativeToFile, ok = quadletUnitFile.Lookup(quadletGroup, KeyFile)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("no File key specified")
|
||||
}
|
||||
case "unit":
|
||||
relativeToFile = quadletUnitFile.Path
|
||||
default:
|
||||
// Path / URL handling is for .build files only
|
||||
if quadletGroup != BuildGroup {
|
||||
return "", fmt.Errorf("unsupported value for %s: %s ", ServiceKeyWorkingDirectory, setWorkingDirectory)
|
||||
}
|
||||
|
||||
// Any value other than the above cases will be returned as context
|
||||
context = setWorkingDirectory
|
||||
|
||||
// If we have a relative path, set the WorkingDirectory to that of the
|
||||
// quadletUnitFile
|
||||
if !filepath.IsAbs(context) {
|
||||
relativeToFile = quadletUnitFile.Path
|
||||
}
|
||||
}
|
||||
|
||||
if len(relativeToFile) > 0 && !isURL(context) {
|
||||
// If WorkingDirectory is already set in the Service section do not change it
|
||||
workingDir, ok := quadletUnitFile.Lookup(ServiceGroup, ServiceKeyWorkingDirectory)
|
||||
if ok && len(workingDir) > 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
fileInWorkingDir, err := getAbsolutePath(quadletUnitFile, relativeToFile)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
serviceUnitFile.Add(ServiceGroup, ServiceKeyWorkingDirectory, filepath.Dir(fileInWorkingDir))
|
||||
}
|
||||
|
||||
return nil
|
||||
return context, nil
|
||||
}
|
||||
|
||||
func lookupAndAddString(unit *parser.UnitFile, group, key, flag string, podman *PodmanCmdline) {
|
||||
@ -1736,21 +1952,23 @@ func lookupAndAddBoolean(unit *parser.UnitFile, group, key, flag string, podman
|
||||
}
|
||||
|
||||
func handleImageSource(quadletImageName string, serviceUnitFile *parser.UnitFile, names map[string]string) (string, error) {
|
||||
if strings.HasSuffix(quadletImageName, ".image") {
|
||||
for _, suffix := range []string{".build", ".image"} {
|
||||
if strings.HasSuffix(quadletImageName, suffix) {
|
||||
// since there is no default name conversion, the actual image name must exist in the names map
|
||||
imageName, ok := names[quadletImageName]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("requested Quadlet image %s was not found", imageName)
|
||||
return "", fmt.Errorf("requested Quadlet image %s was not found", quadletImageName)
|
||||
}
|
||||
|
||||
// the systemd unit name is $name-image.service
|
||||
imageServiceName := replaceExtension(quadletImageName, ".service", "", "-image")
|
||||
// the systemd unit name is $name-$suffix.service
|
||||
imageServiceName := replaceExtension(quadletImageName, ".service", "", fmt.Sprintf("-%s", suffix[1:]))
|
||||
|
||||
serviceUnitFile.Add(UnitGroup, "Requires", imageServiceName)
|
||||
serviceUnitFile.Add(UnitGroup, "After", imageServiceName)
|
||||
|
||||
quadletImageName = imageName
|
||||
}
|
||||
}
|
||||
|
||||
return quadletImageName, nil
|
||||
}
|
||||
|
14
test/e2e/quadlet/annotation.build
Normal file
14
test/e2e/quadlet/annotation.build
Normal file
@ -0,0 +1,14 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args "--annotation" "org.foo.Arg0=arg0"
|
||||
## assert-podman-args "--annotation" "org.foo.Arg1=arg1"
|
||||
## assert-podman-args "--annotation" "org.foo.Arg2=arg 2"
|
||||
## assert-podman-args "--annotation" "org.foo.Arg3=arg3"
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Annotation=org.foo.Arg1=arg1 "org.foo.Arg2=arg 2" \
|
||||
org.foo.Arg3=arg3
|
||||
|
||||
Annotation=org.foo.Arg0=arg0
|
8
test/e2e/quadlet/arch.build
Normal file
8
test/e2e/quadlet/arch.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args --arch=aarch64
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Arch=aarch64
|
8
test/e2e/quadlet/authfile.build
Normal file
8
test/e2e/quadlet/authfile.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args --authfile=/etc/certs/auth.json
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
AuthFile=/etc/certs/auth.json
|
13
test/e2e/quadlet/basic.build
Normal file
13
test/e2e/quadlet/basic.build
Normal file
@ -0,0 +1,13 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-key-is "Unit" "After" "network-online.target"
|
||||
## assert-key-is "Unit" "Wants" "network-online.target"
|
||||
## assert-key-is "Unit" "RequiresMountsFor" "%t/containers"
|
||||
## assert-key-is-regex "Service" "WorkingDirectory" "/.*/podman-e2e-.*/subtest-.*/quadlet"
|
||||
## assert-key-is "Service" "Type" "oneshot"
|
||||
## assert-key-is "Service" "RemainAfterExit" "yes"
|
||||
## assert-key-is "Service" "SyslogIdentifier" "%N"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
6
test/e2e/quadlet/build-not-found.quadlet.volume
Normal file
6
test/e2e/quadlet/build-not-found.quadlet.volume
Normal file
@ -0,0 +1,6 @@
|
||||
## assert-failed
|
||||
## assert-stderr-contains "requested Quadlet image not-found.build was not found"
|
||||
|
||||
[Volume]
|
||||
Driver=image
|
||||
Image=not-found.build
|
8
test/e2e/quadlet/build.quadlet.volume
Normal file
8
test/e2e/quadlet/build.quadlet.volume
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-args --driver=image
|
||||
## assert-podman-args --opt image=localhost/imagename
|
||||
## assert-key-is "Unit" "Requires" "basic-build.service"
|
||||
## assert-key-is "Unit" "After" "basic-build.service"
|
||||
|
||||
[Volume]
|
||||
Driver=image
|
||||
Image=basic.build
|
8
test/e2e/quadlet/containersconfmodule.build
Normal file
8
test/e2e/quadlet/containersconfmodule.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-global-args "build" "--module=/etc/container/1.conf"
|
||||
## assert-podman-global-args "build" "--module=/etc/container/2.conf"
|
||||
|
||||
[Build]
|
||||
ImageTag=image:latest
|
||||
SetWorkingDirectory=unit
|
||||
ContainersConfModule=/etc/container/1.conf
|
||||
ContainersConfModule=/etc/container/2.conf
|
10
test/e2e/quadlet/dns-options.build
Normal file
10
test/e2e/quadlet/dns-options.build
Normal file
@ -0,0 +1,10 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args "--dns-option=ndots:1"
|
||||
## assert-podman-args "--dns-option=color:blue"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
DNSOption=ndots:1
|
||||
DNSOption=color:blue
|
10
test/e2e/quadlet/dns-search.build
Normal file
10
test/e2e/quadlet/dns-search.build
Normal file
@ -0,0 +1,10 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args "--dns-search=foo.com"
|
||||
## assert-podman-args "--dns-search=bar.com"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
DNSSearch=foo.com
|
||||
DNSSearch=bar.com
|
10
test/e2e/quadlet/dns.build
Normal file
10
test/e2e/quadlet/dns.build
Normal file
@ -0,0 +1,10 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args "--dns=8.7.7.7"
|
||||
## assert-podman-args "--dns=8.8.8.8"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
DNS=8.7.7.7
|
||||
DNS=8.8.8.8
|
14
test/e2e/quadlet/env.build
Normal file
14
test/e2e/quadlet/env.build
Normal file
@ -0,0 +1,14 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args --env "FOO1=foo1"
|
||||
## assert-podman-args --env "FOO2=foo2 "
|
||||
## assert-podman-args --env "FOO3=foo3"
|
||||
## assert-podman-args --env "REPLACE=replaced"
|
||||
## assert-podman-args --env "FOO4=foo\\nfoo"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Environment=FOO1=foo1 "FOO2=foo2 " \
|
||||
FOO3=foo3 REPLACE=replace
|
||||
Environment=REPLACE=replaced 'FOO4=foo\nfoo'
|
5
test/e2e/quadlet/file-abs.build
Normal file
5
test/e2e/quadlet/file-abs.build
Normal file
@ -0,0 +1,5 @@
|
||||
## assert-podman-final-args --file=/etc/containers/systemd/Containerfile
|
||||
|
||||
[Build]
|
||||
File=/etc/containers/systemd/Containerfile
|
||||
ImageTag=localhost/imagename
|
6
test/e2e/quadlet/file-https.build
Normal file
6
test/e2e/quadlet/file-https.build
Normal file
@ -0,0 +1,6 @@
|
||||
## assert-podman-args --tag=localhost/podman-hello
|
||||
## assert-podman-args --file=https://raw.githubusercontent.com/containers/PodmanHello/main/Containerfile
|
||||
|
||||
[Build]
|
||||
File=https://raw.githubusercontent.com/containers/PodmanHello/main/Containerfile
|
||||
ImageTag=localhost/podman-hello
|
6
test/e2e/quadlet/file-rel-no-wd.build
Normal file
6
test/e2e/quadlet/file-rel-no-wd.build
Normal file
@ -0,0 +1,6 @@
|
||||
## assert-failed
|
||||
## assert-stderr-contains "relative path in File key requires SetWorkingDirectory key to be set"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
File=Containerfile
|
7
test/e2e/quadlet/file-rel.build
Normal file
7
test/e2e/quadlet/file-rel.build
Normal file
@ -0,0 +1,7 @@
|
||||
## assert-podman-final-args .
|
||||
## assert-podman-args-regex "--file=Containerfile"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=.
|
||||
File=Containerfile
|
8
test/e2e/quadlet/force-rm.build
Normal file
8
test/e2e/quadlet/force-rm.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args --force-rm=false
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
ForceRM=no
|
9
test/e2e/quadlet/globalargs.build
Normal file
9
test/e2e/quadlet/globalargs.build
Normal file
@ -0,0 +1,9 @@
|
||||
## assert-podman-global-args "build" "--identity=path=/etc/identity"
|
||||
## assert-podman-global-args "build" "--syslog"
|
||||
## assert-podman-global-args "build" "--log-level=debug"
|
||||
|
||||
[Build]
|
||||
ImageTag=image:latest
|
||||
SetWorkingDirectory=unit
|
||||
GlobalArgs=--identity=path=/etc/identity
|
||||
GlobalArgs=--syslog --log-level=debug
|
8
test/e2e/quadlet/group-add.build
Normal file
8
test/e2e/quadlet/group-add.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-args "--group-add=keep-groups"
|
||||
## assert-podman-args "--group-add=users"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
GroupAdd=keep-groups
|
||||
GroupAdd=users
|
6
test/e2e/quadlet/image-not-found.quadlet.volume
Normal file
6
test/e2e/quadlet/image-not-found.quadlet.volume
Normal file
@ -0,0 +1,6 @@
|
||||
## assert-failed
|
||||
## assert-stderr-contains "requested Quadlet image not-found.image was not found"
|
||||
|
||||
[Volume]
|
||||
Driver=image
|
||||
Image=not-found.image
|
8
test/e2e/quadlet/image.quadlet.volume
Normal file
8
test/e2e/quadlet/image.quadlet.volume
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-args --driver=image
|
||||
## assert-podman-args --opt image=localhost/imagename
|
||||
## assert-key-is "Unit" "Requires" "basic-image.service"
|
||||
## assert-key-is "Unit" "After" "basic-image.service"
|
||||
|
||||
[Volume]
|
||||
Driver=image
|
||||
Image=basic.image
|
14
test/e2e/quadlet/label.build
Normal file
14
test/e2e/quadlet/label.build
Normal file
@ -0,0 +1,14 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args "--label" "org.foo.Arg0=arg0"
|
||||
## assert-podman-args "--label" "org.foo.Arg1=arg1"
|
||||
## assert-podman-args "--label" "org.foo.Arg2=arg 2"
|
||||
## assert-podman-args "--label" "org.foo.Arg3=arg3"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Label=org.foo.Arg1=arg1 "org.foo.Arg2=arg 2" \
|
||||
org.foo.Arg3=arg3
|
||||
|
||||
Label=org.foo.Arg0=arg0
|
5
test/e2e/quadlet/neither-workingdirectory-nor-file.build
Normal file
5
test/e2e/quadlet/neither-workingdirectory-nor-file.build
Normal file
@ -0,0 +1,5 @@
|
||||
## assert-failed
|
||||
## assert-stderr-contains "neither SetWorkingDirectory, nor File key specified"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
8
test/e2e/quadlet/network.build
Normal file
8
test/e2e/quadlet/network.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args "--network=host"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Network=host
|
8
test/e2e/quadlet/network.quadlet.build
Normal file
8
test/e2e/quadlet/network.quadlet.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-args "--network=systemd-basic"
|
||||
## assert-key-is "Unit" "Requires" "basic-network.service"
|
||||
## assert-key-is "Unit" "After" "network-online.target" "basic-network.service"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Network=basic.network
|
5
test/e2e/quadlet/no-imagetag.build
Normal file
5
test/e2e/quadlet/no-imagetag.build
Normal file
@ -0,0 +1,5 @@
|
||||
## assert-failed
|
||||
## assert-stderr-contains "no ImageTag key specified"
|
||||
|
||||
[Build]
|
||||
SetWorkingDirectory=unit
|
14
test/e2e/quadlet/podmanargs.build
Normal file
14
test/e2e/quadlet/podmanargs.build
Normal file
@ -0,0 +1,14 @@
|
||||
## assert-podman-args "--foo"
|
||||
## assert-podman-args "--bar"
|
||||
## assert-podman-args "--also"
|
||||
## assert-podman-args "--with-key=value"
|
||||
## assert-podman-args "--with-space" "yes"
|
||||
|
||||
[Build]
|
||||
ImageTag=image:latest
|
||||
SetWorkingDirectory=unit
|
||||
PodmanArgs="--foo" \
|
||||
--bar
|
||||
PodmanArgs=--also
|
||||
PodmanArgs=--with-key=value
|
||||
PodmanArgs=--with-space yes
|
8
test/e2e/quadlet/pull.build
Normal file
8
test/e2e/quadlet/pull.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args --pull=never
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Pull=never
|
8
test/e2e/quadlet/secrets.build
Normal file
8
test/e2e/quadlet/secrets.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-args "--secret" "mysecret"
|
||||
## assert-podman-args "--secret" "id=mysecret,src=mysecret.txt"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Secret=mysecret
|
||||
Secret=id=mysecret,src=mysecret.txt
|
5
test/e2e/quadlet/setworkingdirectory-is-abs.build
Normal file
5
test/e2e/quadlet/setworkingdirectory-is-abs.build
Normal file
@ -0,0 +1,5 @@
|
||||
## assert-podman-final-args /etc/containers/systemd
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=/etc/containers/systemd
|
8
test/e2e/quadlet/setworkingdirectory-is-archive.build
Normal file
8
test/e2e/quadlet/setworkingdirectory-is-archive.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-final-args https://github.com/containers/PodmanHello/archive/refs/heads/main.tar.gz
|
||||
## assert-podman-args --tag=localhost/podman-hello-archive
|
||||
## assert-podman-args --file=PodmanHello-main/Containerfile
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/podman-hello-archive
|
||||
File=PodmanHello-main/Containerfile
|
||||
SetWorkingDirectory=https://github.com/containers/PodmanHello/archive/refs/heads/main.tar.gz
|
7
test/e2e/quadlet/setworkingdirectory-is-file-abs.build
Normal file
7
test/e2e/quadlet/setworkingdirectory-is-file-abs.build
Normal file
@ -0,0 +1,7 @@
|
||||
## assert-podman-args --file=/etc/containers/systemd/Containerfile
|
||||
## assert-key-is "Service" "WorkingDirectory" "/etc/containers/systemd"
|
||||
|
||||
[Build]
|
||||
File=/etc/containers/systemd/Containerfile
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=file
|
7
test/e2e/quadlet/setworkingdirectory-is-file-rel.build
Normal file
7
test/e2e/quadlet/setworkingdirectory-is-file-rel.build
Normal file
@ -0,0 +1,7 @@
|
||||
## assert-podman-args --file=Containerfile
|
||||
## assert-key-is-regex "Service" "WorkingDirectory" "/.*/podman-e2e-.*/subtest-.*/quadlet"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
File=Containerfile
|
||||
SetWorkingDirectory=file
|
6
test/e2e/quadlet/setworkingdirectory-is-git.build
Normal file
6
test/e2e/quadlet/setworkingdirectory-is-git.build
Normal file
@ -0,0 +1,6 @@
|
||||
## assert-podman-final-args git://git@git.sr.ht/~emersion/sr.ht-container-compose
|
||||
## assert-podman-args --tag=localhost/podman-hello
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/podman-hello
|
||||
SetWorkingDirectory=git://git@git.sr.ht/~emersion/sr.ht-container-compose
|
6
test/e2e/quadlet/setworkingdirectory-is-github.build
Normal file
6
test/e2e/quadlet/setworkingdirectory-is-github.build
Normal file
@ -0,0 +1,6 @@
|
||||
## assert-podman-final-args github.com/containers/PodmanHello.git
|
||||
## assert-podman-args --tag=localhost/podman-hello
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/podman-hello
|
||||
SetWorkingDirectory=github.com/containers/PodmanHello.git
|
6
test/e2e/quadlet/setworkingdirectory-is-https-git.build
Normal file
6
test/e2e/quadlet/setworkingdirectory-is-https-git.build
Normal file
@ -0,0 +1,6 @@
|
||||
## assert-podman-final-args https://github.com/containers/PodmanHello.git
|
||||
## assert-podman-args --tag=localhost/podman-hello
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/podman-hello
|
||||
SetWorkingDirectory=https://github.com/containers/PodmanHello.git
|
6
test/e2e/quadlet/setworkingdirectory-is-rel.build
Normal file
6
test/e2e/quadlet/setworkingdirectory-is-rel.build
Normal file
@ -0,0 +1,6 @@
|
||||
## assert-podman-final-args .
|
||||
## assert-key-is-regex "Service" "WorkingDirectory" "/.*/podman-e2e-.*/subtest-.*/quadlet"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=.
|
8
test/e2e/quadlet/target.build
Normal file
8
test/e2e/quadlet/target.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args --target=my-app
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Target=my-app
|
8
test/e2e/quadlet/tls-verify.build
Normal file
8
test/e2e/quadlet/tls-verify.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args --tls-verify=false
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
TLSVerify=no
|
8
test/e2e/quadlet/variant.build
Normal file
8
test/e2e/quadlet/variant.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-final-args-regex /.*/podman-e2e-.*/subtest-.*/quadlet
|
||||
## assert-podman-args --tag=localhost/imagename
|
||||
## assert-podman-args --variant=arm/v7
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Variant=arm/v7
|
17
test/e2e/quadlet/volume.build
Normal file
17
test/e2e/quadlet/volume.build
Normal file
@ -0,0 +1,17 @@
|
||||
## assert-podman-args -v /host/dir:/container/volume
|
||||
## assert-podman-args -v /host/dir2:/container/volume2:Z
|
||||
## assert-podman-args-regex -v .*/podman-e2e-.*/subtest-.*/quadlet/host/dir3:/container/volume3
|
||||
## assert-podman-args -v named:/container/named
|
||||
## assert-podman-args -v systemd-quadlet:/container/quadlet
|
||||
## assert-podman-args -v %h/container:/container/volume4
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Volume=/host/dir:/container/volume
|
||||
Volume=/host/dir2:/container/volume2:Z
|
||||
Volume=./host/dir3:/container/volume3
|
||||
Volume=/container/empty
|
||||
Volume=named:/container/named
|
||||
Volume=quadlet.volume:/container/quadlet
|
||||
Volume=%h/container:/container/volume4
|
8
test/e2e/quadlet/volume.quadlet.build
Normal file
8
test/e2e/quadlet/volume.quadlet.build
Normal file
@ -0,0 +1,8 @@
|
||||
## assert-podman-args "-v" "systemd-basic:/volume/basic"
|
||||
## assert-key-is "Unit" "Requires" "basic-volume.service"
|
||||
## assert-key-is "Unit" "After" "network-online.target" "basic-volume.service"
|
||||
|
||||
[Build]
|
||||
ImageTag=localhost/imagename
|
||||
SetWorkingDirectory=unit
|
||||
Volume=basic.volume:/volume/basic
|
@ -50,6 +50,8 @@ func loadQuadletTestcase(path string) *quadletTestcase {
|
||||
service += "-network"
|
||||
case ".image":
|
||||
service += "-image"
|
||||
case ".build":
|
||||
service += "-build"
|
||||
case ".pod":
|
||||
service += "-pod"
|
||||
}
|
||||
@ -606,6 +608,44 @@ var _ = Describe("quadlet system generator", func() {
|
||||
err error
|
||||
generatedDir string
|
||||
quadletDir string
|
||||
|
||||
runQuadletTestCase = func(fileName string, exitCode int, errString string) {
|
||||
testcase := loadQuadletTestcase(filepath.Join("quadlet", fileName))
|
||||
|
||||
// Write the tested file to the quadlet dir
|
||||
err = os.WriteFile(filepath.Join(quadletDir, fileName), testcase.data, 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Also copy any extra snippets
|
||||
snippetdirs := []string{fileName + ".d"}
|
||||
if ok, genericFileName := getGenericTemplateFile(fileName); ok {
|
||||
snippetdirs = append(snippetdirs, genericFileName+".d")
|
||||
}
|
||||
for _, snippetdir := range snippetdirs {
|
||||
dotdDir := filepath.Join("quadlet", snippetdir)
|
||||
if s, err := os.Stat(dotdDir); err == nil && s.IsDir() {
|
||||
dotdDirDest := filepath.Join(quadletDir, snippetdir)
|
||||
err = os.Mkdir(dotdDirDest, os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = CopyDirectory(dotdDir, dotdDirDest)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
// Run quadlet to convert the file
|
||||
session := podmanTest.Quadlet([]string{"--user", "--no-kmsg-log", generatedDir}, quadletDir)
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(exitCode))
|
||||
|
||||
// Print any stderr output
|
||||
errs := session.ErrorToString()
|
||||
if errs != "" {
|
||||
GinkgoWriter.Println("error:", session.ErrorToString())
|
||||
}
|
||||
Expect(errs).Should(ContainSubstring(errString))
|
||||
|
||||
testcase.check(generatedDir, session)
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
@ -747,43 +787,7 @@ BOGUS=foo
|
||||
})
|
||||
|
||||
DescribeTable("Running quadlet test case",
|
||||
func(fileName string, exitCode int, errString string) {
|
||||
testcase := loadQuadletTestcase(filepath.Join("quadlet", fileName))
|
||||
|
||||
// Write the tested file to the quadlet dir
|
||||
err = os.WriteFile(filepath.Join(quadletDir, fileName), testcase.data, 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Also copy any extra snippets
|
||||
snippetdirs := []string{fileName + ".d"}
|
||||
if ok, genericFileName := getGenericTemplateFile(fileName); ok {
|
||||
snippetdirs = append(snippetdirs, genericFileName+".d")
|
||||
}
|
||||
for _, snippetdir := range snippetdirs {
|
||||
dotdDir := filepath.Join("quadlet", snippetdir)
|
||||
if s, err := os.Stat(dotdDir); err == nil && s.IsDir() {
|
||||
dotdDirDest := filepath.Join(quadletDir, snippetdir)
|
||||
err = os.Mkdir(dotdDirDest, os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = CopyDirectory(dotdDir, dotdDirDest)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
// Run quadlet to convert the file
|
||||
session := podmanTest.Quadlet([]string{"--user", "--no-kmsg-log", generatedDir}, quadletDir)
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(Exit(exitCode))
|
||||
|
||||
// Print any stderr output
|
||||
errs := session.ErrorToString()
|
||||
if errs != "" {
|
||||
GinkgoWriter.Println("error:", session.ErrorToString())
|
||||
}
|
||||
Expect(errs).Should(ContainSubstring(errString))
|
||||
|
||||
testcase.check(generatedDir, session)
|
||||
},
|
||||
runQuadletTestCase,
|
||||
Entry("Basic container", "basic.container", 0, ""),
|
||||
Entry("annotation.container", "annotation.container", 0, ""),
|
||||
Entry("autoupdate.container", "autoupdate.container", 0, ""),
|
||||
@ -880,6 +884,8 @@ BOGUS=foo
|
||||
Entry("image-no-image.volume", "image-no-image.volume", 1, "converting \"image-no-image.volume\": the key Image is mandatory when using the image driver"),
|
||||
Entry("Volume - global args", "globalargs.volume", 0, ""),
|
||||
Entry("Volume - Containers Conf Modules", "containersconfmodule.volume", 0, ""),
|
||||
Entry("Volume - Quadlet image (.build) not found", "build-not-found.quadlet.volume", 1, "converting \"build-not-found.quadlet.volume\": requested Quadlet image not-found.build was not found"),
|
||||
Entry("Volume - Quadlet image (.image) not found", "image-not-found.quadlet.volume", 1, "converting \"image-not-found.quadlet.volume\": requested Quadlet image not-found.image was not found"),
|
||||
|
||||
Entry("Absolute Path", "absolute.path.kube", 0, ""),
|
||||
Entry("Basic kube", "basic.kube", 0, ""),
|
||||
@ -944,6 +950,44 @@ BOGUS=foo
|
||||
Entry("Image - Containers Conf Modules", "containersconfmodule.image", 0, ""),
|
||||
Entry("Image - Unit After Override", "unit-after-override.image", 0, ""),
|
||||
|
||||
Entry("Build - Basic", "basic.build", 0, ""),
|
||||
Entry("Build - Annotation Key", "annotation.build", 0, ""),
|
||||
Entry("Build - Arch Key", "arch.build", 0, ""),
|
||||
Entry("Build - AuthFile Key", "authfile.build", 0, ""),
|
||||
Entry("Build - DNS Key", "dns.build", 0, ""),
|
||||
Entry("Build - DNSOptions Key", "dns-options.build", 0, ""),
|
||||
Entry("Build - DNSSearch Key", "dns-search.build", 0, ""),
|
||||
Entry("Build - Environment Key", "env.build", 0, ""),
|
||||
Entry("Build - File Key absolute", "file-abs.build", 0, ""),
|
||||
Entry("Build - File Key relative", "file-rel.build", 0, ""),
|
||||
Entry("Build - File Key relative no WD", "file-rel-no-wd.build", 1, "converting \"file-rel-no-wd.build\": relative path in File key requires SetWorkingDirectory key to be set"),
|
||||
Entry("Build - File Key HTTP(S) URL", "file-https.build", 0, ""),
|
||||
Entry("Build - ForceRM Key", "force-rm.build", 0, ""),
|
||||
Entry("Build - GlobalArgs", "globalargs.build", 0, ""),
|
||||
Entry("Build - GroupAdd Key", "group-add.build", 0, ""),
|
||||
Entry("Build - Containers Conf Modules", "containersconfmodule.build", 0, ""),
|
||||
Entry("Build - Label Key", "label.build", 0, ""),
|
||||
Entry("Build - Neither WorkingDirectory nor File Key", "neither-workingdirectory-nor-file.build", 1, "converting \"neither-workingdirectory-nor-file.build\": neither SetWorkingDirectory, nor File key specified"),
|
||||
Entry("Build - Network Key host", "network.build", 0, ""),
|
||||
Entry("Build - Network Key quadlet", "network.quadlet.build", 0, ""),
|
||||
Entry("Build - No ImageTag Key", "no-imagetag.build", 1, "converting \"no-imagetag.build\": no ImageTag key specified"),
|
||||
Entry("Build - PodmanArgs", "podmanargs.build", 0, ""),
|
||||
Entry("Build - Pull Key", "pull.build", 0, ""),
|
||||
Entry("Build - Secrets", "secrets.build", 0, ""),
|
||||
Entry("Build - SetWorkingDirectory is absolute path", "setworkingdirectory-is-abs.build", 0, ""),
|
||||
Entry("Build - SetWorkingDirectory is absolute File= path", "setworkingdirectory-is-file-abs.build", 0, ""),
|
||||
Entry("Build - SetWorkingDirectory is relative path", "setworkingdirectory-is-rel.build", 0, ""),
|
||||
Entry("Build - SetWorkingDirectory is relative File= path", "setworkingdirectory-is-file-rel.build", 0, ""),
|
||||
Entry("Build - SetWorkingDirectory is https://.git URL", "setworkingdirectory-is-https-git.build", 0, ""),
|
||||
Entry("Build - SetWorkingDirectory is git:// URL", "setworkingdirectory-is-git.build", 0, ""),
|
||||
Entry("Build - SetWorkingDirectory is github.com URL", "setworkingdirectory-is-github.build", 0, ""),
|
||||
Entry("Build - SetWorkingDirectory is archive URL", "setworkingdirectory-is-archive.build", 0, ""),
|
||||
Entry("Build - Target Key", "target.build", 0, ""),
|
||||
Entry("Build - TLSVerify Key", "tls-verify.build", 0, ""),
|
||||
Entry("Build - Variant Key", "variant.build", 0, ""),
|
||||
Entry("Build - Volume Key", "volume.build", 0, ""),
|
||||
Entry("Build - Volume Key quadlet", "volume.quadlet.build", 0, ""),
|
||||
|
||||
Entry("basic.pod", "basic.pod", 0, ""),
|
||||
Entry("name.pod", "name.pod", 0, ""),
|
||||
Entry("network.pod", "network.pod", 0, ""),
|
||||
@ -952,4 +996,19 @@ BOGUS=foo
|
||||
Entry("volume.pod", "volume.pod", 0, ""),
|
||||
)
|
||||
|
||||
DescribeTable("Running quadlet test case with dependencies",
|
||||
func(fileName string, exitCode int, errString string, dependencyFiles []string) {
|
||||
// Write additional files this test depends on to the quadlet dir
|
||||
for _, dependencyFileName := range dependencyFiles {
|
||||
dependencyTestCase := loadQuadletTestcase(filepath.Join("quadlet", dependencyFileName))
|
||||
err = os.WriteFile(filepath.Join(quadletDir, dependencyFileName), dependencyTestCase.data, 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
runQuadletTestCase(fileName, exitCode, errString)
|
||||
},
|
||||
Entry("Volume - Quadlet image (.build)", "build.quadlet.volume", 0, "", []string{"basic.build"}),
|
||||
Entry("Volume - Quadlet image (.image)", "image.quadlet.volume", 0, "", []string{"basic.image"}),
|
||||
)
|
||||
|
||||
})
|
||||
|
Reference in New Issue
Block a user