diff --git a/cmd/podman/kube/generate.go b/cmd/podman/kube/generate.go index ab8e83c498..6da5ebf25f 100644 --- a/cmd/podman/kube/generate.go +++ b/cmd/podman/kube/generate.go @@ -81,6 +81,9 @@ func generateFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) { flags.Int32VarP(&generateOptions.Replicas, replicasFlagName, "r", 1, "Set the replicas number for Deployment kind") _ = cmd.RegisterFlagCompletionFunc(replicasFlagName, completion.AutocompleteNone) + noTruncAnnotationsFlagName := "no-trunc" + flags.BoolVar(&generateOptions.UseLongAnnotations, noTruncAnnotationsFlagName, false, "Don't truncate annotations to Kubernetes length (63 chars)") + flags.SetNormalizeFunc(utils.AliasFlags) } diff --git a/cmd/podman/kube/play.go b/cmd/podman/kube/play.go index f0b5e296d7..482d544b57 100644 --- a/cmd/podman/kube/play.go +++ b/cmd/podman/kube/play.go @@ -165,6 +165,9 @@ func playFlags(cmd *cobra.Command) { flags.StringSliceVar(&playOptions.ConfigMaps, configmapFlagName, []string{}, "`Pathname` of a YAML file containing a kubernetes configmap") _ = cmd.RegisterFlagCompletionFunc(configmapFlagName, completion.AutocompleteDefault) + noTruncFlagName := "no-trunc" + flags.BoolVar(&playOptions.UseLongAnnotations, noTruncFlagName, false, "Use annotations that are not truncated to the Kubernetes maximum length of 63 characters") + if !registry.IsRemote() { certDirFlagName := "cert-dir" flags.StringVar(&playOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys") @@ -240,7 +243,7 @@ func play(cmd *cobra.Command, args []string) error { playOptions.Annotations = make(map[string]string) } annotation := splitN[1] - if len(annotation) > define.MaxKubeAnnotation { + if len(annotation) > define.MaxKubeAnnotation && !playOptions.UseLongAnnotations { return fmt.Errorf("annotation exceeds maximum size, %d, of kubernetes annotation: %s", define.MaxKubeAnnotation, annotation) } playOptions.Annotations[splitN[0]] = annotation diff --git a/docs/source/markdown/podman-kube-generate.1.md b/docs/source/markdown/podman-kube-generate.1.md index a35a15776e..4cc19d1574 100644 --- a/docs/source/markdown/podman-kube-generate.1.md +++ b/docs/source/markdown/podman-kube-generate.1.md @@ -34,6 +34,11 @@ Note that the generated Kubernetes YAML file can be used to re-run the deploymen 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` + #### **--replicas**, **-r**=*replica count* The value to set `replicas` to when generating a **Deployment** kind. diff --git a/docs/source/markdown/podman-kube-play.1.md.in b/docs/source/markdown/podman-kube-play.1.md.in index 0f2d2ff401..ab29fb93a0 100644 --- a/docs/source/markdown/podman-kube-play.1.md.in +++ b/docs/source/markdown/podman-kube-play.1.md.in @@ -214,6 +214,10 @@ 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/kube.go b/libpod/kube.go index 429ead9b5a..2dc6298a61 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -34,14 +34,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 bool) (*v1.Pod, error) { +func GenerateForKube(ctx context.Context, ctrs []*Container, getService, useLongAnnotations bool) (*v1.Pod, error) { // Generate the v1.Pod yaml description - return simplePodWithV1Containers(ctx, ctrs, getService) + return simplePodWithV1Containers(ctx, ctrs, getService, useLongAnnotations) } // GenerateForKube takes a slice of libpod containers and generates // one v1.Pod description -func (p *Pod) GenerateForKube(ctx context.Context, getService bool) (*v1.Pod, []v1.ServicePort, error) { +func (p *Pod) GenerateForKube(ctx context.Context, getService, useLongAnnotations bool) (*v1.Pod, []v1.ServicePort, error) { // Generate the v1.Pod yaml description var ( ports []v1.ContainerPort @@ -91,7 +91,7 @@ func (p *Pod) GenerateForKube(ctx context.Context, getService bool) (*v1.Pod, [] hostNetwork = infraContainer.NetworkMode() == string(namespaces.NetworkMode(specgen.Host)) hostUsers = infraContainer.IDMappings().HostUIDMapping && infraContainer.IDMappings().HostGIDMapping } - pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork, hostUsers, getService) + pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork, hostUsers, getService, useLongAnnotations) if err != nil { return nil, servicePorts, err } @@ -370,9 +370,9 @@ func newServicePortState() servicePortState { } } -func TruncateKubeAnnotation(str string) string { +func truncateKubeAnnotation(str string, useLongAnnotations bool) string { str = strings.TrimSpace(str) - if utf8.RuneCountInString(str) < define.MaxKubeAnnotation { + if useLongAnnotations || utf8.RuneCountInString(str) < define.MaxKubeAnnotation { return str } trunc := string([]rune(str)[:define.MaxKubeAnnotation]) @@ -426,7 +426,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 bool) (*v1.Pod, error) { +func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, ports []v1.ContainerPort, hostNetwork, hostUsers, getService, useLongAnnotations bool) (*v1.Pod, error) { deDupPodVolumes := make(map[string]*v1.Volume) first := true podContainers := make([]v1.Container, 0, len(containers)) @@ -445,11 +445,11 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po if define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k) { continue } - podAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = TruncateKubeAnnotation(v) + podAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = truncateKubeAnnotation(v, useLongAnnotations) } // Convert auto-update labels into kube annotations - for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels()) { - podAnnotations[k] = TruncateKubeAnnotation(v) + for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels(), useLongAnnotations) { + podAnnotations[k] = truncateKubeAnnotation(v, useLongAnnotations) } isInit := ctr.IsInitCtr() // Since hostname is only set at pod level, set the hostname to the hostname of the first container we encounter @@ -466,7 +466,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) + podAnnotations[define.BindMountPrefix] = truncateKubeAnnotation(k+":"+v, useLongAnnotations) } // Since port bindings for the pod are handled by the // infra container, wipe them here only if we are sharing the net namespace @@ -574,7 +574,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 bool) (*v1.Pod, error) { +func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getService, useLongAnnotations bool) (*v1.Pod, error) { kubeCtrs := make([]v1.Container, 0, len(ctrs)) kubeInitCtrs := []v1.Container{} kubeVolumes := make([]v1.Volume, 0) @@ -591,12 +591,12 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic if define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k) { continue } - kubeAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = TruncateKubeAnnotation(v) + kubeAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = truncateKubeAnnotation(v, useLongAnnotations) } // Convert auto-update labels into kube annotations - for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels()) { - kubeAnnotations[k] = TruncateKubeAnnotation(v) + for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels(), useLongAnnotations) { + kubeAnnotations[k] = truncateKubeAnnotation(v, useLongAnnotations) } isInit := ctr.IsInitCtr() @@ -643,7 +643,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic return nil, err } for k, v := range annotations { - kubeAnnotations[define.BindMountPrefix] = TruncateKubeAnnotation(k + ":" + v) + kubeAnnotations[define.BindMountPrefix] = truncateKubeAnnotation(k+":"+v, useLongAnnotations) } if isInit { kubeInitCtrs = append(kubeInitCtrs, kubeCtr) @@ -1266,7 +1266,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) map[string]string { +func getAutoUpdateAnnotations(ctrName string, ctrLabels map[string]string, useLongAnnotations bool) map[string]string { autoUpdateLabel := "io.containers.autoupdate" annotations := make(map[string]string) @@ -1276,7 +1276,7 @@ func getAutoUpdateAnnotations(ctrName string, ctrLabels map[string]string) map[s // 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) + annotations[kc] = truncateKubeAnnotation(v, useLongAnnotations) } } diff --git a/pkg/api/handlers/libpod/generate.go b/pkg/api/handlers/libpod/generate.go index 439bc60da9..60450f1105 100644 --- a/pkg/api/handlers/libpod/generate.go +++ b/pkg/api/handlers/libpod/generate.go @@ -93,6 +93,7 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) { Service bool `schema:"service"` Type string `schema:"type"` Replicas int32 `schema:"replicas"` + NoTrunc bool `schema:"noTrunc"` }{ // Defaults would go here. Replicas: 1, @@ -115,7 +116,12 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) { } containerEngine := abi.ContainerEngine{Libpod: runtime} - options := entities.GenerateKubeOptions{Service: query.Service, Type: generateType, Replicas: query.Replicas} + options := entities.GenerateKubeOptions{ + Service: query.Service, + Type: generateType, + Replicas: query.Replicas, + UseLongAnnotations: query.NoTrunc, + } report, err := containerEngine.GenerateKube(r.Context(), query.Names, options) if err != nil { utils.Error(w, http.StatusInternalServerError, fmt.Errorf("generating YAML: %w", err)) diff --git a/pkg/api/handlers/libpod/kube.go b/pkg/api/handlers/libpod/kube.go index 2f53f82d7e..b1575700e5 100644 --- a/pkg/api/handlers/libpod/kube.go +++ b/pkg/api/handlers/libpod/kube.go @@ -29,6 +29,7 @@ func KubePlay(w http.ResponseWriter, r *http.Request) { StaticMACs []string `schema:"staticMACs"` NoHosts bool `schema:"noHosts"` PublishPorts []string `schema:"publishPorts"` + NoTrunc bool `schema:"noTrunc"` Wait bool `schema:"wait"` ServiceContainer bool `schema:"serviceContainer"` }{ @@ -85,21 +86,22 @@ func KubePlay(w http.ResponseWriter, r *http.Request) { containerEngine := abi.ContainerEngine{Libpod: runtime} options := entities.PlayKubeOptions{ - Annotations: query.Annotations, - Authfile: authfile, - Username: username, - Password: password, - Networks: query.Network, - NoHosts: query.NoHosts, - Quiet: true, - LogDriver: logDriver, - LogOptions: query.LogOptions, - StaticIPs: staticIPs, - StaticMACs: staticMACs, - IsRemote: true, - PublishPorts: query.PublishPorts, - Wait: query.Wait, - ServiceContainer: query.ServiceContainer, + Annotations: query.Annotations, + Authfile: authfile, + Username: username, + Password: password, + Networks: query.Network, + NoHosts: query.NoHosts, + Quiet: true, + LogDriver: logDriver, + LogOptions: query.LogOptions, + StaticIPs: staticIPs, + StaticMACs: staticMACs, + IsRemote: true, + PublishPorts: query.PublishPorts, + Wait: query.Wait, + ServiceContainer: query.ServiceContainer, + UseLongAnnotations: query.NoTrunc, } if _, found := r.URL.Query()["tlsVerify"]; found { options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) diff --git a/pkg/api/server/register_kube.go b/pkg/api/server/register_kube.go index aee2e0081a..22c30ad656 100644 --- a/pkg/api/server/register_kube.go +++ b/pkg/api/server/register_kube.go @@ -58,6 +58,11 @@ func (s *APIServer) registerKubeHandlers(r *mux.Router) error { // type: boolean // default: false // description: Clean up all objects created when a SIGTERM is received or pods exit. + // - in: query + // name: noTrunc + // type: boolean + // default: false + // description: use annotations that are not truncated to the Kubernetes maximum length of 63 characters // - in: body // name: request // description: Kubernetes YAML file. @@ -125,6 +130,11 @@ func (s *APIServer) registerKubeHandlers(r *mux.Router) error { // format: int32 // default: 0 // description: Set the replica number for Deployment kind. + // - in: query + // name: noTrunc + // type: boolean + // default: false + // description: don't truncate annotations to the Kubernetes maximum length of 63 characters // produces: // - text/vnd.yaml // - application/json diff --git a/pkg/bindings/generate/types.go b/pkg/bindings/generate/types.go index ec45c732dd..3910638b1b 100644 --- a/pkg/bindings/generate/types.go +++ b/pkg/bindings/generate/types.go @@ -10,6 +10,8 @@ type KubeOptions struct { Type *string // Replicas - the value to set in the replicas field for a Deployment Replicas *int32 + // NoTrunc - don't truncate annotations to the Kubernetes maximum length of 63 characters + NoTrunc *bool } // SystemdOptions are optional options for generating systemd files diff --git a/pkg/bindings/generate/types_kube_options.go b/pkg/bindings/generate/types_kube_options.go index aba2d7ef98..6a863cc23c 100644 --- a/pkg/bindings/generate/types_kube_options.go +++ b/pkg/bindings/generate/types_kube_options.go @@ -61,3 +61,18 @@ func (o *KubeOptions) GetReplicas() int32 { } return *o.Replicas } + +// WithNoTrunc set field NoTrunc to given value +func (o *KubeOptions) WithNoTrunc(value bool) *KubeOptions { + o.NoTrunc = &value + return o +} + +// GetNoTrunc returns value of field NoTrunc +func (o *KubeOptions) GetNoTrunc() bool { + if o.NoTrunc == nil { + var z bool + return z + } + return *o.NoTrunc +} diff --git a/pkg/bindings/kube/types.go b/pkg/bindings/kube/types.go index a5352333ba..9bb368e8d3 100644 --- a/pkg/bindings/kube/types.go +++ b/pkg/bindings/kube/types.go @@ -44,6 +44,9 @@ type PlayOptions struct { LogOptions *[]string // Start - don't start the pod if false Start *bool + // NoTrunc - use annotations that were not truncated to the + // Kubernetes maximum of 63 characters + NoTrunc *bool // Userns - define the user namespace to use. Userns *string // Force - remove volumes on --down diff --git a/pkg/bindings/kube/types_play_options.go b/pkg/bindings/kube/types_play_options.go index bd83c6f576..37bfbc2835 100644 --- a/pkg/bindings/kube/types_play_options.go +++ b/pkg/bindings/kube/types_play_options.go @@ -273,6 +273,21 @@ func (o *PlayOptions) GetStart() bool { return *o.Start } +// WithNoTrunc set field NoTrunc to given value +func (o *PlayOptions) WithNoTrunc(value bool) *PlayOptions { + o.NoTrunc = &value + return o +} + +// GetNoTrunc returns value of field NoTrunc +func (o *PlayOptions) GetNoTrunc() bool { + if o.NoTrunc == nil { + var z bool + return z + } + return *o.NoTrunc +} + // WithUserns set field Userns to given value func (o *PlayOptions) WithUserns(value string) *PlayOptions { o.Userns = &value diff --git a/pkg/domain/entities/generate.go b/pkg/domain/entities/generate.go index a78f5d952e..eaa20bb98d 100644 --- a/pkg/domain/entities/generate.go +++ b/pkg/domain/entities/generate.go @@ -35,6 +35,8 @@ type GenerateKubeOptions struct { Type string // Replicas - the value to set in the replicas field for a Deployment Replicas int32 + // UseLongAnnotations - don't truncate annotations to the Kubernetes maximum length of 63 characters + UseLongAnnotations bool } type KubeGenerateOptions = GenerateKubeOptions diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go index 184062278e..3a247d4c2c 100644 --- a/pkg/domain/entities/play.go +++ b/pkg/domain/entities/play.go @@ -59,6 +59,9 @@ type PlayKubeOptions struct { Start types.OptionalBool // ServiceContainer - creates a service container that is started before and is stopped after all pods. ServiceContainer bool + // UseLongAnnotations - use annotations that were not truncated to the + // Kubernetes maximum length of 63 characters + UseLongAnnotations bool // Userns - define the user namespace to use. Userns string // IsRemote - was the request triggered by running podman-remote diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index 6c8510afbf..a8d30bb916 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) + po, err := libpod.GenerateForKube(ctx, ctrs, options.Service, options.UseLongAnnotations) if err != nil { return nil, err } @@ -273,7 +273,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) + po, sp, err := p.GenerateForKube(ctx, options.Service, options.UseLongAnnotations) if err != nil { return nil, nil, err } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 7e2570fc5d..2883c6b41f 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -229,8 +229,8 @@ 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 { - return nil, fmt.Errorf("invalid annotation %q=%q value length exceeds Kubernetetes max %d", name, val, define.MaxKubeAnnotation) + 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 { diff --git a/pkg/domain/infra/tunnel/kube.go b/pkg/domain/infra/tunnel/kube.go index ca943374f1..fff83a5946 100644 --- a/pkg/domain/infra/tunnel/kube.go +++ b/pkg/domain/infra/tunnel/kube.go @@ -46,7 +46,7 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, // // Note: Caller is responsible for closing returned Reader func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, opts entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { - options := new(generate.KubeOptions).WithService(opts.Service).WithType(opts.Type).WithReplicas(opts.Replicas) + options := new(generate.KubeOptions).WithService(opts.Service).WithType(opts.Type).WithReplicas(opts.Replicas).WithNoTrunc(opts.UseLongAnnotations) return generate.Kube(ic.ClientCtx, nameOrIDs, options) } @@ -73,6 +73,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, opts en options.WithStart(start == types.OptionalBoolTrue) } options.WithPublishPorts(opts.PublishPorts) + options.WithNoTrunc(opts.UseLongAnnotations) return play.KubeWithBody(ic.ClientCtx, body, options) } diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index dcc0a4ce09..09ebec6592 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -1559,4 +1559,102 @@ USER test1` Expect(pod.Name).To(Equal("testpod")) Expect(pod.Spec.Hostname).To(Equal("")) }) + + It("podman kube generate --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, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"kube", "generate", "--no-trunc", ctrName}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + 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("podman kube generate 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, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"kube", "generate", ctrName}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + 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 kube generate --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(Exit(0)) + + session = podmanTest.Podman([]string{"create", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, "--pod", podName, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"kube", "generate", "--no-trunc", podName}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + 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("podman kube generate 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(Exit(0)) + + session = podmanTest.Podman([]string{"create", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, "--pod", podName, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"kube", "generate", podName}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + 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"))) + }) }) diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go index d3af40e9ba..03a1d03647 100644 --- a/test/e2e/play_kube_test.go +++ b/test/e2e/play_kube_test.go @@ -5341,4 +5341,44 @@ spec: Expect(kube).Should(Exit(125)) Expect(kube.ErrorToString()).To(ContainSubstring("since Network Namespace set to host: invalid argument")) }) + + It("podman kube play test with --no-trunc", func() { + ctrName := "demo" + vol1 := filepath.Join(podmanTest.TempDir, RandomString(99)) + err := os.MkdirAll(vol1, 0755) + Expect(err).ToNot(HaveOccurred()) + + session := podmanTest.Podman([]string{"run", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + file := filepath.Join(podmanTest.TempDir, ctrName+".yml") + session = podmanTest.Podman([]string{"kube", "generate", "--no-trunc", "-f", file, ctrName}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"kube", "play", "--no-trunc", file}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + + It("podman kube play test 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{"run", "-v", vol1 + ":/tmp/foo:Z", "--name", ctrName, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + file := filepath.Join(podmanTest.TempDir, ctrName+".yml") + session = podmanTest.Podman([]string{"kube", "generate", "--no-trunc", "-f", file, ctrName}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"kube", "play", file}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + }) }) diff --git a/test/system/700-play.bats b/test/system/700-play.bats index 62bfe24787..2bcea8b7d1 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -431,12 +431,27 @@ _EOF 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" =~ "invalid annotation \"test\"=\"$RANDOMSTRING\"" "Expected to fail with annotation length greater than 63" + 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" { + RANDOMSTRING=$(random_string 65) + + _write_test_yaml "annotations=test: ${RANDOMSTRING}" command=id + run_podman play kube --no-trunc - < $PODMAN_TMPDIR/test.yaml } @test "podman kube play - default log driver" {