From 9b03cacc87c4d59fc301c21ef73ddc301ec753fb Mon Sep 17 00:00:00 2001 From: baude Date: Sun, 9 Dec 2018 14:26:21 -0600 Subject: 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 --- cmd/podman/play_kube.go | 245 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 cmd/podman/play_kube.go (limited to 'cmd/podman/play_kube.go') 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 +} -- cgit v1.2.3-54-g00ecf