From 71fd15726d4420b76cfb9cf56a39b8b6f6be8253 Mon Sep 17 00:00:00 2001 From: Misaki Kasumi Date: Fri, 30 Aug 2024 18:47:45 +0800 Subject: [PATCH] quadlet: support container network reusing Signed-off-by: Misaki Kasumi --- docs/source/markdown/podman-systemd.unit.5.md | 4 ++ pkg/systemd/quadlet/quadlet.go | 40 ++++++++++++++----- test/e2e/quadlet/network.reuse.container | 7 ++++ test/e2e/quadlet/network.reuse.name.container | 7 ++++ test/e2e/quadlet_test.go | 2 + 5 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 test/e2e/quadlet/network.reuse.container create mode 100644 test/e2e/quadlet/network.reuse.name.container diff --git a/docs/source/markdown/podman-systemd.unit.5.md b/docs/source/markdown/podman-systemd.unit.5.md index 6e6afaf18b..db6f79af2d 100644 --- a/docs/source/markdown/podman-systemd.unit.5.md +++ b/docs/source/markdown/podman-systemd.unit.5.md @@ -638,6 +638,10 @@ As a special case, if the `name` of the network ends with `.network`, a Podman n a dependency on the `$name-network.service`. Such a network can be automatically created by using a `$name.network` Quadlet file. +Another special case is that if the `name` ends with `.container`, +the container will reuse the network stack of another container created by `$name.container`. +The generated systemd service contains a dependency on `$name.service`. + This key can be listed multiple times. ### `NetworkAlias=` diff --git a/pkg/systemd/quadlet/quadlet.go b/pkg/systemd/quadlet/quadlet.go index 837f76dc03..57b79be2d2 100644 --- a/pkg/systemd/quadlet/quadlet.go +++ b/pkg/systemd/quadlet/quadlet.go @@ -921,6 +921,13 @@ func ConvertContainer(container *parser.UnitFile, isUser bool, unitsInfoMap map[ service.AddCmdline(ServiceGroup, "ExecStart", podman.Args) + // XXX: only %N is handled. + // it is difficult to properly implement specifiers handling without consulting systemd. + resourceName := strings.ReplaceAll(containerName, "%N", unitInfo.ServiceName) + if !strings.Contains(resourceName, "%") { + unitInfo.ResourceName = resourceName + } + return service, nil } @@ -1859,23 +1866,38 @@ func addNetworks(quadletUnitFile *parser.UnitFile, groupName string, serviceUnit 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. - networkUnitInfo, ok := unitsInfoMap[quadletNetworkName] + + isNetworkUnit := strings.HasSuffix(quadletNetworkName, ".network") + isContainerUnit := strings.HasSuffix(quadletNetworkName, ".container") + + if isNetworkUnit || isContainerUnit { + unitInfo, ok := unitsInfoMap[quadletNetworkName] if !ok { - return fmt.Errorf("requested Quadlet image %s was not found", quadletNetworkName) + return fmt.Errorf("requested Quadlet unit %s was not found", quadletNetworkName) + } + + // XXX: this is usually because a '@' in service name + if len(unitInfo.ResourceName) == 0 { + return fmt.Errorf("cannot get the resource name of %s", quadletNetworkName) } // the systemd unit name is $serviceName.service - networkServiceName := networkUnitInfo.ServiceFileName() + serviceFileName := unitInfo.ServiceFileName() - serviceUnitFile.Add(UnitGroup, "Requires", networkServiceName) - serviceUnitFile.Add(UnitGroup, "After", networkServiceName) + serviceUnitFile.Add(UnitGroup, "Requires", serviceFileName) + serviceUnitFile.Add(UnitGroup, "After", serviceFileName) if found { - network = fmt.Sprintf("%s:%s", networkUnitInfo.ResourceName, options) + if isContainerUnit { + return fmt.Errorf("extra options are not supported when joining another container's network") + } + network = fmt.Sprintf("%s:%s", unitInfo.ResourceName, options) } else { - network = networkUnitInfo.ResourceName + if isContainerUnit { + network = fmt.Sprintf("container:%s", unitInfo.ResourceName) + } else { + network = unitInfo.ResourceName + } } } diff --git a/test/e2e/quadlet/network.reuse.container b/test/e2e/quadlet/network.reuse.container new file mode 100644 index 0000000000..85fe3b4508 --- /dev/null +++ b/test/e2e/quadlet/network.reuse.container @@ -0,0 +1,7 @@ +## assert-podman-args "--network=container:systemd-basic" +## assert-key-is "Unit" "Requires" "basic.service" +## assert-key-is "Unit" "After" "network-online.target" "basic.service" + +[Container] +Image=localhost/imagename +Network=basic.container diff --git a/test/e2e/quadlet/network.reuse.name.container b/test/e2e/quadlet/network.reuse.name.container new file mode 100644 index 0000000000..2458cecb5e --- /dev/null +++ b/test/e2e/quadlet/network.reuse.name.container @@ -0,0 +1,7 @@ +## assert-podman-args "--network=container:foobar" +## assert-key-is "Unit" "Requires" "name.service" +## assert-key-is "Unit" "After" "network-online.target" "name.service" + +[Container] +Image=localhost/imagename +Network=name.container diff --git a/test/e2e/quadlet_test.go b/test/e2e/quadlet_test.go index 0f092cdcce..639d06457e 100644 --- a/test/e2e/quadlet_test.go +++ b/test/e2e/quadlet_test.go @@ -1077,6 +1077,8 @@ BOGUS=foo Entry("Container - Quadlet Network overriding service name", "network.quadlet.servicename.container", []string{"service-name.network"}), Entry("Container - Quadlet Volume overriding service name", "volume.servicename.container", []string{"service-name.volume"}), Entry("Container - Quadlet build with multiple tags", "build.multiple-tags.container", []string{"multiple-tags.build"}), + Entry("Container - Reuse another container's network", "network.reuse.container", []string{"basic.container"}), + Entry("Container - Reuse another named container's network", "network.reuse.name.container", []string{"name.container"}), Entry("Volume - Quadlet image (.build)", "build.quadlet.volume", []string{"basic.build"}), Entry("Volume - Quadlet image (.image)", "image.quadlet.volume", []string{"basic.image"}),