summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2018-12-09 14:26:21 -0600
committerbaude <bbaude@redhat.com>2018-12-19 14:20:55 -0600
commit9b03cacc87c4d59fc301c21ef73ddc301ec753fb (patch)
treeec7318272db56a8a3ba11b5ac1b01d262b21742c /cmd
parenteddfe6ba628d17435559ba32a8ef748c386105aa (diff)
downloadpodman-9b03cacc87c4d59fc301c21ef73ddc301ec753fb.tar.gz
podman-9b03cacc87c4d59fc301c21ef73ddc301ec753fb.tar.bz2
podman-9b03cacc87c4d59fc301c21ef73ddc301ec753fb.zip
Add Play
podman play kube adds the ability for the user to recreate pods and containers from a Kubernetes YAML file in libpod. Signed-off-by: baude <bbaude@redhat.com>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/create.go63
-rw-r--r--cmd/podman/generate_kube.go1
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/play.go23
-rw-r--r--cmd/podman/play_kube.go245
-rw-r--r--cmd/podman/varlink/io.podman.varlink12
6 files changed, 316 insertions, 29 deletions
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 870eb28d6..2b31a6423 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -146,37 +146,10 @@ func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container
return nil, nil, err
}
- runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
- if err != nil {
- return nil, nil, err
- }
-
- options, err := createConfig.GetContainerCreateOptions(runtime)
- if err != nil {
- return nil, nil, err
- }
-
- became, ret, err := joinOrCreateRootlessUserNamespace(createConfig, runtime)
+ ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx)
if err != nil {
return nil, nil, err
}
- if became {
- os.Exit(ret)
- }
-
- ctr, err := runtime.NewContainer(ctx, runtimeSpec, options...)
- if err != nil {
- return nil, nil, err
- }
-
- createConfigJSON, err := json.Marshal(createConfig)
- if err != nil {
- return nil, nil, err
- }
- if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil {
- return nil, nil, err
- }
-
if cidFile != nil {
_, err = cidFile.WriteString(ctr.ID())
if err != nil {
@@ -913,3 +886,37 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l
}
return rootless.BecomeRootInUserNS()
}
+
+func createContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateConfig, ctx context.Context) (*libpod.Container, error) {
+ runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ options, err := createConfig.GetContainerCreateOptions(r)
+ if err != nil {
+ return nil, err
+ }
+
+ became, ret, err := joinOrCreateRootlessUserNamespace(createConfig, r)
+ if err != nil {
+ return nil, err
+ }
+ if became {
+ os.Exit(ret)
+ }
+
+ ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
+ if err != nil {
+ return nil, err
+ }
+
+ createConfigJSON, err := json.Marshal(createConfig)
+ if err != nil {
+ return nil, err
+ }
+ if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil {
+ return nil, err
+ }
+ return ctr, nil
+}
diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go
index 8f2f0de32..f63bd431b 100644
--- a/cmd/podman/generate_kube.go
+++ b/cmd/podman/generate_kube.go
@@ -33,7 +33,6 @@ var (
}
)
-// generateKubeYAMLCmdgenerates or replays kube
func generateKubeYAMLCmd(c *cli.Context) error {
var (
podYAML *v1.Pod
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 2db6c5dec..f47a75761 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -90,6 +90,7 @@ func main() {
portCommand,
pullCommand,
pushCommand,
+ playCommand,
restartCommand,
rmCommand,
rmiCommand,
diff --git a/cmd/podman/play.go b/cmd/podman/play.go
new file mode 100644
index 000000000..adbab3480
--- /dev/null
+++ b/cmd/podman/play.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "github.com/urfave/cli"
+)
+
+var (
+ playSubCommands = []cli.Command{
+ playKubeCommand,
+ }
+
+ playDescription = "Play a pod and its containers from a structured file."
+ playCommand = cli.Command{
+ Name: "play",
+ Usage: "play a container or pod",
+ Description: playDescription,
+ ArgsUsage: "",
+ Subcommands: playSubCommands,
+ UseShortOptionHandling: true,
+ OnUsageError: usageErrorHandler,
+ Hidden: true,
+ }
+)
diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go
new file mode 100644
index 000000000..f165c5f0f
--- /dev/null
+++ b/cmd/podman/play_kube.go
@@ -0,0 +1,245 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "github.com/containers/image/types"
+ "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ image2 "github.com/containers/libpod/libpod/image"
+ ns "github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/spec"
+ "github.com/containers/storage"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/ghodss/yaml"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+ "k8s.io/api/core/v1"
+)
+
+var (
+ playKubeFlags = []cli.Flag{
+ cli.StringFlag{
+ Name: "authfile",
+ Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ",
+ },
+ cli.StringFlag{
+ Name: "cert-dir",
+ Usage: "`pathname` of a directory containing TLS certificates and keys",
+ },
+ cli.StringFlag{
+ Name: "creds",
+ Usage: "`credentials` (USERNAME:PASSWORD) to use for authenticating to a registry",
+ },
+ cli.BoolFlag{
+ Name: "quiet, q",
+ Usage: "Suppress output information when pulling images",
+ },
+ cli.StringFlag{
+ Name: "signature-policy",
+ Usage: "`pathname` of signature policy file (not usually used)",
+ },
+ cli.BoolTFlag{
+ Name: "tls-verify",
+ Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
+ },
+ }
+ playKubeDescription = "Play a Pod and its containers based on a Kubrernetes YAML"
+ playKubeCommand = cli.Command{
+ Name: "kube",
+ Usage: "Play a pod based on Kubernetes YAML",
+ Description: playKubeDescription,
+ Action: playKubeYAMLCmd,
+ Flags: sortFlags(playKubeFlags),
+ ArgsUsage: "kubernetes YAML file",
+ UseShortOptionHandling: true,
+ OnUsageError: usageErrorHandler,
+ }
+)
+
+func playKubeYAMLCmd(c *cli.Context) error {
+ var (
+ podOptions []libpod.PodCreateOption
+ podYAML v1.Pod
+ registryCreds *types.DockerAuthConfig
+ containers []*libpod.Container
+ writer io.Writer
+ )
+
+ ctx := getContext()
+ if rootless.IsRootless() {
+ return errors.Wrapf(libpod.ErrNotImplemented, "rootless users")
+ }
+ args := c.Args()
+ if len(args) > 1 {
+ return errors.New("you can only play one kubernetes file at a time")
+ }
+ if len(args) < 1 {
+ return errors.New("you must supply at least one file")
+ }
+
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ content, err := ioutil.ReadFile(args[0])
+ if err != nil {
+ return err
+ }
+
+ if err := yaml.Unmarshal(content, &podYAML); err != nil {
+ return errors.Wrapf(err, "unable to read %s as YAML", args[0])
+ }
+
+ podOptions = append(podOptions, libpod.WithInfraContainer())
+ podOptions = append(podOptions, libpod.WithPodName(podYAML.ObjectMeta.Name))
+ // TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml
+
+ nsOptions, err := shared.GetNamespaceOptions(strings.Split(DefaultKernelNamespaces, ","))
+ if err != nil {
+ return err
+ }
+ podOptions = append(podOptions, nsOptions...)
+ podPorts := getPodPorts(podYAML.Spec.Containers)
+ podOptions = append(podOptions, libpod.WithInfraContainerPorts(podPorts))
+
+ // Create the Pod
+ pod, err := runtime.NewPod(ctx, podOptions...)
+ if err != nil {
+ return err
+ }
+ // Print the Pod's ID
+ fmt.Println(pod.ID())
+
+ podInfraID, err := pod.InfraContainerID()
+ if err != nil {
+ return err
+ }
+
+ namespaces := map[string]string{
+ // Disabled during code review per mheon
+ //"pid": fmt.Sprintf("container:%s", podInfraID),
+ "net": fmt.Sprintf("container:%s", podInfraID),
+ "user": fmt.Sprintf("container:%s", podInfraID),
+ "ipc": fmt.Sprintf("container:%s", podInfraID),
+ "uts": fmt.Sprintf("container:%s", podInfraID),
+ }
+ if !c.Bool("quiet") {
+ writer = os.Stderr
+ }
+
+ dockerRegistryOptions := image2.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ DockerCertPath: c.String("cert-dir"),
+ }
+ if c.IsSet("tls-verify") {
+ dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify"))
+ }
+
+ for _, container := range podYAML.Spec.Containers {
+ newImage, err := runtime.ImageRuntime().New(ctx, container.Image, c.String("signature-policy"), c.String("authfile"), writer, &dockerRegistryOptions, image2.SigningOptions{}, false)
+ if err != nil {
+ return err
+ }
+ createConfig := kubeContainerToCreateConfig(container, runtime, newImage, namespaces)
+ if err != nil {
+ return err
+ }
+ ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx)
+ if err != nil {
+ return err
+ }
+ containers = append(containers, ctr)
+ }
+
+ // start the containers
+ for _, ctr := range containers {
+ if err := ctr.Start(ctx); err != nil {
+ // Making this a hard failure here to avoid a mess
+ // the other containers are in created status
+ return err
+ }
+ fmt.Println(ctr.ID())
+ }
+
+ return nil
+}
+
+// getPodPorts converts a slice of kube container descriptions to an
+// array of ocicni portmapping descriptions usable in libpod
+func getPodPorts(containers []v1.Container) []ocicni.PortMapping {
+ var infraPorts []ocicni.PortMapping
+ for _, container := range containers {
+ for _, p := range container.Ports {
+ portBinding := ocicni.PortMapping{
+ HostPort: p.HostPort,
+ ContainerPort: p.ContainerPort,
+ Protocol: strings.ToLower(string(p.Protocol)),
+ }
+ if p.HostIP != "" {
+ logrus.Debug("HostIP on port bindings is not supported")
+ }
+ infraPorts = append(infraPorts, portBinding)
+ }
+ }
+ return infraPorts
+}
+
+// kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container
+func kubeContainerToCreateConfig(containerYAML v1.Container, runtime *libpod.Runtime, newImage *image2.Image, namespaces map[string]string) *createconfig.CreateConfig {
+ var (
+ containerConfig createconfig.CreateConfig
+ envs map[string]string
+ )
+
+ containerConfig.Runtime = runtime
+ containerConfig.Image = containerYAML.Image
+ containerConfig.ImageID = newImage.ID()
+ containerConfig.Name = containerYAML.Name
+ containerConfig.Tty = containerYAML.TTY
+ containerConfig.WorkDir = containerYAML.WorkingDir
+ if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil {
+ containerConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem
+ }
+ if containerYAML.SecurityContext.Privileged != nil {
+ containerConfig.Privileged = *containerYAML.SecurityContext.Privileged
+ }
+
+ if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil {
+ containerConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation
+ }
+
+ containerConfig.Command = containerYAML.Command
+ containerConfig.StopSignal = 15
+
+ // If the user does not pass in ID mappings, just set to basics
+ if containerConfig.IDMappings == nil {
+ containerConfig.IDMappings = &storage.IDMappingOptions{}
+ }
+
+ containerConfig.NetMode = ns.NetworkMode(namespaces["net"])
+ containerConfig.IpcMode = ns.IpcMode(namespaces["ipc"])
+ containerConfig.UtsMode = ns.UTSMode(namespaces["uts"])
+ // disabled in code review per mheon
+ //containerConfig.PidMode = ns.PidMode(namespaces["pid"])
+ containerConfig.UsernsMode = ns.UsernsMode(namespaces["user"])
+
+ if len(containerYAML.Env) > 0 {
+ envs = make(map[string]string)
+ }
+ // Environment Variables
+ for _, e := range containerYAML.Env {
+ envs[e.Name] = e.Value
+ }
+ containerConfig.Env = envs
+ return &containerConfig
+}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 376bbc950..c1b7c703a 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -1016,6 +1016,18 @@ method UnmountContainer(name: string, force: bool) -> ()
# This function is not implemented yet.
method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
+# GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod
+# and its containers. The description is in YAML. See also [ReplayKube](ReplayKube).
+method GenerateKube() -> (notimplemented: NotImplemented)
+
+# GenerateKubeService generates a Kubernetes v1 Service description of a Podman container or pod
+# and its containers. The description is in YAML. See also [GenerateKube](GenerateKube).
+method GenerateKubeService() -> (notimplemented: NotImplemented)
+
+# ReplayKube recreates a pod and its containers based on a Kubernetes v1 Pod description (in YAML)
+# like that created by GenerateKube. See also [GenerateKube](GenerateKube).
+method ReplayKube() -> (notimplemented: NotImplemented)
+
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
error ImageNotFound (name: string)