Skip to content

Commit

Permalink
Merge pull request containers#19102 from jakecorrenti/kube-generate-p…
Browse files Browse the repository at this point in the history
…rint-annotations

Add `--no-trunc` flag to maintain original annotation length
  • Loading branch information
openshift-merge-robot authored Jul 11, 2023
2 parents d31b9eb + 7b54fd8 commit 77b36ca
Show file tree
Hide file tree
Showing 20 changed files with 269 additions and 42 deletions.
3 changes: 3 additions & 0 deletions cmd/podman/kube/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
5 changes: 4 additions & 1 deletion cmd/podman/kube/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions docs/source/markdown/podman-kube-generate.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions docs/source/markdown/podman-kube-play.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
36 changes: 18 additions & 18 deletions libpod/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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])
Expand Down Expand Up @@ -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))
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand All @@ -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)
}
}

Expand Down
8 changes: 7 additions & 1 deletion pkg/api/handlers/libpod/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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))
Expand Down
32 changes: 17 additions & 15 deletions pkg/api/handlers/libpod/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}{
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 10 additions & 0 deletions pkg/api/server/register_kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions pkg/bindings/generate/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions pkg/bindings/generate/types_kube_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions pkg/bindings/kube/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions pkg/bindings/kube/types_play_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/domain/entities/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions pkg/domain/entities/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pkg/domain/infra/abi/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion pkg/domain/infra/tunnel/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand All @@ -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)
}

Expand Down
Loading

0 comments on commit 77b36ca

Please sign in to comment.