From 795851edd3d9fd670ea489e9a8aec059c98e1ff4 Mon Sep 17 00:00:00 2001 From: Ygal Blum Date: Mon, 5 Aug 2024 07:12:54 +0300 Subject: [PATCH] Quadlet - Allow the user to override the default service name Add support for the ServiceName key for all unit types Extend the PodInfo struct into UnitInfo to consolidate all prepopulated data into a single map Use the NodesInfo map instead of the resourceName Update the UnitInfo in the convert function instead of returning it No need to replace extension anymore just remove it All e2e tests with dependencies on other Quadlet files moved to a separate section Add the capability of overriding the service name in the test Add e2e tests for the new functionality Adjust integration tests Update the MAN page Signed-off-by: Ygal Blum --- cmd/quadlet/main.go | 84 +++--- pkg/systemd/quadlet/quadlet.go | 277 ++++++++++++------ .../quadlet/build.quadlet.servicename.volume | 8 + .../quadlet/image.quadlet.servicename.volume | 8 + test/e2e/quadlet/mount.container | 8 +- test/e2e/quadlet/mount.servicename.container | 6 + .../quadlet/network.quadlet.servicename.build | 8 + .../network.quadlet.servicename.container | 7 + .../quadlet/network.quadlet.servicename.kube | 8 + .../quadlet/network.servicename.quadlet.pod | 6 + test/e2e/quadlet/service-name.build | 6 + test/e2e/quadlet/service-name.container | 5 + test/e2e/quadlet/service-name.image | 5 + test/e2e/quadlet/service-name.kube | 5 + test/e2e/quadlet/service-name.network | 5 + test/e2e/quadlet/service-name.pod | 2 +- test/e2e/quadlet/service-name.volume | 5 + test/e2e/quadlet/volume.build | 4 +- test/e2e/quadlet/volume.container | 4 +- test/e2e/quadlet/volume.pod | 4 +- .../quadlet/volume.quadlet.servicename.build | 8 + test/e2e/quadlet/volume.servicename.container | 6 + test/e2e/quadlet/volume.servicename.pod | 6 + test/e2e/quadlet_test.go | 85 ++++-- test/system/252-quadlet.bats | 10 +- 25 files changed, 409 insertions(+), 171 deletions(-) create mode 100644 test/e2e/quadlet/build.quadlet.servicename.volume create mode 100644 test/e2e/quadlet/image.quadlet.servicename.volume create mode 100644 test/e2e/quadlet/mount.servicename.container create mode 100644 test/e2e/quadlet/network.quadlet.servicename.build create mode 100644 test/e2e/quadlet/network.quadlet.servicename.container create mode 100644 test/e2e/quadlet/network.quadlet.servicename.kube create mode 100644 test/e2e/quadlet/network.servicename.quadlet.pod create mode 100644 test/e2e/quadlet/service-name.build create mode 100644 test/e2e/quadlet/service-name.container create mode 100644 test/e2e/quadlet/service-name.image create mode 100644 test/e2e/quadlet/service-name.kube create mode 100644 test/e2e/quadlet/service-name.network create mode 100644 test/e2e/quadlet/service-name.volume create mode 100644 test/e2e/quadlet/volume.quadlet.servicename.build create mode 100644 test/e2e/quadlet/volume.servicename.container create mode 100644 test/e2e/quadlet/volume.servicename.pod diff --git a/cmd/quadlet/main.go b/cmd/quadlet/main.go index fd5a792308..7a59f66740 100644 --- a/cmd/quadlet/main.go +++ b/cmd/quadlet/main.go @@ -486,34 +486,47 @@ func warnIfAmbiguousName(unit *parser.UnitFile, group string) { } } -func generatePodsInfoMap(units []*parser.UnitFile) map[string]*quadlet.PodInfo { - podsInfoMap := make(map[string]*quadlet.PodInfo) +func generateUnitsInfoMap(units []*parser.UnitFile) map[string]*quadlet.UnitInfo { + unitsInfoMap := make(map[string]*quadlet.UnitInfo) for _, unit := range units { - if !strings.HasSuffix(unit.Filename, ".pod") { + var serviceName string + var containers []string + var resourceName string + + switch { + case strings.HasSuffix(unit.Filename, ".container"): + serviceName = quadlet.GetContainerServiceName(unit) + case strings.HasSuffix(unit.Filename, ".volume"): + serviceName = quadlet.GetVolumeServiceName(unit) + case strings.HasSuffix(unit.Filename, ".kube"): + serviceName = quadlet.GetKubeServiceName(unit) + case strings.HasSuffix(unit.Filename, ".network"): + serviceName = quadlet.GetNetworkServiceName(unit) + case strings.HasSuffix(unit.Filename, ".image"): + serviceName = quadlet.GetImageServiceName(unit) + case strings.HasSuffix(unit.Filename, ".build"): + serviceName = quadlet.GetBuildServiceName(unit) + // 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) + resourceName = quadlet.GetBuiltImageName(unit) + case strings.HasSuffix(unit.Filename, ".pod"): + serviceName = quadlet.GetPodServiceName(unit) + containers = make([]string, 0) + default: + Logf("Unsupported file type %q", unit.Filename) continue } - serviceName := quadlet.GetPodServiceName(unit) - podsInfoMap[unit.Filename] = &quadlet.PodInfo{ - ServiceName: serviceName, - Containers: make([]string, 0), + unitsInfoMap[unit.Filename] = &quadlet.UnitInfo{ + ServiceName: serviceName, + Containers: containers, + ResourceName: resourceName, } } - 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 - } - } + return unitsInfoMap } func main() { @@ -612,40 +625,30 @@ func process() error { }) // Generate the PodsInfoMap to allow containers to link to their pods and add themselves to the pod's containers list - podsInfoMap := generatePodsInfoMap(units) - - // 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) + unitsInfoMap := generateUnitsInfoMap(units) for _, unit := range units { var service *parser.UnitFile - var name string var err error switch { case strings.HasSuffix(unit.Filename, ".container"): warnIfAmbiguousName(unit, quadlet.ContainerGroup) - service, err = quadlet.ConvertContainer(unit, resourceNames, isUserFlag, podsInfoMap) + service, err = quadlet.ConvertContainer(unit, isUserFlag, unitsInfoMap) case strings.HasSuffix(unit.Filename, ".volume"): warnIfAmbiguousName(unit, quadlet.VolumeGroup) - service, name, err = quadlet.ConvertVolume(unit, unit.Filename, resourceNames) + service, err = quadlet.ConvertVolume(unit, unit.Filename, unitsInfoMap) case strings.HasSuffix(unit.Filename, ".kube"): - service, err = quadlet.ConvertKube(unit, resourceNames, isUserFlag) + service, err = quadlet.ConvertKube(unit, unitsInfoMap, isUserFlag) case strings.HasSuffix(unit.Filename, ".network"): - service, name, err = quadlet.ConvertNetwork(unit, unit.Filename) + service, err = quadlet.ConvertNetwork(unit, unit.Filename, unitsInfoMap) case strings.HasSuffix(unit.Filename, ".image"): warnIfAmbiguousName(unit, quadlet.ImageGroup) - service, name, err = quadlet.ConvertImage(unit) + service, err = quadlet.ConvertImage(unit, unitsInfoMap) case strings.HasSuffix(unit.Filename, ".build"): - service, name, err = quadlet.ConvertBuild(unit, resourceNames) + service, err = quadlet.ConvertBuild(unit, unitsInfoMap) case strings.HasSuffix(unit.Filename, ".pod"): - service, err = quadlet.ConvertPod(unit, unit.Filename, podsInfoMap, resourceNames) + service, err = quadlet.ConvertPod(unit, unit.Filename, unitsInfoMap) default: Logf("Unsupported file type %q", unit.Filename) continue @@ -656,9 +659,6 @@ func process() error { continue } - if name != "" { - resourceNames[unit.Filename] = name - } service.Path = path.Join(outputPath, service.Filename) if dryRunFlag { diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 4beaf081be..2762f7d06a 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -170,9 +170,15 @@ const ( KeyYaml = "Yaml" ) -type PodInfo struct { +type UnitInfo struct { + // The name of the generated systemd service unit ServiceName string - Containers []string + // The name of the podman resource created by the service + ResourceName string + + // For .pod units + // List of containers in a pod + Containers []string } var ( @@ -245,6 +251,7 @@ var ( KeySecurityLabelLevel: true, KeySecurityLabelNested: true, KeySecurityLabelType: true, + KeyServiceName: true, KeyShmSize: true, KeyStopSignal: true, KeyStopTimeout: true, @@ -275,6 +282,7 @@ var ( KeyLabel: true, KeyOptions: true, KeyPodmanArgs: true, + KeyServiceName: true, KeyType: true, KeyUser: true, KeyVolumeName: true, @@ -295,6 +303,7 @@ var ( KeyInternal: true, KeyNetworkName: true, KeyOptions: true, + KeyServiceName: true, KeySubnet: true, KeyPodmanArgs: true, } @@ -316,6 +325,7 @@ var ( KeyRemapUid: true, KeyRemapUidSize: true, KeyRemapUsers: true, + KeyServiceName: true, KeySetWorkingDirectory: true, KeyUserNS: true, KeyYaml: true, @@ -335,6 +345,7 @@ var ( KeyImageTag: true, KeyOS: true, KeyPodmanArgs: true, + KeyServiceName: true, KeyTLSVerify: true, KeyVariant: true, } @@ -359,6 +370,7 @@ var ( KeyPodmanArgs: true, KeyPull: true, KeySecret: true, + KeyServiceName: true, KeySetWorkingDirectory: true, KeyTarget: true, KeyTLSVerify: true, @@ -379,7 +391,11 @@ var ( } ) -func replaceExtension(name string, extension string, extraPrefix string, extraSuffix string) string { +func (u *UnitInfo) ServiceFileName() string { + return fmt.Sprintf("%s.service", u.ServiceName) +} + +func removeExtension(name string, extraPrefix string, extraSuffix string) string { baseName := name dot := strings.LastIndexByte(name, '.') @@ -387,7 +403,7 @@ func replaceExtension(name string, extension string, extraPrefix string, extraSu baseName = name[:dot] } - return extraPrefix + baseName + extraSuffix + extension + return extraPrefix + baseName + extraSuffix } func isURL(urlCandidate string) bool { @@ -456,9 +472,14 @@ 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, names map[string]string, isUser bool, podsInfoMap map[string]*PodInfo) (*parser.UnitFile, error) { +func ConvertContainer(container *parser.UnitFile, isUser bool, unitsInfoMap map[string]*UnitInfo) (*parser.UnitFile, error) { + unitInfo, ok := unitsInfoMap[container.Filename] + if !ok { + return nil, fmt.Errorf("internal error while processing container %s", container.Filename) + } + service := container.Dup() - service.Filename = replaceExtension(container.Filename, ".service", "", "") + service.Filename = unitInfo.ServiceFileName() // Add a dependency on network-online.target so the image pull does not happen // before network is ready @@ -491,7 +512,7 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse if len(image) > 0 { var err error - if image, err = handleImageSource(image, service, names); err != nil { + if image, err = handleImageSource(image, service, unitsInfoMap); err != nil { return nil, err } } @@ -567,7 +588,9 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse podman.addf("--tz=%s", timezone) } - addNetworks(container, ContainerGroup, service, names, podman) + if err := addNetworks(container, ContainerGroup, service, unitsInfoMap, podman); err != nil { + return nil, err + } networkAliases := container.LookupAll(ContainerGroup, KeyNetworkAlias) for _, networkAlias := range networkAliases { @@ -753,7 +776,7 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse podman.add("--tmpfs", tmpfs) } - if err := addVolumes(container, service, ContainerGroup, names, podman); err != nil { + if err := addVolumes(container, service, ContainerGroup, unitsInfoMap, podman); err != nil { return nil, err } @@ -827,7 +850,7 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse mounts := container.LookupAllArgs(ContainerGroup, KeyMount) for _, mount := range mounts { - mountStr, err := resolveContainerMountParams(container, service, mount, names) + mountStr, err := resolveContainerMountParams(container, service, mount, unitsInfoMap) if err != nil { return nil, err } @@ -845,7 +868,7 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse podman.add("--pull", pull) } - if err := handlePod(container, service, ContainerGroup, podsInfoMap, podman); err != nil { + if err := handlePod(container, service, ContainerGroup, unitsInfoMap, podman); err != nil { return nil, err } @@ -881,12 +904,17 @@ func ConvertContainer(container *parser.UnitFile, names map[string]string, isUse // The original Network group is kept around as X-Network. // 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) { +func ConvertNetwork(network *parser.UnitFile, name string, unitsInfoMap map[string]*UnitInfo) (*parser.UnitFile, error) { + unitInfo, ok := unitsInfoMap[network.Filename] + if !ok { + return nil, fmt.Errorf("internal error while processing network %s", network.Filename) + } + service := network.Dup() - service.Filename = replaceExtension(network.Filename, ".service", "", "-network") + service.Filename = unitInfo.ServiceFileName() 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 */ @@ -895,7 +923,7 @@ func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, st // 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-", "") + networkName = removeExtension(name, "systemd-", "") } // Need the containers filesystem mounted to start podman @@ -924,10 +952,10 @@ func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, st ipRanges := network.LookupAll(NetworkGroup, KeyIPRange) 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]) @@ -939,7 +967,7 @@ func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, st } } } 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, KeyInternal, false); internal { @@ -976,7 +1004,9 @@ func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, st // The default syslog identifier is the exec basename (podman) which isn't very useful here "SyslogIdentifier", "%N") - return service, networkName, nil + // Store the name of the created resource + unitInfo.ResourceName = networkName + return service, nil } // Convert a quadlet volume file (unit file with a Volume group) to a systemd @@ -985,12 +1015,17 @@ func ConvertNetwork(network *parser.UnitFile, name string) (*parser.UnitFile, st // The original Volume group is kept around as X-Volume. // Also returns the canonical volume name, either auto-generated or user-defined via the VolumeName // key-value. -func ConvertVolume(volume *parser.UnitFile, name string, names map[string]string) (*parser.UnitFile, string, error) { +func ConvertVolume(volume *parser.UnitFile, name string, unitsInfoMap map[string]*UnitInfo) (*parser.UnitFile, error) { + unitInfo, ok := unitsInfoMap[volume.Filename] + if !ok { + return nil, fmt.Errorf("internal error while processing network %s", volume.Filename) + } + service := volume.Dup() - service.Filename = replaceExtension(volume.Filename, ".service", "", "-volume") + service.Filename = unitInfo.ServiceFileName() 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 */ @@ -999,7 +1034,7 @@ func ConvertVolume(volume *parser.UnitFile, name string, names map[string]string // 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-", "") + volumeName = removeExtension(name, "systemd-", "") } // Need the containers filesystem mounted to start podman @@ -1023,11 +1058,11 @@ func ConvertVolume(volume *parser.UnitFile, name string, names map[string]string imageName, ok := volume.Lookup(VolumeGroup, KeyImage) if !ok { - return nil, "", fmt.Errorf("the key %s is mandatory when using the image driver", KeyImage) + return nil, fmt.Errorf("the key %s is mandatory when using the image driver", KeyImage) } - imageName, err := handleImageSource(imageName, service, names) + imageName, err := handleImageSource(imageName, service, unitsInfoMap) if err != nil { - return nil, "", err + return nil, err } opts.WriteString(imageName) @@ -1072,7 +1107,7 @@ func ConvertVolume(volume *parser.UnitFile, name string, names map[string]string 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") } } @@ -1084,7 +1119,7 @@ func ConvertVolume(volume *parser.UnitFile, name string, names map[string]string } 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") } } } @@ -1108,12 +1143,20 @@ func ConvertVolume(volume *parser.UnitFile, name string, names map[string]string // The default syslog identifier is the exec basename (podman) which isn't very useful here "SyslogIdentifier", "%N") - return service, volumeName, nil + // Store the name of the created resource + unitInfo.ResourceName = volumeName + + return service, nil } -func ConvertKube(kube *parser.UnitFile, names map[string]string, isUser bool) (*parser.UnitFile, error) { +func ConvertKube(kube *parser.UnitFile, unitsInfoMap map[string]*UnitInfo, isUser bool) (*parser.UnitFile, error) { + unitInfo, ok := unitsInfoMap[kube.Filename] + if !ok { + return nil, fmt.Errorf("internal error while processing network %s", kube.Filename) + } + service := kube.Dup() - service.Filename = replaceExtension(kube.Filename, ".service", "", "") + service.Filename = unitInfo.ServiceFileName() if kube.Path != "" { service.Add(UnitGroup, "SourcePath", kube.Path) @@ -1192,7 +1235,9 @@ func ConvertKube(kube *parser.UnitFile, names map[string]string, isUser bool) (* return nil, err } - addNetworks(kube, KubeGroup, service, names, execStart) + if err := addNetworks(kube, KubeGroup, service, unitsInfoMap, execStart); err != nil { + return nil, err + } updateMaps := kube.LookupAllStrv(KubeGroup, KeyAutoUpdate) for _, update := range updateMaps { @@ -1246,9 +1291,14 @@ func ConvertKube(kube *parser.UnitFile, names map[string]string, isUser bool) (* return service, nil } -func ConvertImage(image *parser.UnitFile) (*parser.UnitFile, string, error) { +func ConvertImage(image *parser.UnitFile, unitsInfoMap map[string]*UnitInfo) (*parser.UnitFile, error) { + unitInfo, ok := unitsInfoMap[image.Filename] + if !ok { + return nil, fmt.Errorf("internal error while processing network %s", image.Filename) + } + service := image.Dup() - service.Filename = replaceExtension(image.Filename, ".service", "", "-image") + service.Filename = unitInfo.ServiceFileName() // Add a dependency on network-online.target so the image pull does not happen // before network is ready @@ -1263,12 +1313,12 @@ func ConvertImage(image *parser.UnitFile) (*parser.UnitFile, string, error) { } if err := checkForUnknownKeys(image, ImageGroup, supportedImageKeys); err != nil { - return nil, "", err + return nil, err } imageName, ok := image.Lookup(ImageGroup, KeyImage) if !ok || len(imageName) == 0 { - return nil, "", fmt.Errorf("no Image key specified") + return nil, fmt.Errorf("no Image key specified") } /* Rename old Network group to x-Network so that systemd ignores it */ @@ -1321,12 +1371,25 @@ func ConvertImage(image *parser.UnitFile) (*parser.UnitFile, string, error) { imageName = name } - return service, imageName, nil + // Store the name of the created resource + unitInfo.ResourceName = imageName + + return service, nil } -func ConvertBuild(build *parser.UnitFile, names map[string]string) (*parser.UnitFile, string, error) { +func ConvertBuild(build *parser.UnitFile, unitsInfoMap map[string]*UnitInfo) (*parser.UnitFile, error) { + unitInfo, ok := unitsInfoMap[build.Filename] + if !ok { + return nil, fmt.Errorf("internal error while processing network %s", build.Filename) + } + + // Fast fail is ResouceName is not set + if len(unitInfo.ResourceName) == 0 { + return nil, fmt.Errorf("no ImageTag key specified") + } + service := build.Dup() - service.Filename = replaceExtension(build.Filename, ".service", "", "-build") + service.Filename = unitInfo.ServiceFileName() // Add a dependency on network-online.target so the image pull does not happen // before network is ready @@ -1347,7 +1410,7 @@ func ConvertBuild(build *parser.UnitFile, names map[string]string) (*parser.Unit } if err := checkForUnknownKeys(build, BuildGroup, supportedBuildKeys); err != nil { - return nil, "", err + return nil, err } podman := createBasePodmanCommand(build, BuildGroup) @@ -1405,22 +1468,19 @@ func ConvertBuild(build *parser.UnitFile, names map[string]string) (*parser.Unit 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", unitInfo.ResourceName) + + if err := addNetworks(build, BuildGroup, service, unitsInfoMap, podman); err != nil { + return nil, err } - 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 + if err := addVolumes(build, service, BuildGroup, unitsInfoMap, podman); err != nil { + return nil, err } // In order to build an image locally, we need either a File key pointing directly at a @@ -1429,13 +1489,13 @@ func ConvertBuild(build *parser.UnitFile, names map[string]string) (*parser.Unit // an archive. context, err := handleSetWorkingDirectory(build, service, BuildGroup) if err != nil { - return nil, "", err + 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") + return nil, fmt.Errorf("neither SetWorkingDirectory, nor File key specified") } if len(filePath) > 0 { @@ -1450,7 +1510,7 @@ func ConvertBuild(build *parser.UnitFile, names map[string]string) (*parser.Unit } 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") + return nil, fmt.Errorf("relative path in File key requires SetWorkingDirectory key to be set") } podman.add(workingDirectory) } @@ -1465,7 +1525,7 @@ func ConvertBuild(build *parser.UnitFile, names map[string]string) (*parser.Unit // which isn't very useful here "SyslogIdentifier", "%N") - return service, builtImageName, nil + return service, nil } func GetBuiltImageName(buildUnit *parser.UnitFile) string { @@ -1475,21 +1535,49 @@ func GetBuiltImageName(buildUnit *parser.UnitFile) string { return "" } -func GetPodServiceName(podUnit *parser.UnitFile) string { - if serviceName, ok := podUnit.Lookup(PodGroup, KeyServiceName); ok { - return serviceName - } - return replaceExtension(podUnit.Filename, "", "", "-pod") +func GetContainerServiceName(podUnit *parser.UnitFile) string { + return getServiceName(podUnit, ContainerGroup, "") } -func ConvertPod(podUnit *parser.UnitFile, name string, podsInfoMap map[string]*PodInfo, names map[string]string) (*parser.UnitFile, error) { - podInfo, ok := podsInfoMap[podUnit.Filename] +func GetKubeServiceName(podUnit *parser.UnitFile) string { + return getServiceName(podUnit, KubeGroup, "") +} + +func GetVolumeServiceName(podUnit *parser.UnitFile) string { + return getServiceName(podUnit, VolumeGroup, "-volume") +} + +func GetNetworkServiceName(podUnit *parser.UnitFile) string { + return getServiceName(podUnit, NetworkGroup, "-network") +} + +func GetImageServiceName(podUnit *parser.UnitFile) string { + return getServiceName(podUnit, ImageGroup, "-image") +} + +func GetBuildServiceName(podUnit *parser.UnitFile) string { + return getServiceName(podUnit, BuildGroup, "-build") +} + +func GetPodServiceName(podUnit *parser.UnitFile) string { + return getServiceName(podUnit, PodGroup, "-pod") +} + +func getServiceName(quadletUnitFile *parser.UnitFile, groupName string, defaultExtraSuffix string) string { + if serviceName, ok := quadletUnitFile.Lookup(groupName, KeyServiceName); ok { + return serviceName + } + return removeExtension(quadletUnitFile.Filename, "", defaultExtraSuffix) +} + +func ConvertPod(podUnit *parser.UnitFile, name string, unitsInfoMap map[string]*UnitInfo) (*parser.UnitFile, error) { + unitInfo, ok := unitsInfoMap[podUnit.Filename] if !ok { return nil, fmt.Errorf("internal error while processing pod %s", podUnit.Filename) } service := podUnit.Dup() - service.Filename = fmt.Sprintf("%s.service", podInfo.ServiceName) + service.Filename = unitInfo.ServiceFileName() if podUnit.Path != "" { service.Add(UnitGroup, "SourcePath", podUnit.Path) @@ -1502,7 +1590,7 @@ func ConvertPod(podUnit *parser.UnitFile, name string, podsInfoMap map[string]*P // Derive pod name from unit name (with added prefix), or use user-provided name. podName, ok := podUnit.Lookup(PodGroup, KeyPodName) if !ok || len(podName) == 0 { - podName = replaceExtension(name, "", "systemd-", "") + podName = removeExtension(name, "systemd-", "") } /* Rename old Pod group to x-Pod so that systemd ignores it */ @@ -1511,7 +1599,7 @@ func ConvertPod(podUnit *parser.UnitFile, name string, podsInfoMap map[string]*P // Need the containers filesystem mounted to start podman service.Add(UnitGroup, "RequiresMountsFor", "%t/containers") - for _, containerService := range podInfo.Containers { + for _, containerService := range unitInfo.Containers { service.Add(UnitGroup, "Wants", containerService) service.Add(UnitGroup, "Before", containerService) } @@ -1555,14 +1643,16 @@ func ConvertPod(podUnit *parser.UnitFile, name string, podsInfoMap map[string]*P return nil, err } - addNetworks(podUnit, PodGroup, service, names, execStartPre) + if err := addNetworks(podUnit, PodGroup, service, unitsInfoMap, execStartPre); err != nil { + return nil, err + } networkAliases := podUnit.LookupAll(PodGroup, KeyNetworkAlias) for _, networkAlias := range networkAliases { execStartPre.add("--network-alias", networkAlias) } - if err := addVolumes(podUnit, service, PodGroup, names, execStartPre); err != nil { + if err := addVolumes(podUnit, service, PodGroup, unitsInfoMap, execStartPre); err != nil { return nil, err } @@ -1714,34 +1804,35 @@ func handleUserRemap(unitFile *parser.UnitFile, groupName string, podman *Podman return nil } -func addNetworks(quadletUnitFile *parser.UnitFile, groupName string, serviceUnitFile *parser.UnitFile, names map[string]string, podman *PodmanCmdline) { +func addNetworks(quadletUnitFile *parser.UnitFile, groupName string, serviceUnitFile *parser.UnitFile, unitsInfoMap map[string]*UnitInfo, podman *PodmanCmdline) error { 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 if none is specified by the user. - networkName := names[quadletNetworkName] - if networkName == "" { - networkName = replaceExtension(quadletNetworkName, "", "systemd-", "") + networkUnitInfo, ok := unitsInfoMap[quadletNetworkName] + if !ok { + return fmt.Errorf("requested Quadlet image %s was not found", quadletNetworkName) } - // the systemd unit name is $name-network.service - networkServiceName := replaceExtension(quadletNetworkName, ".service", "", "-network") + // the systemd unit name is $serviceName.service + networkServiceName := networkUnitInfo.ServiceFileName() serviceUnitFile.Add(UnitGroup, "Requires", networkServiceName) serviceUnitFile.Add(UnitGroup, "After", networkServiceName) if found { - network = fmt.Sprintf("%s:%s", networkName, options) + network = fmt.Sprintf("%s:%s", networkUnitInfo.ResourceName, options) } else { - network = networkName + network = networkUnitInfo.ResourceName } } podman.addf("--network=%s", network) } } + return nil } // Systemd Specifiers start with % with the exception of %% @@ -1851,7 +1942,7 @@ func handleLogOpt(unitFile *parser.UnitFile, groupName string, podman *PodmanCmd } } -func handleStorageSource(quadletUnitFile, serviceUnitFile *parser.UnitFile, source string, names map[string]string) (string, error) { +func handleStorageSource(quadletUnitFile, serviceUnitFile *parser.UnitFile, source string, unitsInfoMap map[string]*UnitInfo) (string, error) { if source[0] == '.' { var err error source, err = getAbsolutePath(quadletUnitFile, source) @@ -1863,19 +1954,17 @@ 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 if none has been provided by the user. - volumeName := names[source] - if volumeName == "" { - volumeName = replaceExtension(source, "", "systemd-", "") + volumeUnitInfo, ok := unitsInfoMap[source] + if !ok { + return "", fmt.Errorf("requested Quadlet image %s was not found", source) } - // the systemd unit name is $name-volume.service - volumeServiceName := replaceExtension(source, ".service", "", "-volume") - - source = volumeName - + // the systemd unit name is $serviceName.service + volumeServiceName := volumeUnitInfo.ServiceFileName() serviceUnitFile.Add(UnitGroup, "Requires", volumeServiceName) serviceUnitFile.Add(UnitGroup, "After", volumeServiceName) + + source = volumeUnitInfo.ResourceName } return source, nil @@ -1989,29 +2078,29 @@ func lookupAndAddBoolean(unit *parser.UnitFile, group, key, flag string, podman } } -func handleImageSource(quadletImageName string, serviceUnitFile *parser.UnitFile, names map[string]string) (string, error) { +func handleImageSource(quadletImageName string, serviceUnitFile *parser.UnitFile, unitsInfoMap map[string]*UnitInfo) (string, error) { 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] + unitInfo, ok := unitsInfoMap[quadletImageName] if !ok { return "", fmt.Errorf("requested Quadlet image %s was not found", quadletImageName) } // the systemd unit name is $name-$suffix.service - imageServiceName := replaceExtension(quadletImageName, ".service", "", fmt.Sprintf("-%s", suffix[1:])) + imageServiceName := unitInfo.ServiceFileName() serviceUnitFile.Add(UnitGroup, "Requires", imageServiceName) serviceUnitFile.Add(UnitGroup, "After", imageServiceName) - quadletImageName = imageName + quadletImageName = unitInfo.ResourceName } } return quadletImageName, nil } -func resolveContainerMountParams(containerUnitFile, serviceUnitFile *parser.UnitFile, mount string, names map[string]string) (string, error) { +func resolveContainerMountParams(containerUnitFile, serviceUnitFile *parser.UnitFile, mount string, unitsInfoMap map[string]*UnitInfo) (string, error) { mountType, tokens, err := specgenutilexternal.FindMountType(mount) if err != nil { return "", err @@ -2035,7 +2124,7 @@ func resolveContainerMountParams(containerUnitFile, serviceUnitFile *parser.Unit } } - resolvedSource, err := handleStorageSource(containerUnitFile, serviceUnitFile, originalSource, names) + resolvedSource, err := handleStorageSource(containerUnitFile, serviceUnitFile, originalSource, unitsInfoMap) if err != nil { return "", err } @@ -2080,21 +2169,21 @@ func createBasePodmanCommand(unitFile *parser.UnitFile, groupName string) *Podma return podman } -func handlePod(quadletUnitFile, serviceUnitFile *parser.UnitFile, groupName string, podsInfoMap map[string]*PodInfo, podman *PodmanCmdline) error { +func handlePod(quadletUnitFile, serviceUnitFile *parser.UnitFile, groupName string, unitsInfoMap map[string]*UnitInfo, podman *PodmanCmdline) error { pod, ok := quadletUnitFile.Lookup(groupName, KeyPod) if ok && len(pod) > 0 { if !strings.HasSuffix(pod, ".pod") { return fmt.Errorf("pod %s is not Quadlet based", pod) } - podInfo, ok := podsInfoMap[pod] + podInfo, ok := unitsInfoMap[pod] if !ok { return fmt.Errorf("quadlet pod unit %s does not exist", pod) } podman.add("--pod-id-file", fmt.Sprintf("%%t/%s.pod-id", podInfo.ServiceName)) - podServiceName := fmt.Sprintf("%s.service", podInfo.ServiceName) + podServiceName := podInfo.ServiceFileName() serviceUnitFile.Add(UnitGroup, "BindsTo", podServiceName) serviceUnitFile.Add(UnitGroup, "After", podServiceName) @@ -2103,7 +2192,7 @@ func handlePod(quadletUnitFile, serviceUnitFile *parser.UnitFile, groupName stri return nil } -func addVolumes(quadletUnitFile, serviceUnitFile *parser.UnitFile, groupName string, names map[string]string, podman *PodmanCmdline) error { +func addVolumes(quadletUnitFile, serviceUnitFile *parser.UnitFile, groupName string, unitsInfoMap map[string]*UnitInfo, podman *PodmanCmdline) error { volumes := quadletUnitFile.LookupAll(groupName, KeyVolume) for _, volume := range volumes { parts := strings.SplitN(volume, ":", 3) @@ -2123,7 +2212,7 @@ func addVolumes(quadletUnitFile, serviceUnitFile *parser.UnitFile, groupName str if source != "" { var err error - source, err = handleStorageSource(quadletUnitFile, serviceUnitFile, source, names) + source, err = handleStorageSource(quadletUnitFile, serviceUnitFile, source, unitsInfoMap) if err != nil { return err } diff --git a/test/e2e/quadlet/build.quadlet.servicename.volume b/test/e2e/quadlet/build.quadlet.servicename.volume new file mode 100644 index 0000000000..c3e6a92084 --- /dev/null +++ b/test/e2e/quadlet/build.quadlet.servicename.volume @@ -0,0 +1,8 @@ +## assert-podman-args --driver=image +## assert-podman-args --opt image=localhost/imagename +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "basic.service" + +[Volume] +Driver=image +Image=service-name.build diff --git a/test/e2e/quadlet/image.quadlet.servicename.volume b/test/e2e/quadlet/image.quadlet.servicename.volume new file mode 100644 index 0000000000..43d4a8f786 --- /dev/null +++ b/test/e2e/quadlet/image.quadlet.servicename.volume @@ -0,0 +1,8 @@ +## assert-podman-args --driver=image +## assert-podman-args --opt image=localhost/imagename +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "basic.service" + +[Volume] +Driver=image +Image=service-name.image diff --git a/test/e2e/quadlet/mount.container b/test/e2e/quadlet/mount.container index 9969571bb1..bd156dc72b 100644 --- a/test/e2e/quadlet/mount.container +++ b/test/e2e/quadlet/mount.container @@ -8,10 +8,10 @@ Mount=type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared Mount=type=bind,src=/path/on/host,dst=/path/in/container,relabel=shared,U=true ## assert-podman-args-key-val "--mount" "," "type=volume,source=vol1,destination=/path/in/container,ro=true" Mount=type=volume,source=vol1,destination=/path/in/container,ro=true -## assert-podman-args-key-val "--mount" "," "type=volume,source=systemd-vol2,destination=/path/in/container,ro=true" -## assert-key-is "Unit" "Requires" "vol2-volume.service" -## assert-key-is "Unit" "After" "network-online.target" "vol2-volume.service" -Mount=type=volume,source=vol2.volume,destination=/path/in/container,ro=true +## assert-podman-args-key-val "--mount" "," "type=volume,source=systemd-basic,destination=/path/in/container,ro=true" +## assert-key-is "Unit" "Requires" "basic-volume.service" +## assert-key-is "Unit" "After" "network-online.target" "basic-volume.service" +Mount=type=volume,source=basic.volume,destination=/path/in/container,ro=true ## assert-podman-args-key-val "--mount" "," "type=tmpfs,tmpfs-size=512M,destination=/path/in/container" Mount=type=tmpfs,tmpfs-size=512M,destination=/path/in/container ## assert-podman-args-key-val "--mount" "," "type=image,source=fedora,destination=/fedora-image,rw=true" diff --git a/test/e2e/quadlet/mount.servicename.container b/test/e2e/quadlet/mount.servicename.container new file mode 100644 index 0000000000..95be8f82af --- /dev/null +++ b/test/e2e/quadlet/mount.servicename.container @@ -0,0 +1,6 @@ +[Container] +Image=localhost/imagename +## assert-podman-args-key-val "--mount" "," "type=volume,source=test-volume,destination=/path/in/container,ro=true" +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "network-online.target" "basic.service" +Mount=type=volume,source=service-name.volume,destination=/path/in/container,ro=true diff --git a/test/e2e/quadlet/network.quadlet.servicename.build b/test/e2e/quadlet/network.quadlet.servicename.build new file mode 100644 index 0000000000..5e458028a4 --- /dev/null +++ b/test/e2e/quadlet/network.quadlet.servicename.build @@ -0,0 +1,8 @@ +## assert-podman-args "--network=test-network" +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "network-online.target" "basic.service" + +[Build] +ImageTag=localhost/imagename +SetWorkingDirectory=unit +Network=service-name.network diff --git a/test/e2e/quadlet/network.quadlet.servicename.container b/test/e2e/quadlet/network.quadlet.servicename.container new file mode 100644 index 0000000000..f4d2dc80a6 --- /dev/null +++ b/test/e2e/quadlet/network.quadlet.servicename.container @@ -0,0 +1,7 @@ +## assert-podman-args "--network=test-network" +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "network-online.target" "basic.service" + +[Container] +Image=localhost/imagename +Network=service-name.network diff --git a/test/e2e/quadlet/network.quadlet.servicename.kube b/test/e2e/quadlet/network.quadlet.servicename.kube new file mode 100644 index 0000000000..f72646ab90 --- /dev/null +++ b/test/e2e/quadlet/network.quadlet.servicename.kube @@ -0,0 +1,8 @@ +## assert-podman-args "--network=test-network" +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "basic.service" + + +[Kube] +Yaml=deployment.yml +Network=service-name.network diff --git a/test/e2e/quadlet/network.servicename.quadlet.pod b/test/e2e/quadlet/network.servicename.quadlet.pod new file mode 100644 index 0000000000..8f0cf9810c --- /dev/null +++ b/test/e2e/quadlet/network.servicename.quadlet.pod @@ -0,0 +1,6 @@ +## assert-podman-pre-args "--network=test-network" +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "basic.service" + +[Pod] +Network=service-name.network diff --git a/test/e2e/quadlet/service-name.build b/test/e2e/quadlet/service-name.build new file mode 100644 index 0000000000..6f6581023d --- /dev/null +++ b/test/e2e/quadlet/service-name.build @@ -0,0 +1,6 @@ +## assert-podman-args --tag=localhost/imagename + +[Build] +ServiceName=basic +ImageTag=localhost/imagename +SetWorkingDirectory=unit diff --git a/test/e2e/quadlet/service-name.container b/test/e2e/quadlet/service-name.container new file mode 100644 index 0000000000..d58163282b --- /dev/null +++ b/test/e2e/quadlet/service-name.container @@ -0,0 +1,5 @@ +## assert-podman-final-args localhost/imagename + +[Container] +ServiceName=basic +Image=localhost/imagename diff --git a/test/e2e/quadlet/service-name.image b/test/e2e/quadlet/service-name.image new file mode 100644 index 0000000000..a22c25dd1a --- /dev/null +++ b/test/e2e/quadlet/service-name.image @@ -0,0 +1,5 @@ +## assert-podman-final-args localhost/imagename + +[Image] +ServiceName=basic +Image=localhost/imagename diff --git a/test/e2e/quadlet/service-name.kube b/test/e2e/quadlet/service-name.kube new file mode 100644 index 0000000000..13500acaf5 --- /dev/null +++ b/test/e2e/quadlet/service-name.kube @@ -0,0 +1,5 @@ +## assert-podman-final-args-regex .*/podman-e2e-.*/subtest-.*/quadlet/deployment.yml + +[Kube] +ServiceName=basic +Yaml=deployment.yml diff --git a/test/e2e/quadlet/service-name.network b/test/e2e/quadlet/service-name.network new file mode 100644 index 0000000000..1efda88bf3 --- /dev/null +++ b/test/e2e/quadlet/service-name.network @@ -0,0 +1,5 @@ +## assert-podman-final-args "test-network" + +[Network] +ServiceName=basic +NetworkName=test-network diff --git a/test/e2e/quadlet/service-name.pod b/test/e2e/quadlet/service-name.pod index 6f2842e083..353309a592 100644 --- a/test/e2e/quadlet/service-name.pod +++ b/test/e2e/quadlet/service-name.pod @@ -1,5 +1,5 @@ ## assert-podman-pre-args "--name=test-pod" [Pod] +ServiceName=basic PodName=test-pod -ServiceName=test-pod diff --git a/test/e2e/quadlet/service-name.volume b/test/e2e/quadlet/service-name.volume new file mode 100644 index 0000000000..59071822fd --- /dev/null +++ b/test/e2e/quadlet/service-name.volume @@ -0,0 +1,5 @@ +## assert-podman-final-args "test-volume" + +[Volume] +ServiceName=basic +VolumeName=test-volume diff --git a/test/e2e/quadlet/volume.build b/test/e2e/quadlet/volume.build index f63ac66fc6..bcbbf37157 100644 --- a/test/e2e/quadlet/volume.build +++ b/test/e2e/quadlet/volume.build @@ -2,7 +2,7 @@ ## 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 systemd-basic:/container/quadlet ## assert-podman-args -v %h/container:/container/volume4 [Build] @@ -13,5 +13,5 @@ Volume=/host/dir2:/container/volume2:Z Volume=./host/dir3:/container/volume3 Volume=/container/empty Volume=named:/container/named -Volume=quadlet.volume:/container/quadlet +Volume=basic.volume:/container/quadlet Volume=%h/container:/container/volume4 diff --git a/test/e2e/quadlet/volume.container b/test/e2e/quadlet/volume.container index b3d033c6f8..1e6e884111 100644 --- a/test/e2e/quadlet/volume.container +++ b/test/e2e/quadlet/volume.container @@ -2,7 +2,7 @@ ## 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 systemd-basic:/container/quadlet ## assert-podman-args -v %h/container:/container/volume4 [Container] @@ -12,5 +12,5 @@ Volume=/host/dir2:/container/volume2:Z Volume=./host/dir3:/container/volume3 Volume=/container/empty Volume=named:/container/named -Volume=quadlet.volume:/container/quadlet +Volume=basic.volume:/container/quadlet Volume=%h/container:/container/volume4 diff --git a/test/e2e/quadlet/volume.pod b/test/e2e/quadlet/volume.pod index b2fa6117fa..1c1b6578df 100644 --- a/test/e2e/quadlet/volume.pod +++ b/test/e2e/quadlet/volume.pod @@ -2,7 +2,7 @@ ## assert-podman-pre-args -v /host/dir2:/container/volume2:Z ## assert-podman-pre-args-regex -v .*/podman-e2e-.*/subtest-.*/quadlet/host/dir3:/container/volume3 ## assert-podman-pre-args -v named:/container/named -## assert-podman-pre-args -v systemd-quadlet:/container/quadlet +## assert-podman-pre-args -v systemd-basic:/container/quadlet ## assert-podman-pre-args -v %h/container:/container/volume4 [Pod] @@ -11,5 +11,5 @@ Volume=/host/dir2:/container/volume2:Z Volume=./host/dir3:/container/volume3 Volume=/container/empty Volume=named:/container/named -Volume=quadlet.volume:/container/quadlet +Volume=basic.volume:/container/quadlet Volume=%h/container:/container/volume4 diff --git a/test/e2e/quadlet/volume.quadlet.servicename.build b/test/e2e/quadlet/volume.quadlet.servicename.build new file mode 100644 index 0000000000..d33df93335 --- /dev/null +++ b/test/e2e/quadlet/volume.quadlet.servicename.build @@ -0,0 +1,8 @@ +## assert-podman-args "-v" "test-volume:/volume/basic" +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "network-online.target" "basic.service" + +[Build] +ImageTag=localhost/imagename +SetWorkingDirectory=unit +Volume=service-name.volume:/volume/basic diff --git a/test/e2e/quadlet/volume.servicename.container b/test/e2e/quadlet/volume.servicename.container new file mode 100644 index 0000000000..dece6a4af3 --- /dev/null +++ b/test/e2e/quadlet/volume.servicename.container @@ -0,0 +1,6 @@ +[Container] +Image=localhost/imagename +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "network-online.target" "basic.service" +## assert-podman-args -v test-volume:/container/quadlet +Volume=service-name.volume:/container/quadlet diff --git a/test/e2e/quadlet/volume.servicename.pod b/test/e2e/quadlet/volume.servicename.pod new file mode 100644 index 0000000000..07e64c4539 --- /dev/null +++ b/test/e2e/quadlet/volume.servicename.pod @@ -0,0 +1,6 @@ +## assert-podman-pre-args -v test-volume:/container/quadlet +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "basic.service" + +[Pod] +Volume=service-name.volume:/container/quadlet diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 1814cd55e6..e93b20eb6f 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -36,10 +36,7 @@ func getGenericTemplateFile(fileName string) (bool, string) { return false, "" } -func loadQuadletTestcase(path string) *quadletTestcase { - data, err := os.ReadFile(path) - Expect(err).ToNot(HaveOccurred()) - +func calcServiceName(path string) string { base := filepath.Base(path) ext := filepath.Ext(base) service := base[:len(base)-len(ext)] @@ -55,6 +52,23 @@ func loadQuadletTestcase(path string) *quadletTestcase { case ".pod": service += "-pod" } + return service +} + +func loadQuadletTestcase(path string) *quadletTestcase { + return loadQuadletTestcaseWithServiceName(path, "") +} + +func loadQuadletTestcaseWithServiceName(path, serviceName string) *quadletTestcase { + data, err := os.ReadFile(path) + Expect(err).ToNot(HaveOccurred()) + + var service string + if len(serviceName) > 0 { + service = serviceName + } else { + service = calcServiceName(path) + } service += ".service" checks := make([][]string, 0) @@ -609,8 +623,8 @@ var _ = Describe("quadlet system generator", func() { generatedDir string quadletDir string - runQuadletTestCase = func(fileName string, exitCode int, errString string) { - testcase := loadQuadletTestcase(filepath.Join("quadlet", fileName)) + runQuadletTestCaseWithServiceName = func(fileName string, exitCode int, errString string, serviceName string) { + testcase := loadQuadletTestcaseWithServiceName(filepath.Join("quadlet", fileName), serviceName) // Write the tested file to the quadlet dir err = os.WriteFile(filepath.Join(quadletDir, fileName), testcase.data, 0644) @@ -647,6 +661,10 @@ var _ = Describe("quadlet system generator", func() { testcase.check(generatedDir, session) } + runQuadletTestCase = func(fileName string, exitCode int, errString string) { + runQuadletTestCaseWithServiceName(fileName, exitCode, errString, "") + } + runSuccessQuadletTestCase = func(fileName string) { runQuadletTestCase(fileName, 0, "") } @@ -831,11 +849,9 @@ BOGUS=foo Entry("logdriver.container", "logdriver.container"), Entry("logopt.container", "logopt.container"), Entry("mask.container", "mask.container"), - Entry("mount.container", "mount.container"), Entry("name.container", "name.container"), Entry("nestedselinux.container", "nestedselinux.container"), Entry("network.container", "network.container"), - Entry("network.quadlet.container", "network.quadlet.container"), Entry("notify.container", "notify.container"), Entry("notify-healthy.container", "notify-healthy.container"), Entry("oneshot.container", "oneshot.container"), @@ -870,7 +886,6 @@ BOGUS=foo Entry("unmask.container", "unmask.container"), Entry("user.container", "user.container"), Entry("userns.container", "userns.container"), - Entry("volume.container", "volume.container"), Entry("workingdir.container", "workingdir.container"), Entry("Container - global args", "globalargs.container"), Entry("Container - Containers Conf Modules", "containersconfmodule.container"), @@ -902,7 +917,6 @@ BOGUS=foo Entry("Kube - PodmanArgs", "podmanargs.kube"), Entry("Kube - Publish IPv4 ports", "ports.kube"), Entry("Kube - Publish IPv6 ports", "ports_ipv6.kube"), - Entry("Kube - Quadlet Network", "network.quadlet.kube"), Entry("Kube - User Remap Auto with IDs", "remap-auto2.kube"), Entry("Kube - User Remap Auto", "remap-auto.kube"), Entry("Syslog Identifier", "syslog.identifier.kube"), @@ -967,7 +981,6 @@ BOGUS=foo Entry("Build - Containers Conf Modules", "containersconfmodule.build"), Entry("Build - Label Key", "label.build"), Entry("Build - Network Key host", "network.build"), - Entry("Build - Network Key quadlet", "network.quadlet.build"), Entry("Build - PodmanArgs", "podmanargs.build"), Entry("Build - Pull Key", "pull.build"), Entry("Build - Secrets", "secrets.build"), @@ -982,15 +995,11 @@ BOGUS=foo Entry("Build - Target Key", "target.build"), Entry("Build - TLSVerify Key", "tls-verify.build"), Entry("Build - Variant Key", "variant.build"), - Entry("Build - Volume Key", "volume.build"), - Entry("Build - Volume Key quadlet", "volume.quadlet.build"), Entry("basic.pod", "basic.pod"), Entry("name.pod", "name.pod"), Entry("network.pod", "network.pod"), - Entry("network-quadlet.pod", "network.quadlet.pod"), Entry("podmanargs.pod", "podmanargs.pod"), - Entry("volume.pod", "volume.pod"), Entry("Pod - NetworkAlias", "network-alias.pod"), ) @@ -1026,8 +1035,21 @@ BOGUS=foo Entry("Build - No ImageTag Key", "no-imagetag.build", "converting \"no-imagetag.build\": no ImageTag key specified"), ) - DescribeTable("Running quadlet test case with dependencies", - func(fileName string, exitCode int, errString string, dependencyFiles []string) { + DescribeTable("Running success quadlet with ServiceName test case", + func(fileName, serviceName string) { + runQuadletTestCaseWithServiceName(fileName, 0, "", serviceName) + }, + Entry("Build", "service-name.build", "basic"), + Entry("Container", "service-name.container", "basic"), + Entry("Image", "service-name.image", "basic"), + Entry("Kube", "service-name.kube", "basic"), + Entry("Network", "service-name.network", "basic"), + Entry("Pod", "service-name.pod", "basic"), + Entry("Volume", "service-name.volume", "basic"), + ) + + DescribeTable("Running quadlet success test case with dependencies", + func(fileName string, dependencyFiles []string) { // Write additional files this test depends on to the quadlet dir for _, dependencyFileName := range dependencyFiles { dependencyTestCase := loadQuadletTestcase(filepath.Join("quadlet", dependencyFileName)) @@ -1035,10 +1057,33 @@ BOGUS=foo Expect(err).ToNot(HaveOccurred()) } - runQuadletTestCase(fileName, exitCode, errString) + runSuccessQuadletTestCase(fileName) }, - Entry("Volume - Quadlet image (.build)", "build.quadlet.volume", 0, "", []string{"basic.build"}), - Entry("Volume - Quadlet image (.image)", "image.quadlet.volume", 0, "", []string{"basic.image"}), + Entry("Container - Mount", "mount.container", []string{"basic.volume"}), + Entry("Container - Quadlet Network", "network.quadlet.container", []string{"basic.network"}), + Entry("Container - Quadlet Volume", "volume.container", []string{"basic.volume"}), + Entry("Container - Mount overriding service name", "mount.servicename.container", []string{"service-name.volume"}), + Entry("Container - Quadlet Network overriding service name", "network.quadlet.servicename.container", []string{"service-name.network"}), + Entry("Container - Quadlet Volume overriding service name", "volume.servicename.container", []string{"service-name.volume"}), + + Entry("Volume - Quadlet image (.build)", "build.quadlet.volume", []string{"basic.build"}), + Entry("Volume - Quadlet image (.image)", "image.quadlet.volume", []string{"basic.image"}), + Entry("Volume - Quadlet image (.build) overriding service name", "build.quadlet.servicename.volume", []string{"service-name.build"}), + Entry("Volume - Quadlet image (.image) overriding service name", "image.quadlet.servicename.volume", []string{"service-name.image"}), + + Entry("Kube - Quadlet Network", "network.quadlet.kube", []string{"basic.network"}), + Entry("Kube - Quadlet Network overriding service name", "network.quadlet.servicename.kube", []string{"service-name.network"}), + + Entry("Build - Network Key quadlet", "network.quadlet.build", []string{"basic.network"}), + Entry("Build - Volume Key", "volume.build", []string{"basic.volume"}), + Entry("Build - Volume Key quadlet", "volume.quadlet.build", []string{"basic.volume"}), + Entry("Build - Network Key quadlet overriding service name", "network.quadlet.servicename.build", []string{"service-name.network"}), + Entry("Build - Volume Key quadlet overriding service name", "volume.quadlet.servicename.build", []string{"service-name.volume"}), + + Entry("Pod - Quadlet Network", "network.quadlet.pod", []string{"basic.network"}), + Entry("Pod - Quadlet Volume", "volume.pod", []string{"basic.volume"}), + Entry("Pod - Quadlet Network overriding service name", "network.servicename.quadlet.pod", []string{"service-name.network"}), + Entry("Pod - Quadlet Volume overriding service name", "volume.servicename.pod", []string{"service-name.volume"}), ) }) diff --git a/test/system/252-quadlet.bats b/test/system/252-quadlet.bats index 8d8494d35a..90677763c5 100644 --- a/test/system/252-quadlet.bats +++ b/test/system/252-quadlet.bats @@ -383,8 +383,9 @@ EOF [Volume] EOF + local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX) # Have quadlet create the systemd unit file for the volume unit - run_quadlet "$quadlet_vol_file" + run_quadlet "$quadlet_vol_file" "$quadlet_tmpdir" # Save the volume service name since the variable will be overwritten local vol_service=$QUADLET_SERVICE_NAME @@ -399,7 +400,7 @@ Volume=$quadlet_vol_unit:/tmp EOF # Have quadlet create the systemd unit file for the container unit - run_quadlet "$quadlet_file" + run_quadlet "$quadlet_file" "$quadlet_tmpdir" # Save the container service name for readability local container_service=$QUADLET_SERVICE_NAME @@ -510,8 +511,9 @@ EOF [Network] EOF + local quadlet_tmpdir=$(mktemp -d --tmpdir=$PODMAN_TMPDIR quadlet.XXXXXX) # Have quadlet create the systemd unit file for the network unit - run_quadlet "$quadlet_network_file" + run_quadlet "$quadlet_network_file" "$quadlet_tmpdir" # Save the volume service name since the variable will be overwritten local network_service=$QUADLET_SERVICE_NAME @@ -525,7 +527,7 @@ Exec=top Network=$quadlet_network_unit EOF - run_quadlet "$quadlet_file" + run_quadlet "$quadlet_file" "$quadlet_tmpdir" # Save the container service name for readability local container_service=$QUADLET_SERVICE_NAME