aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/shared/pod.go18
-rw-r--r--cmd/podmanV2/Makefile2
-rw-r--r--cmd/podmanV2/common/netflags.go108
-rw-r--r--cmd/podmanV2/common/types.go3
-rw-r--r--cmd/podmanV2/common/util.go43
-rw-r--r--cmd/podmanV2/containers/top.go91
-rw-r--r--cmd/podmanV2/pods/create.go132
7 files changed, 396 insertions, 1 deletions
diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go
index 3046953b5..50bd88e08 100644
--- a/cmd/podman/shared/pod.go
+++ b/cmd/podman/shared/pod.go
@@ -162,7 +162,7 @@ func FilterAllPodsWithFilterFunc(r *libpod.Runtime, filters ...libpod.PodFilter)
func GenerateFilterFunction(r *libpod.Runtime, filters []string) ([]libpod.PodFilter, error) {
var filterFuncs []libpod.PodFilter
for _, f := range filters {
- filterSplit := strings.Split(f, "=")
+ filterSplit := strings.SplitN(f, "=", 2)
if len(filterSplit) < 2 {
return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
@@ -256,6 +256,22 @@ func generatePodFilterFuncs(filter, filterValue string) (
}
return false
}, nil
+ case "label":
+ var filterArray = strings.SplitN(filterValue, "=", 2)
+ var filterKey = filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ return func(p *libpod.Pod) bool {
+ for labelKey, labelValue := range p.Labels() {
+ if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
+ return true
+ }
+ }
+ return false
+ }, nil
}
return nil, errors.Errorf("%s is an invalid filter", filter)
}
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/containers/top.go b/cmd/podmanV2/containers/top.go
new file mode 100644
index 000000000..a86c12e2a
--- /dev/null
+++ b/cmd/podmanV2/containers/top.go
@@ -0,0 +1,91 @@
+package containers
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/psgo"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ topDescription = fmt.Sprintf(`Similar to system "top" command.
+
+ Specify format descriptors to alter the output.
+
+ Running "podman top -l pid pcpu seccomp" will print the process ID, the CPU percentage and the seccomp mode of each process of the latest container.
+ Format Descriptors:
+ %s`, strings.Join(psgo.ListDescriptors(), ","))
+
+ topOptions = entities.TopOptions{}
+
+ topCommand = &cobra.Command{
+ Use: "top [flags] CONTAINER [FORMAT-DESCRIPTORS|ARGS]",
+ Short: "Display the running processes of a container",
+ Long: topDescription,
+ PersistentPreRunE: preRunE,
+ RunE: top,
+ Args: cobra.ArbitraryArgs,
+ Example: `podman top ctrID
+podman top --latest
+podman top ctrID pid seccomp args %C
+podman top ctrID -eo user,pid,comm`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: topCommand,
+ })
+
+ topCommand.SetHelpTemplate(registry.HelpTemplate())
+ topCommand.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := topCommand.Flags()
+ flags.SetInterspersed(false)
+ flags.BoolVar(&topOptions.ListDescriptors, "list-descriptors", false, "")
+ flags.BoolVarP(&topOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+
+ _ = flags.MarkHidden("list-descriptors") // meant only for bash completion
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+
+func top(cmd *cobra.Command, args []string) error {
+ if topOptions.ListDescriptors {
+ fmt.Println(strings.Join(psgo.ListDescriptors(), "\n"))
+ return nil
+ }
+
+ if len(args) < 1 && !topOptions.Latest {
+ return errors.Errorf("you must provide the name or id of a running container")
+ }
+
+ if topOptions.Latest {
+ topOptions.Descriptors = args
+ } else {
+ topOptions.NameOrID = args[0]
+ topOptions.Descriptors = args[1:]
+ }
+
+ topResponse, err := registry.ContainerEngine().ContainerTop(context.Background(), topOptions)
+ if err != nil {
+ return err
+ }
+
+ w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
+ for _, proc := range topResponse.Value {
+ if _, err := fmt.Fprintln(w, proc); err != nil {
+ return err
+ }
+ }
+ return w.Flush()
+}
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 <pod_id> 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
+}