mirror of https://github.com/containers/podman.git
Merge pull request #19211 from jakecorrenti/add-reserved-flag-generate
Add `--podman-only` flag to `podman generate kube`
This commit is contained in:
commit
49a924cf39
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue