From d856fb43e11b3a4a93b423d330ce3435afddde4b Mon Sep 17 00:00:00 2001 From: Niall Crowe Date: Thu, 4 Aug 2022 09:02:54 +0100 Subject: Add "podman kube generate" command "podman kube generate" creates Kubernetes YAML from Podman containers, pods or volumes. Users will still be able to use "podman generate kube" as an alias of "kube generate". Signed-off-by: Niall Crowe --- cmd/podman/common/completion.go | 4 + cmd/podman/generate/generate.go | 6 +- cmd/podman/generate/kube.go | 80 -------- cmd/podman/generate/spec.go | 4 +- cmd/podman/generate/systemd.go | 4 +- cmd/podman/kube/down.go | 2 +- cmd/podman/kube/generate.go | 109 +++++++++++ cmd/podman/kube/kube.go | 4 +- cmd/podman/kube/play.go | 9 +- docs/source/markdown/links/podman-generate-kube.1 | 1 + docs/source/markdown/podman-generate-kube.1.md | 219 ---------------------- docs/source/markdown/podman-generate.1.md | 2 +- docs/source/markdown/podman-kube-down.1.md | 2 +- docs/source/markdown/podman-kube-generate.1.md | 219 ++++++++++++++++++++++ docs/source/markdown/podman-kube-play.1.md.in | 7 +- docs/source/markdown/podman-kube.1.md | 14 +- pkg/api/handlers/libpod/kube.go | 4 + pkg/api/server/register_kube.go | 115 ++++++++++++ pkg/api/server/register_play.go | 82 -------- pkg/api/server/server.go | 2 +- pkg/bindings/kube/kube.go | 6 + pkg/domain/entities/generate.go | 2 + test/e2e/generate_kube_test.go | 28 +-- 23 files changed, 502 insertions(+), 423 deletions(-) delete mode 100644 cmd/podman/generate/kube.go create mode 100644 cmd/podman/kube/generate.go create mode 100644 docs/source/markdown/links/podman-generate-kube.1 delete mode 100644 docs/source/markdown/podman-generate-kube.1.md create mode 100644 docs/source/markdown/podman-kube-generate.1.md create mode 100644 pkg/api/server/register_kube.go delete mode 100644 pkg/api/server/register_play.go diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 71c62a7af..b3a816aa4 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -544,6 +544,10 @@ func AutocompleteForKube(cmd *cobra.Command, args []string, toComplete string) ( return objs, cobra.ShellCompDirectiveNoFileComp } +func AutocompleteForGenerate(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return AutocompleteForKube(cmd, args, toComplete) +} + // AutocompleteContainersAndPods - Autocomplete container names and pod names. func AutocompleteContainersAndPods(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if !validCurrentCmdLine(cmd, args, toComplete) { diff --git a/cmd/podman/generate/generate.go b/cmd/podman/generate/generate.go index 0a12961f4..f1dea6816 100644 --- a/cmd/podman/generate/generate.go +++ b/cmd/podman/generate/generate.go @@ -1,4 +1,4 @@ -package pods +package generate import ( "github.com/containers/podman/v4/cmd/podman/registry" @@ -9,7 +9,7 @@ import ( var ( // Command: podman _generate_ - generateCmd = &cobra.Command{ + GenerateCmd = &cobra.Command{ Use: "generate", Short: "Generate structured data based on containers, pods or volumes", Long: "Generate structured data (e.g., Kubernetes YAML or systemd units) based on containers, pods or volumes.", @@ -20,6 +20,6 @@ var ( func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Command: generateCmd, + Command: GenerateCmd, }) } diff --git a/cmd/podman/generate/kube.go b/cmd/podman/generate/kube.go deleted file mode 100644 index 7bfc3dcf7..000000000 --- a/cmd/podman/generate/kube.go +++ /dev/null @@ -1,80 +0,0 @@ -package pods - -import ( - "fmt" - "io" - "io/ioutil" - "os" - - "github.com/containers/common/pkg/completion" - "github.com/containers/podman/v4/cmd/podman/common" - "github.com/containers/podman/v4/cmd/podman/registry" - "github.com/containers/podman/v4/cmd/podman/utils" - "github.com/containers/podman/v4/pkg/domain/entities" - "github.com/spf13/cobra" -) - -var ( - kubeOptions = entities.GenerateKubeOptions{} - kubeFile = "" - kubeDescription = `Command generates Kubernetes Pod, Service or PersistenVolumeClaim YAML (v1 specification) from Podman containers, pods or volumes. - - Whether the input is for a container or pod, Podman will always generate the specification as a pod.` - - kubeCmd = &cobra.Command{ - Use: "kube [options] {CONTAINER...|POD...|VOLUME...}", - Short: "Generate Kubernetes YAML from containers, pods or volumes.", - Long: kubeDescription, - RunE: kube, - Args: cobra.MinimumNArgs(1), - ValidArgsFunction: common.AutocompleteForKube, - Example: `podman generate kube ctrID - podman generate kube podID - podman generate kube --service podID - podman generate kube volumeName - podman generate kube ctrID podID volumeName --service`, - } -) - -func init() { - registry.Commands = append(registry.Commands, registry.CliCommand{ - Command: kubeCmd, - Parent: generateCmd, - }) - flags := kubeCmd.Flags() - flags.BoolVarP(&kubeOptions.Service, "service", "s", false, "Generate YAML for a Kubernetes service object") - - filenameFlagName := "filename" - flags.StringVarP(&kubeFile, filenameFlagName, "f", "", "Write output to the specified path") - _ = kubeCmd.RegisterFlagCompletionFunc(filenameFlagName, completion.AutocompleteDefault) - - flags.SetNormalizeFunc(utils.AliasFlags) -} - -func kube(cmd *cobra.Command, args []string) error { - report, err := registry.ContainerEngine().GenerateKube(registry.GetContext(), args, kubeOptions) - if err != nil { - return err - } - - content, err := ioutil.ReadAll(report.Reader) - if err != nil { - return err - } - if r, ok := report.Reader.(io.ReadCloser); ok { - defer r.Close() - } - - if cmd.Flags().Changed("filename") { - if _, err := os.Stat(kubeFile); err == nil { - return fmt.Errorf("cannot write to %q; file exists", kubeFile) - } - if err := ioutil.WriteFile(kubeFile, content, 0644); err != nil { - return fmt.Errorf("cannot write to %q: %w", kubeFile, err) - } - return nil - } - - fmt.Println(string(content)) - return nil -} diff --git a/cmd/podman/generate/spec.go b/cmd/podman/generate/spec.go index 1cf967424..bf451ebc5 100644 --- a/cmd/podman/generate/spec.go +++ b/cmd/podman/generate/spec.go @@ -1,4 +1,4 @@ -package pods +package generate import ( "fmt" @@ -31,7 +31,7 @@ var ( func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Command: specCmd, - Parent: generateCmd, + Parent: GenerateCmd, }) opts = &entities.GenerateSpecOptions{} flags := specCmd.Flags() diff --git a/cmd/podman/generate/systemd.go b/cmd/podman/generate/systemd.go index 1ece64a30..e40416534 100644 --- a/cmd/podman/generate/systemd.go +++ b/cmd/podman/generate/systemd.go @@ -1,4 +1,4 @@ -package pods +package generate import ( "encoding/json" @@ -57,7 +57,7 @@ var ( func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Command: systemdCmd, - Parent: generateCmd, + Parent: GenerateCmd, }) flags := systemdCmd.Flags() flags.BoolVarP(&systemdOptions.Name, "name", "n", false, "Use container/pod names instead of IDs") diff --git a/cmd/podman/kube/down.go b/cmd/podman/kube/down.go index b8c025928..a670d911c 100644 --- a/cmd/podman/kube/down.go +++ b/cmd/podman/kube/down.go @@ -1,4 +1,4 @@ -package pods +package kube import ( "github.com/containers/podman/v4/cmd/podman/common" diff --git a/cmd/podman/kube/generate.go b/cmd/podman/kube/generate.go new file mode 100644 index 000000000..6df4b55fc --- /dev/null +++ b/cmd/podman/kube/generate.go @@ -0,0 +1,109 @@ +package kube + +import ( + "fmt" + "io" + "io/ioutil" + "os" + + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v4/cmd/podman/common" + "github.com/containers/podman/v4/cmd/podman/generate" + "github.com/containers/podman/v4/cmd/podman/registry" + "github.com/containers/podman/v4/cmd/podman/utils" + "github.com/containers/podman/v4/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + generateOptions = entities.GenerateKubeOptions{} + generateFile = "" + generateDescription = `Command generates Kubernetes Pod, Service or PersistenVolumeClaim YAML (v1 specification) from Podman containers, pods or volumes. + + Whether the input is for a container or pod, Podman will always generate the specification as a pod.` + + generateKubeCmd = &cobra.Command{ + Use: "generate [options] {CONTAINER...|POD...|VOLUME...}", + Short: "Generate Kubernetes YAML from containers, pods or volumes.", + Long: generateDescription, + RunE: generateKube, + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: common.AutocompleteForGenerate, + Example: `podman kube generate ctrID + podman kube generate podID + podman kube generate --service podID + podman kube generate volumeName + podman kube generate ctrID podID volumeName --service`, + } + kubeGenerateDescription = generateDescription + + kubeGenerateCmd = &cobra.Command{ + Use: "kube [options] {CONTAINER...|POD...|VOLUME...}", + Short: "Generate Kubernetes YAML from containers, pods or volumes.", + Long: kubeGenerateDescription, + RunE: kubeGenerate, + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: common.AutocompleteForGenerate, + Example: `podman kube generate ctrID + podman kube generate podID + podman kube generate --service podID + podman kube generate volumeName + podman kube generate ctrID podID volumeName --service`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: generateKubeCmd, + Parent: kubeCmd, + }) + generateFlags(generateKubeCmd) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: kubeGenerateCmd, + Parent: generate.GenerateCmd, + }) + generateFlags(kubeGenerateCmd) +} + +func generateFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVarP(&generateOptions.Service, "service", "s", false, "Generate YAML for a Kubernetes service object") + + filenameFlagName := "filename" + flags.StringVarP(&generateFile, filenameFlagName, "f", "", "Write output to the specified path") + _ = cmd.RegisterFlagCompletionFunc(filenameFlagName, completion.AutocompleteDefault) + + flags.SetNormalizeFunc(utils.AliasFlags) +} + +func generateKube(cmd *cobra.Command, args []string) error { + report, err := registry.ContainerEngine().GenerateKube(registry.GetContext(), args, generateOptions) + if err != nil { + return err + } + content, err := ioutil.ReadAll(report.Reader) + if err != nil { + return err + } + if r, ok := report.Reader.(io.ReadCloser); ok { + defer r.Close() + } + + if cmd.Flags().Changed("filename") { + if _, err := os.Stat(generateFile); err == nil { + return fmt.Errorf("cannot write to %q; file exists", generateFile) + } + if err := ioutil.WriteFile(generateFile, content, 0644); err != nil { + return fmt.Errorf("cannot write to %q: %w", generateFile, err) + } + return nil + } + + fmt.Println(string(content)) + return nil +} + +func kubeGenerate(cmd *cobra.Command, args []string) error { + return generateKube(cmd, args) +} diff --git a/cmd/podman/kube/kube.go b/cmd/podman/kube/kube.go index 68f55a157..2dab68c19 100644 --- a/cmd/podman/kube/kube.go +++ b/cmd/podman/kube/kube.go @@ -1,4 +1,4 @@ -package pods +package kube import ( "github.com/containers/podman/v4/cmd/podman/registry" @@ -14,7 +14,7 @@ var ( Long: "Play structured data (e.g., Kubernetes YAML) based on containers, pods or volumes.", RunE: validate.SubCommandExists, } - + // Command: podman _play_ playKubeParentCmd = &cobra.Command{ Use: "play", Short: "Play containers, pods or volumes from a structured file", diff --git a/cmd/podman/kube/play.go b/cmd/podman/kube/play.go index 07c4b59b9..d7719e28e 100644 --- a/cmd/podman/kube/play.go +++ b/cmd/podman/kube/play.go @@ -1,4 +1,4 @@ -package pods +package kube import ( "bytes" @@ -47,7 +47,7 @@ var ( Use: "play [options] KUBEFILE|-", Short: "Play a pod or volume based on Kubernetes YAML.", Long: playDescription, - RunE: Play, + RunE: play, Args: cobra.ExactArgs(1), ValidArgsFunction: common.AutocompleteDefaultOneArg, Example: `podman kube play nginx.yml @@ -181,11 +181,10 @@ func playFlags(cmd *cobra.Command) { } } -func Play(cmd *cobra.Command, args []string) error { +func play(cmd *cobra.Command, args []string) error { if playOptions.ServiceContainer && !playOptions.StartCLI { // Sanity check to be future proof return fmt.Errorf("--service-container does not work with --start=stop") } - // TLS verification in c/image is controlled via a `types.OptionalBool` // which allows for distinguishing among set-true, set-false, unspecified // which is important to implement a sane way of dealing with defaults of @@ -260,7 +259,7 @@ func Play(cmd *cobra.Command, args []string) error { } func playKube(cmd *cobra.Command, args []string) error { - return Play(cmd, args) + return play(cmd, args) } func readerFromArg(fileName string) (*bytes.Reader, error) { diff --git a/docs/source/markdown/links/podman-generate-kube.1 b/docs/source/markdown/links/podman-generate-kube.1 new file mode 100644 index 000000000..f726a4222 --- /dev/null +++ b/docs/source/markdown/links/podman-generate-kube.1 @@ -0,0 +1 @@ +.so man1/podman-kube-generate.1 diff --git a/docs/source/markdown/podman-generate-kube.1.md b/docs/source/markdown/podman-generate-kube.1.md deleted file mode 100644 index bd7bc711c..000000000 --- a/docs/source/markdown/podman-generate-kube.1.md +++ /dev/null @@ -1,219 +0,0 @@ -% podman-generate-kube(1) -## NAME -podman-generate-kube - Generate Kubernetes YAML based on containers, pods or volumes - -## SYNOPSIS -**podman generate kube** [*options*] *container...* | *pod...* | *volume...* - -## DESCRIPTION -**podman generate kube** will generate Kubernetes YAML (v1 specification) from Podman containers, pods or volumes. Regardless of whether -the input is for containers or pods, Podman will always generate the specification as a Pod. The input may be in the form -of one or more containers, pods or volumes names or IDs. - -`Podman Containers or Pods` - -Volumes appear in the generated YAML according to two different volume types. Bind-mounted volumes become *hostPath* volume types and named volumes become *persistentVolumeClaim* volume types. Generated *hostPath* volume types will be one of three subtypes depending on the state of the host path: *DirectoryOrCreate* when no file or directory exists at the host, *Directory* when host path is a directory, or *File* when host path is a file. The value for *claimName* for a *persistentVolumeClaim* is the name of the named volume registered in Podman. - -Potential name conflicts between volumes are avoided by using a standard naming scheme for each volume type. The *hostPath* volume types are named according to the path on the host machine, replacing forward slashes with hyphens less any leading and trailing forward slashes. The special case of the filesystem root, `/`, translates to the name `root`. Additionally, the name is suffixed with `-host` to avoid naming conflicts with *persistentVolumeClaim* volumes. Each *persistentVolumeClaim* volume type uses the name of its associated named volume suffixed with `-pvc`. - -Note that if an init container is created with type `once` and the pod has been started, the init container will not show up in the generated kube YAML as `once` type init containers are deleted after they are run. If the pod has only been created and not started, it will be in the generated kube YAML. -Init containers created with type `always` will always be generated in the kube YAML as they are never deleted, even after running to completion. - -*Note*: When using volumes and generating a Kubernetes YAML for an unprivileged and rootless podman container on an **SELinux enabled system**, one of the following options must be completed: - * Add the "privileged: true" option to the pod spec - * Add `type: spc_t` under the `securityContext` `seLinuxOptions` in the pod spec - * Relabel the volume via the CLI command `chcon -t container_file_t -R ` - -Once completed, the correct permissions will be in place to access the volume when the pod/container is created in a Kubernetes cluster. - -Note that the generated Kubernetes YAML file can be used to re-run the deployment via podman-play-kube(1). - -## OPTIONS - -#### **--filename**, **-f**=*filename* - -Output to the given file, instead of STDOUT. If the file already exists, `generate kube` will refuse to replace it and return an error. - -#### **--service**, **-s** - -Generate a Kubernetes service object in addition to the Pods. Used to generate a Service specification for the corresponding Pod output. In particular, if the object has portmap bindings, the service specification will include a NodePort declaration to expose the service. A -random port is assigned by Podman in the specification. - -## EXAMPLES - -Create Kubernetes Pod YAML for a container called `some-mariadb`. -``` -$ sudo podman generate kube some-mariadb -# Save the output of this file and use kubectl create -f to import -# it into Kubernetes. -# -# Created with podman-0.11.2-dev -apiVersion: v1 -kind: Pod -metadata: - creationTimestamp: 2018-12-03T19:07:59Z - labels: - app: some-mariadb - name: some-mariadb-libpod -spec: - containers: - - command: - - docker-entrypoint.sh - - mysqld - env: - - name: HOSTNAME - - name: GOSU_VERSION - value: "1.10" - - name: GPG_KEYS - value: "199369E5404BD5FC7D2FE43BCBCB082A1BB943DB \t177F4010FE56CA3336300305F1656F24C74CD1D8 - \t430BDF5C56E7C94E848EE60C1C4CBDCDCD2EFD2A \t4D1BB29D63D98E422B2113B19334A25F8507EFA5" - - name: MARIADB_MAJOR - value: "10.3" - - name: MARIADB_VERSION - value: 1:10.3.10+maria~bionic - - name: MYSQL_ROOT_PASSWORD - value: x - image: quay.io/baude/demodb:latest - name: some-mariadb - ports: - - containerPort: 3306 - hostPort: 36533 - resources: {} - securityContext: - capabilities: - drop: - - CAP_MKNOD - - CAP_NET_RAW - - CAP_AUDIT_WRITE - tty: true -status: {} -``` - -Create Kubernetes Pod YAML for a container with the directory `/home/user/my-data` on the host bind-mounted in the container to `/volume`. -``` -$ podman generate kube my-container-with-bind-mounted-data -# Save the output of this file and use kubectl create -f to import -# it into Kubernetes. -# -# Created with podman-3.1.0-dev -apiVersion: v1 -kind: Pod -metadata: - creationTimestamp: "2021-03-18T16:26:08Z" - labels: - app: my-container-with-bind-mounted-data - name: my-container-with-bind-mounted-data -spec: - containers: - - command: - - /bin/sh - image: docker.io/library/alpine:latest - name: test-bind-mount - resources: {} - securityContext: - capabilities: - drop: - - CAP_MKNOD - - CAP_NET_RAW - - CAP_AUDIT_WRITE - volumeMounts: - - mountPath: /volume - name: home-user-my-data-host - restartPolicy: Never - volumes: - - hostPath: - path: /home/user/my-data - type: Directory - name: home-user-my-data-host -status: {} -``` - -Create Kubernetes Pod YAML for a container with the named volume `priceless-data` mounted in the container at `/volume`. -``` -$ podman generate kube my-container-using-priceless-data -# Save the output of this file and use kubectl create -f to import -# it into Kubernetes. -# -# Created with podman-3.1.0-dev -apiVersion: v1 -kind: Pod -metadata: - creationTimestamp: "2021-03-18T16:26:08Z" - labels: - app: my-container-using-priceless-data - name: my-container-using-priceless-data -spec: - containers: - - command: - - /bin/sh - image: docker.io/library/alpine:latest - name: test-bind-mount - resources: {} - securityContext: - capabilities: - drop: - - CAP_MKNOD - - CAP_NET_RAW - - CAP_AUDIT_WRITE - volumeMounts: - - mountPath: /volume - name: priceless-data-pvc - restartPolicy: Never - volumes: - - name: priceless-data-pvc - persistentVolumeClaim: - claimName: priceless-data -status: {} -``` - -Create Kubernetes Pod YAML for a pod called `demoweb` and include a service. -``` -$ sudo podman generate kube -s demoweb -# Save the output of this file and use kubectl create -f to import -# it into Kubernetes. -# -# Created with podman-0.12.2-dev -apiVersion: v1 -kind: Pod -metadata: - creationTimestamp: 2018-12-18T15:16:06Z - labels: - app: demoweb - name: demoweb-libpod -spec: - containers: - - command: - - python3 - - /root/code/graph.py - image: quay.io/baude/demoweb:latest - name: practicalarchimedes - resources: {} - tty: true - workingDir: /root/code -status: {} ---- -apiVersion: v1 -kind: Service -metadata: - creationTimestamp: 2018-12-18T15:16:06Z - labels: - app: demoweb - name: demoweb-libpod -spec: - ports: - - name: "8050" - nodePort: 31269 - port: 8050 - targetPort: 0 - selector: - app: demoweb - type: NodePort -status: - loadBalancer: {} -``` - -## SEE ALSO -**[podman(1)](podman.1.md)**, **[podman-container(1)](podman-container.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-kube-play(1)](podman-kube-play.1.md)** - -## HISTORY -December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/docs/source/markdown/podman-generate.1.md b/docs/source/markdown/podman-generate.1.md index 8466068f2..d84a9e098 100644 --- a/docs/source/markdown/podman-generate.1.md +++ b/docs/source/markdown/podman-generate.1.md @@ -13,7 +13,7 @@ The generate command will create structured output (like YAML) based on a contai | Command | Man Page | Description | |---------|------------------------------------------------------------|-------------------------------------------------------------------------------------| -| kube | [podman-generate-kube(1)](podman-generate-kube.1.md) | Generate Kubernetes YAML based on containers, pods or volumes. | +| kube | [podman-kube-generate(1)](podman-kube-generate.1.md) | Generate Kubernetes YAML based on containers, pods or volumes. | | spec | [podman-generate-spec(1)](podman-generate-spec.1.md) | Generate Specgen JSON based on containers or pods. | | systemd | [podman-generate-systemd(1)](podman-generate-systemd.1.md) | Generate systemd unit file(s) for a container or pod. | diff --git a/docs/source/markdown/podman-kube-down.1.md b/docs/source/markdown/podman-kube-down.1.md index 35725043b..92abd4ba3 100644 --- a/docs/source/markdown/podman-kube-down.1.md +++ b/docs/source/markdown/podman-kube-down.1.md @@ -40,4 +40,4 @@ $ cat demo.yml | podman kube play - ``` ## SEE ALSO -**[podman(1)](podman.1.md)**, **[podman-kube(1)](podman-kube.1.md)**, **[podman-kube-play(1)](podman-kube-play.1.md)**, **[podman-generate-kube(1)](podman-generate-kube.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)** +**[podman(1)](podman.1.md)**, **[podman-kube(1)](podman-kube.1.md)**, **[podman-kube-play(1)](podman-kube-play.1.md)**, **[podman-kube-generate(1)](podman-kube-generate.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)** diff --git a/docs/source/markdown/podman-kube-generate.1.md b/docs/source/markdown/podman-kube-generate.1.md new file mode 100644 index 000000000..f46f86de6 --- /dev/null +++ b/docs/source/markdown/podman-kube-generate.1.md @@ -0,0 +1,219 @@ +-% podman-kube-generate(1) +## NAME +podman-kube-generate - Generate Kubernetes YAML based on containers, pods or volumes + +## SYNOPSIS +**podman kube generate** [*options*] *container...* | *pod...* | *volume...* + +## DESCRIPTION +**podman kube generate** will generate Kubernetes YAML (v1 specification) from Podman containers, pods or volumes. Regardless of whether +the input is for containers or pods, Podman will always generate the specification as a Pod. The input may be in the form +of one or more containers, pods or volumes names or IDs. + +`Podman Containers or Pods` + +Volumes appear in the generated YAML according to two different volume types. Bind-mounted volumes become *hostPath* volume types and named volumes become *persistentVolumeClaim* volume types. Generated *hostPath* volume types will be one of three subtypes depending on the state of the host path: *DirectoryOrCreate* when no file or directory exists at the host, *Directory* when host path is a directory, or *File* when host path is a file. The value for *claimName* for a *persistentVolumeClaim* is the name of the named volume registered in Podman. + +Potential name conflicts between volumes are avoided by using a standard naming scheme for each volume type. The *hostPath* volume types are named according to the path on the host machine, replacing forward slashes with hyphens less any leading and trailing forward slashes. The special case of the filesystem root, `/`, translates to the name `root`. Additionally, the name is suffixed with `-host` to avoid naming conflicts with *persistentVolumeClaim* volumes. Each *persistentVolumeClaim* volume type uses the name of its associated named volume suffixed with `-pvc`. + +Note that if an init container is created with type `once` and the pod has been started, the init container will not show up in the generated kube YAML as `once` type init containers are deleted after they are run. If the pod has only been created and not started, it will be in the generated kube YAML. +Init containers created with type `always` will always be generated in the kube YAML as they are never deleted, even after running to completion. + +*Note*: When using volumes and generating a Kubernetes YAML for an unprivileged and rootless podman container on an **SELinux enabled system**, one of the following options must be completed: + * Add the "privileged: true" option to the pod spec + * Add `type: spc_t` under the `securityContext` `seLinuxOptions` in the pod spec + * Relabel the volume via the CLI command `chcon -t container_file_t -R ` + +Once completed, the correct permissions will be in place to access the volume when the pod/container is created in a Kubernetes cluster. + +Note that the generated Kubernetes YAML file can be used to re-run the deployment via podman-play-kube(1). + +## OPTIONS + +#### **--filename**, **-f**=*filename* + +Output to the given file, instead of STDOUT. If the file already exists, `kube generate` will refuse to replace it and return an error. + +#### **--service**, **-s** + +Generate a Kubernetes service object in addition to the Pods. Used to generate a Service specification for the corresponding Pod output. In particular, if the object has portmap bindings, the service specification will include a NodePort declaration to expose the service. A +random port is assigned by Podman in the specification. + +## EXAMPLES + +Create Kubernetes Pod YAML for a container called `some-mariadb`. +``` +$ sudo podman kube generate some-mariadb +# Save the output of this file and use kubectl create -f to import +# it into Kubernetes. +# +# Created with podman-0.11.2-dev +apiVersion: v1 +kind: Pod +metadata: + creationTimestamp: 2018-12-03T19:07:59Z + labels: + app: some-mariadb + name: some-mariadb-libpod +spec: + containers: + - command: + - docker-entrypoint.sh + - mysqld + env: + - name: HOSTNAME + - name: GOSU_VERSION + value: "1.10" + - name: GPG_KEYS + value: "199369E5404BD5FC7D2FE43BCBCB082A1BB943DB \t177F4010FE56CA3336300305F1656F24C74CD1D8 + \t430BDF5C56E7C94E848EE60C1C4CBDCDCD2EFD2A \t4D1BB29D63D98E422B2113B19334A25F8507EFA5" + - name: MARIADB_MAJOR + value: "10.3" + - name: MARIADB_VERSION + value: 1:10.3.10+maria~bionic + - name: MYSQL_ROOT_PASSWORD + value: x + image: quay.io/baude/demodb:latest + name: some-mariadb + ports: + - containerPort: 3306 + hostPort: 36533 + resources: {} + securityContext: + capabilities: + drop: + - CAP_MKNOD + - CAP_NET_RAW + - CAP_AUDIT_WRITE + tty: true +status: {} +``` + +Create Kubernetes Pod YAML for a container with the directory `/home/user/my-data` on the host bind-mounted in the container to `/volume`. +``` +$ podman kube generate my-container-with-bind-mounted-data +# Save the output of this file and use kubectl create -f to import +# it into Kubernetes. +# +# Created with podman-3.1.0-dev +apiVersion: v1 +kind: Pod +metadata: + creationTimestamp: "2021-03-18T16:26:08Z" + labels: + app: my-container-with-bind-mounted-data + name: my-container-with-bind-mounted-data +spec: + containers: + - command: + - /bin/sh + image: docker.io/library/alpine:latest + name: test-bind-mount + resources: {} + securityContext: + capabilities: + drop: + - CAP_MKNOD + - CAP_NET_RAW + - CAP_AUDIT_WRITE + volumeMounts: + - mountPath: /volume + name: home-user-my-data-host + restartPolicy: Never + volumes: + - hostPath: + path: /home/user/my-data + type: Directory + name: home-user-my-data-host +status: {} +``` + +Create Kubernetes Pod YAML for a container with the named volume `priceless-data` mounted in the container at `/volume`. +``` +$ podman kube generate my-container-using-priceless-data +# Save the output of this file and use kubectl create -f to import +# it into Kubernetes. +# +# Created with podman-3.1.0-dev +apiVersion: v1 +kind: Pod +metadata: + creationTimestamp: "2021-03-18T16:26:08Z" + labels: + app: my-container-using-priceless-data + name: my-container-using-priceless-data +spec: + containers: + - command: + - /bin/sh + image: docker.io/library/alpine:latest + name: test-bind-mount + resources: {} + securityContext: + capabilities: + drop: + - CAP_MKNOD + - CAP_NET_RAW + - CAP_AUDIT_WRITE + volumeMounts: + - mountPath: /volume + name: priceless-data-pvc + restartPolicy: Never + volumes: + - name: priceless-data-pvc + persistentVolumeClaim: + claimName: priceless-data +status: {} +``` + +Create Kubernetes Pod YAML for a pod called `demoweb` and include a service. +``` +$ sudo podman kube generate -s demoweb +# Save the output of this file and use kubectl create -f to import +# it into Kubernetes. +# +# Created with podman-0.12.2-dev +apiVersion: v1 +kind: Pod +metadata: + creationTimestamp: 2018-12-18T15:16:06Z + labels: + app: demoweb + name: demoweb-libpod +spec: + containers: + - command: + - python3 + - /root/code/graph.py + image: quay.io/baude/demoweb:latest + name: practicalarchimedes + resources: {} + tty: true + workingDir: /root/code +status: {} +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: 2018-12-18T15:16:06Z + labels: + app: demoweb + name: demoweb-libpod +spec: + ports: + - name: "8050" + nodePort: 31269 + port: 8050 + targetPort: 0 + selector: + app: demoweb + type: NodePort +status: + loadBalancer: {} +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**, **[podman-container(1)](podman-container.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-kube-play(1)](podman-kube-play.1.md)**, **[podman-kube-down(1)](podman-kube-down.1.md)** + +## HISTORY +December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/docs/source/markdown/podman-kube-play.1.md.in b/docs/source/markdown/podman-kube-play.1.md.in index 9e9fc7f38..74565b75b 100644 --- a/docs/source/markdown/podman-kube-play.1.md.in +++ b/docs/source/markdown/podman-kube-play.1.md.in @@ -10,7 +10,7 @@ podman-kube-play - Create containers, pods and volumes based on Kubernetes YAML **podman kube play** will read in a structured file of Kubernetes YAML. It will then recreate the containers, pods or volumes described in the YAML. Containers within a pod are then started and the ID of the new Pod or the name of the new Volume is output. If the yaml file is specified as "-" then `podman kube play` will read the YAML file from stdin. Using the `--down` command line option, it is also capable of tearing down the pods created by a previous run of `podman kube play`. Using the `--replace` command line option, it will tear down the pods(if any) created by a previous run of `podman kube play` and recreate the pods with the Kubernetes YAML file. -Ideally the input file would be one created by Podman (see podman-generate-kube(1)). This would guarantee a smooth import and expected results. +Ideally the input file would be one created by Podman (see podman-kube-generate(1)). This would guarantee a smooth import and expected results. Currently, the supported Kubernetes kinds are: - Pod @@ -320,7 +320,4 @@ $ podman kube play demo.yml --network net1:ip=10.89.1.5 --network net2:ip=10.89. Please take into account that networks must be created first using podman-network-create(1). ## SEE ALSO -**[podman(1)](podman.1.md)**, **[podman-kube(1)](podman-kube.1.md)**, **[podman-kube-down(1)](podman-kube-down.1.md)**, **[podman-network-create(1)](podman-network-create.1.md)**, **[podman-generate-kube(1)](podman-generate-kube.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)** - -## HISTORY -December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) +**[podman(1)](podman.1.md)**, **[podman-kube(1)](podman-kube.1.md)**, **[podman-kube-down(1)](podman-kube-down.1.md)**, **[podman-network-create(1)](podman-network-create.1.md)**, **[podman-kube-generate(1)](podman-kube-generate.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)** diff --git a/docs/source/markdown/podman-kube.1.md b/docs/source/markdown/podman-kube.1.md index 7a6978a2b..0d3654011 100644 --- a/docs/source/markdown/podman-kube.1.md +++ b/docs/source/markdown/podman-kube.1.md @@ -12,10 +12,14 @@ file input. Containers will be automatically started. ## COMMANDS -| Command | Man Page | Description | -| ------- | --------------------------------------------------- | ---------------------------------------------------------------------------- | -| down | [podman-kube-down(1)](podman-kube-down.1.md) | Remove containers and pods based on Kubernetes YAML. | -| play | [podman-kube-play(1)](podman-kube-play.1.md) | Create containers, pods and volumes based on Kubernetes YAML. | +| Command | Man Page | Description | +| ------- | ---------------------------------------------------- | ----------------------------------------------------------------------------- | +| down | [podman-kube-down(1)](podman-kube-down.1.md) | Remove containers and pods based on Kubernetes YAML. | +| generate | [podman-kube-generate(1)](podman-kube-generate.1.md) | Generate Kubernetes YAML based on containers, pods or volumes. | +| play | [podman-kube-play(1)](podman-kube-play.1.md) | Create containers, pods and volumes based on Kubernetes YAML. | ## SEE ALSO -**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-container(1)](podman-container.1.md)**, **[podman-generate(1)](podman-generate.1.md)**, **[podman-kube-play(1)](podman-kube-play.1.md)**, **[podman-kube-down(1)](podman-kube-down.1.md)** +**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-container(1)](podman-container.1.md)**, **[podman-kube-play(1)](podman-kube-play.1.md)**, **[podman-kube-down(1)](podman-kube-down.1.md)**, **[podman-kube-generate(1)](podman-kube-generate.1.md)** + +## HISTORY +December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff --git a/pkg/api/handlers/libpod/kube.go b/pkg/api/handlers/libpod/kube.go index 6cad58795..5c891b707 100644 --- a/pkg/api/handlers/libpod/kube.go +++ b/pkg/api/handlers/libpod/kube.go @@ -121,3 +121,7 @@ func KubePlayDown(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusOK, report) } + +func KubeGenerate(w http.ResponseWriter, r *http.Request) { + GenerateKube(w, r) +} diff --git a/pkg/api/server/register_kube.go b/pkg/api/server/register_kube.go new file mode 100644 index 000000000..0c3cd1d04 --- /dev/null +++ b/pkg/api/server/register_kube.go @@ -0,0 +1,115 @@ +package server + +import ( + "net/http" + + "github.com/containers/podman/v4/pkg/api/handlers/libpod" + "github.com/gorilla/mux" +) + +func (s *APIServer) registerKubeHandlers(r *mux.Router) error { + // swagger:operation POST /libpod/play/kube libpod PlayKubeLibpod + // --- + // tags: + // - containers + // - pods + // summary: Play a Kubernetes YAML file. + // description: Create and run pods based on a Kubernetes YAML file (pod or service kind). + // parameters: + // - in: query + // name: network + // type: array + // description: USe the network mode or specify an array of networks. + // items: + // type: string + // - in: query + // name: tlsVerify + // type: boolean + // default: true + // description: Require HTTPS and verify signatures when contacting registries. + // - in: query + // name: logDriver + // type: string + // description: Logging driver for the containers in the pod. + // - in: query + // name: start + // type: boolean + // default: true + // description: Start the pod after creating it. + // - in: query + // name: staticIPs + // type: array + // description: Static IPs used for the pods. + // items: + // type: string + // - in: query + // name: staticMACs + // type: array + // description: Static MACs used for the pods. + // items: + // type: string + // - in: body + // name: request + // description: Kubernetes YAML file. + // schema: + // type: string + // produces: + // - application/json + // responses: + // 200: + // $ref: "#/responses/playKubeResponseLibpod" + // 500: + // $ref: "#/responses/internalError" + r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKube)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/kube/play"), s.APIHandler(libpod.KubePlay)).Methods(http.MethodPost) + // swagger:operation DELETE /libpod/play/kube libpod PlayKubeDownLibpod + // --- + // tags: + // - containers + // - pods + // summary: Remove pods from kube play + // description: Tears down pods defined in a YAML file + // produces: + // - application/json + // responses: + // 200: + // $ref: "#/responses/playKubeResponseLibpod" + // 500: + // $ref: "#/responses/internalError" + r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKubeDown)).Methods(http.MethodDelete) + r.HandleFunc(VersionedPath("/libpod/kube/play"), s.APIHandler(libpod.KubePlayDown)).Methods(http.MethodDelete) + // swagger:operation GET /libpod/generate/kube libpod GenerateKubeLibpod + // --- + // tags: + // - containers + // - pods + // summary: Generate a Kubernetes YAML file. + // description: Generate Kubernetes YAML based on a pod or container. + // parameters: + // - in: query + // name: names + // type: array + // items: + // type: string + // required: true + // description: Name or ID of the container or pod. + // - in: query + // name: service + // type: boolean + // default: false + // description: Generate YAML for a Kubernetes service object. + // produces: + // - text/vnd.yaml + // - application/json + // responses: + // 200: + // description: Kubernetes YAML file describing pod + // schema: + // type: string + // format: binary + // 500: + // $ref: "#/responses/internalError" + r.HandleFunc(VersionedPath("/libpod/generate/kube"), s.APIHandler(libpod.GenerateKube)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/kube/generate"), s.APIHandler(libpod.KubeGenerate)).Methods(http.MethodGet) + return nil +} diff --git a/pkg/api/server/register_play.go b/pkg/api/server/register_play.go deleted file mode 100644 index 76e150504..000000000 --- a/pkg/api/server/register_play.go +++ /dev/null @@ -1,82 +0,0 @@ -package server - -import ( - "net/http" - - "github.com/containers/podman/v4/pkg/api/handlers/libpod" - "github.com/gorilla/mux" -) - -func (s *APIServer) registerPlayHandlers(r *mux.Router) error { - // swagger:operation POST /libpod/play/kube libpod PlayKubeLibpod - // --- - // tags: - // - containers - // - pods - // summary: Play a Kubernetes YAML file. - // description: Create and run pods based on a Kubernetes YAML file (pod or service kind). - // parameters: - // - in: query - // name: network - // type: array - // description: USe the network mode or specify an array of networks. - // items: - // type: string - // - in: query - // name: tlsVerify - // type: boolean - // default: true - // description: Require HTTPS and verify signatures when contacting registries. - // - in: query - // name: logDriver - // type: string - // description: Logging driver for the containers in the pod. - // - in: query - // name: start - // type: boolean - // default: true - // description: Start the pod after creating it. - // - in: query - // name: staticIPs - // type: array - // description: Static IPs used for the pods. - // items: - // type: string - // - in: query - // name: staticMACs - // type: array - // description: Static MACs used for the pods. - // items: - // type: string - // - in: body - // name: request - // description: Kubernetes YAML file. - // schema: - // type: string - // produces: - // - application/json - // responses: - // 200: - // $ref: "#/responses/playKubeResponseLibpod" - // 500: - // $ref: "#/responses/internalError" - r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKube)).Methods(http.MethodPost) - r.HandleFunc(VersionedPath("/libpod/kube/play"), s.APIHandler(libpod.KubePlay)).Methods(http.MethodPost) - // swagger:operation DELETE /libpod/play/kube libpod PlayKubeDownLibpod - // --- - // tags: - // - containers - // - pods - // summary: Remove pods from kube play - // description: Tears down pods defined in a YAML file - // produces: - // - application/json - // responses: - // 200: - // $ref: "#/responses/playKubeResponseLibpod" - // 500: - // $ref: "#/responses/internalError" - r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKubeDown)).Methods(http.MethodDelete) - r.HandleFunc(VersionedPath("/libpod/kube/play"), s.APIHandler(libpod.KubePlayDown)).Methods(http.MethodDelete) - return nil -} diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 5482a8ec2..39423dabe 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -130,7 +130,7 @@ func newServer(runtime *libpod.Runtime, listener net.Listener, opts entities.Ser server.registerMonitorHandlers, server.registerNetworkHandlers, server.registerPingHandlers, - server.registerPlayHandlers, + server.registerKubeHandlers, server.registerPluginsHandlers, server.registerPodsHandlers, server.registerSecretHandlers, diff --git a/pkg/bindings/kube/kube.go b/pkg/bindings/kube/kube.go index db40c5134..e727439cf 100644 --- a/pkg/bindings/kube/kube.go +++ b/pkg/bindings/kube/kube.go @@ -10,6 +10,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v4/pkg/auth" "github.com/containers/podman/v4/pkg/bindings" + "github.com/containers/podman/v4/pkg/bindings/generate" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/sirupsen/logrus" ) @@ -94,3 +95,8 @@ func DownWithBody(ctx context.Context, body io.Reader) (*entities.KubePlayReport } return &report, nil } + +// Kube generate Kubernetes YAML (v1 specification) +func Generate(ctx context.Context, nameOrIDs []string, options generate.KubeOptions) (*entities.GenerateKubeReport, error) { + return generate.Kube(ctx, nameOrIDs, &options) +} diff --git a/pkg/domain/entities/generate.go b/pkg/domain/entities/generate.go index cc5fbb6fb..f18e79b47 100644 --- a/pkg/domain/entities/generate.go +++ b/pkg/domain/entities/generate.go @@ -46,6 +46,8 @@ type GenerateKubeOptions struct { Service bool } +type KubeGenerateOptions = GenerateKubeOptions + // GenerateKubeReport // // FIXME: Podman4.0 should change io.Reader to io.ReaderCloser diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 5e9881c4f..142f32d19 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -58,7 +58,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "top"}) + kube := podmanTest.Podman([]string{"kube", "generate", "top"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -115,7 +115,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "test"}) + kube := podmanTest.Podman([]string{"kube", "generate", "test"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -130,7 +130,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "-s", "test-ctr"}) + kube := podmanTest.Podman([]string{"kube", "generate", "-s", "test-ctr"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -182,7 +182,7 @@ var _ = Describe("Podman generate kube", func() { pod2.WaitWithDefaultTimeout() Expect(pod2).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "pod1", "pod2"}) + kube := podmanTest.Podman([]string{"kube", "generate", "pod1", "pod2"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -203,7 +203,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "toppod"}) + kube := podmanTest.Podman([]string{"kube", "generate", "toppod"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -398,7 +398,7 @@ var _ = Describe("Podman generate kube", func() { ctr2Session.WaitWithDefaultTimeout() Expect(ctr2Session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube := podmanTest.Podman([]string{"kube", "generate", podName}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -500,7 +500,7 @@ var _ = Describe("Podman generate kube", func() { ctr1Session.WaitWithDefaultTimeout() Expect(ctr1Session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube := podmanTest.Podman([]string{"kube", "generate", podName}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -562,7 +562,7 @@ var _ = Describe("Podman generate kube", func() { ctr2Session.WaitWithDefaultTimeout() Expect(ctr2Session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", podName}) + kube := podmanTest.Podman([]string{"kube", "generate", podName}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -665,7 +665,7 @@ var _ = Describe("Podman generate kube", func() { Expect(inspect.OutputToString()).To(ContainSubstring("100:200")) outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") - kube := podmanTest.Podman([]string{"generate", "kube", "-f", outputFile, podName}) + kube := podmanTest.Podman([]string{"kube", "generate", "-f", outputFile, podName}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -788,7 +788,7 @@ var _ = Describe("Podman generate kube", func() { Expect(session).Should(Exit(0)) outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") - kube := podmanTest.Podman([]string{"generate", "kube", podName, "-f", outputFile}) + kube := podmanTest.Podman([]string{"kube", "generate", podName, "-f", outputFile}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -857,7 +857,7 @@ var _ = Describe("Podman generate kube", func() { pod2.WaitWithDefaultTimeout() Expect(pod2).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "top1", "top2"}) + kube := podmanTest.Podman([]string{"kube", "generate", "top1", "top2"}) kube.WaitWithDefaultTimeout() Expect(kube).To(ExitWithError()) }) @@ -911,7 +911,7 @@ var _ = Describe("Podman generate kube", func() { top.WaitWithDefaultTimeout() Expect(top).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "pod1"}) + kube := podmanTest.Podman([]string{"kube", "generate", "pod1"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -973,7 +973,7 @@ var _ = Describe("Podman generate kube", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube = podmanTest.Podman([]string{"generate", "kube", "test1"}) + kube = podmanTest.Podman([]string{"kube", "generate", "test1"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) @@ -1026,7 +1026,7 @@ ENTRYPOINT ["sleep"]` session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) - kube = podmanTest.Podman([]string{"generate", "kube", "testpod-2"}) + kube = podmanTest.Podman([]string{"kube", "generate", "testpod-2"}) kube.WaitWithDefaultTimeout() Expect(kube).Should(Exit(0)) -- cgit v1.2.3-54-g00ecf