kube: alias OCI runtime annotations without underscores

Fixes: #26871
Signed-off-by: Jiwoo Ahn <ikwydls1314@gmail.com>
This commit is contained in:
Jiwoo Ahn 2026-06-19 16:36:29 +09:00
parent 1fecb62b34
commit fb8a272fe3
4 changed files with 83 additions and 3 deletions

View file

@ -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,
},
}

View file

@ -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, "_", "")
}

View file

@ -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

View file

@ -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