From fb8a272fe3ef5a99cce40026124b1ad8e5236a76 Mon Sep 17 00:00:00 2001 From: Jiwoo Ahn Date: Fri, 19 Jun 2026 16:36:29 +0900 Subject: [PATCH] kube: alias OCI runtime annotations without underscores Fixes: #26871 Signed-off-by: Jiwoo Ahn --- libpod/define/annotations.go | 24 ++++++++++++++++++- libpod/kube.go | 13 ++++++++-- pkg/specgen/generate/kube/kube.go | 9 +++++++ test/e2e/play_kube_test.go | 40 +++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 3 deletions(-) diff --git a/libpod/define/annotations.go b/libpod/define/annotations.go index 20797ff034..b7eeba7ed8 100644 --- a/libpod/define/annotations.go +++ b/libpod/define/annotations.go @@ -7,6 +7,12 @@ const ( // RunOCIKeepOriginalGroups tells the OCI runtime to leak the users // current groups into the container RunOCIKeepOriginalGroups = "run.oci.keep_original_groups" + // KubeMountContextTypeAnnotation is the Kubernetes-safe annotation used to + // round-trip RunOCIMountContextType through kube generate/play. + KubeMountContextTypeAnnotation = "io.podman.annotations.mount-context-type" + // KubeKeepOriginalGroupsAnnotation is the Kubernetes-safe annotation used to + // round-trip RunOCIKeepOriginalGroups through kube generate/play. + KubeKeepOriginalGroupsAnnotation = "io.podman.annotations.keep-original-groups" // InspectAnnotationCIDFile is used by Inspect to determine if a // container ID file was created for the container. // If an annotation with this key is found in the OCI spec, it will be @@ -186,10 +192,26 @@ const ( // already reserved annotation that Podman sets during container creation. func IsReservedAnnotation(value string) bool { switch value { - case InspectAnnotationCIDFile, InspectAnnotationAutoremove, InspectAnnotationPrivileged, InspectAnnotationPublishAll, InspectAnnotationInit, InspectAnnotationLabel, InspectAnnotationSeccomp, InspectAnnotationApparmor, InspectResponseTrue, InspectResponseFalse, VolumesFromAnnotation: + case InspectAnnotationCIDFile, InspectAnnotationAutoremove, InspectAnnotationPrivileged, InspectAnnotationPublishAll, InspectAnnotationInit, InspectAnnotationLabel, InspectAnnotationSeccomp, InspectAnnotationApparmor, InspectResponseTrue, InspectResponseFalse, VolumesFromAnnotation, KubeMountContextTypeAnnotation, KubeKeepOriginalGroupsAnnotation: return true default: return false } } + +type AnnotationAlias struct { + Runtime string + Kube string +} + +var OCIRuntimeAnnotationAliases = []AnnotationAlias{ + { + Runtime: RunOCIKeepOriginalGroups, + Kube: KubeKeepOriginalGroupsAnnotation, + }, + { + Runtime: RunOCIMountContextType, + Kube: KubeMountContextTypeAnnotation, + }, +} diff --git a/libpod/kube.go b/libpod/kube.go index 023a1f03f3..cb3c131d93 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -618,7 +618,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po if !podmanOnly && (define.IsReservedAnnotation(k)) { continue } - podAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = v + podAnnotations[fmt.Sprintf("%s/%s", kubeAnnotationAlias(k), removeUnderscores(ctr.Name()))] = v } // Convert auto-update labels into kube annotations maps.Copy(podAnnotations, getAutoUpdateAnnotations(ctr.Name(), ctr.Labels())) @@ -764,7 +764,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic if !podmanOnly && define.IsReservedAnnotation(k) { continue } - kubeAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = v + kubeAnnotations[fmt.Sprintf("%s/%s", kubeAnnotationAlias(k), removeUnderscores(ctr.Name()))] = v } // Convert auto-update labels into kube annotations @@ -1449,6 +1449,15 @@ func generateKubeVolumeDeviceFromLinuxDevice(devices []specs.LinuxDevice) []v1.V return volumeDevices } +func kubeAnnotationAlias(annotationKey string) string { + for _, alias := range define.OCIRuntimeAnnotationAliases { + if alias.Runtime == annotationKey { + return alias.Kube + } + } + return annotationKey +} + func removeUnderscores(s string) string { return strings.ReplaceAll(s, "_", "") } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index ddb4ce5188..c5adf258c5 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -42,6 +42,14 @@ import ( cdiparser "tags.cncf.io/container-device-interface/pkg/parser" ) +func restoreKubeAnnotationAliases(annotations map[string]string, containerName string) { + for _, alias := range define.OCIRuntimeAnnotationAliases { + if value, ok := annotations[alias.Kube+"/"+containerName]; ok { + annotations[alias.Runtime] = value + } + } +} + func ToPodOpt(_ context.Context, podName string, p entities.PodCreateOptions, publishAllPorts bool, podYAML *v1.PodTemplateSpec) (entities.PodCreateOptions, error) { p.Net = &entities.NetOptions{NoHosts: p.Net.NoHosts, NoHostname: p.Net.NoHostname} @@ -377,6 +385,7 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener annotations[ann.SandboxID] = opts.PodInfraID } s.Annotations = annotations + restoreKubeAnnotationAliases(s.Annotations, opts.Container.Name) if containerCIDFile, ok := opts.Annotations[define.InspectAnnotationCIDFile+"/"+opts.Container.Name]; ok { s.Annotations[define.InspectAnnotationCIDFile] = containerCIDFile diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 80ceeee6f0..5739208cb1 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -6092,6 +6092,46 @@ spec: Expect(kube).Should(ExitWithError(125, "annotation "+define.VolumesFromAnnotation+" without target volume is reserved for internal use")) }) + It("test with keep-groups OCI annotation round trip", func() { + SkipIfRemote("the --group-add keep-groups option is not supported in remote mode") + + ctr := "ctr-keep-groups" + ctrNameInKubePod := ctr + "-pod-" + ctr + outputFile := filepath.Join(podmanTest.TempDir, "pod.yaml") + kubeAnnotation := define.KubeKeepOriginalGroupsAnnotation + "/" + ctr + + podmanTest.PodmanExitCleanly("create", "--name", ctr, "--group-add", "keep-groups", CITEST_IMAGE, "top") + podmanTest.PodmanExitCleanly("kube", "generate", "-f", outputFile, ctr) + + kubeYaml, err := os.ReadFile(outputFile) + Expect(err).ToNot(HaveOccurred()) + Expect(kubeYaml).ToNot(ContainSubstring(define.RunOCIKeepOriginalGroups + "/" + ctr)) + Expect(kubeYaml).To(ContainSubstring(kubeAnnotation + `: "1"`)) + + podmanTest.PodmanExitCleanly("kube", "play", "--start=false", outputFile) + inspect := podmanTest.PodmanExitCleanly("inspect", "-f", "{{ index .Config.Annotations \""+define.RunOCIKeepOriginalGroups+"\" }}", ctrNameInKubePod) + Expect(inspect.OutputToString()).To(Equal("1")) + }) + + It("test with mount-context-type OCI annotation round trip", func() { + ctr := "ctr-mount-context" + ctrNameInKubePod := ctr + "-pod-" + ctr + outputFile := filepath.Join(podmanTest.TempDir, "pod.yaml") + kubeAnnotation := define.KubeMountContextTypeAnnotation + "/" + ctr + + podmanTest.PodmanExitCleanly("create", "--name", ctr, "--annotation", define.RunOCIMountContextType+"=rootcontext", CITEST_IMAGE, "top") + podmanTest.PodmanExitCleanly("kube", "generate", "-f", outputFile, ctr) + + kubeYaml, err := os.ReadFile(outputFile) + Expect(err).ToNot(HaveOccurred()) + Expect(kubeYaml).ToNot(ContainSubstring(define.RunOCIMountContextType + "/" + ctr)) + Expect(kubeYaml).To(ContainSubstring(kubeAnnotation + `: rootcontext`)) + + podmanTest.PodmanExitCleanly("kube", "play", "--start=false", outputFile) + inspect := podmanTest.PodmanExitCleanly("inspect", "-f", "{{ index .Config.Annotations \""+define.RunOCIMountContextType+"\" }}", ctrNameInKubePod) + Expect(inspect.OutputToString()).To(Equal("rootcontext")) + }) + It("test with reserved autoremove annotation in yaml", func() { ctr := "ctr" ctrNameInKubePod := ctr + "-pod-" + ctr