Add --no-trunc flag to maintain original annotation length

Adds a `--no-trunc` flag to `podman kube generate` preventing the
annotations from being trimmed at 63 characters. However, due to
the fact the annotations will not be trimmed, any annotation that is
longer than 63 characters means this YAML will no longer be Kubernetes
compatible. However, these YAML files can still be used with `podman
kube play` due to the addition of the new flag below.

Adds a `--no-trunc` flag to `podman kube play` supporting YAML files with
annotations that were not truncated to the Kubernetes maximum length of
63 characters.

Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me>
This commit is contained in:
Jake Correnti
2023-07-03 14:17:48 -04:00
parent d31b9eb9e7
commit 7b54fd84ec
20 changed files with 269 additions and 42 deletions

View File

@ -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") flags.Int32VarP(&generateOptions.Replicas, replicasFlagName, "r", 1, "Set the replicas number for Deployment kind")
_ = cmd.RegisterFlagCompletionFunc(replicasFlagName, completion.AutocompleteNone) _ = 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) flags.SetNormalizeFunc(utils.AliasFlags)
} }

View File

@ -165,6 +165,9 @@ func playFlags(cmd *cobra.Command) {
flags.StringSliceVar(&playOptions.ConfigMaps, configmapFlagName, []string{}, "`Pathname` of a YAML file containing a kubernetes configmap") flags.StringSliceVar(&playOptions.ConfigMaps, configmapFlagName, []string{}, "`Pathname` of a YAML file containing a kubernetes configmap")
_ = cmd.RegisterFlagCompletionFunc(configmapFlagName, completion.AutocompleteDefault) _ = 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() { if !registry.IsRemote() {
certDirFlagName := "cert-dir" certDirFlagName := "cert-dir"
flags.StringVar(&playOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys") 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) playOptions.Annotations = make(map[string]string)
} }
annotation := splitN[1] 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) return fmt.Errorf("annotation exceeds maximum size, %d, of kubernetes annotation: %s", define.MaxKubeAnnotation, annotation)
} }
playOptions.Annotations[splitN[0]] = annotation playOptions.Annotations[splitN[0]] = annotation

View File

@ -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. 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* #### **--replicas**, **-r**=*replica count*
The value to set `replicas` to when generating a **Deployment** kind. The value to set `replicas` to when generating a **Deployment** kind.

View File

@ -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. 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]* #### **--publish**=*[[ip:][hostPort]:]containerPort[/protocol]*
Define or override a port definition in the YAML file. Define or override a port definition in the YAML file.

View File

@ -34,14 +34,14 @@ import (
// GenerateForKube takes a slice of libpod containers and generates // GenerateForKube takes a slice of libpod containers and generates
// one v1.Pod description that includes just a single container. // 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 // 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 // GenerateForKube takes a slice of libpod containers and generates
// one v1.Pod description // 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 // Generate the v1.Pod yaml description
var ( var (
ports []v1.ContainerPort 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)) hostNetwork = infraContainer.NetworkMode() == string(namespaces.NetworkMode(specgen.Host))
hostUsers = infraContainer.IDMappings().HostUIDMapping && infraContainer.IDMappings().HostGIDMapping 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 { if err != nil {
return nil, servicePorts, err 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) str = strings.TrimSpace(str)
if utf8.RuneCountInString(str) < define.MaxKubeAnnotation { if useLongAnnotations || utf8.RuneCountInString(str) < define.MaxKubeAnnotation {
return str return str
} }
trunc := string([]rune(str)[:define.MaxKubeAnnotation]) trunc := string([]rune(str)[:define.MaxKubeAnnotation])
@ -426,7 +426,7 @@ func containersToServicePorts(containers []v1.Container) ([]v1.ServicePort, erro
return sps, nil 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) deDupPodVolumes := make(map[string]*v1.Volume)
first := true first := true
podContainers := make([]v1.Container, 0, len(containers)) 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) { if define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k) {
continue 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 // Convert auto-update labels into kube annotations
for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels()) { for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels(), useLongAnnotations) {
podAnnotations[k] = TruncateKubeAnnotation(v) podAnnotations[k] = truncateKubeAnnotation(v, useLongAnnotations)
} }
isInit := ctr.IsInitCtr() isInit := ctr.IsInitCtr()
// Since hostname is only set at pod level, set the hostname to the hostname of the first container we encounter // 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 return nil, err
} }
for k, v := range annotations { 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 // Since port bindings for the pod are handled by the
// infra container, wipe them here only if we are sharing the net namespace // 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 // 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. // 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)) kubeCtrs := make([]v1.Container, 0, len(ctrs))
kubeInitCtrs := []v1.Container{} kubeInitCtrs := []v1.Container{}
kubeVolumes := make([]v1.Volume, 0) kubeVolumes := make([]v1.Volume, 0)
@ -591,12 +591,12 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic
if define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k) { if define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k) {
continue 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 // Convert auto-update labels into kube annotations
for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels()) { for k, v := range getAutoUpdateAnnotations(ctr.Name(), ctr.Labels(), useLongAnnotations) {
kubeAnnotations[k] = TruncateKubeAnnotation(v) kubeAnnotations[k] = truncateKubeAnnotation(v, useLongAnnotations)
} }
isInit := ctr.IsInitCtr() isInit := ctr.IsInitCtr()
@ -643,7 +643,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic
return nil, err return nil, err
} }
for k, v := range annotations { for k, v := range annotations {
kubeAnnotations[define.BindMountPrefix] = TruncateKubeAnnotation(k + ":" + v) kubeAnnotations[define.BindMountPrefix] = truncateKubeAnnotation(k+":"+v, useLongAnnotations)
} }
if isInit { if isInit {
kubeInitCtrs = append(kubeInitCtrs, kubeCtr) kubeInitCtrs = append(kubeInitCtrs, kubeCtr)
@ -1266,7 +1266,7 @@ func removeUnderscores(s string) string {
// getAutoUpdateAnnotations searches for auto-update container labels // getAutoUpdateAnnotations searches for auto-update container labels
// and returns them as kube annotations // 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" autoUpdateLabel := "io.containers.autoupdate"
annotations := make(map[string]string) 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 // since labels can variate between containers within a pod, they will be
// identified with the container name when converted into kube annotations // identified with the container name when converted into kube annotations
kc := fmt.Sprintf("%s/%s", k, ctrName) kc := fmt.Sprintf("%s/%s", k, ctrName)
annotations[kc] = TruncateKubeAnnotation(v) annotations[kc] = truncateKubeAnnotation(v, useLongAnnotations)
} }
} }

