Comply to Kubernetes specifications for annotation size.

An annotation is a pair of key-value. The key has two parts, viz. a name and an optional prefix in DNS format.

The limitations on name is 63, prefix 253 chars. The limitation on total size of all key+value pairs combined is 256KB.

https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/#syntax-and-character-set

Fixes: https://github.com/containers/podman/issues/21663

Signed-off-by: Vikas Goel <vikas.goel@gmail.com>
This commit is contained in:
Vikas Goel
2024-02-16 19:45:52 -08:00
parent 6c7f987ab3
commit 89b415ba37
13 changed files with 256 additions and 218 deletions

View File

@ -83,6 +83,7 @@ func generateFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) {
noTruncAnnotationsFlagName := "no-trunc"
flags.BoolVar(&generateOptions.UseLongAnnotations, noTruncAnnotationsFlagName, false, "Don't truncate annotations to Kubernetes length (63 chars)")
_ = flags.MarkHidden(noTruncAnnotationsFlagName)
podmanOnlyFlagName := "podman-only"
flags.BoolVar(&generateOptions.PodmanOnly, podmanOnlyFlagName, false, "Add podman-only reserved annotations to the generated YAML file (Cannot be used by Kubernetes)")

View File

@ -22,6 +22,7 @@ import (
"github.com/containers/podman/v5/cmd/podman/utils"
"github.com/containers/podman/v5/libpod/define"
"github.com/containers/podman/v5/libpod/shutdown"
"github.com/containers/podman/v5/pkg/annotations"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/containers/podman/v5/pkg/errorhandling"
"github.com/containers/podman/v5/pkg/util"
@ -171,6 +172,7 @@ func playFlags(cmd *cobra.Command) {
noTruncFlagName := "no-trunc"
flags.BoolVar(&playOptions.UseLongAnnotations, noTruncFlagName, false, "Use annotations that are not truncated to the Kubernetes maximum length of 63 characters")
_ = flags.MarkHidden(noTruncFlagName)
if !registry.IsRemote() {
certDirFlagName := "cert-dir"
@ -253,12 +255,13 @@ func play(cmd *cobra.Command, args []string) error {
if playOptions.Annotations == nil {
playOptions.Annotations = make(map[string]string)
}
if len(val) > define.MaxKubeAnnotation && !playOptions.UseLongAnnotations {
return fmt.Errorf("annotation exceeds maximum size, %d, of kubernetes annotation: %s", define.MaxKubeAnnotation, val)
}
playOptions.Annotations[key] = val
}
if err := annotations.ValidateAnnotations(playOptions.Annotations); err != nil {
return err
}
for _, mac := range playOptions.macs {
m, err := net.ParseMAC(mac)
if err != nil {

View File

@ -39,11 +39,6 @@ Also note that both Deployment and DaemonSet can only have `restartPolicy` set t
Output to the given file instead of STDOUT. If the file already exists, `kube generate` refuses to replace it and returns an error.
#### **--no-trunc**
Don't truncate annotations to the Kubernetes maximum length of 63 characters.
Note: enabling this flag means the generated YAML file is not Kubernetes compatible and can only be used with `podman kube play`
#### **--podman-only**
Add podman-only reserved annotations in generated YAML file (Cannot be used by Kubernetes)

View File

@ -217,10 +217,6 @@ When no network option is specified and *host* network mode is not configured in
This option conflicts with host added in the Kubernetes YAML.
#### **--no-trunc**
Use annotations that are not truncated to the Kubernetes maximum length of 63 characters
#### **--publish**=*[[ip:][hostPort]:]containerPort[/protocol]*
Define or override a port definition in the YAML file.

View File

@ -160,8 +160,8 @@ const (
// the k8s behavior of waiting for the intialDelaySeconds to be over before updating the status
KubeHealthCheckAnnotation = "io.podman.annotations.kube.health.check"
// MaxKubeAnnotation is the max length of annotations allowed by Kubernetes.
MaxKubeAnnotation = 63
// TotalAnnotationSizeLimitB is the max length of annotations allowed by Kubernetes.
TotalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
)
// IsReservedAnnotation returns true if the specified value corresponds to an

View File

@ -13,7 +13,6 @@ import (
"strconv"
"strings"
"time"
"unicode/utf8"
"github.com/containers/common/libnetwork/types"
"github.com/containers/common/pkg/config"
@ -36,14 +35,14 @@ import (
// GenerateForKube takes a slice of libpod containers and generates
// one v1.Pod description that includes just a single container.
func GenerateForKube(ctx context.Context, ctrs []*Container, getService, useLongAnnotations, podmanOnly bool) (*v1.Pod, error) {
func GenerateForKube(ctx context.Context, ctrs []*Container, getService, podmanOnly bool) (*v1.Pod, error) {
// Generate the v1.Pod yaml description
return simplePodWithV1Containers(ctx, ctrs, getService, useLongAnnotations, podmanOnly)
return simplePodWithV1Containers(ctx, ctrs, getService, podmanOnly)
}
// GenerateForKube takes a slice of libpod containers and generates
// one v1.Pod description
func (p *Pod) GenerateForKube(ctx context.Context, getService, useLongAnnotations, podmanOnly bool) (*v1.Pod, []v1.ServicePort, error) {
func (p *Pod) GenerateForKube(ctx context.Context, getService, podmanOnly bool) (*v1.Pod, []v1.ServicePort, error) {
// Generate the v1.Pod yaml description
var (
ports []v1.ContainerPort
@ -95,7 +94,7 @@ func (p *Pod) GenerateForKube(ctx context.Context, getService, useLongAnnotation
hostUsers = infraContainer.IDMappings().HostUIDMapping && infraContainer.IDMappings().HostGIDMapping
infraName = infraContainer.config.Name
}
pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork, hostUsers, getService, useLongAnnotations, podmanOnly, infraName)
pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork, hostUsers, getService, podmanOnly, infraName)
if err != nil {
return nil, servicePorts, err
}
@ -451,16 +450,6 @@ func newServicePortState() servicePortState {
}
}
func truncateKubeAnnotation(str string, useLongAnnotations bool) string {
str = strings.TrimSpace(str)
if useLongAnnotations || utf8.RuneCountInString(str) < define.MaxKubeAnnotation {
return str
}
trunc := string([]rune(str)[:define.MaxKubeAnnotation])
logrus.Warnf("Truncation Annotation: %q to %q: Kubernetes only allows %d characters", str, trunc, define.MaxKubeAnnotation)
return trunc
}
// containerPortsToServicePorts takes a slice of containerports and generates a
// slice of service ports
func (state *servicePortState) containerPortsToServicePorts(containerPorts []v1.ContainerPort) ([]v1.ServicePort, error) {
@ -507,7 +496,7 @@ func containersToServicePorts(containers []v1.Container) ([]v1.ServicePort, erro
return sps, nil
}
func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, ports []v1.ContainerPort, hostNetwork, hostUsers, getService, useLongAnnotations, podmanOnly bool, infraName string) (*v1.Pod, error) {
func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, ports []v1.ContainerPort, hostNetwork, hostUsers, getService, podmanOnly bool, infraName string) (*v1.Pod, error) {
deDupPodVolumes := make(map[string]*v1.Volume)
first := true
podContainers := make([]v1.Container, 0, len(containers))
@ -529,11 +518,11 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po
if !podmanOnly && (define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k)) {
continue
}
podAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = truncateKubeAnnotation(v, useLongAnnotations)
podAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = v
}
// Convert auto-update labels into kube annotations
for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels(), useLongAnnotations) {
podAnnotations[k] = truncateKubeAnnotation(v, useLongAnnotations)
for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels()) {
podAnnotations[k] = v
}
isInit := ctr.IsInitCtr()
// Since hostname is only set at pod level, set the hostname to the hostname of the first container we encounter
@ -556,7 +545,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po
return nil, err
}
for k, v := range annotations {
podAnnotations[define.BindMountPrefix] = truncateKubeAnnotation(k+":"+v, useLongAnnotations)
podAnnotations[define.BindMountPrefix] = k + ":" + v
}
// Since port bindings for the pod are handled by the
// infra container, wipe them here only if we are sharing the net namespace
@ -605,7 +594,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po
// If the infraName is not the podID-infra, that means the user set another infra name using
// --infra-name during pod creation
if infraName != "" && infraName != p.ID()[:12]+"-infra" {
podAnnotations[define.InfraNameAnnotation] = truncateKubeAnnotation(infraName, useLongAnnotations)
podAnnotations[define.InfraNameAnnotation] = infraName
}
}
}
@ -674,7 +663,7 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta
// simplePodWithV1Containers is a function used by inspect when kube yaml needs to be generated
// for a single container. we "insert" that container description in a pod.
func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getService, useLongAnnotations, podmanOnly bool) (*v1.Pod, error) {
func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getService, podmanOnly bool) (*v1.Pod, error) {
kubeCtrs := make([]v1.Container, 0, len(ctrs))
kubeInitCtrs := []v1.Container{}
kubeVolumes := make([]v1.Volume, 0)
@ -694,12 +683,12 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic
if !podmanOnly && (define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k)) {
continue
}
kubeAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = truncateKubeAnnotation(v, useLongAnnotations)
kubeAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = v
}
// Convert auto-update labels into kube annotations
for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels(), useLongAnnotations) {
kubeAnnotations[k] = truncateKubeAnnotation(v, useLongAnnotations)
for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels()) {
kubeAnnotations[k] = v
}
isInit := ctr.IsInitCtr()
@ -752,7 +741,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic
return nil, err
}
for k, v := range annotations {
kubeAnnotations[define.BindMountPrefix] = truncateKubeAnnotation(k+":"+v, useLongAnnotations)
kubeAnnotations[define.BindMountPrefix] = k + ":" + v
}
if isInit {
kubeInitCtrs = append(kubeInitCtrs, kubeCtr)
@ -1384,7 +1373,7 @@ func removeUnderscores(s string) string {
// getAutoUpdateAnnotations searches for auto-update container labels
// and returns them as kube annotations
func getAutoUpdateAnnotations(ctrName string, ctrLabels map[string]string, useLongAnnotations bool) map[string]string {
func getAutoUpdateAnnotations(ctrName string, ctrLabels map[string]string) map[string]string {
autoUpdateLabel := "io.containers.autoupdate"
annotations := make(map[string]string)
@ -1394,7 +1383,7 @@ func getAutoUpdateAnnotations(ctrName string, ctrLabels map[string]string, useLo
// since labels can variate between containers within a pod, they will be
// identified with the container name when converted into kube annotations
kc := fmt.Sprintf("%s/%s", k, ctrName)
annotations[kc] = truncateKubeAnnotation(v, useLongAnnotations)
annotations[kc] = v
}
}

124
pkg/annotations/validate.go Normal file
View File

@ -0,0 +1,124 @@
package annotations
import (
"fmt"
"regexp"
"strings"
"github.com/containers/podman/v5/libpod/define"
)
// regexErrorMsg returns a string explanation of a regex validation failure.
func regexErrorMsg(msg string, fmt string, examples ...string) string {
if len(examples) == 0 {
return msg + " (regex used for validation is '" + fmt + "')"
}
msg += " (e.g. "
for i := range examples {
if i > 0 {
msg += " or "
}
msg += "'" + examples[i] + "', "
}
msg += "regex used for validation is '" + fmt + "')"
return msg
}
const dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
const dns1123SubdomainFmt string = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*"
const dns1123SubdomainErrorMsg string = "annotations must be formatted as a valid lowercase RFC1123 subdomain of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character"
// DNS1123SubdomainMaxLength is a subdomain's max length in DNS (RFC 1123)
const DNS1123SubdomainMaxLength int = 253
var dns1123SubdomainRegexp = regexp.MustCompile("^" + dns1123SubdomainFmt + "$")
// isDNS1123Subdomain tests for a string that conforms to the definition of a
// subdomain in DNS (RFC 1123).
func isDNS1123Subdomain(value string) error {
if len(value) > DNS1123SubdomainMaxLength {
return fmt.Errorf("prefix part must be no more than %d characters", DNS1123SubdomainMaxLength)
}
if !dns1123SubdomainRegexp.MatchString(value) {
return fmt.Errorf(regexErrorMsg(dns1123SubdomainErrorMsg, dns1123SubdomainFmt, "example.com"))
}
return nil
}
const qnameCharFmt string = "[A-Za-z0-9]"
const qnameExtCharFmt string = "[-A-Za-z0-9_.]"
const qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt
const qualifiedNameErrMsg string = "must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
const qualifiedNameMaxLength int = 63
var qualifiedNameRegexp = regexp.MustCompile("^" + qualifiedNameFmt + "$")
// isQualifiedName tests whether the value passed is what Kubernetes calls a
// "qualified name". This is a format used in various places throughout the
// system. If the value is not valid, a list of error strings is returned.
// Otherwise an empty list (or nil) is returned.
func isQualifiedName(value string) error {
parts := strings.Split(value, "/")
var name string
switch len(parts) {
case 1:
name = parts[0]
case 2:
var prefix string
prefix, name = parts[0], parts[1]
if len(prefix) == 0 {
return fmt.Errorf("prefix part of %s must be non-empty", value)
} else if err := isDNS1123Subdomain(prefix); err != nil {
return err
}
default:
return fmt.Errorf("a qualified name of %s "+
regexErrorMsg(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc")+
" with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')", value)
}
if len(name) == 0 {
return fmt.Errorf("name part of %s must be non-empty", value)
} else if len(name) > qualifiedNameMaxLength {
return fmt.Errorf("name part of %s must be no more than %d characters", value, qualifiedNameMaxLength)
}
if !qualifiedNameRegexp.MatchString(name) {
return fmt.Errorf("name part of %s "+
regexErrorMsg(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc"), value)
}
return nil
}
func validateAnnotationsSize(annotations map[string]string) error {
var totalSize int64
for k, v := range annotations {
totalSize += (int64)(len(k)) + (int64)(len(v))
}
if totalSize > (int64)(define.TotalAnnotationSizeLimitB) {
return fmt.Errorf("annotations size %d is larger than limit %d", totalSize, define.TotalAnnotationSizeLimitB)
}
return nil
}
// ValidateAnnotations validates that a set of annotations are correctly
// defined.
func ValidateAnnotations(annotations map[string]string) error {
for k := range annotations {
// The rule is QualifiedName except that case doesn't matter,
// so convert to lowercase before checking.
if err := isQualifiedName(strings.ToLower(k)); err != nil {
return err
}
}
if err := validateAnnotationsSize(annotations); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,80 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package annotations
import (
"strings"
"testing"
"github.com/containers/podman/v5/libpod/define"
)
func TestValidateAnnotations(t *testing.T) {
successCases := []map[string]string{
{"simple": "bar"},
{"now-with-dashes": "bar"},
{"1-starts-with-num": "bar"},
{"1234": "bar"},
{"simple/simple": "bar"},
{"now-with-dashes/simple": "bar"},
{"now-with-dashes/now-with-dashes": "bar"},
{"now.with.dots/simple": "bar"},
{"now-with.dashes-and.dots/simple": "bar"},
{"1-num.2-num/3-num": "bar"},
{"1234/5678": "bar"},
{"1.2.3.4/5678": "bar"},
{"UpperCase123": "bar"},
{"a": strings.Repeat("b", define.TotalAnnotationSizeLimitB-1)},
{
"a": strings.Repeat("b", define.TotalAnnotationSizeLimitB/2-1),
"c": strings.Repeat("d", define.TotalAnnotationSizeLimitB/2-1),
},
}
for i := range successCases {
if err := ValidateAnnotations(successCases[i]); err != nil {
t.Errorf("case[%d] expected success, got %v", i, err)
}
}
nameErrorCases := []map[string]string{
{"nospecialchars^=@": "bar"},
{"cantendwithadash-": "bar"},
{"only/one/slash": "bar"},
{strings.Repeat("a", 254): "bar"},
}
for i := range nameErrorCases {
if err := ValidateAnnotations(nameErrorCases[i]); err == nil {
t.Errorf("case[%d]: expected failure", i)
}
}
totalSizeErrorCases := []map[string]string{
{"a": strings.Repeat("b", define.TotalAnnotationSizeLimitB)},
{
"a": strings.Repeat("b", define.TotalAnnotationSizeLimitB/2),
"c": strings.Repeat("d", define.TotalAnnotationSizeLimitB/2),
},
}
for i := range totalSizeErrorCases {
if err := ValidateAnnotations(totalSizeErrorCases[i]); err == nil {
t.Errorf("case[%d] expected failure", i)
}
}
}

View File

@ -207,7 +207,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string,
// Generate the kube pods from containers.
if len(ctrs) >= 1 {
po, err := libpod.GenerateForKube(ctx, ctrs, options.Service, options.UseLongAnnotations, options.PodmanOnly)
po, err := libpod.GenerateForKube(ctx, ctrs, options.Service, options.PodmanOnly)
if err != nil {
return nil, err
}
@ -283,7 +283,7 @@ func getKubePods(ctx context.Context, pods []*libpod.Pod, options entities.Gener
svcs := [][]byte{}
for _, p := range pods {
po, sp, err := p.GenerateForKube(ctx, options.Service, options.UseLongAnnotations, options.PodmanOnly)
po, sp, err := p.GenerateForKube(ctx, options.Service, options.PodmanOnly)
if err != nil {
return nil, nil, err
}

View File

@ -23,6 +23,7 @@ import (
"github.com/containers/podman/v5/cmd/podman/parse"
"github.com/containers/podman/v5/libpod"
"github.com/containers/podman/v5/libpod/define"
"github.com/containers/podman/v5/pkg/annotations"
"github.com/containers/podman/v5/pkg/domain/entities"
entitiesTypes "github.com/containers/podman/v5/pkg/domain/entities/types"
v1apps "github.com/containers/podman/v5/pkg/k8s.io/api/apps/v1"
@ -289,11 +290,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
podTemplateSpec.ObjectMeta = podYAML.ObjectMeta
podTemplateSpec.Spec = podYAML.Spec
for name, val := range podYAML.Annotations {
if len(val) > define.MaxKubeAnnotation && !options.UseLongAnnotations {
return nil, fmt.Errorf("annotation %q=%q value length exceeds Kubernetes max %d", name, val, define.MaxKubeAnnotation)
}
}
for name, val := range options.Annotations {
if podYAML.Annotations == nil {
podYAML.Annotations = make(map[string]string)
@ -301,6 +298,10 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
podYAML.Annotations[name] = val
}
if err := annotations.ValidateAnnotations(podYAML.Annotations); err != nil {
return nil, err
}
r, proxies, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex, podYAML.Annotations, configMaps, serviceContainer)
if err != nil {
return nil, err

View File

@ -1562,117 +1562,6 @@ USER test1`
Expect(pod.Spec.Hostname).To(Equal(""))
})
It("--no-trunc on container with long annotation", func() {
ctrName := "demo"
vol1 := filepath.Join(podmanTest.TempDir, RandomString(99))
err := os.MkdirAll(vol1, 0755)
Expect(err).ToNot(HaveOccurred())
session := podmanTest.Podman([]string{"create", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, CITEST_IMAGE})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
kube := podmanTest.Podman([]string{"kube", "generate", "--no-trunc", ctrName})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(ExitCleanly())
pod := new(v1.Pod)
err = yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).ToNot(HaveOccurred())
Expect(pod.Annotations).To(HaveKeyWithValue(define.BindMountPrefix, vol1+":Z"))
Expect(pod.Annotations).To(Not(HaveKeyWithValue(define.BindMountPrefix, vol1[:define.MaxKubeAnnotation])))
})
It("on container with long annotation", func() {
ctrName := "demo"
vol1 := filepath.Join(podmanTest.TempDir, RandomString(99))
err := os.MkdirAll(vol1, 0755)
Expect(err).ToNot(HaveOccurred())
session := podmanTest.Podman([]string{"create", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, CITEST_IMAGE})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
kube := podmanTest.Podman([]string{"kube", "generate", ctrName})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(Exit(0))
if IsRemote() {
Expect(kube.ErrorToString()).To(BeEmpty())
} else {
Expect(kube.ErrorToString()).To(ContainSubstring("Truncation Annotation:"))
Expect(kube.ErrorToString()).To(ContainSubstring("Kubernetes only allows 63 characters"))
}
pod := new(v1.Pod)
err = yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).ToNot(HaveOccurred())
Expect(pod.Annotations).To(HaveKeyWithValue(define.BindMountPrefix, vol1[:define.MaxKubeAnnotation]))
Expect(pod.Annotations).To(Not(HaveKeyWithValue(define.BindMountPrefix, vol1+":Z")))
})
It("--no-trunc on pod with long annotation", func() {
ctrName := "demoCtr"
podName := "demoPod"
vol1 := filepath.Join(podmanTest.TempDir, RandomString(99))
err := os.MkdirAll(vol1, 0755)
Expect(err).ToNot(HaveOccurred())
session := podmanTest.Podman([]string{"pod", "create", podName})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
session = podmanTest.Podman([]string{"create", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, "--pod", podName, CITEST_IMAGE})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
kube := podmanTest.Podman([]string{"kube", "generate", "--no-trunc", podName})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(ExitCleanly())
pod := new(v1.Pod)
err = yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).ToNot(HaveOccurred())
Expect(pod.Annotations).To(HaveKeyWithValue(define.BindMountPrefix, vol1+":Z"))
Expect(pod.Annotations).To(Not(HaveKeyWithValue(define.BindMountPrefix, vol1[:define.MaxKubeAnnotation])))
})
It("on pod with long annotation", func() {
ctrName := "demoCtr"
podName := "demoPod"
vol1 := filepath.Join(podmanTest.TempDir, RandomString(99))
err := os.MkdirAll(vol1, 0755)
Expect(err).ToNot(HaveOccurred())
session := podmanTest.Podman([]string{"pod", "create", podName})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
session = podmanTest.Podman([]string{"create", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, "--pod", podName, CITEST_IMAGE})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
kube := podmanTest.Podman([]string{"kube", "generate", podName})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(Exit(0))
if IsRemote() {
Expect(kube.ErrorToString()).To(BeEmpty())
} else {
Expect(kube.ErrorToString()).To(ContainSubstring("Truncation Annotation:"))
Expect(kube.ErrorToString()).To(ContainSubstring("Kubernetes only allows 63 characters"))
}
pod := new(v1.Pod)
err = yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).ToNot(HaveOccurred())
Expect(pod.Annotations).To(HaveKeyWithValue(define.BindMountPrefix, vol1[:define.MaxKubeAnnotation]))
Expect(pod.Annotations).To(Not(HaveKeyWithValue(define.BindMountPrefix, vol1+":Z")))
})
It("--podman-only on container with --volumes-from", func() {
ctr1 := "ctr1"
ctr2 := "ctr2"

View File

@ -5897,44 +5897,29 @@ spec:
Expect(kube.ErrorToString()).To(ContainSubstring("since Network Namespace set to host: invalid argument"))
})
It("test with --no-trunc", func() {
ctrName := "demo"
vol1 := filepath.Join(podmanTest.TempDir, RandomString(99))
err := os.MkdirAll(vol1, 0755)
It("test with annotation size beyond limits", func() {
key := "name"
val := RandomString(define.TotalAnnotationSizeLimitB - len(key) + 1)
pod := getPod(withAnnotation(key, val))
err := generateKubeYaml("pod", pod, kubeYaml)
Expect(err).ToNot(HaveOccurred())
session := podmanTest.Podman([]string{"run", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, CITEST_IMAGE})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
file := filepath.Join(podmanTest.TempDir, ctrName+".yml")
session = podmanTest.Podman([]string{"kube", "generate", "--no-trunc", "-f", file, ctrName})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
session = podmanTest.Podman([]string{"kube", "play", "--no-trunc", file})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(Exit(125))
Expect(kube.ErrorToString()).To(ContainSubstring("annotations size " + strconv.Itoa(len(key+val)) + " is larger than limit " + strconv.Itoa(define.TotalAnnotationSizeLimitB)))
})
It("test with long annotation", func() {
ctrName := "demo"
vol1 := filepath.Join(podmanTest.TempDir, RandomString(99))
err := os.MkdirAll(vol1, 0755)
It("test with annotation size within limits", func() {
key := "name"
val := RandomString(define.TotalAnnotationSizeLimitB - len(key))
pod := getPod(withAnnotation(key, val))
err := generateKubeYaml("pod", pod, kubeYaml)
Expect(err).ToNot(HaveOccurred())
session := podmanTest.Podman([]string{"run", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, CITEST_IMAGE})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
file := filepath.Join(podmanTest.TempDir, ctrName+".yml")
session = podmanTest.Podman([]string{"kube", "generate", "--no-trunc", "-f", file, ctrName})
session.WaitWithDefaultTimeout()
Expect(session).Should(ExitCleanly())
session = podmanTest.Podman([]string{"kube", "play", file})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(125))
kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(ExitCleanly())
})
It("test pod with volumes-from annotation in yaml", func() {

View File

@ -455,32 +455,7 @@ _EOF
run_podman pod rm -t 0 -f test_pod
}
@test "podman play --annotation > Max" {
TESTDIR=$PODMAN_TMPDIR/testdir
RANDOMSTRING=$(random_string 65)
mkdir -p $TESTDIR
echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml
run_podman 125 play kube --annotation "name=$RANDOMSTRING" $PODMAN_TMPDIR/test.yaml
assert "$output" =~ "annotation exceeds maximum size, 63, of kubernetes annotation:" "Expected to fail with Length greater than 63"
}
@test "podman play --no-trunc --annotation > Max" {
TESTDIR=$PODMAN_TMPDIR/testdir
RANDOMSTRING=$(random_string 65)
mkdir -p $TESTDIR
echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml
run_podman play kube --no-trunc --annotation "name=$RANDOMSTRING" $PODMAN_TMPDIR/test.yaml
}
@test "podman play Yaml with annotation > Max" {
RANDOMSTRING=$(random_string 65)
_write_test_yaml "annotations=test: ${RANDOMSTRING}" command=id
run_podman 125 play kube - < $PODMAN_TMPDIR/test.yaml
assert "$output" =~ "annotation \"test\"=\"$RANDOMSTRING\" value length exceeds Kubernetes max 63" "Expected to fail with annotation length greater than 63"
}
@test "podman play Yaml --no-trunc with annotation > Max" {
@test "podman play Yaml deprecated --no-trunc annotation" {
RANDOMSTRING=$(random_string 65)
_write_test_yaml "annotations=test: ${RANDOMSTRING}" command=id