From 6ed3b8841f00d2aa1f86678c079a119e027b0d66 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Mon, 4 May 2020 10:52:29 -0400 Subject: Fix errors found when comparing podman v1 --help versus V2 Mainly add missing commands to podman image, podman containers, podman system Also fix some informations messages and descriptions. Signed-off-by: Daniel J Walsh --- cmd/podman/build.go | 470 -------------------------------- cmd/podman/containers/logs.go | 2 +- cmd/podman/containers/prune.go | 6 +- cmd/podman/containers/rm.go | 2 +- cmd/podman/healthcheck/healthcheck.go | 4 +- cmd/podman/images/build.go | 491 ++++++++++++++++++++++++++++++++++ cmd/podman/images/diff.go | 5 +- cmd/podman/images/history.go | 21 +- cmd/podman/images/import.go | 23 +- cmd/podman/images/load.go | 19 +- cmd/podman/images/save.go | 23 +- cmd/podman/images/tag.go | 16 ++ cmd/podman/images/untag.go | 16 ++ cmd/podman/pods/pod.go | 2 +- cmd/podman/pods/ps.go | 2 +- cmd/podman/pods/stats.go | 2 +- cmd/podman/pods/top.go | 2 +- cmd/podman/root.go | 2 +- cmd/podman/system/info.go | 22 +- 19 files changed, 642 insertions(+), 488 deletions(-) delete mode 100644 cmd/podman/build.go create mode 100644 cmd/podman/images/build.go (limited to 'cmd/podman') diff --git a/cmd/podman/build.go b/cmd/podman/build.go deleted file mode 100644 index 43a2f7ab5..000000000 --- a/cmd/podman/build.go +++ /dev/null @@ -1,470 +0,0 @@ -package main - -import ( - "os" - "path/filepath" - "strings" - - "github.com/containers/buildah" - "github.com/containers/buildah/imagebuildah" - buildahCLI "github.com/containers/buildah/pkg/cli" - "github.com/containers/buildah/pkg/parse" - "github.com/containers/libpod/cmd/podman/registry" - "github.com/containers/libpod/cmd/podman/utils" - "github.com/containers/libpod/pkg/domain/entities" - "github.com/docker/go-units" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -// buildFlagsWrapper are local to cmd/ as the build code is using Buildah-internal -// types. Hence, after parsing, we are converting buildFlagsWrapper to the entities' -// options which essentially embed the Buildah types. -type buildFlagsWrapper struct { - // Buildah stuff first - buildahCLI.BudResults - buildahCLI.LayerResults - buildahCLI.FromAndBudResults - buildahCLI.NameSpaceResults - buildahCLI.UserNSResults - - // SquashAll squashes all layers into a single layer. - SquashAll bool -} - -var ( - // Command: podman _diff_ Object_ID - buildDescription = "Builds an OCI or Docker image using instructions from one or more Containerfiles and a specified build context directory." - buildCmd = &cobra.Command{ - Use: "build [flags] [CONTEXT]", - Short: "Build an image using instructions from Containerfiles", - Long: buildDescription, - TraverseChildren: true, - RunE: build, - Example: `podman build . - podman build --creds=username:password -t imageName -f Containerfile.simple . - podman build --layers --force-rm --tag imageName .`, - } - - buildOpts = buildFlagsWrapper{} -) - -// useLayers returns false if BUILDAH_LAYERS is set to "0" or "false" -// otherwise it returns true -func useLayers() string { - layers := os.Getenv("BUILDAH_LAYERS") - if strings.ToLower(layers) == "false" || layers == "0" { - return "false" - } - return "true" -} - -func init() { - registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: buildCmd, - }) - flags := buildCmd.Flags() - - // Podman flags - flags.BoolVarP(&buildOpts.SquashAll, "squash-all", "", false, "Squash all layers into a single layer") - - // Bud flags - budFlags := buildahCLI.GetBudFlags(&buildOpts.BudResults) - // --pull flag - flag := budFlags.Lookup("pull") - if err := flag.Value.Set("true"); err != nil { - logrus.Errorf("unable to set --pull to true: %v", err) - } - flag.DefValue = "true" - flags.AddFlagSet(&budFlags) - - // Layer flags - layerFlags := buildahCLI.GetLayerFlags(&buildOpts.LayerResults) - // --layers flag - flag = layerFlags.Lookup("layers") - useLayersVal := useLayers() - if err := flag.Value.Set(useLayersVal); err != nil { - logrus.Errorf("unable to set --layers to %v: %v", useLayersVal, err) - } - flag.DefValue = useLayersVal - // --force-rm flag - flag = layerFlags.Lookup("force-rm") - if err := flag.Value.Set("true"); err != nil { - logrus.Errorf("unable to set --force-rm to true: %v", err) - } - flag.DefValue = "true" - flags.AddFlagSet(&layerFlags) - - // FromAndBud flags - fromAndBudFlags, err := buildahCLI.GetFromAndBudFlags(&buildOpts.FromAndBudResults, &buildOpts.UserNSResults, &buildOpts.NameSpaceResults) - if err != nil { - logrus.Errorf("error setting up build flags: %v", err) - os.Exit(1) - } - flags.AddFlagSet(&fromAndBudFlags) -} - -// build executes the build command. -func build(cmd *cobra.Command, args []string) error { - if (cmd.Flags().Changed("squash") && cmd.Flags().Changed("layers")) || - (cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("layers")) || - (cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("squash")) { - return errors.New("cannot specify --squash, --squash-all and --layers options together") - } - - contextDir, containerFiles, err := extractContextAndFiles(args, buildOpts.File) - if err != nil { - return err - } - - ie, err := registry.NewImageEngine(cmd, args) - if err != nil { - return err - } - - apiBuildOpts, err := buildFlagsWrapperToOptions(cmd, contextDir, &buildOpts) - if err != nil { - return err - } - - _, err = ie.Build(registry.GetContext(), containerFiles, *apiBuildOpts) - return err -} - -// extractContextAndFiles parses args and files to extract a context directory -// and {Container,Docker}files. -// -// TODO: this was copied and altered from the v1 client which in turn was -// copied and altered from the Buildah code. Ideally, all of this code should -// be cleanly consolidated into a package that is shared between Buildah and -// Podman. -func extractContextAndFiles(args, files []string) (string, []string, error) { - // Extract container files from the CLI (i.e., --file/-f) first. - var containerFiles []string - for _, f := range files { - if f == "-" { - containerFiles = append(containerFiles, "/dev/stdin") - } else { - containerFiles = append(containerFiles, f) - } - } - - // Determine context directory. - var contextDir string - if len(args) > 0 { - // The context directory could be a URL. Try to handle that. - tempDir, subDir, err := imagebuildah.TempDirForURL("", "buildah", args[0]) - if err != nil { - return "", nil, errors.Wrapf(err, "error prepping temporary context directory") - } - if tempDir != "" { - // We had to download it to a temporary directory. - // Delete it later. - defer func() { - if err = os.RemoveAll(tempDir); err != nil { - logrus.Errorf("error removing temporary directory %q: %v", contextDir, err) - } - }() - contextDir = filepath.Join(tempDir, subDir) - } else { - // Nope, it was local. Use it as is. - absDir, err := filepath.Abs(args[0]) - if err != nil { - return "", nil, errors.Wrapf(err, "error determining path to directory %q", args[0]) - } - contextDir = absDir - } - } else { - // No context directory or URL was specified. Try to use the home of - // the first locally-available Containerfile. - for i := range containerFiles { - if strings.HasPrefix(containerFiles[i], "http://") || - strings.HasPrefix(containerFiles[i], "https://") || - strings.HasPrefix(containerFiles[i], "git://") || - strings.HasPrefix(containerFiles[i], "github.com/") { - continue - } - absFile, err := filepath.Abs(containerFiles[i]) - if err != nil { - return "", nil, errors.Wrapf(err, "error determining path to file %q", containerFiles[i]) - } - contextDir = filepath.Dir(absFile) - break - } - } - - if contextDir == "" { - return "", nil, errors.Errorf("no context directory and no Containerfile specified") - } - if !utils.IsDir(contextDir) { - return "", nil, errors.Errorf("context must be a directory: %q", contextDir) - } - if len(containerFiles) == 0 { - if utils.FileExists(filepath.Join(contextDir, "Containerfile")) { - containerFiles = append(containerFiles, filepath.Join(contextDir, "Containerfile")) - } else { - containerFiles = append(containerFiles, filepath.Join(contextDir, "Dockerfile")) - } - } - - return contextDir, containerFiles, nil -} - -// buildFlagsWrapperToOptions converts the local build flags to the build options used -// in the API which embed Buildah types used across the build code. Doing the -// conversion here prevents the API from doing that (redundantly). -// -// TODO: this code should really be in Buildah. -func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buildFlagsWrapper) (*entities.BuildOptions, error) { - output := "" - tags := []string{} - if c.Flag("tag").Changed { - tags = flags.Tag - if len(tags) > 0 { - output = tags[0] - tags = tags[1:] - } - } - - pullPolicy := imagebuildah.PullNever - if flags.Pull { - pullPolicy = imagebuildah.PullIfMissing - } - if flags.PullAlways { - pullPolicy = imagebuildah.PullAlways - } - - args := make(map[string]string) - if c.Flag("build-arg").Changed { - for _, arg := range flags.BuildArg { - av := strings.SplitN(arg, "=", 2) - if len(av) > 1 { - args[av[0]] = av[1] - } else { - delete(args, av[0]) - } - } - } - // Check to see if the BUILDAH_LAYERS environment variable is set and - // override command-line. - if _, ok := os.LookupEnv("BUILDAH_LAYERS"); ok { - flags.Layers = true - } - - // `buildah bud --layers=false` acts like `docker build --squash` does. - // That is all of the new layers created during the build process are - // condensed into one, any layers present prior to this build are - // retained without condensing. `buildah bud --squash` squashes both - // new and old layers down into one. Translate Podman commands into - // Buildah. Squash invoked, retain old layers, squash new layers into - // one. - if c.Flags().Changed("squash") && buildOpts.Squash { - flags.Squash = false - flags.Layers = false - } - // Squash-all invoked, squash both new and old layers into one. - if c.Flags().Changed("squash-all") { - flags.Squash = true - flags.Layers = false - } - - var stdin, stdout, stderr, reporter *os.File - stdin = os.Stdin - stdout = os.Stdout - stderr = os.Stderr - reporter = os.Stderr - - if c.Flag("logfile").Changed { - f, err := os.OpenFile(flags.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) - if err != nil { - return nil, errors.Errorf("error opening logfile %q: %v", flags.Logfile, err) - } - defer f.Close() - logrus.SetOutput(f) - stdout = f - stderr = f - reporter = f - } - - var memoryLimit, memorySwap int64 - var err error - if c.Flags().Changed("memory") { - memoryLimit, err = units.RAMInBytes(flags.Memory) - if err != nil { - return nil, err - } - } - - if c.Flags().Changed("memory-swap") { - memorySwap, err = units.RAMInBytes(flags.MemorySwap) - if err != nil { - return nil, err - } - } - - nsValues, err := getNsValues(flags) - if err != nil { - return nil, err - } - - networkPolicy := buildah.NetworkDefault - for _, ns := range nsValues { - if ns.Name == "none" { - networkPolicy = buildah.NetworkDisabled - break - } else if !filepath.IsAbs(ns.Path) { - networkPolicy = buildah.NetworkEnabled - break - } - } - - // `buildah bud --layers=false` acts like `docker build --squash` does. - // That is all of the new layers created during the build process are - // condensed into one, any layers present prior to this build are retained - // without condensing. `buildah bud --squash` squashes both new and old - // layers down into one. Translate Podman commands into Buildah. - // Squash invoked, retain old layers, squash new layers into one. - if c.Flags().Changed("squash") && flags.Squash { - flags.Squash = false - flags.Layers = false - } - // Squash-all invoked, squash both new and old layers into one. - if c.Flags().Changed("squash-all") { - flags.Squash = true - flags.Layers = false - } - - compression := imagebuildah.Gzip - if flags.DisableCompression { - compression = imagebuildah.Uncompressed - } - - isolation, err := parse.IsolationOption(flags.Isolation) - if err != nil { - return nil, errors.Wrapf(err, "error parsing ID mapping options") - } - - usernsOption, idmappingOptions, err := parse.IDMappingOptions(c, isolation) - if err != nil { - return nil, errors.Wrapf(err, "error parsing ID mapping options") - } - nsValues = append(nsValues, usernsOption...) - - systemContext, err := parse.SystemContextFromOptions(c) - if err != nil { - return nil, errors.Wrapf(err, "error building system context") - } - - format := "" - flags.Format = strings.ToLower(flags.Format) - switch { - case strings.HasPrefix(flags.Format, buildah.OCI): - format = buildah.OCIv1ImageManifest - case strings.HasPrefix(flags.Format, buildah.DOCKER): - format = buildah.Dockerv2ImageManifest - default: - return nil, errors.Errorf("unrecognized image type %q", flags.Format) - } - - runtimeFlags := []string{} - for _, arg := range flags.RuntimeFlags { - runtimeFlags = append(runtimeFlags, "--"+arg) - } - - // FIXME: the code below needs to be enabled (and adjusted) once the - // global/root flags are supported. - - // conf, err := runtime.GetConfig() - // if err != nil { - // return err - // } - // if conf != nil && conf.Engine.CgroupManager == config.SystemdCgroupsManager { - // runtimeFlags = append(runtimeFlags, "--systemd-cgroup") - // } - - opts := imagebuildah.BuildOptions{ - AddCapabilities: flags.CapAdd, - AdditionalTags: tags, - Annotations: flags.Annotation, - Architecture: flags.Arch, - Args: args, - BlobDirectory: flags.BlobCache, - CNIConfigDir: flags.CNIConfigDir, - CNIPluginPath: flags.CNIPlugInPath, - CommonBuildOpts: &buildah.CommonBuildOptions{ - AddHost: flags.AddHost, - CgroupParent: flags.CgroupParent, - CPUPeriod: flags.CPUPeriod, - CPUQuota: flags.CPUQuota, - CPUShares: flags.CPUShares, - CPUSetCPUs: flags.CPUSetCPUs, - CPUSetMems: flags.CPUSetMems, - Memory: memoryLimit, - MemorySwap: memorySwap, - ShmSize: flags.ShmSize, - Ulimit: flags.Ulimit, - Volumes: flags.Volumes, - }, - Compression: compression, - ConfigureNetwork: networkPolicy, - ContextDirectory: contextDir, - // DefaultMountsFilePath: FIXME: this requires global flags to be working! - Devices: flags.Devices, - DropCapabilities: flags.CapDrop, - Err: stderr, - ForceRmIntermediateCtrs: flags.ForceRm, - IDMappingOptions: idmappingOptions, - IIDFile: flags.Iidfile, - In: stdin, - Isolation: isolation, - Labels: flags.Label, - Layers: flags.Layers, - NamespaceOptions: nsValues, - NoCache: flags.NoCache, - OS: flags.OS, - Out: stdout, - Output: output, - OutputFormat: format, - PullPolicy: pullPolicy, - Quiet: flags.Quiet, - RemoveIntermediateCtrs: flags.Rm, - ReportWriter: reporter, - RuntimeArgs: runtimeFlags, - SignBy: flags.SignBy, - SignaturePolicyPath: flags.SignaturePolicy, - Squash: flags.Squash, - SystemContext: systemContext, - Target: flags.Target, - TransientMounts: flags.Volumes, - } - - return &entities.BuildOptions{BuildOptions: opts}, nil -} - -func getNsValues(flags *buildFlagsWrapper) ([]buildah.NamespaceOption, error) { - var ret []buildah.NamespaceOption - if flags.Network != "" { - switch { - case flags.Network == "host": - ret = append(ret, buildah.NamespaceOption{ - Name: string(specs.NetworkNamespace), - Host: true, - }) - case flags.Network == "container": - ret = append(ret, buildah.NamespaceOption{ - Name: string(specs.NetworkNamespace), - }) - case flags.Network[0] == '/': - ret = append(ret, buildah.NamespaceOption{ - Name: string(specs.NetworkNamespace), - Path: flags.Network, - }) - default: - return nil, errors.Errorf("unsupported configuration network=%s", flags.Network) - } - } - return ret, nil -} diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go index 5dec71fdd..2b8c3ed5f 100644 --- a/cmd/podman/containers/logs.go +++ b/cmd/podman/containers/logs.go @@ -27,7 +27,7 @@ var ( ` logsCommand = &cobra.Command{ Use: "logs [flags] CONTAINER [CONTAINER...]", - Short: "Fetch the logs of one or more container", + Short: "Fetch the logs of one or more containers", Long: logsDescription, RunE: logs, Example: `podman logs ctrID diff --git a/cmd/podman/containers/prune.go b/cmd/podman/containers/prune.go index d4bea48f9..38168a6e4 100644 --- a/cmd/podman/containers/prune.go +++ b/cmd/podman/containers/prune.go @@ -18,10 +18,10 @@ import ( var ( pruneDescription = fmt.Sprintf(`podman container prune - Removes all stopped | exited containers`) + Removes all non running containers`) pruneCommand = &cobra.Command{ Use: "prune [flags]", - Short: "Remove all stopped | exited containers", + Short: "Remove all non running containers", Long: pruneDescription, RunE: prune, Example: `podman container prune`, @@ -50,7 +50,7 @@ func prune(cmd *cobra.Command, args []string) error { } if !force { reader := bufio.NewReader(os.Stdin) - fmt.Println("WARNING! This will remove all stopped containers.") + fmt.Println("WARNING! This will remove all non running containers.") fmt.Print("Are you sure you want to continue? [y/N] ") answer, err := reader.ReadString('\n') if err != nil { diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index 96549cead..2a0f9cc6a 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -35,7 +35,7 @@ var ( containerRmCommand = &cobra.Command{ Use: rmCommand.Use, - Short: rmCommand.Use, + Short: rmCommand.Short, Long: rmCommand.Long, RunE: rmCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/healthcheck/healthcheck.go b/cmd/podman/healthcheck/healthcheck.go index ce90dba31..f48701624 100644 --- a/cmd/podman/healthcheck/healthcheck.go +++ b/cmd/podman/healthcheck/healthcheck.go @@ -11,8 +11,8 @@ var ( // Command: healthcheck healthCmd = &cobra.Command{ Use: "healthcheck", - Short: "Manage Healthcheck", - Long: "Manage Healthcheck", + Short: "Manage health checks on containers", + Long: "Run health checks on containers", TraverseChildren: true, RunE: validate.SubCommandExists, } diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go new file mode 100644 index 000000000..06a7efd25 --- /dev/null +++ b/cmd/podman/images/build.go @@ -0,0 +1,491 @@ +package images + +import ( + "os" + "path/filepath" + "strings" + + "github.com/containers/buildah" + "github.com/containers/buildah/imagebuildah" + buildahCLI "github.com/containers/buildah/pkg/cli" + "github.com/containers/buildah/pkg/parse" + "github.com/containers/libpod/cmd/podman/registry" + "github.com/containers/libpod/cmd/podman/utils" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/docker/go-units" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// buildFlagsWrapper are local to cmd/ as the build code is using Buildah-internal +// types. Hence, after parsing, we are converting buildFlagsWrapper to the entities' +// options which essentially embed the Buildah types. +type buildFlagsWrapper struct { + // Buildah stuff first + buildahCLI.BudResults + buildahCLI.LayerResults + buildahCLI.FromAndBudResults + buildahCLI.NameSpaceResults + buildahCLI.UserNSResults + + // SquashAll squashes all layers into a single layer. + SquashAll bool +} + +var ( + // Command: podman _diff_ Object_ID + buildDescription = "Builds an OCI or Docker image using instructions from one or more Containerfiles and a specified build context directory." + buildCmd = &cobra.Command{ + Use: "build [flags] [CONTEXT]", + Short: "Build an image using instructions from Containerfiles", + Long: buildDescription, + TraverseChildren: true, + RunE: build, + Example: `podman build . + podman build --creds=username:password -t imageName -f Containerfile.simple . + podman build --layers --force-rm --tag imageName .`, + } + + imageBuildCmd = &cobra.Command{ + Args: buildCmd.Args, + Use: buildCmd.Use, + Short: buildCmd.Short, + Long: buildCmd.Long, + RunE: buildCmd.RunE, + Example: `podman image build . + podman image build --creds=username:password -t imageName -f Containerfile.simple . + podman image build --layers --force-rm --tag imageName .`, + } + + buildOpts = buildFlagsWrapper{} +) + +// useLayers returns false if BUILDAH_LAYERS is set to "0" or "false" +// otherwise it returns true +func useLayers() string { + layers := os.Getenv("BUILDAH_LAYERS") + if strings.ToLower(layers) == "false" || layers == "0" { + return "false" + } + return "true" +} + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: buildCmd, + }) + buildFlags(buildCmd.Flags()) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imageBuildCmd, + Parent: imageCmd, + }) + buildFlags(imageBuildCmd.Flags()) +} + +func buildFlags(flags *pflag.FlagSet) { + // Podman flags + flags.BoolVarP(&buildOpts.SquashAll, "squash-all", "", false, "Squash all layers into a single layer") + + // Bud flags + budFlags := buildahCLI.GetBudFlags(&buildOpts.BudResults) + // --pull flag + flag := budFlags.Lookup("pull") + if err := flag.Value.Set("true"); err != nil { + logrus.Errorf("unable to set --pull to true: %v", err) + } + flag.DefValue = "true" + flags.AddFlagSet(&budFlags) + + // Layer flags + layerFlags := buildahCLI.GetLayerFlags(&buildOpts.LayerResults) + // --layers flag + flag = layerFlags.Lookup("layers") + useLayersVal := useLayers() + if err := flag.Value.Set(useLayersVal); err != nil { + logrus.Errorf("unable to set --layers to %v: %v", useLayersVal, err) + } + flag.DefValue = useLayersVal + // --force-rm flag + flag = layerFlags.Lookup("force-rm") + if err := flag.Value.Set("true"); err != nil { + logrus.Errorf("unable to set --force-rm to true: %v", err) + } + flag.DefValue = "true" + flags.AddFlagSet(&layerFlags) + + // FromAndBud flags + fromAndBudFlags, err := buildahCLI.GetFromAndBudFlags(&buildOpts.FromAndBudResults, &buildOpts.UserNSResults, &buildOpts.NameSpaceResults) + if err != nil { + logrus.Errorf("error setting up build flags: %v", err) + os.Exit(1) + } + flags.AddFlagSet(&fromAndBudFlags) +} + +// build executes the build command. +func build(cmd *cobra.Command, args []string) error { + if (cmd.Flags().Changed("squash") && cmd.Flags().Changed("layers")) || + (cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("layers")) || + (cmd.Flags().Changed("squash-all") && cmd.Flags().Changed("squash")) { + return errors.New("cannot specify --squash, --squash-all and --layers options together") + } + + contextDir, containerFiles, err := extractContextAndFiles(args, buildOpts.File) + if err != nil { + return err + } + + ie, err := registry.NewImageEngine(cmd, args) + if err != nil { + return err + } + + apiBuildOpts, err := buildFlagsWrapperToOptions(cmd, contextDir, &buildOpts) + if err != nil { + return err + } + + _, err = ie.Build(registry.GetContext(), containerFiles, *apiBuildOpts) + return err +} + +// extractContextAndFiles parses args and files to extract a context directory +// and {Container,Docker}files. +// +// TODO: this was copied and altered from the v1 client which in turn was +// copied and altered from the Buildah code. Ideally, all of this code should +// be cleanly consolidated into a package that is shared between Buildah and +// Podman. +func extractContextAndFiles(args, files []string) (string, []string, error) { + // Extract container files from the CLI (i.e., --file/-f) first. + var containerFiles []string + for _, f := range files { + if f == "-" { + containerFiles = append(containerFiles, "/dev/stdin") + } else { + containerFiles = append(containerFiles, f) + } + } + + // Determine context directory. + var contextDir string + if len(args) > 0 { + // The context directory could be a URL. Try to handle that. + tempDir, subDir, err := imagebuildah.TempDirForURL("", "buildah", args[0]) + if err != nil { + return "", nil, errors.Wrapf(err, "error prepping temporary context directory") + } + if tempDir != "" { + // We had to download it to a temporary directory. + // Delete it later. + defer func() { + if err = os.RemoveAll(tempDir); err != nil { + logrus.Errorf("error removing temporary directory %q: %v", contextDir, err) + } + }() + contextDir = filepath.Join(tempDir, subDir) + } else { + // Nope, it was local. Use it as is. + absDir, err := filepath.Abs(args[0]) + if err != nil { + return "", nil, errors.Wrapf(err, "error determining path to directory %q", args[0]) + } + contextDir = absDir + } + } else { + // No context directory or URL was specified. Try to use the home of + // the first locally-available Containerfile. + for i := range containerFiles { + if strings.HasPrefix(containerFiles[i], "http://") || + strings.HasPrefix(containerFiles[i], "https://") || + strings.HasPrefix(containerFiles[i], "git://") || + strings.HasPrefix(containerFiles[i], "github.com/") { + continue + } + absFile, err := filepath.Abs(containerFiles[i]) + if err != nil { + return "", nil, errors.Wrapf(err, "error determining path to file %q", containerFiles[i]) + } + contextDir = filepath.Dir(absFile) + break + } + } + + if contextDir == "" { + return "", nil, errors.Errorf("no context directory and no Containerfile specified") + } + if !utils.IsDir(contextDir) { + return "", nil, errors.Errorf("context must be a directory: %q", contextDir) + } + if len(containerFiles) == 0 { + if utils.FileExists(filepath.Join(contextDir, "Containerfile")) { + containerFiles = append(containerFiles, filepath.Join(contextDir, "Containerfile")) + } else { + containerFiles = append(containerFiles, filepath.Join(contextDir, "Dockerfile")) + } + } + + return contextDir, containerFiles, nil +} + +// buildFlagsWrapperToOptions converts the local build flags to the build options used +// in the API which embed Buildah types used across the build code. Doing the +// conversion here prevents the API from doing that (redundantly). +// +// TODO: this code should really be in Buildah. +func buildFlagsWrapperToOptions(c *cobra.Command, contextDir string, flags *buildFlagsWrapper) (*entities.BuildOptions, error) { + output := "" + tags := []string{} + if c.Flag("tag").Changed { + tags = flags.Tag + if len(tags) > 0 { + output = tags[0] + tags = tags[1:] + } + } + + pullPolicy := imagebuildah.PullNever + if flags.Pull { + pullPolicy = imagebuildah.PullIfMissing + } + if flags.PullAlways { + pullPolicy = imagebuildah.PullAlways + } + + args := make(map[string]string) + if c.Flag("build-arg").Changed { + for _, arg := range flags.BuildArg { + av := strings.SplitN(arg, "=", 2) + if len(av) > 1 { + args[av[0]] = av[1] + } else { + delete(args, av[0]) + } + } + } + // Check to see if the BUILDAH_LAYERS environment variable is set and + // override command-line. + if _, ok := os.LookupEnv("BUILDAH_LAYERS"); ok { + flags.Layers = true + } + + // `buildah bud --layers=false` acts like `docker build --squash` does. + // That is all of the new layers created during the build process are + // condensed into one, any layers present prior to this build are + // retained without condensing. `buildah bud --squash` squashes both + // new and old layers down into one. Translate Podman commands into + // Buildah. Squash invoked, retain old layers, squash new layers into + // one. + if c.Flags().Changed("squash") && buildOpts.Squash { + flags.Squash = false + flags.Layers = false + } + // Squash-all invoked, squash both new and old layers into one. + if c.Flags().Changed("squash-all") { + flags.Squash = true + flags.Layers = false + } + + var stdin, stdout, stderr, reporter *os.File + stdin = os.Stdin + stdout = os.Stdout + stderr = os.Stderr + reporter = os.Stderr + + if c.Flag("logfile").Changed { + f, err := os.OpenFile(flags.Logfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + if err != nil { + return nil, errors.Errorf("error opening logfile %q: %v", flags.Logfile, err) + } + defer f.Close() + logrus.SetOutput(f) + stdout = f + stderr = f + reporter = f + } + + var memoryLimit, memorySwap int64 + var err error + if c.Flags().Changed("memory") { + memoryLimit, err = units.RAMInBytes(flags.Memory) + if err != nil { + return nil, err + } + } + + if c.Flags().Changed("memory-swap") { + memorySwap, err = units.RAMInBytes(flags.MemorySwap) + if err != nil { + return nil, err + } + } + + nsValues, err := getNsValues(flags) + if err != nil { + return nil, err + } + + networkPolicy := buildah.NetworkDefault + for _, ns := range nsValues { + if ns.Name == "none" { + networkPolicy = buildah.NetworkDisabled + break + } else if !filepath.IsAbs(ns.Path) { + networkPolicy = buildah.NetworkEnabled + break + } + } + + // `buildah bud --layers=false` acts like `docker build --squash` does. + // That is all of the new layers created during the build process are + // condensed into one, any layers present prior to this build are retained + // without condensing. `buildah bud --squash` squashes both new and old + // layers down into one. Translate Podman commands into Buildah. + // Squash invoked, retain old layers, squash new layers into one. + if c.Flags().Changed("squash") && flags.Squash { + flags.Squash = false + flags.Layers = false + } + // Squash-all invoked, squash both new and old layers into one. + if c.Flags().Changed("squash-all") { + flags.Squash = true + flags.Layers = false + } + + compression := imagebuildah.Gzip + if flags.DisableCompression { + compression = imagebuildah.Uncompressed + } + + isolation, err := parse.IsolationOption(flags.Isolation) + if err != nil { + return nil, errors.Wrapf(err, "error parsing ID mapping options") + } + + usernsOption, idmappingOptions, err := parse.IDMappingOptions(c, isolation) + if err != nil { + return nil, errors.Wrapf(err, "error parsing ID mapping options") + } + nsValues = append(nsValues, usernsOption...) + + systemContext, err := parse.SystemContextFromOptions(c) + if err != nil { + return nil, errors.Wrapf(err, "error building system context") + } + + format := "" + flags.Format = strings.ToLower(flags.Format) + switch { + case strings.HasPrefix(flags.Format, buildah.OCI): + format = buildah.OCIv1ImageManifest + case strings.HasPrefix(flags.Format, buildah.DOCKER): + format = buildah.Dockerv2ImageManifest + default: + return nil, errors.Errorf("unrecognized image type %q", flags.Format) + } + + runtimeFlags := []string{} + for _, arg := range flags.RuntimeFlags { + runtimeFlags = append(runtimeFlags, "--"+arg) + } + + // FIXME: the code below needs to be enabled (and adjusted) once the + // global/root flags are supported. + + // conf, err := runtime.GetConfig() + // if err != nil { + // return err + // } + // if conf != nil && conf.Engine.CgroupManager == config.SystemdCgroupsManager { + // runtimeFlags = append(runtimeFlags, "--systemd-cgroup") + // } + + opts := imagebuildah.BuildOptions{ + AddCapabilities: flags.CapAdd, + AdditionalTags: tags, + Annotations: flags.Annotation, + Architecture: flags.Arch, + Args: args, + BlobDirectory: flags.BlobCache, + CNIConfigDir: flags.CNIConfigDir, + CNIPluginPath: flags.CNIPlugInPath, + CommonBuildOpts: &buildah.CommonBuildOptions{ + AddHost: flags.AddHost, + CgroupParent: flags.CgroupParent, + CPUPeriod: flags.CPUPeriod, + CPUQuota: flags.CPUQuota, + CPUShares: flags.CPUShares, + CPUSetCPUs: flags.CPUSetCPUs, + CPUSetMems: flags.CPUSetMems, + Memory: memoryLimit, + MemorySwap: memorySwap, + ShmSize: flags.ShmSize, + Ulimit: flags.Ulimit, + Volumes: flags.Volumes, + }, + Compression: compression, + ConfigureNetwork: networkPolicy, + ContextDirectory: contextDir, + // DefaultMountsFilePath: FIXME: this requires global flags to be working! + Devices: flags.Devices, + DropCapabilities: flags.CapDrop, + Err: stderr, + ForceRmIntermediateCtrs: flags.ForceRm, + IDMappingOptions: idmappingOptions, + IIDFile: flags.Iidfile, + In: stdin, + Isolation: isolation, + Labels: flags.Label, + Layers: flags.Layers, + NamespaceOptions: nsValues, + NoCache: flags.NoCache, + OS: flags.OS, + Out: stdout, + Output: output, + OutputFormat: format, + PullPolicy: pullPolicy, + Quiet: flags.Quiet, + RemoveIntermediateCtrs: flags.Rm, + ReportWriter: reporter, + RuntimeArgs: runtimeFlags, + SignBy: flags.SignBy, + SignaturePolicyPath: flags.SignaturePolicy, + Squash: flags.Squash, + SystemContext: systemContext, + Target: flags.Target, + TransientMounts: flags.Volumes, + } + + return &entities.BuildOptions{BuildOptions: opts}, nil +} + +func getNsValues(flags *buildFlagsWrapper) ([]buildah.NamespaceOption, error) { + var ret []buildah.NamespaceOption + if flags.Network != "" { + switch { + case flags.Network == "host": + ret = append(ret, buildah.NamespaceOption{ + Name: string(specs.NetworkNamespace), + Host: true, + }) + case flags.Network == "container": + ret = append(ret, buildah.NamespaceOption{ + Name: string(specs.NetworkNamespace), + }) + case flags.Network[0] == '/': + ret = append(ret, buildah.NamespaceOption{ + Name: string(specs.NetworkNamespace), + Path: flags.Network, + }) + default: + return nil, errors.Errorf("unsupported configuration network=%s", flags.Network) + } + } + return ret, nil +} diff --git a/cmd/podman/images/diff.go b/cmd/podman/images/diff.go index 7cfacfc6c..c24f98369 100644 --- a/cmd/podman/images/diff.go +++ b/cmd/podman/images/diff.go @@ -6,6 +6,7 @@ import ( "github.com/containers/libpod/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var ( @@ -28,9 +29,11 @@ func init() { Command: diffCmd, Parent: imageCmd, }) + diffFlags(diffCmd.Flags()) +} +func diffFlags(flags *pflag.FlagSet) { diffOpts = &entities.DiffOptions{} - flags := diffCmd.Flags() flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") _ = flags.MarkDeprecated("archive", "Provided for backwards compatibility, has no impact on output.") flags.StringVar(&diffOpts.Format, "format", "", "Change the output format") diff --git a/cmd/podman/images/history.go b/cmd/podman/images/history.go index ce153aa46..17a80557e 100644 --- a/cmd/podman/images/history.go +++ b/cmd/podman/images/history.go @@ -15,6 +15,7 @@ import ( "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var ( @@ -32,6 +33,15 @@ var ( RunE: history, } + imageHistoryCmd = &cobra.Command{ + Args: historyCmd.Args, + Use: historyCmd.Use, + Short: historyCmd.Short, + Long: historyCmd.Long, + RunE: historyCmd.RunE, + Example: `podman image history imageID`, + } + opts = struct { human bool noTrunc bool @@ -45,8 +55,17 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: historyCmd, }) + historyFlags(historyCmd.Flags()) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imageHistoryCmd, + Parent: imageCmd, + }) + historyFlags(imageHistoryCmd.Flags()) +} - flags := historyCmd.Flags() +func historyFlags(flags *pflag.FlagSet) { flags.StringVar(&opts.format, "format", "", "Change the output to JSON or a Go template") flags.BoolVarP(&opts.human, "human", "H", true, "Display sizes and dates in human readable format") flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Do not truncate the output") diff --git a/cmd/podman/images/import.go b/cmd/podman/images/import.go index 1c0568762..0e16128ce 100644 --- a/cmd/podman/images/import.go +++ b/cmd/podman/images/import.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var ( @@ -26,6 +27,17 @@ var ( cat ctr.tar | podman -q import --message "importing the ctr.tar tarball" - image-imported cat ctr.tar | podman import -`, } + + imageImportCommand = &cobra.Command{ + Args: cobra.MinimumNArgs(1), + Use: importCommand.Use, + Short: importCommand.Short, + Long: importCommand.Long, + RunE: importCommand.RunE, + Example: `podman image import http://example.com/ctr.tar url-image + cat ctr.tar | podman -q image import --message "importing the ctr.tar tarball" - image-imported + cat ctr.tar | podman image import -`, + } ) var ( @@ -37,8 +49,17 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: importCommand, }) + importFlags(importCommand.Flags()) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imageImportCommand, + Parent: imageCmd, + }) + importFlags(imageImportCommand.Flags()) +} - flags := importCommand.Flags() +func importFlags(flags *pflag.FlagSet) { flags.StringArrayVarP(&importOpts.Changes, "change", "c", []string{}, "Apply the following possible instructions to the created image (default []): CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR") flags.StringVarP(&importOpts.Message, "message", "m", "", "Set commit message for imported image") flags.BoolVarP(&importOpts.Quiet, "quiet", "q", false, "Suppress output") diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go index f49f95002..d34c794c6 100644 --- a/cmd/podman/images/load.go +++ b/cmd/podman/images/load.go @@ -15,6 +15,7 @@ import ( "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" "golang.org/x/crypto/ssh/terminal" ) @@ -27,6 +28,14 @@ var ( RunE: load, Args: cobra.MaximumNArgs(1), } + + imageLoadCommand = &cobra.Command{ + Args: cobra.MinimumNArgs(1), + Use: loadCommand.Use, + Short: loadCommand.Short, + Long: loadCommand.Long, + RunE: loadCommand.RunE, + } ) var ( @@ -38,8 +47,16 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: loadCommand, }) + loadFlags(loadCommand.Flags()) + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imageLoadCommand, + Parent: imageCmd, + }) + loadFlags(imageLoadCommand.Flags()) +} - flags := loadCommand.Flags() +func loadFlags(flags *pflag.FlagSet) { flags.StringVarP(&loadOpts.Input, "input", "i", "", "Read from specified archive file (default: stdin)") flags.BoolVarP(&loadOpts.Quiet, "quiet", "q", false, "Suppress the output") flags.StringVar(&loadOpts.SignaturePolicy, "signature-policy", "", "Pathname of signature policy file") diff --git a/cmd/podman/images/save.go b/cmd/podman/images/save.go index 8f7832074..56953e41c 100644 --- a/cmd/podman/images/save.go +++ b/cmd/podman/images/save.go @@ -13,6 +13,7 @@ import ( "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" "golang.org/x/crypto/ssh/terminal" ) @@ -43,6 +44,16 @@ var ( podman save --format docker-dir -o ubuntu-dir ubuntu podman save > alpine-all.tar alpine:latest`, } + imageSaveCommand = &cobra.Command{ + Args: saveCommand.Args, + Use: saveCommand.Use, + Short: saveCommand.Short, + Long: saveCommand.Long, + RunE: saveCommand.RunE, + Example: `podman image save --quiet -o myimage.tar imageID + podman image save --format docker-dir -o ubuntu-dir ubuntu + podman image save > alpine-all.tar alpine:latest`, + } ) var ( @@ -54,7 +65,17 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: saveCommand, }) - flags := saveCommand.Flags() + saveFlags(saveCommand.Flags()) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imageSaveCommand, + Parent: imageCmd, + }) + saveFlags(imageSaveCommand.Flags()) +} + +func saveFlags(flags *pflag.FlagSet) { flags.BoolVar(&saveOpts.Compress, "compress", false, "Compress tarball image layers when saving to a directory using the 'dir' transport. (default is same compression type as source)") flags.StringVar(&saveOpts.Format, "format", define.V2s2Archive, "Save image to oci-archive, oci-dir (directory with oci manifest type), docker-archive, docker-dir (directory with v2s2 manifest type)") flags.StringVarP(&saveOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)") diff --git a/cmd/podman/images/tag.go b/cmd/podman/images/tag.go index 411313a9b..dae3416c4 100644 --- a/cmd/podman/images/tag.go +++ b/cmd/podman/images/tag.go @@ -18,6 +18,17 @@ var ( podman tag imageID:latest myNewImage:newTag podman tag httpd myregistryhost:5000/fedora/httpd:v2`, } + + imageTagCommand = &cobra.Command{ + Args: tagCommand.Args, + Use: tagCommand.Use, + Short: tagCommand.Short, + Long: tagCommand.Long, + RunE: tagCommand.RunE, + Example: `podman image tag 0e3bbc2 fedora:latest + podman image tag imageID:latest myNewImage:newTag + podman image tag httpd myregistryhost:5000/fedora/httpd:v2`, + } ) func init() { @@ -25,6 +36,11 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: tagCommand, }) + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imageTagCommand, + Parent: imageCmd, + }) } func tag(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/images/untag.go b/cmd/podman/images/untag.go index 3218844b7..266a3f115 100644 --- a/cmd/podman/images/untag.go +++ b/cmd/podman/images/untag.go @@ -17,6 +17,17 @@ var ( podman untag imageID:latest otherImageName:latest podman untag httpd myregistryhost:5000/fedora/httpd:v2`, } + + imageUntagCommand = &cobra.Command{ + Args: untagCommand.Args, + Use: untagCommand.Use, + Short: untagCommand.Short, + Long: untagCommand.Long, + RunE: untagCommand.RunE, + Example: `podman image untag 0e3bbc2 + podman image untag imageID:latest otherImageName:latest + podman image untag httpd myregistryhost:5000/fedora/httpd:v2`, + } ) func init() { @@ -24,6 +35,11 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: untagCommand, }) + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: imageUntagCommand, + Parent: imageCmd, + }) } func untag(cmd *cobra.Command, args []string) error { diff --git a/cmd/podman/pods/pod.go b/cmd/podman/pods/pod.go index edca08202..ed265ef90 100644 --- a/cmd/podman/pods/pod.go +++ b/cmd/podman/pods/pod.go @@ -16,7 +16,7 @@ var ( podCmd = &cobra.Command{ Use: "pod", Short: "Manage pods", - Long: "Manage pods", + Long: "Pods are a group of one or more containers sharing the same network, pid and ipc namespaces.", TraverseChildren: true, RunE: validate.SubCommandExists, } diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index b97dfeb66..5703bd172 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -26,7 +26,7 @@ var ( psCmd = &cobra.Command{ Use: "ps", Aliases: []string{"ls", "list"}, - Short: "list pods", + Short: "List pods", Long: psDescription, RunE: pods, Args: validate.NoArgs, diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index 7c3597d9a..d3950fdbc 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -35,7 +35,7 @@ var ( // Command: podman pod _pod_ statsCmd = &cobra.Command{ Use: "stats [flags] [POD...]", - Short: "Display resource-usage statistics of pods", + Short: "Display a live stream of resource usage statistics for the containers in one or more pods", Long: statsDescription, RunE: stats, Example: `podman pod stats diff --git a/cmd/podman/pods/top.go b/cmd/podman/pods/top.go index ad602f4ea..9cf2bd525 100644 --- a/cmd/podman/pods/top.go +++ b/cmd/podman/pods/top.go @@ -25,7 +25,7 @@ var ( topCommand = &cobra.Command{ Use: "top [flags] POD [FORMAT-DESCRIPTORS|ARGS]", - Short: "Display the running processes in a pod", + Short: "Display the running processes of containers in a pod", Long: topDescription, RunE: top, Args: cobra.ArbitraryArgs, diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 375faf8b1..502b6c03c 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -212,7 +212,7 @@ func rootFlags(opts *entities.PodmanConfig, flags *pflag.FlagSet) { flags.StringSliceVar(&opts.Identities, "identity", []string{}, "path to SSH identity file") cfg := opts.Config - flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, opts.CGroupUsage) + flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") flags.StringVar(&opts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results") flags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary") flags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network") diff --git a/cmd/podman/system/info.go b/cmd/podman/system/info.go index 26be794c5..dad63bcd4 100644 --- a/cmd/podman/system/info.go +++ b/cmd/podman/system/info.go @@ -10,6 +10,7 @@ import ( "github.com/containers/libpod/pkg/domain/entities" "github.com/ghodss/yaml" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var ( @@ -25,6 +26,15 @@ var ( RunE: info, Example: `podman info`, } + + systemInfoCommand = &cobra.Command{ + Args: infoCommand.Args, + Use: infoCommand.Use, + Short: infoCommand.Short, + Long: infoCommand.Long, + RunE: infoCommand.RunE, + Example: `podman system info`, + } ) var ( @@ -37,7 +47,17 @@ func init() { Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: infoCommand, }) - flags := infoCommand.Flags() + infoFlags(infoCommand.Flags()) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, + Command: systemInfoCommand, + Parent: systemCmd, + }) + infoFlags(systemInfoCommand.Flags()) +} + +func infoFlags(flags *pflag.FlagSet) { flags.BoolVarP(&debug, "debug", "D", false, "Display additional debug information") flags.StringVarP(&inFormat, "format", "f", "", "Change the output format to JSON or a Go template") } -- cgit v1.2.3-54-g00ecf