View File

@ -93,6 +93,7 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) {
Service bool `schema:"service"` Service bool `schema:"service"`
Type string `schema:"type"` Type string `schema:"type"`
Replicas int32 `schema:"replicas"` Replicas int32 `schema:"replicas"`
NoTrunc bool `schema:"noTrunc"`
}{ }{
// Defaults would go here. // Defaults would go here.
Replicas: 1, Replicas: 1,
@ -115,7 +116,12 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) {
} }
containerEngine := abi.ContainerEngine{Libpod: runtime} 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) report, err := containerEngine.GenerateKube(r.Context(), query.Names, options)
if err != nil { if err != nil {
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("generating YAML: %w", err)) utils.Error(w, http.StatusInternalServerError, fmt.Errorf("generating YAML: %w", err))

View File

@ -29,6 +29,7 @@ func KubePlay(w http.ResponseWriter, r *http.Request) {
StaticMACs []string `schema:"staticMACs"` StaticMACs []string `schema:"staticMACs"`
NoHosts bool `schema:"noHosts"` NoHosts bool `schema:"noHosts"`
PublishPorts []string `schema:"publishPorts"` PublishPorts []string `schema:"publishPorts"`
NoTrunc bool `schema:"noTrunc"`
Wait bool `schema:"wait"` Wait bool `schema:"wait"`
ServiceContainer bool `schema:"serviceContainer"` ServiceContainer bool `schema:"serviceContainer"`
}{ }{
@ -85,21 +86,22 @@ func KubePlay(w http.ResponseWriter, r *http.Request) {
containerEngine := abi.ContainerEngine{Libpod: runtime} containerEngine := abi.ContainerEngine{Libpod: runtime}
options := entities.PlayKubeOptions{ options := entities.PlayKubeOptions{
Annotations: query.Annotations, Annotations: query.Annotations,
Authfile: authfile, Authfile: authfile,
Username: username, Username: username,
Password: password, Password: password,
Networks: query.Network, Networks: query.Network,
NoHosts: query.NoHosts, NoHosts: query.NoHosts,
Quiet: true, Quiet: true,
LogDriver: logDriver, LogDriver: logDriver,
LogOptions: query.LogOptions, LogOptions: query.LogOptions,
StaticIPs: staticIPs, StaticIPs: staticIPs,
StaticMACs: staticMACs, StaticMACs: staticMACs,
IsRemote: true, IsRemote: true,
PublishPorts: query.PublishPorts, PublishPorts: query.PublishPorts,
Wait: query.Wait, Wait: query.Wait,
ServiceContainer: query.ServiceContainer, ServiceContainer: query.ServiceContainer,
UseLongAnnotations: query.NoTrunc,
} }
if _, found := r.URL.Query()["tlsVerify"]; found { if _, found := r.URL.Query()["tlsVerify"]; found {
options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)

View File

@ -58,6 +58,11 @@ func (s *APIServer) registerKubeHandlers(r *mux.Router) error {
// type: boolean // type: boolean
// default: false // default: false
// description: Clean up all objects created when a SIGTERM is received or pods exit. // 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 // - in: body
// name: request // name: request
// description: Kubernetes YAML file. // description: Kubernetes YAML file.
@ -125,6 +130,11 @@ func (s *APIServer) registerKubeHandlers(r *mux.Router) error {
// format: int32 // format: int32
// default: 0 // default: 0
// description: Set the replica number for Deployment kind. // 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: // produces:
// - text/vnd.yaml // - text/vnd.yaml
// - application/json // - application/json

View File

@ -10,6 +10,8 @@ type KubeOptions struct {
Type *string Type *string
// Replicas - the value to set in the replicas field for a Deployment // Replicas - the value to set in the replicas field for a Deployment
Replicas *int32 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 // SystemdOptions are optional options for generating systemd files

View File

@ -61,3 +61,18 @@ func (o *KubeOptions) GetReplicas() int32 {
} }
return *o.Replicas 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
}

View File

@ -44,6 +44,9 @@ type PlayOptions struct {
LogOptions *[]string LogOptions *[]string
// Start - don't start the pod if false // Start - don't start the pod if false
Start *bool 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 - define the user namespace to use.
Userns *string Userns *string
// Force - remove volumes on --down // Force - remove volumes on --down

View File

@ -273,6 +273,21 @@ func (o *PlayOptions) GetStart() bool {
return *o.Start 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 // WithUserns set field Userns to given value
func (o *PlayOptions) WithUserns(value string) *PlayOptions { func (o *PlayOptions) WithUserns(value string) *PlayOptions {
o.Userns = &value o.Userns = &value

View File

@ -35,6 +35,8 @@ type GenerateKubeOptions struct {
Type string Type string
// Replicas - the value to set in the replicas field for a Deployment // Replicas - the value to set in the replicas field for a Deployment
Replicas int32 Replicas int32
// UseLongAnnotations - don't truncate annotations to the Kubernetes maximum length of 63 characters
UseLongAnnotations bool
} }
type KubeGenerateOptions = GenerateKubeOptions type KubeGenerateOptions = GenerateKubeOptions

View File

@ -59,6 +59,9 @@ type PlayKubeOptions struct {
Start types.OptionalBool Start types.OptionalBool
// ServiceContainer - creates a service container that is started before and is stopped after all pods. // ServiceContainer - creates a service container that is started before and is stopped after all pods.
ServiceContainer bool 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 - define the user namespace to use.
Userns string Userns string
// IsRemote - was the request triggered by running podman-remote // IsRemote - was the request triggered by running podman-remote

View File

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

View File

@ -229,8 +229,8 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
podTemplateSpec.ObjectMeta = podYAML.ObjectMeta podTemplateSpec.ObjectMeta = podYAML.ObjectMeta
podTemplateSpec.Spec = podYAML.Spec podTemplateSpec.Spec = podYAML.Spec
for name, val := range podYAML.Annotations { for name, val := range podYAML.Annotations {
if len(val) > define.MaxKubeAnnotation { if len(val) > define.MaxKubeAnnotation && !options.UseLongAnnotations {
return nil, fmt.Errorf("invalid annotation %q=%q value length exceeds Kubernetetes max %d", name, val, define.MaxKubeAnnotation) return nil, fmt.Errorf("annotation %q=%q value length exceeds Kubernetes max %d", name, val, define.MaxKubeAnnotation)
} }
} }
for name, val := range options.Annotations { for name, val := range options.Annotations {

View File

@ -46,7 +46,7 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string,
// //
// Note: Caller is responsible for closing returned Reader // Note: Caller is responsible for closing returned Reader
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, opts entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { 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) 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.WithStart(start == types.OptionalBoolTrue)
} }
options.WithPublishPorts(opts.PublishPorts) options.WithPublishPorts(opts.PublishPorts)
options.WithNoTrunc(opts.UseLongAnnotations)
return play.KubeWithBody(ic.ClientCtx, body, options) return play.KubeWithBody(ic.ClientCtx, body, options)
} }

View File

@ -1559,4 +1559,102 @@ USER test1`
Expect(pod.Name).To(Equal("testpod")) Expect(pod.Name).To(Equal("testpod"))
Expect(pod.Spec.Hostname).To(Equal("")) 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")))
})
}) })

View File

@ -5341,4 +5341,44 @@ spec:
Expect(kube).Should(Exit(125)) Expect(kube).Should(Exit(125))
Expect(kube.ErrorToString()).To(ContainSubstring("since Network Namespace set to host: invalid argument")) 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))
})
}) })

View File

@ -431,12 +431,27 @@ _EOF
assert "$output" =~ "annotation exceeds maximum size, 63, of kubernetes annotation:" "Expected to fail with Length greater than 63" 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" { @test "podman play Yaml with annotation > Max" {
RANDOMSTRING=$(random_string 65) RANDOMSTRING=$(random_string 65)
_write_test_yaml "annotations=test: ${RANDOMSTRING}" command=id _write_test_yaml "annotations=test: ${RANDOMSTRING}" command=id
run_podman 125 play kube - < $PODMAN_TMPDIR/test.yaml run_podman 125 play kube - < $PODMAN_TMPDIR/test.yaml
assert "$output" =~ "invalid annotation \"test\"=\"$RANDOMSTRING\"" "Expected to fail with annotation length greater than 63" 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" { @test "podman kube play - default log driver" {