From e56d5295614b745115abf0198f7b67ae157aae1e Mon Sep 17 00:00:00 2001 From: Brent Baude Date: Tue, 24 Mar 2020 07:28:36 -0500 Subject: podmanv2 pod create using podspecgen using the factory approach similar to container, we now create pods based on a pod spec generator. wired up the podmanv2 pod create command, podcreatewithspec binding, simple binding test, and apiv2 endpoint. also included some code refactoring as it introduced as easy circular import. Signed-off-by: Brent Baude --- cmd/podmanV2/Makefile | 2 + cmd/podmanV2/common/netflags.go | 108 ++++++++++++++++++++++++++++++++ cmd/podmanV2/common/types.go | 3 + cmd/podmanV2/common/util.go | 43 +++++++++++++ cmd/podmanV2/pods/create.go | 132 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 288 insertions(+) create mode 100644 cmd/podmanV2/Makefile create mode 100644 cmd/podmanV2/common/netflags.go create mode 100644 cmd/podmanV2/common/types.go create mode 100644 cmd/podmanV2/common/util.go create mode 100644 cmd/podmanV2/pods/create.go (limited to 'cmd/podmanV2') diff --git a/cmd/podmanV2/Makefile b/cmd/podmanV2/Makefile new file mode 100644 index 000000000..147a78d9c --- /dev/null +++ b/cmd/podmanV2/Makefile @@ -0,0 +1,2 @@ +all: + GO111MODULE=off go build -tags 'ABISupport' diff --git a/cmd/podmanV2/common/netflags.go b/cmd/podmanV2/common/netflags.go new file mode 100644 index 000000000..758f155c8 --- /dev/null +++ b/cmd/podmanV2/common/netflags.go @@ -0,0 +1,108 @@ +package common + +import ( + "net" + + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/rootless" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func getDefaultNetwork() string { + if rootless.IsRootless() { + return "slirp4netns" + } + return "bridge" +} + +func GetNetFlags() *pflag.FlagSet { + netFlags := pflag.FlagSet{} + netFlags.StringSlice( + "add-host", []string{}, + "Add a custom host-to-IP mapping (host:ip) (default [])", + ) + netFlags.StringSlice( + "dns", []string{}, + "Set custom DNS servers", + ) + netFlags.StringSlice( + "dns-opt", []string{}, + "Set custom DNS options", + ) + netFlags.StringSlice( + "dns-search", []string{}, + "Set custom DNS search domains", + ) + netFlags.String( + "ip", "", + "Specify a static IPv4 address for the container", + ) + netFlags.String( + "mac-address", "", + "Container MAC address (e.g. 92:d0:c6:0a:29:33)", + ) + netFlags.String( + "network", getDefaultNetwork(), + "Connect a container to a network", + ) + netFlags.StringSliceP( + "publish", "p", []string{}, + "Publish a container's port, or a range of ports, to the host (default [])", + ) + netFlags.Bool( + "no-hosts", false, + "Do not create /etc/hosts within the container, instead use the version from the image", + ) + return &netFlags +} + +func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) { + var ( + err error + ) + opts := entities.NetOptions{} + opts.AddHosts, err = cmd.Flags().GetStringSlice("add-host") + if err != nil { + return nil, err + } + servers, err := cmd.Flags().GetStringSlice("dns") + if err != nil { + return nil, err + } + for _, d := range servers { + if d == "none" { + opts.DNSHost = true + break + } + opts.DNSServers = append(opts.DNSServers, net.ParseIP(d)) + } + opts.DNSSearch, err = cmd.Flags().GetStringSlice("dns-search") + if err != nil { + return nil, err + } + + m, err := cmd.Flags().GetString("mac-address") + if err != nil { + return nil, err + } + if len(m) > 0 { + mac, err := net.ParseMAC(m) + if err != nil { + return nil, err + } + opts.StaticMAC = &mac + } + inputPorts, err := cmd.Flags().GetStringSlice("publish") + if err != nil { + return nil, err + } + if len(inputPorts) > 0 { + opts.PublishPorts, err = createPortBindings(inputPorts) + if err != nil { + return nil, err + } + } + opts.NoHosts, err = cmd.Flags().GetBool("no-hosts") + return &opts, err +} diff --git a/cmd/podmanV2/common/types.go b/cmd/podmanV2/common/types.go new file mode 100644 index 000000000..2427ae975 --- /dev/null +++ b/cmd/podmanV2/common/types.go @@ -0,0 +1,3 @@ +package common + +var DefaultKernelNamespaces = "cgroup,ipc,net,uts" diff --git a/cmd/podmanV2/common/util.go b/cmd/podmanV2/common/util.go new file mode 100644 index 000000000..47bbe12fa --- /dev/null +++ b/cmd/podmanV2/common/util.go @@ -0,0 +1,43 @@ +package common + +import ( + "strconv" + + "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/docker/go-connections/nat" + "github.com/pkg/errors" +) + +// createPortBindings iterates ports mappings and exposed ports into a format CNI understands +func createPortBindings(ports []string) ([]ocicni.PortMapping, error) { + // TODO wants someone to rewrite this code in the future + var portBindings []ocicni.PortMapping + // The conversion from []string to natBindings is temporary while mheon reworks the port + // deduplication code. Eventually that step will not be required. + _, natBindings, err := nat.ParsePortSpecs(ports) + if err != nil { + return nil, err + } + for containerPb, hostPb := range natBindings { + var pm ocicni.PortMapping + pm.ContainerPort = int32(containerPb.Int()) + for _, i := range hostPb { + var hostPort int + var err error + pm.HostIP = i.HostIP + if i.HostPort == "" { + hostPort = containerPb.Int() + } else { + hostPort, err = strconv.Atoi(i.HostPort) + if err != nil { + return nil, errors.Wrapf(err, "unable to convert host port to integer") + } + } + + pm.HostPort = int32(hostPort) + pm.Protocol = containerPb.Proto() + portBindings = append(portBindings, pm) + } + } + return portBindings, nil +} diff --git a/cmd/podmanV2/pods/create.go b/cmd/podmanV2/pods/create.go new file mode 100644 index 000000000..ab8957ee3 --- /dev/null +++ b/cmd/podmanV2/pods/create.go @@ -0,0 +1,132 @@ +package pods + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/containers/libpod/cmd/podmanV2/common" + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/errorhandling" + "github.com/containers/libpod/pkg/specgen" + "github.com/containers/libpod/pkg/util" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + podCreateDescription = `After creating the pod, the pod ID is printed to stdout. + + You can then start it at any time with the podman pod start command. The pod will be created with the initial state 'created'.` + + createCommand = &cobra.Command{ + Use: "create", + Args: cobra.NoArgs, + Short: "Create a new empty pod", + Long: podCreateDescription, + RunE: create, + } +) + +var ( + createOptions entities.PodCreateOptions + labels, labelFile []string + podIDFile string + share string +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: createCommand, + Parent: podCmd, + }) + flags := createCommand.Flags() + flags.SetInterspersed(false) + flags.AddFlagSet(common.GetNetFlags()) + flags.StringVar(&createOptions.CGroupParent, "cgroup-parent", "", "Set parent cgroup for the pod") + flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") + flags.StringVar(&createOptions.InfraImage, "infra-image", define.DefaultInfraImage, "The image of the infra container to associate with the pod") + flags.StringVar(&createOptions.InfraCommand, "infra-command", define.DefaultInfraCommand, "The command to run on the infra container when the pod is started") + flags.StringSliceVar(&labelFile, "label-file", []string{}, "Read in a line delimited file of labels") + flags.StringSliceVarP(&labels, "label", "l", []string{}, "Set metadata on pod (default [])") + flags.StringVarP(&createOptions.Name, "name", "n", "", "Assign a name to the pod") + flags.StringVarP(&createOptions.Hostname, "hostname", "", "", "Set a hostname to the pod") + flags.StringVar(&podIDFile, "pod-id-file", "", "Write the pod ID to the file") + flags.StringVar(&share, "share", common.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") +} + +func create(cmd *cobra.Command, args []string) error { + var ( + err error + podIdFile *os.File + ) + createOptions.Labels, err = parse.GetAllLabels(labelFile, labels) + if err != nil { + return errors.Wrapf(err, "unable to process labels") + } + + if !createOptions.Infra && cmd.Flag("share").Changed && share != "none" && share != "" { + return errors.Errorf("You cannot share kernel namespaces on the pod level without an infra container") + } + createOptions.Share = strings.Split(share, ",") + if cmd.Flag("pod-id-file").Changed { + podIdFile, err = util.OpenExclusiveFile(podIDFile) + if err != nil && os.IsExist(err) { + return errors.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", podIDFile) + } + if err != nil { + return errors.Errorf("error opening pod-id-file %s", podIDFile) + } + defer errorhandling.CloseQuiet(podIdFile) + defer errorhandling.SyncQuiet(podIdFile) + } + + createOptions.Net, err = common.NetFlagsToNetOptions(cmd) + if err != nil { + return err + } + netInput, err := cmd.Flags().GetString("network") + if err != nil { + return err + } + n := specgen.Namespace{} + switch netInput { + case "bridge": + n.NSMode = specgen.Bridge + case "host": + n.NSMode = specgen.Host + case "slip4netns": + n.NSMode = specgen.Slirp + default: + if strings.HasPrefix(netInput, "container:") { //nolint + split := strings.Split(netInput, ":") + if len(split) != 2 { + return errors.Errorf("invalid network paramater: %q", netInput) + } + n.NSMode = specgen.FromContainer + n.Value = split[1] + } else if strings.HasPrefix(netInput, "ns:") { + return errors.New("the ns: network option is not supported for pods") + } else { + n.NSMode = specgen.Bridge + createOptions.Net.CNINetworks = strings.Split(netInput, ",") + } + } + if len(createOptions.Net.PublishPorts) > 0 { + if !createOptions.Infra { + return errors.Errorf("you must have an infra container to publish port bindings to the host") + } + } + + response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions) + if err != nil { + return err + } + fmt.Println(response.Id) + return nil +} -- cgit v1.2.3-54-g00ecf