Merge pull request #19211 from jakecorrenti/add-reserved-flag-generate

Add `--podman-only` flag to `podman generate kube`
This commit is contained in:
OpenShift Merge Robot 2023-07-16 17:34:35 +02:00 committed by GitHub
commit 49a924cf39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 230 additions and 16 deletions

View File

@ -84,6 +84,9 @@ 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)")
podmanOnlyFlagName := "podman-only"
flags.BoolVar(&generateOptions.PodmanOnly, podmanOnlyFlagName, false, "Add podman-only reserved annotations to the generated YAML file (Cannot be used by Kubernetes)")
flags.SetNormalizeFunc(utils.AliasFlags)
}

View File

@ -39,6 +39,10 @@ Output to the given file instead of STDOUT. If the file already exists, `kube ge
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)
#### **--replicas**, **-r**=*replica count*
The value to set `replicas` to when generating a **Deployment** kind.

View File

@ -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, useLongAnnotations bool) (*v1.Pod, error) {
func GenerateForKube(ctx context.Context, ctrs []*Container, getService, useLongAnnotations, podmanOnly bool) (*v1.Pod, error) {
// Generate the v1.Pod yaml description
return simplePodWithV1Containers(ctx, ctrs, getService, useLongAnnotations)
return simplePodWithV1Containers(ctx, ctrs, getService, useLongAnnotations, podmanOnly)
}
// GenerateForKube takes a slice of libpod containers and generates
// one v1.Pod description
func (p *Pod) GenerateForKube(ctx context.Context, getService, useLongAnnotations bool) (*v1.Pod, []v1.ServicePort, error) {
func (p *Pod) GenerateForKube(ctx context.Context, getService, useLongAnnotations, podmanOnly 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, useLongAnnotation
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, useLongAnnotations)
pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork, hostUsers, getService, useLongAnnotations, podmanOnly)
if err != nil {
return nil, servicePorts, err
}
@ -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, useLongAnnotations bool) (*v1.Pod, error) {
func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, ports []v1.ContainerPort, hostNetwork, hostUsers, getService, useLongAnnotations, podmanOnly bool) (*v1.Pod, error) {
deDupPodVolumes := make(map[string]*v1.Volume)
first := true
podContainers := make([]v1.Container, 0, len(containers))
@ -442,7 +442,7 @@ func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, po
for _, ctr := range containers {
if !ctr.IsInfra() {
for k, v := range ctr.config.Spec.Annotations {
if define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k) {
if !podmanOnly && (define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k)) {
continue
}
podAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = truncateKubeAnnotation(v, useLongAnnotations)
@ -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, useLongAnnotations bool) (*v1.Pod, error) {
func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getService, useLongAnnotations, podmanOnly bool) (*v1.Pod, error) {
kubeCtrs := make([]v1.Container, 0, len(ctrs))
kubeInitCtrs := []v1.Container{}
kubeVolumes := make([]v1.Volume, 0)
@ -588,7 +588,7 @@ func simplePodWithV1Containers(ctx context.Context, ctrs []*Container, getServic
for _, ctr := range ctrs {
ctrNames = append(ctrNames, removeUnderscores(ctr.Name()))
for k, v := range ctr.config.Spec.Annotations {
if define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k) {
if !podmanOnly && (define.IsReservedAnnotation(k) || annotations.IsReservedAnnotation(k)) {
continue
}
kubeAnnotations[fmt.Sprintf("%s/%s", k, removeUnderscores(ctr.Name()))] = truncateKubeAnnotation(v, useLongAnnotations)

View File

@ -89,11 +89,12 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
Names []string `schema:"names"`
Service bool `schema:"service"`
Type string `schema:"type"`
Replicas int32 `schema:"replicas"`
NoTrunc bool `schema:"noTrunc"`
PodmanOnly bool `schema:"podmanOnly"`
Names []string `schema:"names"`
Service bool `schema:"service"`
Type string `schema:"type"`
Replicas int32 `schema:"replicas"`
NoTrunc bool `schema:"noTrunc"`
}{
// Defaults would go here.
Replicas: 1,
@ -117,6 +118,7 @@ func GenerateKube(w http.ResponseWriter, r *http.Request) {
containerEngine := abi.ContainerEngine{Libpod: runtime}
options := entities.GenerateKubeOptions{
PodmanOnly: query.PodmanOnly,
Service: query.Service,
Type: generateType,
Replicas: query.Replicas,

View File

@ -139,6 +139,11 @@ func (s *APIServer) registerKubeHandlers(r *mux.Router) error {
// type: boolean
// default: false
// description: don't truncate annotations to the Kubernetes maximum length of 63 characters
// - in: query
// name: podmanOnly
// type: boolean
// default: false
// description: add podman-only reserved annotations in generated YAML file (cannot be used by Kubernetes)
// produces:
// - text/vnd.yaml
// - application/json

View File

@ -4,6 +4,8 @@ package generate
//
//go:generate go run ../generator/generator.go KubeOptions
type KubeOptions struct {
// PodmanOnly - add podman-only reserved annotations to generated YAML file (Cannot be used by Kubernetes)
PodmanOnly *bool
// Service - generate YAML for a Kubernetes _service_ object.
Service *bool
// Type - the k8s kind to be generated i.e Pod or Deployment

View File

@ -17,6 +17,21 @@ func (o *KubeOptions) ToParams() (url.Values, error) {
return util.ToParams(o)
}
// WithPodmanOnly set field PodmanOnly to given value
func (o *KubeOptions) WithPodmanOnly(value bool) *KubeOptions {
o.PodmanOnly = &value
return o
}
// GetPodmanOnly returns value of field PodmanOnly
func (o *KubeOptions) GetPodmanOnly() bool {
if o.PodmanOnly == nil {
var z bool
return z
}
return *o.PodmanOnly
}
// WithService set field Service to given value
func (o *KubeOptions) WithService(value bool) *KubeOptions {
o.Service = &value

View File

@ -29,6 +29,8 @@ type GenerateSystemdReport struct {
// GenerateKubeOptions control the generation of Kubernetes YAML files.
type GenerateKubeOptions struct {
// PodmanOnly - add podman-only reserved annotations in the generated YAML file (Cannot be used by Kubernetes)
PodmanOnly bool
// Service - generate YAML for a Kubernetes _service_ object.
Service bool
// Type - the k8s kind to be generated i.e Pod or Deployment

View File

@ -207,7 +207,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string,
// Generate the kube pods from containers.
if len(ctrs) >= 1 {
po, err := libpod.GenerateForKube(ctx, ctrs, options.Service, options.UseLongAnnotations)
po, err := libpod.GenerateForKube(ctx, ctrs, options.Service, options.UseLongAnnotations, options.PodmanOnly)
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, options.UseLongAnnotations)
po, sp, err := p.GenerateForKube(ctx, options.Service, options.UseLongAnnotations, options.PodmanOnly)
if err != nil {
return nil, nil, err
}

View File

@ -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).WithNoTrunc(opts.UseLongAnnotations)
options := new(generate.KubeOptions).WithService(opts.Service).WithType(opts.Type).WithReplicas(opts.Replicas).WithNoTrunc(opts.UseLongAnnotations).WithPodmanOnly(opts.PodmanOnly)
return generate.Kube(ic.ClientCtx, nameOrIDs, options)
}

View File

@ -1,6 +1,7 @@
package integration
import (
"fmt"
"os"
"os/user"
"path/filepath"
@ -1657,4 +1658,184 @@ USER test1`
Expect(pod.Annotations).To(HaveKeyWithValue(define.BindMountPrefix, vol1[:define.MaxKubeAnnotation]))
Expect(pod.Annotations).To(Not(HaveKeyWithValue(define.BindMountPrefix, vol1+":Z")))
})
It("podman kube generate --podman-only on container with --volumes-from", func() {
ctr1 := "ctr1"
ctr2 := "ctr2"
vol1 := filepath.Join(podmanTest.TempDir, "vol-test1")
err := os.MkdirAll(vol1, 0755)
Expect(err).ToNot(HaveOccurred())
session := podmanTest.Podman([]string{"create", "--name", ctr1, "-v", vol1, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
session = podmanTest.Podman([]string{"create", "--volumes-from", ctr1, "--name", ctr2, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", ctr2})
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.InspectAnnotationVolumesFrom+"/"+ctr2, ctr1))
})
It("podman kube generate --podman-only on container with --rm", func() {
ctr := "ctr"
session := podmanTest.Podman([]string{"create", "--rm", "--name", ctr, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", ctr})
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.InspectAnnotationAutoremove+"/"+ctr, define.InspectResponseTrue))
})
It("podman kube generate --podman-only on container with --privileged", func() {
ctr := "ctr"
session := podmanTest.Podman([]string{"create", "--privileged", "--name", ctr, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", ctr})
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.InspectAnnotationPrivileged+"/"+ctr, define.InspectResponseTrue))
})
It("podman kube generate --podman-only on container with --init", func() {
ctr := "ctr"
session := podmanTest.Podman([]string{"create", "--init", "--name", ctr, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", ctr})
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.InspectAnnotationInit+"/"+ctr, define.InspectResponseTrue))
})
It("podman kube generate --podman-only on container with --cidfile", func() {
ctr := "ctr"
cidFile := filepath.Join(podmanTest.TempDir, RandomString(10)+".txt")
session := podmanTest.Podman([]string{"create", "--cidfile", cidFile, "--name", ctr, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", ctr})
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.InspectAnnotationCIDFile+"/"+ctr, cidFile))
})
It("podman kube generate --podman-only on container with --security-opt seccomp=unconfined", func() {
ctr := "ctr"
session := podmanTest.Podman([]string{"create", "--security-opt", "seccomp=unconfined", "--name", ctr, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", ctr})
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.InspectAnnotationSeccomp+"/"+ctr, "unconfined"))
})
It("podman kube generate --podman-only on container with --security-opt apparmor=unconfined", func() {
ctr := "ctr"
session := podmanTest.Podman([]string{"create", "--security-opt", "apparmor=unconfined", "--name", ctr, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", ctr})
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.InspectAnnotationApparmor+"/"+ctr, "unconfined"))
})
It("podman kube generate --podman-only on container with --security-opt label=level:s0", func() {
ctr := "ctr"
session := podmanTest.Podman([]string{"create", "--security-opt", "label=level:s0", "--name", ctr, ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", ctr})
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.InspectAnnotationLabel+"/"+ctr, "level:s0"))
})
It("podman kube generate --podman-only on container with --publish-all", func() {
podmanTest.AddImageToRWStore(ALPINE)
dockerfile := fmt.Sprintf(`FROM %s
EXPOSE 2002
EXPOSE 2001-2003
EXPOSE 2004-2005/tcp`, ALPINE)
imageName := "testimg"
podmanTest.BuildImage(dockerfile, imageName, "false")
// Verify that the buildah is just passing through the EXPOSE keys
inspect := podmanTest.Podman([]string{"inspect", imageName})
inspect.WaitWithDefaultTimeout()
image := inspect.InspectImageJSON()
Expect(image).To(HaveLen(1))
Expect(image[0].Config.ExposedPorts).To(HaveLen(3))
Expect(image[0].Config.ExposedPorts).To(HaveKey("2002/tcp"))
Expect(image[0].Config.ExposedPorts).To(HaveKey("2001-2003/tcp"))
Expect(image[0].Config.ExposedPorts).To(HaveKey("2004-2005/tcp"))
ctr := "ctr"
session := podmanTest.Podman([]string{"create", "--publish-all", "--name", ctr, imageName, "true"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", ctr})
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.InspectAnnotationPublishAll+"/"+ctr, define.InspectResponseTrue))
})
})