mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Allow setting volume and network names in Quadlet
This commit extends `Volume` and `Network` unit definitions with two additional parameters, `VolumeName` and `NetworkName`, which will, respectively, set a user-defined name for the corresponding volume and network. This is similar to how the `ContainerName` directive currently works, and should allow for smoother transitions to Quadlet-managed resources. Closes: #19003 Signed-off-by: Alex Palaistras <alex@deuill.org>
This commit is contained in:
@ -8,6 +8,7 @@ import (
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
@ -132,19 +133,22 @@ func isExtSupported(filename string) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func loadUnitsFromDir(sourcePath string, units map[string]*parser.UnitFile) error {
|
||||
func loadUnitsFromDir(sourcePath string) ([]*parser.UnitFile, error) {
|
||||
var prevError error
|
||||
files, err := os.ReadDir(sourcePath)
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
return nil
|
||||
return []*parser.UnitFile{}, nil
|
||||
}
|
||||
|
||||
var units []*parser.UnitFile
|
||||
var seen = make(map[string]struct{})
|
||||
|
||||
for _, file := range files {
|
||||
name := file.Name()
|
||||
if units[name] == nil && isExtSupported(name) {
|
||||
if _, ok := seen[name]; !ok && isExtSupported(name) {
|
||||
path := path.Join(sourcePath, name)
|
||||
|
||||
Debugf("Loading source unit file %s", path)
|
||||
@ -155,11 +159,13 @@ func loadUnitsFromDir(sourcePath string, units map[string]*parser.UnitFile) erro
|
||||
prevError = fmt.Errorf("%s\n%s", prevError, err)
|
||||
}
|
||||
} else {
|
||||
units[name] = f
|
||||
seen[name] = void
|
||||
units = append(units, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
return prevError
|
||||
|
||||
return units, prevError
|
||||
}
|
||||
|
||||
func generateServiceFile(service *parser.UnitFile) error {
|
||||
@ -357,10 +363,12 @@ func process() error {
|
||||
|
||||
sourcePaths := getUnitDirs(isUserFlag)
|
||||
|
||||
units := make(map[string]*parser.UnitFile)
|
||||
var units []*parser.UnitFile
|
||||
for _, d := range sourcePaths {
|
||||
if err := loadUnitsFromDir(d, units); err != nil {
|
||||
if result, err := loadUnitsFromDir(d); err != nil {
|
||||
reportError(err)
|
||||
} else {
|
||||
units = append(units, result...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,29 +387,44 @@ func process() error {
|
||||
}
|
||||
}
|
||||
|
||||
for name, unit := range units {
|
||||
// Sort unit files according to potential inter-dependencies, with Volume and Network units
|
||||
// taking precedence over all others.
|
||||
sort.Slice(units, func(i, j int) bool {
|
||||
name := units[i].Filename
|
||||
return strings.HasSuffix(name, ".volume") || strings.HasSuffix(name, ".network")
|
||||
})
|
||||
|
||||
// A map of network/volume unit file-names, against their calculated names, as needed by Podman.
|
||||
var resourceNames = make(map[string]string)
|
||||
|
||||
for _, unit := range units {
|
||||
var service *parser.UnitFile
|
||||
var name string
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(name, ".container"):
|
||||
case strings.HasSuffix(unit.Filename, ".container"):
|
||||
warnIfAmbiguousName(unit)
|
||||
service, err = quadlet.ConvertContainer(unit, isUserFlag)
|
||||
case strings.HasSuffix(name, ".volume"):
|
||||
service, err = quadlet.ConvertVolume(unit, name)
|
||||
case strings.HasSuffix(name, ".kube"):
|
||||
service, err = quadlet.ConvertKube(unit, isUserFlag)
|
||||
case strings.HasSuffix(name, ".network"):
|
||||
service, err = quadlet.ConvertNetwork(unit, name)
|
||||
service, err = quadlet.ConvertContainer(unit, resourceNames, isUserFlag)
|
||||
case strings.HasSuffix(unit.Filename, ".volume"):
|
||||
service, name, err = quadlet.ConvertVolume(unit, unit.Filename)
|
||||
case strings.HasSuffix(unit.Filename, ".kube"):
|
||||
service, err = quadlet.ConvertKube(unit, resourceNames, isUserFlag)
|
||||
case strings.HasSuffix(unit.Filename, ".network"):
|
||||
service, name, err = quadlet.ConvertNetwork(unit, unit.Filename)
|
||||
default:
|
||||
Logf("Unsupported file type %q", name)
|
||||
Logf("Unsupported file type %q", unit.Filename)
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
reportError(fmt.Errorf("converting %q: %w", name, err))
|
||||
reportError(fmt.Errorf("converting %q: %w", unit.Filename, err))
|
||||
continue
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
resourceNames[unit.Filename] = name
|
||||
}
|
||||
service.Path = path.Join(outputPath, service.Filename)
|
||||
|
||||
if dryRunFlag {
|
||||
|
@ -77,8 +77,9 @@ the container that is run as a service. The resulting service file contains a li
|
||||
options passed to Podman. However, some options also affect the details of how systemd is set up to run and
|
||||
interact with the container.
|
||||
|
||||
By default, the Podman container has the same name as the unit, but with a `systemd-` prefix.
|
||||
I.e. a `$name.container` file creates a `$name.service` unit and a `systemd-$name` Podman container.
|
||||
By default, the Podman container has the same name as the unit, but with a `systemd-` prefix, i.e.
|
||||
a `$name.container` file creates a `$name.service` unit and a `systemd-$name` Podman container. The
|
||||
`ContainerName` option allows for overriding this default name with a user-provided one.
|
||||
|
||||
There is only one required key, `Image`, which defines the container image the service runs.
|
||||
|
||||
@ -633,8 +634,10 @@ Network files are named with a `.network` extension and contain a section `[Netw
|
||||
named Podman network. The generated service is a one-time command that ensures that the network
|
||||
exists on the host, creating it if needed.
|
||||
|
||||
For a network file named `$NAME.network`, the generated Podman network is called `systemd-$NAME`,
|
||||
and the generated service file `$NAME-network.service`.
|
||||
By default, the Podman network has the same name as the unit, but with a `systemd-` prefix, i.e. for
|
||||
a network file named `$NAME.network`, the generated Podman network is called `systemd-$NAME`, and
|
||||
the generated service file is `$NAME-network.service`. The `NetworkName` option allows for
|
||||
overriding this default name with a user-provided one.
|
||||
|
||||
Using network units allows containers to depend on networks being automatically pre-created. This is
|
||||
particularly interesting when using special options to control network creation, as Podman otherwise creates networks with the default options.
|
||||
@ -642,7 +645,7 @@ particularly interesting when using special options to control network creation,
|
||||
Valid options for `[Network]` are listed below:
|
||||
|
||||
| **[Network] options** | **podman network create equivalent** |
|
||||
| ----------------- | ------------------ |
|
||||
|-------------------------------|--------------------------------------|
|
||||
| DisableDNS=true | --disable-dns |
|
||||
| Driver=bridge | --driver bridge |
|
||||
| Gateway=192.168.55.3 | --gateway 192.168.55.3 |
|
||||
@ -651,6 +654,7 @@ Valid options for `[Network]` are listed below:
|
||||
| IPRange=192.168.55.128/25 | --ip-range 192.168.55.128/25 |
|
||||
| IPv6=true | --ipv6 |
|
||||
| Label="YXZ" | --label "XYZ" |
|
||||
| NetworkName=foo | podman network create foo |
|
||||
| Options=isolate | --opt isolate |
|
||||
| PodmanArgs=--dns=192.168.55.1 | --dns=192.168.55.1 |
|
||||
| Subnet=192.5.0.0/16 | --subnet 192.5.0.0/16 |
|
||||
@ -712,6 +716,12 @@ Set one or more OCI labels on the network. The format is a list of
|
||||
|
||||
This key can be listed multiple times.
|
||||
|
||||
### `NetworkName=`
|
||||
|
||||
The (optional) name of the Podman network. If this is not specified, the default value of
|
||||
`systemd-%N` is used, which is the same as the unit name but with a `systemd-` prefix to avoid
|
||||
conflicts with user-managed networks.
|
||||
|
||||
### `Options=`
|
||||
|
||||
Set driver specific options.
|
||||
@ -745,8 +755,10 @@ Volume files are named with a `.volume` extension and contain a section `[Volume
|
||||
named Podman volume. The generated service is a one-time command that ensures that the volume
|
||||
exists on the host, creating it if needed.
|
||||
|
||||
For a volume file named `$NAME.volume`, the generated Podman volume is called `systemd-$NAME`,
|
||||
and the generated service file `$NAME-volume.service`.
|
||||
By default, the Podman volume has the same name as the unit, but with a `systemd-` prefix, i.e. for
|
||||
a volume file named `$NAME.volume`, the generated Podman volume is called `systemd-$NAME`, and the
|
||||
generated service file is `$NAME-volume.service`. The `VolumeName` option allows for overriding this
|
||||
default name with a user-provided one.
|
||||
|
||||
Using volume units allows containers to depend on volumes being automatically pre-created. This is
|
||||
particularly interesting when using special options to control volume creation,
|
||||
@ -755,13 +767,14 @@ as Podman otherwise creates volumes with the default options.
|
||||
Valid options for `[Volume]` are listed below:
|
||||
|
||||
| **[Volume] options** | **podman volume create equivalent** |
|
||||
| ----------------- | ------------------ |
|
||||
|---------------------------|-------------------------------------|
|
||||
| Device=tmpfs | --opt device=tmpfs |
|
||||
| Copy=true | --opt copy |
|
||||
| Group=192 | --opt group=192 |
|
||||
| Label="foo=bar" | --label "foo=bar" |
|
||||
| Options=XYZ | --opt XYZ |
|
||||
| PodmanArgs=--driver=image | --driver=image |
|
||||
| VolumeName=foo | podman volume create foo |
|
||||
|
||||
Supported keys in `[Volume]` section are:
|
||||
|
||||
@ -810,6 +823,12 @@ The filesystem type of `Device` as used by the **mount(8)** commands `-t` option
|
||||
|
||||
The host (numeric) UID, or user name to use as the owner for the volume
|
||||
|
||||
### `VolumeName=`
|
||||
|
||||
The (optional) name of the Podman volume. If this is not specified, the default value of
|
||||
`systemd-%N` is used, which is the same as the unit name but with a `systemd-` prefix to avoid
|
||||
conflicts with user-managed volumes.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
Example `test.container`:
|
||||
|
@ -80,6 +80,7 @@ const (
|
||||
KeyNetworkIPRange = "IPRange"
|
||||
KeyNetworkIPv6 = "IPv6"
|
||||
KeyNetworkInternal = "Internal"
|
||||
KeyNetworkName = "NetworkName"
|
||||
KeyNetworkOptions = "Options"
|
||||
KeyNetworkSubnet = "Subnet"
|
||||
KeyNoNewPrivileges = "NoNewPrivileges"
|
||||
@ -111,6 +112,7 @@ const (
|
||||
KeyUserNS = "UserNS"
|
||||
KeyVolatileTmp = "VolatileTmp"
|
||||
KeyVolume = "Volume"
|
||||
KeyVolumeName = "VolumeName"
|
||||
KeyWorkingDir = "WorkingDir"
|
||||
KeyYaml = "Yaml"
|
||||
)
|
||||
@ -192,6 +194,7 @@ var (
|
||||
KeyPodmanArgs: true,
|
||||
KeyType: true,
|
||||
KeyUser: true,
|
||||
KeyVolumeName: true,
|
||||
}
|
||||
|
||||
// Supported keys in "Network" group
|
||||
@ -204,6 +207,7 @@ var (
|
||||
KeyNetworkIPRange: true,
|
||||
KeyNetworkIPv6: true,
|
||||
KeyNetworkInternal: true,
|
||||
KeyNetworkName: true,
|
||||
KeyNetworkOptions: true,
|
||||
KeyNetworkSubnet: true,
|
||||
KeyPodmanArgs: true,
|
||||
@ -300,7 +304,7 @@ func usernsOpts(kind string, opts []string) string {
|
||||
// service file (unit file with Service group) based on the options in the
|
||||
// Container group.
|
||||
// The original Container group is kept around as X-Container.
|
||||
func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile, error) {
|
||||
func ConvertContainer(container *parser.UnitFile, names map[string]string, isUser bool) (*parser.UnitFile, error) {
|
||||
service := container.Dup()
|
||||
service.Filename = replaceExtension(container.Filename, ".service", "", "")
|
||||
|
||||
@ -386,7 +390,7 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
||||
podman.addf("--tz=%s", timezone)
|
||||
}
|
||||
|
||||
addNetworks(container, ContainerGroup, service, podman)
|
||||
addNetworks(container, ContainerGroup, service, names, podman)
|
||||
|
||||
// Run with a pid1 init to reap zombies by default (as most apps don't do that)
|
||||
runInit, ok := container.LookupBoolean(ContainerGroup, KeyRunInit)
|
||||
@ -555,7 +559,7 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
||||
|
||||
if source != "" {
|
||||
var err error
|
||||
source, err = handleStorageSource(container, service, source)
|
||||
source, err = handleStorageSource(container, service, source, names)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -649,9 +653,9 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
||||
if paramType == "volume" || paramType == "bind" {
|
||||
var err error
|
||||
if paramSource, ok := paramsMap["source"]; ok {
|
||||
paramsMap["source"], err = handleStorageSource(container, service, paramSource)
|
||||
paramsMap["source"], err = handleStorageSource(container, service, paramSource, names)
|
||||
} else if paramSource, ok = paramsMap["src"]; ok {
|
||||
paramsMap["src"], err = handleStorageSource(container, service, paramSource)
|
||||
paramsMap["src"], err = handleStorageSource(container, service, paramSource, names)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -702,18 +706,24 @@ func ConvertContainer(container *parser.UnitFile, isUser bool) (*parser.UnitFile
|
||||
// service file (unit file with Service group) based on the options in the
|
||||
// Network group.
|
||||
// The original Network group is kept around as X-Network.
|
||||
func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, error) {
|
||||
// Also returns the canonical network name, either auto-generated or user-defined via the
|
||||
// NetworkName key-value.
|
||||
func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, string, error) {
|
||||
service := network.Dup()
|
||||
service.Filename = replaceExtension(network.Filename, ".service", "", "-network")
|
||||
|
||||
if err := checkForUnknownKeys(network, NetworkGroup, supportedNetworkKeys); err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
/* Rename old Network group to x-Network so that systemd ignores it */
|
||||
service.RenameGroup(NetworkGroup, XNetworkGroup)
|
||||
|
||||
networkName := replaceExtension(name, "", "systemd-", "")
|
||||
// Derive network name from unit name (with added prefix), or use user-provided name.
|
||||
networkName, ok := network.Lookup(NetworkGroup, KeyNetworkName)
|
||||
if !ok || len(networkName) == 0 {
|
||||
networkName = replaceExtension(name, "", "systemd-", "")
|
||||
}
|
||||
|
||||
// Need the containers filesystem mounted to start podman
|
||||
service.Add(UnitGroup, "RequiresMountsFor", "%t/containers")
|
||||
@ -734,10 +744,10 @@ func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, er
|
||||
ipRanges := network.LookupAll(NetworkGroup, KeyNetworkIPRange)
|
||||
if len(subnets) > 0 {
|
||||
if len(gateways) > len(subnets) {
|
||||
return nil, fmt.Errorf("cannot set more gateways than subnets")
|
||||
return nil, "", fmt.Errorf("cannot set more gateways than subnets")
|
||||
}
|
||||
if len(ipRanges) > len(subnets) {
|
||||
return nil, fmt.Errorf("cannot set more ranges than subnets")
|
||||
return nil, "", fmt.Errorf("cannot set more ranges than subnets")
|
||||
}
|
||||
for i := range subnets {
|
||||
podman.addf("--subnet=%s", subnets[i])
|
||||
@ -749,7 +759,7 @@ func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, er
|
||||
}
|
||||
}
|
||||
} else if len(ipRanges) > 0 || len(gateways) > 0 {
|
||||
return nil, fmt.Errorf("cannot set gateway or range without subnet")
|
||||
return nil, "", fmt.Errorf("cannot set gateway or range without subnet")
|
||||
}
|
||||
|
||||
if internal := network.LookupBooleanWithDefault(NetworkGroup, KeyNetworkInternal, false); internal {
|
||||
@ -786,25 +796,31 @@ func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, er
|
||||
// The default syslog identifier is the exec basename (podman) which isn't very useful here
|
||||
"SyslogIdentifier", "%N")
|
||||
|
||||
return service, nil
|
||||
return service, networkName, nil
|
||||
}
|
||||
|
||||
// Convert a quadlet volume file (unit file with a Volume group) to a systemd
|
||||
// service file (unit file with Service group) based on the options in the
|
||||
// Volume group.
|
||||
// The original Volume group is kept around as X-Volume.
|
||||
func ConvertVolume(volume *parser.UnitFile, name string) (*parser.UnitFile, error) {
|
||||
// Also returns the canonical volume name, either auto-generated or user-defined via the VolumeName
|
||||
// key-value.
|
||||
func ConvertVolume(volume *parser.UnitFile, name string) (*parser.UnitFile, string, error) {
|
||||
service := volume.Dup()
|
||||
service.Filename = replaceExtension(volume.Filename, ".service", "", "-volume")
|
||||
|
||||
if err := checkForUnknownKeys(volume, VolumeGroup, supportedVolumeKeys); err != nil {
|
||||
return nil, err
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
/* Rename old Volume group to x-Volume so that systemd ignores it */
|
||||
service.RenameGroup(VolumeGroup, XVolumeGroup)
|
||||
|
||||
volumeName := replaceExtension(name, "", "systemd-", "")
|
||||
// Derive volume name from unit name (with added prefix), or use user-provided name.
|
||||
volumeName, ok := volume.Lookup(VolumeGroup, KeyVolumeName)
|
||||
if !ok || len(volumeName) == 0 {
|
||||
volumeName = replaceExtension(name, "", "systemd-", "")
|
||||
}
|
||||
|
||||
// Need the containers filesystem mounted to start podman
|
||||
service.Add(UnitGroup, "RequiresMountsFor", "%t/containers")
|
||||
@ -854,7 +870,7 @@ func ConvertVolume(volume *parser.UnitFile, name string) (*parser.UnitFile, erro
|
||||
if devValid {
|
||||
podman.add("--opt", fmt.Sprintf("type=%s", devType))
|
||||
} else {
|
||||
return nil, fmt.Errorf("key Type can't be used without Device")
|
||||
return nil, "", fmt.Errorf("key Type can't be used without Device")
|
||||
}
|
||||
}
|
||||
|
||||
@ -866,7 +882,7 @@ func ConvertVolume(volume *parser.UnitFile, name string) (*parser.UnitFile, erro
|
||||
}
|
||||
opts.WriteString(mountOpts)
|
||||
} else {
|
||||
return nil, fmt.Errorf("key Options can't be used without Device")
|
||||
return nil, "", fmt.Errorf("key Options can't be used without Device")
|
||||
}
|
||||
}
|
||||
|
||||
@ -889,10 +905,10 @@ func ConvertVolume(volume *parser.UnitFile, name string) (*parser.UnitFile, erro
|
||||
// The default syslog identifier is the exec basename (podman) which isn't very useful here
|
||||
"SyslogIdentifier", "%N")
|
||||
|
||||
return service, nil
|
||||
return service, volumeName, nil
|
||||
}
|
||||
|
||||
func ConvertKube(kube *parser.UnitFile, isUser bool) (*parser.UnitFile, error) {
|
||||
func ConvertKube(kube *parser.UnitFile, names map[string]string, isUser bool) (*parser.UnitFile, error) {
|
||||
service := kube.Dup()
|
||||
service.Filename = replaceExtension(kube.Filename, ".service", "", "")
|
||||
|
||||
@ -964,7 +980,7 @@ func ConvertKube(kube *parser.UnitFile, isUser bool) (*parser.UnitFile, error) {
|
||||
|
||||
handleUserNS(kube, KubeGroup, execStart)
|
||||
|
||||
addNetworks(kube, KubeGroup, service, execStart)
|
||||
addNetworks(kube, KubeGroup, service, names, execStart)
|
||||
|
||||
updateMaps := kube.LookupAllStrv(KubeGroup, KeyAutoUpdate)
|
||||
for _, update := range updateMaps {
|
||||
@ -1082,14 +1098,17 @@ func handleUserNS(unitFile *parser.UnitFile, groupName string, podman *PodmanCmd
|
||||
}
|
||||
}
|
||||
|
||||
func addNetworks(quadletUnitFile *parser.UnitFile, groupName string, serviceUnitFile *parser.UnitFile, podman *PodmanCmdline) {
|
||||
func addNetworks(quadletUnitFile *parser.UnitFile, groupName string, serviceUnitFile *parser.UnitFile, names map[string]string, podman *PodmanCmdline) {
|
||||
networks := quadletUnitFile.LookupAll(groupName, KeyNetwork)
|
||||
for _, network := range networks {
|
||||
if len(network) > 0 {
|
||||
quadletNetworkName, options, found := strings.Cut(network, ":")
|
||||
if strings.HasSuffix(quadletNetworkName, ".network") {
|
||||
// the podman network name is systemd-$name
|
||||
networkName := replaceExtension(quadletNetworkName, "", "systemd-", "")
|
||||
// the podman network name is systemd-$name if none is specified by the user.
|
||||
networkName := names[quadletNetworkName]
|
||||
if networkName == "" {
|
||||
networkName = replaceExtension(quadletNetworkName, "", "systemd-", "")
|
||||
}
|
||||
|
||||
// the systemd unit name is $name-network.service
|
||||
networkServiceName := replaceExtension(quadletNetworkName, ".service", "", "-network")
|
||||
@ -1209,7 +1228,7 @@ func handleLogDriver(unitFile *parser.UnitFile, groupName string, podman *Podman
|
||||
}
|
||||
}
|
||||
|
||||
func handleStorageSource(quadletUnitFile, serviceUnitFile *parser.UnitFile, source string) (string, error) {
|
||||
func handleStorageSource(quadletUnitFile, serviceUnitFile *parser.UnitFile, source string, names map[string]string) (string, error) {
|
||||
if source[0] == '.' {
|
||||
var err error
|
||||
source, err = getAbsolutePath(quadletUnitFile, source)
|
||||
@ -1221,8 +1240,11 @@ func handleStorageSource(quadletUnitFile, serviceUnitFile *parser.UnitFile, sour
|
||||
// Absolute path
|
||||
serviceUnitFile.Add(UnitGroup, "RequiresMountsFor", source)
|
||||
} else if strings.HasSuffix(source, ".volume") {
|
||||
// the podman volume name is systemd-$name
|
||||
volumeName := replaceExtension(source, "", "systemd-", "")
|
||||
// the podman volume name is systemd-$name if none has been provided by the user.
|
||||
volumeName := names[source]
|
||||
if volumeName == "" {
|
||||
volumeName = replaceExtension(source, "", "systemd-", "")
|
||||
}
|
||||
|
||||
// the systemd unit name is $name-volume.service
|
||||
volumeServiceName := replaceExtension(source, ".service", "", "-volume")
|
||||
|
4
test/e2e/quadlet/name.network
Normal file
4
test/e2e/quadlet/name.network
Normal file
@ -0,0 +1,4 @@
|
||||
## assert-podman-final-args "test-network"
|
||||
|
||||
[Network]
|
||||
NetworkName=test-network
|
4
test/e2e/quadlet/name.volume
Normal file
4
test/e2e/quadlet/name.volume
Normal file
@ -0,0 +1,4 @@
|
||||
## assert-podman-final-args "test-volume"
|
||||
|
||||
[Volume]
|
||||
VolumeName=test-volume
|
@ -606,6 +606,7 @@ BOGUS=foo
|
||||
Entry("device-copy.volume", "device-copy.volume", 0, ""),
|
||||
Entry("device.volume", "device.volume", 0, ""),
|
||||
Entry("label.volume", "label.volume", 0, ""),
|
||||
Entry("name.volume", "name.volume", 0, ""),
|
||||
Entry("podmanargs.volume", "podmanargs.volume", 0, ""),
|
||||
Entry("uid.volume", "uid.volume", 0, ""),
|
||||
|
||||
@ -635,6 +636,7 @@ BOGUS=foo
|
||||
Entry("Network - Internal network", "internal.network", 0, ""),
|
||||
Entry("Network - Label", "label.network", 0, ""),
|
||||
Entry("Network - Multiple Options", "options.multiple.network", 0, ""),
|
||||
Entry("Network - Name", "name.network", 0, ""),
|
||||
Entry("Network - Options", "options.network", 0, ""),
|
||||
Entry("Network - PodmanArgs", "podmanargs.network", 0, ""),
|
||||
Entry("Network - Range not enough Subnet", "range.less-subnet.network", 1, "converting \"range.less-subnet.network\": cannot set more ranges than subnets"),
|
||||
|
@ -47,8 +47,11 @@ function run_quadlet() {
|
||||
local service=$(quadlet_to_service_name "$sourcefile")
|
||||
|
||||
# quadlet always works on an entire directory, so copy the file
|
||||
# to transform to a tmpdir
|
||||
local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
|
||||
# to transform to the given or newly created tmpdir
|
||||
local quadlet_tmpdir="$2"
|
||||
if [ -z "$quadlet_tmpdir" ]; then
|
||||
quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
|
||||
fi
|
||||
cp $sourcefile $quadlet_tmpdir/
|
||||
|
||||
echo "$_LOG_PROMPT $QUADLET $_DASHUSER $UNIT_DIR"
|
||||
@ -337,6 +340,61 @@ EOF
|
||||
run_podman volume rm $volume_name
|
||||
}
|
||||
|
||||
# A quadlet container depends on a named quadlet volume
|
||||
@test "quadlet - named volume dependency" {
|
||||
# Save the unit name to use as the volume for the container
|
||||
local quadlet_vol_unit=dep_$(random_string).volume
|
||||
local quadlet_vol_file=$PODMAN_TMPDIR/${quadlet_vol_unit}
|
||||
cat > $quadlet_vol_file <<EOF
|
||||
[Volume]
|
||||
VolumeName=foo
|
||||
EOF
|
||||
|
||||
# Have quadlet create the systemd unit file for the volume unit
|
||||
local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
|
||||
run_quadlet "$quadlet_vol_file" "$quadlet_tmpdir"
|
||||
|
||||
# Save the volume service name since the variable will be overwritten
|
||||
local vol_service=$QUADLET_SERVICE_NAME
|
||||
local volume_name="foo"
|
||||
|
||||
local quadlet_file=$PODMAN_TMPDIR/user_$(random_string).container
|
||||
cat > $quadlet_file <<EOF
|
||||
[Container]
|
||||
Image=$IMAGE
|
||||
Exec=top
|
||||
Volume=$quadlet_vol_unit:/tmp
|
||||
EOF
|
||||
|
||||
# Have quadlet create the systemd unit file for the container unit
|
||||
run_quadlet "$quadlet_file" "$quadlet_tmpdir"
|
||||
|
||||
# Save the container service name for readability
|
||||
local container_service=$QUADLET_SERVICE_NAME
|
||||
local container_name=systemd-$(basename $quadlet_file .container)
|
||||
|
||||
# Volume should not exist
|
||||
run_podman 1 volume exists ${volume_name}
|
||||
|
||||
# Start the container service which should also trigger the start of the volume service
|
||||
service_setup $container_service
|
||||
|
||||
# Volume system unit should be active
|
||||
run systemctl show --property=ActiveState "$vol_service"
|
||||
assert "$output" = "ActiveState=active" "volume should be active via dependency"
|
||||
|
||||
# Volume should exist
|
||||
run_podman volume exists ${volume_name}
|
||||
|
||||
# Container should be attached to defined volume
|
||||
run_podman container inspect --format "{{(index .Mounts 0).Name}}" $container_name
|
||||
assert "$output" = "$volume_name" "container should be attached to network $volume_name"
|
||||
|
||||
# Shutdown the service and remove the volume
|
||||
service_cleanup $container_service failed
|
||||
run_podman volume rm $volume_name
|
||||
}
|
||||
|
||||
@test "quadlet - network" {
|
||||
local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).network
|
||||
cat > $quadlet_file <<EOF
|
||||
@ -405,6 +463,58 @@ EOF
|
||||
run_podman network rm $network_name
|
||||
}
|
||||
|
||||
# A quadlet container depends on a named quadlet network
|
||||
@test "quadlet - named network dependency" {
|
||||
# Save the unit name to use as the network for the container
|
||||
local quadlet_network_unit=dep_$(random_string).network
|
||||
local quadlet_network_file=$PODMAN_TMPDIR/${quadlet_network_unit}
|
||||
cat > $quadlet_network_file <<EOF
|
||||
[Network]
|
||||
NetworkName=foo
|
||||
EOF
|
||||
|
||||
# Have quadlet create the systemd unit file for the network unit
|
||||
local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
|
||||
run_quadlet "$quadlet_network_file" "$quadlet_tmpdir"
|
||||
|
||||
# Save the network service name since the variable will be overwritten
|
||||
local network_service=$QUADLET_SERVICE_NAME
|
||||
local network_name="foo"
|
||||
|
||||
local quadlet_file=$PODMAN_TMPDIR/user_$(random_string).container
|
||||
cat > $quadlet_file <<EOF
|
||||
[Container]
|
||||
Image=$IMAGE
|
||||
Exec=top
|
||||
Network=$quadlet_network_unit
|
||||
EOF
|
||||
|
||||
run_quadlet "$quadlet_file" "$quadlet_tmpdir"
|
||||
|
||||
# Save the container service name for readability
|
||||
local container_service=$QUADLET_SERVICE_NAME
|
||||
local container_name=systemd-$(basename $quadlet_file .container)
|
||||
|
||||
# Network should not exist
|
||||
run_podman 1 network exists $network_name
|
||||
|
||||
service_setup $container_service
|
||||
|
||||
# Network system unit should be active
|
||||
run systemctl show --property=ActiveState "$network_service"
|
||||
assert "$output" = "ActiveState=active" "network should be active via dependency"
|
||||
|
||||
# Network should exist
|
||||
run_podman network exists $network_name
|
||||
|
||||
# Container should be attached to defined network
|
||||
run_podman container inspect --format "{{index .NetworkSettings.Networks \"$network_name\"}}" $container_name
|
||||
assert "$output" != "<nil>" "container should be attached to network $network_name"
|
||||
|
||||
service_cleanup $QUADLET_SERVICE_NAME failed
|
||||
run_podman network rm $network_name
|
||||
}
|
||||
|
||||
@test "quadlet kube - basic" {
|
||||
# Create the YAMl file
|
||||
yaml_source="$PODMAN_TMPDIR/basic_$(random_string).yaml"
|
||||
@ -444,6 +554,77 @@ EOF
|
||||
run_podman rmi $(pause_image)
|
||||
}
|
||||
|
||||
@test "quadlet kube - named network dependency" {
|
||||
# Save the unit name to use as the network for the container
|
||||
local quadlet_network_unit=dep_$(random_string).network
|
||||
local quadlet_network_file=$PODMAN_TMPDIR/${quadlet_network_unit}
|
||||
cat > $quadlet_network_file <<EOF
|
||||
[Network]
|
||||
NetworkName=foo
|
||||
EOF
|
||||
|
||||
# Have quadlet create the systemd unit file for the network unit
|
||||
local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX)
|
||||
run_quadlet "$quadlet_network_file" "$quadlet_tmpdir"
|
||||
|
||||
# Save the network service name since the variable will be overwritten
|
||||
local network_service=$QUADLET_SERVICE_NAME
|
||||
local network_name="foo"
|
||||
|
||||
# Create the YAMl file
|
||||
yaml_source="$PODMAN_TMPDIR/basic_$(random_string).yaml"
|
||||
cat >$yaml_source <<EOF
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
app: test
|
||||
name: test_pod
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- top
|
||||
image: $IMAGE
|
||||
name: test
|
||||
EOF
|
||||
|
||||
# Create the Quadlet file
|
||||
local quadlet_file=$PODMAN_TMPDIR/basic_$(random_string).kube
|
||||
cat > $quadlet_file <<EOF
|
||||
[Kube]
|
||||
Yaml=${yaml_source}
|
||||
Network=$quadlet_network_unit
|
||||
EOF
|
||||
|
||||
# Network should not exist
|
||||
run_podman 1 network exists $network_name
|
||||
|
||||
run_quadlet "$quadlet_file" "$quadlet_tmpdir"
|
||||
service_setup $QUADLET_SERVICE_NAME
|
||||
|
||||
# Network system unit should be active
|
||||
run systemctl show --property=ActiveState "$network_service"
|
||||
assert "$output" = "ActiveState=active" "network should be active via dependency"
|
||||
|
||||
# Network should exist
|
||||
run_podman network exists $network_name
|
||||
|
||||
# Ensure we have output. Output is synced via sd-notify (socat in Exec)
|
||||
run journalctl "--since=$STARTED_TIME" --unit="$QUADLET_SERVICE_NAME"
|
||||
assert "$output" =~ '.*Started.*\.service.*'
|
||||
|
||||
run_podman container inspect --format "{{.State.Status}}" test_pod-test
|
||||
assert "$output" =~ "running" "container should be started by systemd and hence be running"
|
||||
|
||||
# Container should be attached to defined network
|
||||
run_podman container inspect --format "{{index .NetworkSettings.Networks \"$network_name\"}}" test_pod-test
|
||||
assert "$output" != "<nil>" "container should be attached to network $network_name"
|
||||
|
||||
service_cleanup $QUADLET_SERVICE_NAME inactive
|
||||
run_podman network rm $network_name
|
||||
run_podman rmi $(pause_image)
|
||||
}
|
||||
|
||||
@test "quadlet - rootfs" {
|
||||
skip_if_no_selinux
|
||||
skip_if_rootless
|
||||
|
Reference in New Issue
Block a user