mirror of
https://github.com/containers/podman.git
synced 2025-05-20 08:36:23 +08:00
generate kube
add the ability to generate kubernetes pod and service yaml representations of libpod containers and pods. Signed-off-by: baude <bbaude@redhat.com>
This commit is contained in:
23
cmd/podman/generate.go
Normal file
23
cmd/podman/generate.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
generateSubCommands = []cli.Command{
|
||||||
|
containerKubeCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
generateDescription = "generate structured data based for a containers and pods"
|
||||||
|
kubeCommand = cli.Command{
|
||||||
|
Name: "generate",
|
||||||
|
Usage: "generated structured data",
|
||||||
|
Description: generateDescription,
|
||||||
|
ArgsUsage: "",
|
||||||
|
Subcommands: generateSubCommands,
|
||||||
|
UseShortOptionHandling: true,
|
||||||
|
OnUsageError: usageErrorHandler,
|
||||||
|
Hidden: true,
|
||||||
|
}
|
||||||
|
)
|
@ -6,10 +6,11 @@ import (
|
|||||||
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
"github.com/containers/libpod/cmd/podman/libpodruntime"
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
"github.com/containers/libpod/pkg/rootless"
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
|
podmanVersion "github.com/containers/libpod/version"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -18,16 +19,15 @@ var (
|
|||||||
Name: "service, s",
|
Name: "service, s",
|
||||||
Usage: "only generate YAML for kubernetes service object",
|
Usage: "only generate YAML for kubernetes service object",
|
||||||
},
|
},
|
||||||
LatestFlag,
|
|
||||||
}
|
}
|
||||||
containerKubeDescription = "Generate Kubernetes Pod YAML"
|
containerKubeDescription = "Generate Kubernetes Pod YAML"
|
||||||
containerKubeCommand = cli.Command{
|
containerKubeCommand = cli.Command{
|
||||||
Name: "generate",
|
Name: "kube",
|
||||||
Usage: "Generate Kubernetes pod YAML for a container",
|
Usage: "Generate Kubernetes pod YAML for a container or pod",
|
||||||
Description: containerKubeDescription,
|
Description: containerKubeDescription,
|
||||||
Flags: sortFlags(containerKubeFlags),
|
Flags: sortFlags(containerKubeFlags),
|
||||||
Action: generateKubeYAMLCmd,
|
Action: generateKubeYAMLCmd,
|
||||||
ArgsUsage: "CONTAINER-NAME",
|
ArgsUsage: "CONTAINER|POD-NAME",
|
||||||
UseShortOptionHandling: true,
|
UseShortOptionHandling: true,
|
||||||
OnUsageError: usageErrorHandler,
|
OnUsageError: usageErrorHandler,
|
||||||
}
|
}
|
||||||
@ -36,9 +36,13 @@ var (
|
|||||||
// generateKubeYAMLCmdgenerates or replays kube
|
// generateKubeYAMLCmdgenerates or replays kube
|
||||||
func generateKubeYAMLCmd(c *cli.Context) error {
|
func generateKubeYAMLCmd(c *cli.Context) error {
|
||||||
var (
|
var (
|
||||||
container *libpod.Container
|
podYAML *v1.Pod
|
||||||
err error
|
container *libpod.Container
|
||||||
output []byte
|
err error
|
||||||
|
output []byte
|
||||||
|
pod *libpod.Pod
|
||||||
|
mashalledBytes []byte
|
||||||
|
servicePorts []v1.ServicePort
|
||||||
)
|
)
|
||||||
|
|
||||||
if rootless.IsRootless() {
|
if rootless.IsRootless() {
|
||||||
@ -46,10 +50,7 @@ func generateKubeYAMLCmd(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) > 1 || (len(args) < 1 && !c.Bool("latest")) {
|
if len(args) > 1 || (len(args) < 1 && !c.Bool("latest")) {
|
||||||
return errors.Errorf("you must provide one container ID or name or --latest")
|
return errors.Errorf("you must provide one container|pod ID or name or --latest")
|
||||||
}
|
|
||||||
if c.Bool("service") {
|
|
||||||
return errors.Wrapf(libpod.ErrNotImplemented, "service generation")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime, err := libpodruntime.GetRuntime(c)
|
runtime, err := libpodruntime.GetRuntime(c)
|
||||||
@ -59,33 +60,43 @@ func generateKubeYAMLCmd(c *cli.Context) error {
|
|||||||
defer runtime.Shutdown(false)
|
defer runtime.Shutdown(false)
|
||||||
|
|
||||||
// Get the container in question
|
// Get the container in question
|
||||||
if c.Bool("latest") {
|
container, err = runtime.LookupContainer(args[0])
|
||||||
container, err = runtime.GetLatestContainer()
|
if err != nil {
|
||||||
|
pod, err = runtime.LookupPod(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
podYAML, servicePorts, err = pod.GenerateForKube()
|
||||||
} else {
|
} else {
|
||||||
container, err = runtime.LookupContainer(args[0])
|
if len(container.Dependencies()) > 0 {
|
||||||
|
return errors.Wrapf(libpod.ErrNotImplemented, "containers with dependencies")
|
||||||
|
}
|
||||||
|
podYAML, err = container.GenerateForKube()
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(container.Dependencies()) > 0 {
|
if c.Bool("service") {
|
||||||
return errors.Wrapf(libpod.ErrNotImplemented, "containers with dependencies")
|
serviceYAML := libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts)
|
||||||
|
mashalledBytes, err = yaml.Marshal(serviceYAML)
|
||||||
|
} else {
|
||||||
|
// Marshall the results
|
||||||
|
mashalledBytes, err = yaml.Marshal(podYAML)
|
||||||
}
|
}
|
||||||
|
|
||||||
podYAML, err := container.InspectForKube()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
developmentComment := []byte("# Generation of Kubenetes YAML is still under development!\n")
|
header := `# Generation of Kubenetes YAML is still under development!
|
||||||
logrus.Warn("This function is still under heavy development.")
|
#
|
||||||
// Marshall the results
|
# Save the output of this file and use kubectl create -f to import
|
||||||
b, err := yaml.Marshal(podYAML)
|
# it into Kubernetes.
|
||||||
if err != nil {
|
#
|
||||||
return err
|
# Created with podman-%s
|
||||||
}
|
`
|
||||||
output = append(output, developmentComment...)
|
output = append(output, []byte(fmt.Sprintf(header, podmanVersion.Version))...)
|
||||||
output = append(output, b...)
|
output = append(output, mashalledBytes...)
|
||||||
// Output the v1.Pod with the v1.Container
|
// Output the v1.Pod with the v1.Container
|
||||||
fmt.Println(string(output))
|
fmt.Println(string(output))
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
kubeSubCommands = []cli.Command{
|
|
||||||
containerKubeCommand,
|
|
||||||
}
|
|
||||||
|
|
||||||
kubeDescription = "Work with Kubernetes objects"
|
|
||||||
kubeCommand = cli.Command{
|
|
||||||
Name: "kube",
|
|
||||||
Usage: "Import and export Kubernetes objections from and to Podman",
|
|
||||||
Description: containerDescription,
|
|
||||||
ArgsUsage: "",
|
|
||||||
Subcommands: kubeSubCommands,
|
|
||||||
UseShortOptionHandling: true,
|
|
||||||
OnUsageError: usageErrorHandler,
|
|
||||||
Hidden: true,
|
|
||||||
}
|
|
||||||
)
|
|
@ -16,6 +16,7 @@
|
|||||||
| [podman-diff(1)](/docs/podman-diff.1.md) | Inspect changes on a container or image's filesystem |[](https://asciinema.org/a/FXfWB9CKYFwYM4EfqW3NSZy1G)|
|
| [podman-diff(1)](/docs/podman-diff.1.md) | Inspect changes on a container or image's filesystem |[](https://asciinema.org/a/FXfWB9CKYFwYM4EfqW3NSZy1G)|
|
||||||
| [podman-exec(1)](/docs/podman-exec.1.md) | Execute a command in a running container
|
| [podman-exec(1)](/docs/podman-exec.1.md) | Execute a command in a running container
|
||||||
| [podman-export(1)](/docs/podman-export.1.md) | Export container's filesystem contents as a tar archive |[](https://asciinema.org/a/913lBIRAg5hK8asyIhhkQVLtV)|
|
| [podman-export(1)](/docs/podman-export.1.md) | Export container's filesystem contents as a tar archive |[](https://asciinema.org/a/913lBIRAg5hK8asyIhhkQVLtV)|
|
||||||
|
| [podman-generate(1)](/docs/podman-generate.1.md) | Generate structured output based on Podman containers and pods | |
|
||||||
| [podman-history(1)](/docs/podman-history.1.md) | Shows the history of an image |[](https://asciinema.org/a/bCvUQJ6DkxInMELZdc5DinNSx)|
|
| [podman-history(1)](/docs/podman-history.1.md) | Shows the history of an image |[](https://asciinema.org/a/bCvUQJ6DkxInMELZdc5DinNSx)|
|
||||||
| [podman-image(1)](/docs/podman-image.1.md) | Manage Images||
|
| [podman-image(1)](/docs/podman-image.1.md) | Manage Images||
|
||||||
| [podman-images(1)](/docs/podman-images.1.md) | List images in local storage |[](https://asciinema.org/a/133649)|
|
| [podman-images(1)](/docs/podman-images.1.md) | List images in local storage |[](https://asciinema.org/a/133649)|
|
||||||
|
@ -859,6 +859,25 @@ _podman_container_wait() {
|
|||||||
_podman_wait
|
_podman_wait
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_podman_generate() {
|
||||||
|
local boolean_options="
|
||||||
|
--help
|
||||||
|
-h
|
||||||
|
"
|
||||||
|
subcommands="
|
||||||
|
kube
|
||||||
|
"
|
||||||
|
__podman_subcommands "$subcommands $aliases" && return
|
||||||
|
|
||||||
|
case "$cur" in
|
||||||
|
-*)
|
||||||
|
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
_podman_container() {
|
_podman_container() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
--help
|
--help
|
||||||
@ -2197,6 +2216,14 @@ _podman_logout() {
|
|||||||
_complete_ "$options_with_args" "$boolean_options"
|
_complete_ "$options_with_args" "$boolean_options"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_podman_generate_kube() {
|
||||||
|
local options_with_args=""
|
||||||
|
|
||||||
|
local boolean_options="
|
||||||
|
-s
|
||||||
|
--service
|
||||||
|
"
|
||||||
|
|
||||||
_podman_container_runlabel() {
|
_podman_container_runlabel() {
|
||||||
local options_with_args="
|
local options_with_args="
|
||||||
--authfile
|
--authfile
|
||||||
@ -2538,6 +2565,7 @@ _podman_podman() {
|
|||||||
diff
|
diff
|
||||||
exec
|
exec
|
||||||
export
|
export
|
||||||
|
generate
|
||||||
history
|
history
|
||||||
images
|
images
|
||||||
import
|
import
|
||||||
|
119
docs/podman-generate-kube.1.md
Normal file
119
docs/podman-generate-kube.1.md
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
% podman-generate Podman Man Pages
|
||||||
|
% Brent Baude
|
||||||
|
% December 2018
|
||||||
|
# NAME
|
||||||
|
podman-generate-kube - Generate Kubernetes YAML
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
**podman generate kube **
|
||||||
|
[**-h**|**--help**]
|
||||||
|
[**-s**][**--service**]
|
||||||
|
CONTAINER|POD
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
**podman generate kube** will generate Kubernetes Pod YAML (v1 specification) from a podman container or pod. Whether
|
||||||
|
the input is for a container or pod, Podman will always generate the specification as a Pod. The input may be in the form
|
||||||
|
of a pod or container name or ID.
|
||||||
|
|
||||||
|
The **service** option can be used to generate a Service specification for the corresponding Pod ouput. 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.
|
||||||
|
|
||||||
|
# OPTIONS:
|
||||||
|
|
||||||
|
**s** **--service**
|
||||||
|
Generate a service file for the resulting Pod YAML.
|
||||||
|
|
||||||
|
## Examples ##
|
||||||
|
|
||||||
|
Create Kubernetes Pod YAML for a container called `some-mariadb` .
|
||||||
|
```
|
||||||
|
$ sudo podman generate kube some-mariadb
|
||||||
|
# Generation of Kubenetes YAML is still under development!
|
||||||
|
#
|
||||||
|
# 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: PATH
|
||||||
|
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
- name: TERM
|
||||||
|
value: xterm
|
||||||
|
- name: HOSTNAME
|
||||||
|
- name: container
|
||||||
|
value: podman
|
||||||
|
- 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
|
||||||
|
protocol: TCP
|
||||||
|
resources: {}
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: true
|
||||||
|
privileged: false
|
||||||
|
readOnlyRootFilesystem: false
|
||||||
|
tty: true
|
||||||
|
workingDir: /
|
||||||
|
status: {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Create Kubernetes service YAML for a container called `some-mariabdb`
|
||||||
|
```
|
||||||
|
$ sudo podman generate kube -s some-mariadb
|
||||||
|
# Generation of Kubenetes YAML is still under development!
|
||||||
|
#
|
||||||
|
# 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: Service
|
||||||
|
metadata:
|
||||||
|
creationTimestamp: 2018-12-03T19:08:24Z
|
||||||
|
labels:
|
||||||
|
app: some-mariadb
|
||||||
|
name: some-mariadb-libpod
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: "3306"
|
||||||
|
nodePort: 30929
|
||||||
|
port: 3306
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: 0
|
||||||
|
selector:
|
||||||
|
app: some-mariadb
|
||||||
|
type: NodePort
|
||||||
|
status:
|
||||||
|
loadBalancer: {}
|
||||||
|
```
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
podman(1), podman-container, podman-pod
|
||||||
|
|
||||||
|
# HISTORY
|
||||||
|
Decemeber 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
|
19
docs/podman-generate.1.md
Normal file
19
docs/podman-generate.1.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
% podman-generate(1)
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
podman\-container - generate structured data based for a containers and pods
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**podman generate** *subcommand*
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
The generate command will create structured output (like YAML) based on a container or pod.
|
||||||
|
|
||||||
|
## COMMANDS
|
||||||
|
|
||||||
|
| Command | Man Page | Description |
|
||||||
|
| ------- | --------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||||
|
| kube | [podman-generate-kube(1)](podman-generate-kube.1.md) | Generate Kubernetes YAML based on a pod or container
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
podman, podman-pod, podman-container
|
142
libpod/kube.go
142
libpod/kube.go
@ -2,7 +2,10 @@ package libpod
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/pkg/lookup"
|
"github.com/containers/libpod/pkg/lookup"
|
||||||
"github.com/containers/libpod/pkg/util"
|
"github.com/containers/libpod/pkg/util"
|
||||||
@ -15,23 +18,127 @@ import (
|
|||||||
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InspectForKube takes a slice of libpod containers and generates
|
// GenerateForKube takes a slice of libpod containers and generates
|
||||||
// one v1.Pod description that includes just a single container.
|
// one v1.Pod description that includes just a single container.
|
||||||
func (c *Container) InspectForKube() (*v1.Pod, error) {
|
func (c *Container) GenerateForKube() (*v1.Pod, error) {
|
||||||
// Generate the v1.Pod yaml description
|
// Generate the v1.Pod yaml description
|
||||||
return simplePodWithV1Container(c)
|
return simplePodWithV1Container(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// simplePodWithV1Container is a function used by inspect when kube yaml needs to be generated
|
// GenerateForKube takes a slice of libpod containers and generates
|
||||||
// for a single container. we "insert" that container description in a pod.
|
// one v1.Pod description
|
||||||
func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) {
|
func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) {
|
||||||
var containers []v1.Container
|
// Generate the v1.Pod yaml description
|
||||||
result, err := containerToV1Container(ctr)
|
var servicePorts []v1.ServicePort
|
||||||
|
|
||||||
|
allContainers, err := p.allContainers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, servicePorts, err
|
||||||
|
}
|
||||||
|
// If the pod has no containers, no sense to generate YAML
|
||||||
|
if len(allContainers) == 0 {
|
||||||
|
return nil, servicePorts, errors.Errorf("pod %s has no containers", p.ID())
|
||||||
|
}
|
||||||
|
// If only an infra container is present, makes no sense to generate YAML
|
||||||
|
if len(allContainers) == 1 && p.HasInfraContainer() {
|
||||||
|
return nil, servicePorts, errors.Errorf("pod %s only has an infra container", p.ID())
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.HasInfraContainer() {
|
||||||
|
infraContainer, err := p.getInfraContainer()
|
||||||
|
if err != nil {
|
||||||
|
return nil, servicePorts, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ports, err := ocicniPortMappingToContainerPort(infraContainer.config.PortMappings)
|
||||||
|
if err != nil {
|
||||||
|
return nil, servicePorts, err
|
||||||
|
}
|
||||||
|
servicePorts = containerPortsToServicePorts(ports)
|
||||||
|
}
|
||||||
|
pod, err := p.podWithContainers(allContainers)
|
||||||
|
return pod, servicePorts, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pod) getInfraContainer() (*Container, error) {
|
||||||
|
infraID, err := p.InfraContainerID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
containers = append(containers, result)
|
return p.runtime.LookupContainer(infraID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKubeServiceFromV1Pod creates a v1 service object from a v1 pod object
|
||||||
|
func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) v1.Service {
|
||||||
|
service := v1.Service{}
|
||||||
|
selector := make(map[string]string)
|
||||||
|
selector["app"] = pod.Labels["app"]
|
||||||
|
ports := servicePorts
|
||||||
|
if len(ports) == 0 {
|
||||||
|
ports = containersToServicePorts(pod.Spec.Containers)
|
||||||
|
}
|
||||||
|
serviceSpec := v1.ServiceSpec{
|
||||||
|
Ports: ports,
|
||||||
|
Selector: selector,
|
||||||
|
Type: v1.ServiceTypeNodePort,
|
||||||
|
}
|
||||||
|
service.Spec = serviceSpec
|
||||||
|
service.ObjectMeta = pod.ObjectMeta
|
||||||
|
tm := v12.TypeMeta{
|
||||||
|
Kind: "Service",
|
||||||
|
APIVersion: pod.TypeMeta.APIVersion,
|
||||||
|
}
|
||||||
|
service.TypeMeta = tm
|
||||||
|
return service
|
||||||
|
}
|
||||||
|
|
||||||
|
// containerPortsToServicePorts takes a slice of containerports and generates a
|
||||||
|
// slice of service ports
|
||||||
|
func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.ServicePort {
|
||||||
|
var sps []v1.ServicePort
|
||||||
|
for _, cp := range containerPorts {
|
||||||
|
nodePort := 30000 + rand.Intn(32767-30000+1)
|
||||||
|
servicePort := v1.ServicePort{
|
||||||
|
Protocol: cp.Protocol,
|
||||||
|
Port: cp.ContainerPort,
|
||||||
|
NodePort: int32(nodePort),
|
||||||
|
Name: strconv.Itoa(int(cp.ContainerPort)),
|
||||||
|
}
|
||||||
|
sps = append(sps, servicePort)
|
||||||
|
}
|
||||||
|
return sps
|
||||||
|
}
|
||||||
|
|
||||||
|
// containersToServicePorts takes a slice of v1.Containers and generates an
|
||||||
|
// inclusive list of serviceports to expose
|
||||||
|
func containersToServicePorts(containers []v1.Container) []v1.ServicePort {
|
||||||
|
var sps []v1.ServicePort
|
||||||
|
// Without the call to rand.Seed, a program will produce the same sequence of pseudo-random numbers
|
||||||
|
// for each execution. Legal nodeport range is 30000-32767
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
for _, ctr := range containers {
|
||||||
|
sps = append(sps, containerPortsToServicePorts(ctr.Ports)...)
|
||||||
|
}
|
||||||
|
return sps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pod) podWithContainers(containers []*Container) (*v1.Pod, error) {
|
||||||
|
var podContainers []v1.Container
|
||||||
|
for _, ctr := range containers {
|
||||||
|
result, err := containerToV1Container(ctr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ctr.IsInfra() {
|
||||||
|
podContainers = append(podContainers, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return addContainersToPodObject(podContainers, p.Name()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addContainersToPodObject(containers []v1.Container, podName string) *v1.Pod {
|
||||||
tm := v12.TypeMeta{
|
tm := v12.TypeMeta{
|
||||||
Kind: "Pod",
|
Kind: "Pod",
|
||||||
APIVersion: "v1",
|
APIVersion: "v1",
|
||||||
@ -39,10 +146,10 @@ func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) {
|
|||||||
|
|
||||||
// Add a label called "app" with the containers name as a value
|
// Add a label called "app" with the containers name as a value
|
||||||
labels := make(map[string]string)
|
labels := make(map[string]string)
|
||||||
labels["app"] = removeUnderscores(ctr.Name())
|
labels["app"] = removeUnderscores(podName)
|
||||||
om := v12.ObjectMeta{
|
om := v12.ObjectMeta{
|
||||||
// The name of the pod is container_name-libpod
|
// The name of the pod is container_name-libpod
|
||||||
Name: fmt.Sprintf("%s-libpod", removeUnderscores(ctr.Name())),
|
Name: fmt.Sprintf("%s-libpod", removeUnderscores(podName)),
|
||||||
Labels: labels,
|
Labels: labels,
|
||||||
// CreationTimestamp seems to be required, so adding it; in doing so, the timestamp
|
// CreationTimestamp seems to be required, so adding it; in doing so, the timestamp
|
||||||
// will reflect time this is run (not container create time) because the conversion
|
// will reflect time this is run (not container create time) because the conversion
|
||||||
@ -57,7 +164,20 @@ func simplePodWithV1Container(ctr *Container) (*v1.Pod, error) {
|
|||||||
ObjectMeta: om,
|
ObjectMeta: om,
|
||||||
Spec: ps,
|
Spec: ps,
|
||||||
}
|
}
|
||||||
return &p, nil
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
// simplePodWithV1Container 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 simplePodWithV1Container(ctr *Container) (*v1.Pod, error) {
|
||||||
|
var containers []v1.Container
|
||||||
|
result, err := containerToV1Container(ctr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
containers = append(containers, result)
|
||||||
|
return addContainersToPodObject(containers, ctr.Name()), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// containerToV1Container converts information we know about a libpod container
|
// containerToV1Container converts information we know about a libpod container
|
||||||
|
106
test/e2e/generate_kube_test.go
Normal file
106
test/e2e/generate_kube_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
. "github.com/containers/libpod/test/utils"
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Podman generate kube", func() {
|
||||||
|
var (
|
||||||
|
tempdir string
|
||||||
|
err error
|
||||||
|
podmanTest *PodmanTestIntegration
|
||||||
|
)
|
||||||
|
|
||||||
|
BeforeEach(func() {
|
||||||
|
tempdir, err = CreateTempDirInTempDir()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
podmanTest = PodmanTestCreate(tempdir)
|
||||||
|
podmanTest.RestoreAllArtifacts()
|
||||||
|
})
|
||||||
|
|
||||||
|
AfterEach(func() {
|
||||||
|
podmanTest.Cleanup()
|
||||||
|
f := CurrentGinkgoTestDescription()
|
||||||
|
timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
|
||||||
|
GinkgoWriter.Write([]byte(timedResult))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman generate pod kube on bogus object", func() {
|
||||||
|
session := podmanTest.Podman([]string{"generate", "kube", "foobar"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Not(Equal(0)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman generate service kube on bogus object", func() {
|
||||||
|
session := podmanTest.Podman([]string{"generate", "kube", "-s", "foobar"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Not(Equal(0)))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman generate kube on container", func() {
|
||||||
|
session := podmanTest.RunTopContainer("top")
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
kube := podmanTest.Podman([]string{"generate", "kube", "top"})
|
||||||
|
kube.WaitWithDefaultTimeout()
|
||||||
|
Expect(kube.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
_, err := yaml.Marshal(kube.OutputToString())
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman generate service kube on container", func() {
|
||||||
|
session := podmanTest.RunTopContainer("top")
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
kube := podmanTest.Podman([]string{"generate", "kube", "-s", "top"})
|
||||||
|
kube.WaitWithDefaultTimeout()
|
||||||
|
Expect(kube.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
_, err := yaml.Marshal(kube.OutputToString())
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman generate kube on pod", func() {
|
||||||
|
_, rc, _ := podmanTest.CreatePod("toppod")
|
||||||
|
Expect(rc).To(Equal(0))
|
||||||
|
|
||||||
|
session := podmanTest.RunTopContainerInPod("topcontainer", "toppod")
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
kube := podmanTest.Podman([]string{"generate", "kube", "toppod"})
|
||||||
|
kube.WaitWithDefaultTimeout()
|
||||||
|
Expect(kube.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
_, err := yaml.Marshal(kube.OutputToString())
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman generate service kube on pod", func() {
|
||||||
|
_, rc, _ := podmanTest.CreatePod("toppod")
|
||||||
|
Expect(rc).To(Equal(0))
|
||||||
|
|
||||||
|
session := podmanTest.RunTopContainerInPod("topcontainer", "toppod")
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
kube := podmanTest.Podman([]string{"generate", "kube", "-s", "toppod"})
|
||||||
|
kube.WaitWithDefaultTimeout()
|
||||||
|
Expect(kube.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
_, err := yaml.Marshal(kube.OutputToString())
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
})
|
||||||
|
})
|
Reference in New Issue
Block a user