diff options
Diffstat (limited to 'cmd')
32 files changed, 1262 insertions, 66 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/inspect.go b/cmd/podmanV2/containers/inspect.go index 635be4789..648289f0b 100644 --- a/cmd/podmanV2/containers/inspect.go +++ b/cmd/podmanV2/containers/inspect.go @@ -1,8 +1,15 @@ package containers import ( + "context" + "fmt" + "os" + "strings" + "text/template" + "github.com/containers/libpod/cmd/podmanV2/registry" "github.com/containers/libpod/pkg/domain/entities" + jsoniter "github.com/json-iterator/go" "github.com/spf13/cobra" ) @@ -12,31 +19,57 @@ var ( Use: "inspect [flags] CONTAINER", Short: "Display the configuration of a container", Long: `Displays the low-level information on a container identified by name or ID.`, - PreRunE: inspectPreRunE, + PreRunE: preRunE, RunE: inspect, Example: `podman container inspect myCtr podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`, } ) +var ( + inspectOptions entities.ContainerInspectOptions +) + func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: inspectCmd, Parent: containerCmd, }) -} - -func inspectPreRunE(cmd *cobra.Command, args []string) (err error) { - err = preRunE(cmd, args) - if err != nil { - return + flags := inspectCmd.Flags() + flags.StringVarP(&inspectOptions.Format, "format", "f", "", "Change the output format to a Go template") + flags.BoolVarP(&inspectOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") + flags.BoolVarP(&inspectOptions.Size, "size", "s", false, "Display total file size") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") } - - _, err = registry.NewImageEngine(cmd, args) - return err } func inspect(cmd *cobra.Command, args []string) error { + responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, inspectOptions) + if err != nil { + return err + } + if inspectOptions.Format == "" { + b, err := jsoniter.MarshalIndent(responses, "", " ") + if err != nil { + return err + } + fmt.Println(string(b)) + return nil + } + format := inspectOptions.Format + if !strings.HasSuffix(format, "\n") { + format += "\n" + } + tmpl, err := template.New("inspect").Parse(format) + if err != nil { + return err + } + for _, i := range responses { + if err := tmpl.Execute(os.Stdout, i); err != nil { + return err + } + } return nil } diff --git a/cmd/podmanV2/containers/kill.go b/cmd/podmanV2/containers/kill.go index 36c4f2528..6e6debfec 100644 --- a/cmd/podmanV2/containers/kill.go +++ b/cmd/podmanV2/containers/kill.go @@ -15,10 +15,11 @@ import ( var ( killDescription = "The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal." killCommand = &cobra.Command{ - Use: "kill [flags] CONTAINER [CONTAINER...]", - Short: "Kill one or more running containers with a specific signal", - Long: killDescription, - RunE: kill, + Use: "kill [flags] CONTAINER [CONTAINER...]", + Short: "Kill one or more running containers with a specific signal", + Long: killDescription, + RunE: kill, + PersistentPreRunE: preRunE, Args: func(cmd *cobra.Command, args []string) error { return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) }, diff --git a/cmd/podmanV2/containers/pause.go b/cmd/podmanV2/containers/pause.go index 99ca34d5c..a9b91b68f 100644 --- a/cmd/podmanV2/containers/pause.go +++ b/cmd/podmanV2/containers/pause.go @@ -15,10 +15,11 @@ import ( var ( pauseDescription = `Pauses one or more running containers. The container name or ID can be used.` pauseCommand = &cobra.Command{ - Use: "pause [flags] CONTAINER [CONTAINER...]", - Short: "Pause all the processes in one or more containers", - Long: pauseDescription, - RunE: pause, + Use: "pause [flags] CONTAINER [CONTAINER...]", + Short: "Pause all the processes in one or more containers", + Long: pauseDescription, + RunE: pause, + PersistentPreRunE: preRunE, Example: `podman pause mywebserver podman pause 860a4b23 podman pause -a`, diff --git a/cmd/podmanV2/containers/restart.go b/cmd/podmanV2/containers/restart.go index 216824389..053891f79 100644 --- a/cmd/podmanV2/containers/restart.go +++ b/cmd/podmanV2/containers/restart.go @@ -18,10 +18,11 @@ var ( A timeout before forcibly stopping can be set, but defaults to 10 seconds.` restartCommand = &cobra.Command{ - Use: "restart [flags] CONTAINER [CONTAINER...]", - Short: "Restart one or more containers", - Long: restartDescription, - RunE: restart, + Use: "restart [flags] CONTAINER [CONTAINER...]", + Short: "Restart one or more containers", + Long: restartDescription, + RunE: restart, + PersistentPreRunE: preRunE, Args: func(cmd *cobra.Command, args []string) error { return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) }, diff --git a/cmd/podmanV2/containers/rm.go b/cmd/podmanV2/containers/rm.go index 916dd2577..75655e4cd 100644 --- a/cmd/podmanV2/containers/rm.go +++ b/cmd/podmanV2/containers/rm.go @@ -19,10 +19,11 @@ var ( Command does not remove images. Running or unusable containers will not be removed without the -f option.` rmCommand = &cobra.Command{ - Use: "rm [flags] CONTAINER [CONTAINER...]", - Short: "Remove one or more containers", - Long: rmDescription, - RunE: rm, + Use: "rm [flags] CONTAINER [CONTAINER...]", + Short: "Remove one or more containers", + Long: rmDescription, + RunE: rm, + PersistentPreRunE: preRunE, Args: func(cmd *cobra.Command, args []string) error { return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) }, diff --git a/cmd/podmanV2/containers/stop.go b/cmd/podmanV2/containers/stop.go index b6d70784d..58d47fd52 100644 --- a/cmd/podmanV2/containers/stop.go +++ b/cmd/podmanV2/containers/stop.go @@ -18,10 +18,11 @@ var ( A timeout to forcibly stop the container can also be set but defaults to 10 seconds otherwise.` stopCommand = &cobra.Command{ - Use: "stop [flags] CONTAINER [CONTAINER...]", - Short: "Stop one or more containers", - Long: stopDescription, - RunE: stop, + Use: "stop [flags] CONTAINER [CONTAINER...]", + Short: "Stop one or more containers", + Long: stopDescription, + RunE: stop, + PersistentPreRunE: preRunE, Args: func(cmd *cobra.Command, args []string) error { return parse.CheckAllLatestAndCIDFile(cmd, args, false, true) }, 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/containers/unpause.go b/cmd/podmanV2/containers/unpause.go index 8b8cde9b2..6a3179f10 100644 --- a/cmd/podmanV2/containers/unpause.go +++ b/cmd/podmanV2/containers/unpause.go @@ -15,10 +15,11 @@ import ( var ( unpauseDescription = `Unpauses one or more previously paused containers. The container name or ID can be used.` unpauseCommand = &cobra.Command{ - Use: "unpause [flags] CONTAINER [CONTAINER...]", - Short: "Unpause the processes in one or more containers", - Long: unpauseDescription, - RunE: unpause, + Use: "unpause [flags] CONTAINER [CONTAINER...]", + Short: "Unpause the processes in one or more containers", + Long: unpauseDescription, + RunE: unpause, + PersistentPreRunE: preRunE, Example: `podman unpause ctrID podman unpause --all`, } diff --git a/cmd/podmanV2/containers/wait.go b/cmd/podmanV2/containers/wait.go index 4367fd9d9..bf3c86200 100644 --- a/cmd/podmanV2/containers/wait.go +++ b/cmd/podmanV2/containers/wait.go @@ -18,10 +18,11 @@ var ( waitDescription = `Block until one or more containers stop and then print their exit codes. ` waitCommand = &cobra.Command{ - Use: "wait [flags] CONTAINER [CONTAINER...]", - Short: "Block on one or more containers", - Long: waitDescription, - RunE: wait, + Use: "wait [flags] CONTAINER [CONTAINER...]", + Short: "Block on one or more containers", + Long: waitDescription, + RunE: wait, + PersistentPreRunE: preRunE, Args: func(cmd *cobra.Command, args []string) error { return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) }, diff --git a/cmd/podmanV2/images/exists.go b/cmd/podmanV2/images/exists.go new file mode 100644 index 000000000..d35d6825e --- /dev/null +++ b/cmd/podmanV2/images/exists.go @@ -0,0 +1,40 @@ +package images + +import ( + "os" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + existsCmd = &cobra.Command{ + Use: "exists IMAGE", + Short: "Check if an image exists in local storage", + Long: `If the named image exists in local storage, podman image exists exits with 0, otherwise the exit code will be 1.`, + Args: cobra.ExactArgs(1), + RunE: exists, + Example: `podman image exists ID + podman image exists IMAGE && podman pull IMAGE`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: existsCmd, + Parent: imageCmd, + }) +} + +func exists(cmd *cobra.Command, args []string) error { + found, err := registry.ImageEngine().Exists(registry.GetContext(), args[0]) + if err != nil { + return err + } + if !found.Value { + os.Exit(1) + } + return nil +} diff --git a/cmd/podmanV2/images/image.go b/cmd/podmanV2/images/image.go index a15c3e826..9fc7b21d1 100644 --- a/cmd/podmanV2/images/image.go +++ b/cmd/podmanV2/images/image.go @@ -28,6 +28,8 @@ func init() { } func preRunE(cmd *cobra.Command, args []string) error { - _, err := registry.NewImageEngine(cmd, args) - return err + if _, err := registry.NewImageEngine(cmd, args); err != nil { + return err + } + return nil } diff --git a/cmd/podmanV2/images/images.go b/cmd/podmanV2/images/images.go index 719846b4c..d00f0996e 100644 --- a/cmd/podmanV2/images/images.go +++ b/cmd/podmanV2/images/images.go @@ -11,13 +11,13 @@ import ( var ( // podman _images_ Alias for podman image _list_ imagesCmd = &cobra.Command{ - Use: strings.Replace(listCmd.Use, "list", "images", 1), - Args: listCmd.Args, - Short: listCmd.Short, - Long: listCmd.Long, - PersistentPreRunE: preRunE, - RunE: listCmd.RunE, - Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1), + Use: strings.Replace(listCmd.Use, "list", "images", 1), + Args: listCmd.Args, + Short: listCmd.Short, + Long: listCmd.Long, + PreRunE: preRunE, + RunE: listCmd.RunE, + Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1), } ) diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go index 4714af3e4..9a5b47299 100644 --- a/cmd/podmanV2/images/list.go +++ b/cmd/podmanV2/images/list.go @@ -212,7 +212,7 @@ func imageListFormat(flags listFlagType) (string, string) { row += "\t{{.Digest}}" } - hdr += "\tID" + hdr += "\tIMAGE ID" if flags.noTrunc { row += "\tsha256:{{.ID}}" } else { diff --git a/cmd/podmanV2/images/prune.go b/cmd/podmanV2/images/prune.go new file mode 100644 index 000000000..6577c458e --- /dev/null +++ b/cmd/podmanV2/images/prune.go @@ -0,0 +1,86 @@ +package images + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + pruneDescription = `Removes all unnamed images from local storage. + + If an image is not being used by a container, it will be removed from the system.` + pruneCmd = &cobra.Command{ + Use: "prune", + Args: cobra.NoArgs, + Short: "Remove unused images", + Long: pruneDescription, + RunE: prune, + Example: `podman image prune`, + } + + pruneOpts = entities.ImagePruneOptions{} + force bool + filter = []string{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: pruneCmd, + Parent: imageCmd, + }) + + flags := pruneCmd.Flags() + flags.BoolVarP(&pruneOpts.All, "all", "a", false, "Remove all unused images, not just dangling ones") + flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation") + flags.StringArrayVar(&filter, "filter", []string{}, "Provide filter values (e.g. 'label=<key>=<value>')") + +} + +func prune(cmd *cobra.Command, args []string) error { + if !force { + reader := bufio.NewReader(os.Stdin) + fmt.Printf(` +WARNING! This will remove all dangling images. +Are you sure you want to continue? [y/N] `) + answer, err := reader.ReadString('\n') + if err != nil { + return errors.Wrapf(err, "error reading input") + } + if strings.ToLower(answer)[0] != 'y' { + return nil + } + } + + // TODO Remove once filter refactor is finished and url.Values rules :) + for _, f := range filter { + t := strings.SplitN(f, "=", 2) + pruneOpts.Filters.Add(t[0], t[1]) + } + + results, err := registry.ImageEngine().Prune(registry.GetContext(), pruneOpts) + if err != nil { + return err + } + + for _, i := range results.Report.Id { + fmt.Println(i) + } + + for _, e := range results.Report.Err { + fmt.Fprint(os.Stderr, e.Error()+"\n") + } + + if results.Size > 0 { + fmt.Fprintf(os.Stdout, "Size: %d\n", results.Size) + } + + return nil +} diff --git a/cmd/podmanV2/images/rm.go b/cmd/podmanV2/images/rm.go new file mode 100644 index 000000000..bb5880de3 --- /dev/null +++ b/cmd/podmanV2/images/rm.go @@ -0,0 +1,70 @@ +package images + +import ( + "fmt" + "os" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + rmDescription = "Removes one or more previously pulled or locally created images." + rmCmd = &cobra.Command{ + Use: "rm [flags] IMAGE [IMAGE...]", + Short: "Removes one or more images from local storage", + Long: rmDescription, + PreRunE: preRunE, + RunE: rm, + Example: `podman image rm imageID + podman image rm --force alpine + podman image rm c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`, + } + + imageOpts = entities.ImageDeleteOptions{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: rmCmd, + Parent: imageCmd, + }) + + flags := rmCmd.Flags() + flags.BoolVarP(&imageOpts.All, "all", "a", false, "Remove all images") + flags.BoolVarP(&imageOpts.Force, "force", "f", false, "Force Removal of the image") +} + +func rm(cmd *cobra.Command, args []string) error { + + if len(args) < 1 && !imageOpts.All { + return errors.Errorf("image name or ID must be specified") + } + if len(args) > 0 && imageOpts.All { + return errors.Errorf("when using the --all switch, you may not pass any images names or IDs") + } + + report, err := registry.ImageEngine().Delete(registry.GetContext(), args, imageOpts) + if err != nil { + switch { + case report != nil && report.ImageNotFound != nil: + fmt.Fprintln(os.Stderr, err.Error()) + registry.SetExitCode(2) + case report != nil && report.ImageInUse != nil: + fmt.Fprintln(os.Stderr, err.Error()) + default: + return err + } + } + + for _, u := range report.Untagged { + fmt.Println("Untagged: " + u) + } + for _, d := range report.Deleted { + fmt.Println("Deleted: " + d) + } + return nil +} diff --git a/cmd/podmanV2/images/rmi.go b/cmd/podmanV2/images/rmi.go new file mode 100644 index 000000000..7f9297bc9 --- /dev/null +++ b/cmd/podmanV2/images/rmi.go @@ -0,0 +1,30 @@ +package images + +import ( + "strings" + + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + rmiCmd = &cobra.Command{ + Use: strings.Replace(rmCmd.Use, "rm ", "rmi ", 1), + Args: rmCmd.Args, + Short: rmCmd.Short, + Long: rmCmd.Long, + PreRunE: rmCmd.PreRunE, + RunE: rmCmd.RunE, + Example: strings.Replace(rmCmd.Example, "podman image rm", "podman rmi", -1), + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: rmiCmd, + }) + rmiCmd.SetHelpTemplate(registry.HelpTemplate()) + rmiCmd.SetUsageTemplate(registry.UsageTemplate()) +} diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go index dc96c26d0..bd9fbb25e 100644 --- a/cmd/podmanV2/main.go +++ b/cmd/podmanV2/main.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" "reflect" "runtime" @@ -16,7 +15,6 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/domain/entities" "github.com/sirupsen/logrus" - "github.com/spf13/cobra" ) func init() { @@ -24,10 +22,7 @@ func init() { logrus.Errorf(err.Error()) os.Exit(1) } - initCobra() -} -func initCobra() { switch runtime.GOOS { case "darwin": fallthrough @@ -46,12 +41,9 @@ func initCobra() { registry.EngineOptions.EngineMode = entities.TunnelMode } } - - cobra.OnInitialize(func() {}) } func main() { - fmt.Fprintf(os.Stderr, "Number of commands: %d\n", len(registry.Commands)) for _, c := range registry.Commands { if Contains(registry.EngineOptions.EngineMode, c.Mode) { parent := rootCmd 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 +} diff --git a/cmd/podmanV2/pods/kill.go b/cmd/podmanV2/pods/kill.go new file mode 100644 index 000000000..06cca916c --- /dev/null +++ b/cmd/podmanV2/pods/kill.go @@ -0,0 +1,68 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podKillDescription = `Signals are sent to the main process of each container inside the specified pod. + + The default signal is SIGKILL, or any signal specified with option --signal.` + killCommand = &cobra.Command{ + Use: "kill [flags] POD [POD...]", + Short: "Send the specified signal or SIGKILL to containers in pod", + Long: podKillDescription, + RunE: kill, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod kill podID + podman pod kill --signal TERM mywebserver + podman pod kill --latest`, + } +) + +var ( + killOpts entities.PodKillOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: killCommand, + Parent: podCmd, + }) + flags := killCommand.Flags() + flags.BoolVarP(&killOpts.All, "all", "a", false, "Kill all containers in all pods") + flags.BoolVarP(&killOpts.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + flags.StringVarP(&killOpts.Signal, "signal", "s", "KILL", "Signal to send to the containers in the pod") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } + +} +func kill(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodKill(context.Background(), args, killOpts) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/pause.go b/cmd/podmanV2/pods/pause.go new file mode 100644 index 000000000..dc86e534d --- /dev/null +++ b/cmd/podmanV2/pods/pause.go @@ -0,0 +1,66 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podPauseDescription = `The pod name or ID can be used. + + All running containers within each specified pod will then be paused.` + pauseCommand = &cobra.Command{ + Use: "pause [flags] POD [POD...]", + Short: "Pause one or more pods", + Long: podPauseDescription, + RunE: pause, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod pause podID1 podID2 + podman pod pause --latest + podman pod pause --all`, + } +) + +var ( + pauseOptions entities.PodPauseOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: pauseCommand, + Parent: podCmd, + }) + flags := pauseCommand.Flags() + flags.BoolVarP(&pauseOptions.All, "all", "a", false, "Pause all running pods") + flags.BoolVarP(&pauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} +func pause(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodPause(context.Background(), args, pauseOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/restart.go b/cmd/podmanV2/pods/restart.go new file mode 100644 index 000000000..1c8709704 --- /dev/null +++ b/cmd/podmanV2/pods/restart.go @@ -0,0 +1,68 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podRestartDescription = `The pod ID or name can be used. + + All of the containers within each of the specified pods will be restarted. If a container in a pod is not currently running it will be started.` + restartCommand = &cobra.Command{ + Use: "restart [flags] POD [POD...]", + Short: "Restart one or more pods", + Long: podRestartDescription, + RunE: restart, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod restart podID1 podID2 + podman pod restart --latest + podman pod restart --all`, + } +) + +var ( + restartOptions = entities.PodRestartOptions{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: restartCommand, + Parent: podCmd, + }) + + flags := restartCommand.Flags() + flags.BoolVarP(&restartOptions.All, "all", "a", false, "Restart all running pods") + flags.BoolVarP(&restartOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} + +func restart(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodRestart(context.Background(), args, restartOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/rm.go b/cmd/podmanV2/pods/rm.go new file mode 100644 index 000000000..b43dd2d6c --- /dev/null +++ b/cmd/podmanV2/pods/rm.go @@ -0,0 +1,71 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podRmDescription = fmt.Sprintf(`podman rm will remove one or more stopped pods and their containers from the host. + + The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed.`) + rmCommand = &cobra.Command{ + Use: "rm [flags] POD [POD...]", + Short: "Remove one or more pods", + Long: podRmDescription, + RunE: rm, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod rm mywebserverpod + podman pod rm -f 860a4b23 + podman pod rm -f -a`, + } +) + +var ( + rmOptions = entities.PodRmOptions{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: rmCommand, + Parent: podCmd, + }) + + flags := rmCommand.Flags() + flags.BoolVarP(&rmOptions.All, "all", "a", false, "Restart all running pods") + flags.BoolVarP(&rmOptions.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false") + flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") + flags.BoolVarP(&rmOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + _ = flags.MarkHidden("ignore") + } +} + +func rm(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodRm(context.Background(), args, rmOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if r.Err == nil { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Err) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/start.go b/cmd/podmanV2/pods/start.go new file mode 100644 index 000000000..11ac312f9 --- /dev/null +++ b/cmd/podmanV2/pods/start.go @@ -0,0 +1,68 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podStartDescription = `The pod name or ID can be used. + + All containers defined in the pod will be started.` + startCommand = &cobra.Command{ + Use: "start [flags] POD [POD...]", + Short: "Start one or more pods", + Long: podStartDescription, + RunE: start, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod start podID + podman pod start --latest + podman pod start --all`, + } +) + +var ( + startOptions = entities.PodStartOptions{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: startCommand, + Parent: podCmd, + }) + + flags := startCommand.Flags() + flags.BoolVarP(&startOptions.All, "all", "a", false, "Restart all running pods") + flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Restart the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} + +func start(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodStart(context.Background(), args, startOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/stop.go b/cmd/podmanV2/pods/stop.go new file mode 100644 index 000000000..2b61850e2 --- /dev/null +++ b/cmd/podmanV2/pods/stop.go @@ -0,0 +1,78 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podStopDescription = `The pod name or ID can be used. + + This command will stop all running containers in each of the specified pods.` + + stopCommand = &cobra.Command{ + Use: "stop [flags] POD [POD...]", + Short: "Stop one or more pods", + Long: podStopDescription, + RunE: stop, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod stop mywebserverpod + podman pod stop --latest + podman pod stop --timeout 0 490eb 3557fb`, + } +) + +var ( + stopOptions = entities.PodStopOptions{ + Timeout: -1, + } + timeout uint +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: stopCommand, + Parent: podCmd, + }) + flags := stopCommand.Flags() + flags.BoolVarP(&stopOptions.All, "all", "a", false, "Stop all running pods") + flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") + flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Stop the latest pod podman is aware of") + flags.UintVarP(&timeout, "timeout", "t", 0, "Seconds to wait for pod stop before killing the container") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + _ = flags.MarkHidden("ignore") + + } +} + +func stop(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + if cmd.Flag("timeout").Changed { + stopOptions.Timeout = int(timeout) + } + responses, err := registry.ContainerEngine().PodStop(context.Background(), args, stopOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/pods/unpause.go b/cmd/podmanV2/pods/unpause.go new file mode 100644 index 000000000..2de7b964f --- /dev/null +++ b/cmd/podmanV2/pods/unpause.go @@ -0,0 +1,66 @@ +package pods + +import ( + "context" + "fmt" + + "github.com/containers/libpod/cmd/podmanV2/parse" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/spf13/cobra" +) + +var ( + podUnpauseDescription = `The podman unpause command will unpause all "paused" containers assigned to the pod. + + The pod name or ID can be used.` + unpauseCommand = &cobra.Command{ + Use: "unpause [flags] POD [POD...]", + Short: "Unpause one or more pods", + Long: podUnpauseDescription, + RunE: unpause, + Args: func(cmd *cobra.Command, args []string) error { + return parse.CheckAllLatestAndCIDFile(cmd, args, false, false) + }, + Example: `podman pod unpause podID1 podID2 + podman pod unpause --all + podman pod unpause --latest`, + } +) + +var ( + unpauseOptions entities.PodunpauseOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: unpauseCommand, + Parent: podCmd, + }) + flags := unpauseCommand.Flags() + flags.BoolVarP(&unpauseOptions.All, "all", "a", false, "Pause all running pods") + flags.BoolVarP(&unpauseOptions.Latest, "latest", "l", false, "Act on the latest pod podman is aware of") + if registry.IsRemote() { + _ = flags.MarkHidden("latest") + } +} +func unpause(cmd *cobra.Command, args []string) error { + var ( + errs utils.OutputErrors + ) + responses, err := registry.ContainerEngine().PodUnpause(context.Background(), args, unpauseOptions) + if err != nil { + return err + } + // in the cli, first we print out all the successful attempts + for _, r := range responses { + if len(r.Errs) == 0 { + fmt.Println(r.Id) + } else { + errs = append(errs, r.Errs...) + } + } + return errs.PrintErrors() +} diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go index f0650a7cf..5cdb8a840 100644 --- a/cmd/podmanV2/registry/registry.go +++ b/cmd/podmanV2/registry/registry.go @@ -10,6 +10,8 @@ import ( "github.com/spf13/cobra" ) +type CobraFuncs func(cmd *cobra.Command, args []string) error + type CliCommand struct { Mode []entities.EngineMode Command *cobra.Command diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go index 68e8b4531..cb4cb4e00 100644 --- a/cmd/podmanV2/root.go +++ b/cmd/podmanV2/root.go @@ -2,24 +2,35 @@ package main import ( "fmt" + "log/syslog" "os" "path" "github.com/containers/libpod/cmd/podmanV2/registry" "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/version" + "github.com/sirupsen/logrus" + logrusSyslog "github.com/sirupsen/logrus/hooks/syslog" "github.com/spf13/cobra" ) -var rootCmd = &cobra.Command{ - Use: path.Base(os.Args[0]), - Long: "Manage pods, containers and images", - SilenceUsage: true, - SilenceErrors: true, - TraverseChildren: true, - RunE: registry.SubCommandExists, - Version: version.Version, -} +var ( + rootCmd = &cobra.Command{ + Use: path.Base(os.Args[0]), + Long: "Manage pods, containers and images", + SilenceUsage: true, + SilenceErrors: true, + TraverseChildren: true, + PersistentPreRunE: preRunE, + RunE: registry.SubCommandExists, + Version: version.Version, + } + + logLevels = entities.NewStringSet("debug", "info", "warn", "error", "fatal", "panic") + logLevel = "error" + useSyslog bool +) func init() { // Override default --help information of `--version` global flag} @@ -28,6 +39,49 @@ func init() { rootCmd.PersistentFlags().BoolVar(&dummyVersion, "version", false, "Version of Podman") rootCmd.PersistentFlags().StringVarP(®istry.EngineOptions.Uri, "remote", "r", "", "URL to access Podman service") rootCmd.PersistentFlags().StringSliceVar(®istry.EngineOptions.Identities, "identity", []string{}, "path to SSH identity file") + rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "error", fmt.Sprintf("Log messages above specified level (%s)", logLevels.String())) + rootCmd.PersistentFlags().BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)") + + cobra.OnInitialize( + logging, + syslogHook, + ) +} + +func preRunE(cmd *cobra.Command, args []string) error { + cmd.SetHelpTemplate(registry.HelpTemplate()) + cmd.SetUsageTemplate(registry.UsageTemplate()) + return nil +} + +func logging() { + if !logLevels.Contains(logLevel) { + fmt.Fprintf(os.Stderr, "Log Level \"%s\" is not supported, choose from: %s\n", logLevel, logLevels.String()) + os.Exit(1) + } + + level, err := logrus.ParseLevel(logLevel) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + logrus.SetLevel(level) + + if logrus.IsLevelEnabled(logrus.InfoLevel) { + logrus.Infof("%s filtering at log level %s", os.Args[0], logrus.GetLevel()) + } +} + +func syslogHook() { + if useSyslog { + hook, err := logrusSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "") + if err != nil { + logrus.WithError(err).Error("Failed to initialize syslog hook") + } + if err == nil { + logrus.AddHook(hook) + } + } } func Execute() { |