diff --git a/cmd/podman/kube/generate.go b/cmd/podman/kube/generate.go index 6bf0357620..39652cdb91 100644 --- a/cmd/podman/kube/generate.go +++ b/cmd/podman/kube/generate.go @@ -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)") diff --git a/cmd/podman/kube/play.go b/cmd/podman/kube/play.go index caa78add38..908e0077b0 100644 --- a/cmd/podman/kube/play.go +++ b/cmd/podman/kube/play.go @@ -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 { diff --git a/docs/source/markdown/podman-kube-generate.1.md b/docs/source/markdown/podman-kube-generate.1.md index 78f2bb1f70..f0f11e506f 100644 --- a/docs/source/markdown/podman-kube-generate.1.md +++ b/docs/source/markdown/podman-kube-generate.1.md @@ -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) diff --git a/docs/source/markdown/podman-kube-play.1.md.in b/docs/source/markdown/podman-kube-play.1.md.in index 93b62a4361..0b5aa0a22c 100644 --- a/docs/source/markdown/podman-kube-play.1.md.in +++ b/docs/source/markdown/podman-kube-play.1.md.in @@ -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. diff --git a/libpod/define/annotations.go b/libpod/define/annotations.go index 3ceac2dfe4..a9d4031ae2 100644 --- a/libpod/define/annotations.go +++ b/libpod/define/annotations.go @@ -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 diff --git a/libpod/kube.go b/libpod/kube.go index 5a45087e94..cd57763f46 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -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 } } diff --git a/pkg/annotations/validate.go b/pkg/annotations/validate.go new file mode 100644 index 0000000000..4ddeea30ed --- /dev/null +++ b/pkg/annotations/validate.go @@ -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 +} diff --git a/pkg/annotations/validate_test.go b/pkg/annotations/validate_test.go new file mode 100644 index 0000000000..b29385fcf9 --- /dev/null +++ b/pkg/annotations/validate_test.go @@ -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) + } + } +} diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index e48e52d446..e424500fbd 100644 --- a/pkg/domain/infra/abi/generate.go +++ b/pkg/domain/infra/abi/generate.go @@ -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 } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index aa6b7013f7..f5a2107950 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -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 diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 05396c1249..1cb5ff5843 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -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" diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index 9a1561e93c..3cfce748ab 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -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() { diff --git a/test/system/700-play.bats b/test/system/700-play.bats index f06859848a..d288581d12 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -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