summaryrefslogtreecommitdiff
path: root/cmd/podman/images
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman/images')
-rw-r--r--cmd/podman/images/build.go491
-rw-r--r--cmd/podman/images/diff.go5
-rw-r--r--cmd/podman/images/history.go21
-rw-r--r--cmd/podman/images/image.go3
-rw-r--r--cmd/podman/images/import.go23
-rw-r--r--cmd/podman/images/load.go20
-rw-r--r--cmd/podman/images/prune.go19
-rw-r--r--cmd/podman/images/pull.go5
-rw-r--r--cmd/podman/images/push.go4
-rw-r--r--cmd/podman/images/rm.go7
-rw-r--r--cmd/podman/images/save.go23
-rw-r--r--cmd/podman/images/search.go8
-rw-r--r--cmd/podman/images/tag.go16
-rw-r--r--cmd/podman/images/trust.go27
-rw-r--r--cmd/podman/images/trust_set.go56
-rw-r--r--cmd/podman/images/trust_show.go77
-rw-r--r--cmd/podman/images/untag.go16
17 files changed, 791 insertions, 30 deletions
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/image.go b/cmd/podman/images/image.go
index 604f49251..790c16c05 100644
--- a/cmd/podman/images/image.go
+++ b/cmd/podman/images/image.go
@@ -2,6 +2,7 @@ package images
import (
"github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/validate"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/spf13/cobra"
)
@@ -16,7 +17,7 @@ var (
Short: "Manage images",
Long: "Manage images",
TraverseChildren: true,
- RunE: registry.SubCommandExists,
+ RunE: validate.SubCommandExists,
}
)
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..4bbffd432 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")
@@ -61,7 +78,6 @@ func load(cmd *cobra.Command, args []string) error {
loadOpts.Tag = "latest"
}
if r, ok := ref.(reference.Named); ok {
- fmt.Println(r.Name())
loadOpts.Name = r.Name()
}
}
diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go
index eb9e4a7e4..7c9e3eb61 100644
--- a/cmd/podman/images/prune.go
+++ b/cmd/podman/images/prune.go
@@ -6,8 +6,9 @@ import (
"os"
"strings"
- "github.com/containers/libpod/cmd/podman/common"
"github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/utils"
+ "github.com/containers/libpod/cmd/podman/validate"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -19,7 +20,7 @@ var (
If an image is not being used by a container, it will be removed from the system.`
pruneCmd = &cobra.Command{
Use: "prune",
- Args: common.NoArgs,
+ Args: validate.NoArgs,
Short: "Remove unused images",
Long: pruneDescription,
RunE: prune,
@@ -71,17 +72,5 @@ Are you sure you want to continue? [y/N] `)
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
+ return utils.PrintImagePruneResults(results)
}
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index fead5f7ed..9e883703f 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -4,7 +4,7 @@ import (
"fmt"
"os"
- buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
@@ -45,6 +45,7 @@ var (
Short: pullCmd.Short,
Long: pullCmd.Long,
RunE: pullCmd.RunE,
+ Args: cobra.ExactArgs(1),
Example: `podman image pull imageName
podman image pull fedora:latest`,
}
@@ -74,7 +75,7 @@ func init() {
// pullFlags set the flags for the pull command.
func pullFlags(flags *pflag.FlagSet) {
flags.BoolVar(&pullOptions.AllTags, "all-tags", false, "All tagged images in the repository will be pulled")
- flags.StringVar(&pullOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&pullOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringVar(&pullOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
flags.StringVar(&pullOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
flags.StringVar(&pullOptions.OverrideArch, "override-arch", "", "Use `ARCH` instead of the architecture of the machine for choosing images")
diff --git a/cmd/podman/images/push.go b/cmd/podman/images/push.go
index 0b3502d61..dd536213f 100644
--- a/cmd/podman/images/push.go
+++ b/cmd/podman/images/push.go
@@ -3,7 +3,7 @@ package images
import (
"os"
- buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
@@ -70,7 +70,7 @@ func init() {
// pushFlags set the flags for the push command.
func pushFlags(flags *pflag.FlagSet) {
- flags.StringVar(&pushOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&pushOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
flags.StringVar(&pushOptions.CertDir, "cert-dir", "", "Path to a directory containing TLS certificates and keys")
flags.BoolVar(&pushOptions.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)")
flags.StringVar(&pushOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
diff --git a/cmd/podman/images/rm.go b/cmd/podman/images/rm.go
index 1cf5fa365..4b9920532 100644
--- a/cmd/podman/images/rm.go
+++ b/cmd/podman/images/rm.go
@@ -5,6 +5,7 @@ import (
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/errorhandling"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -48,7 +49,9 @@ func rm(cmd *cobra.Command, args []string) error {
return errors.Errorf("when using the --all switch, you may not pass any images names or IDs")
}
- report, err := registry.ImageEngine().Remove(registry.GetContext(), args, imageOpts)
+ // Note: certain image-removal errors are non-fatal. Hence, the report
+ // might be set even if err != nil.
+ report, rmErrors := registry.ImageEngine().Remove(registry.GetContext(), args, imageOpts)
if report != nil {
for _, u := range report.Untagged {
fmt.Println("Untagged: " + u)
@@ -62,5 +65,5 @@ func rm(cmd *cobra.Command, args []string) error {
registry.SetExitCode(report.ExitCode)
}
- return err
+ return errorhandling.JoinErrors(rmErrors)
}
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/search.go b/cmd/podman/images/search.go
index a8abfb339..640d497c3 100644
--- a/cmd/podman/images/search.go
+++ b/cmd/podman/images/search.go
@@ -5,8 +5,8 @@ import (
"reflect"
"strings"
- buildahcli "github.com/containers/buildah/pkg/cli"
"github.com/containers/buildah/pkg/formats"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
@@ -87,7 +87,7 @@ func searchFlags(flags *pflag.FlagSet) {
flags.StringVar(&searchOptions.Format, "format", "", "Change the output format to a Go template")
flags.IntVar(&searchOptions.Limit, "limit", 0, "Limit the number of results")
flags.BoolVar(&searchOptions.NoTrunc, "no-trunc", false, "Do not truncate the output")
- flags.StringVar(&searchOptions.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ flags.StringVar(&searchOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
flags.BoolVar(&searchOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
if registry.IsRemote() {
_ = flags.MarkHidden("authfile")
@@ -105,6 +105,10 @@ func imageSearch(cmd *cobra.Command, args []string) error {
return errors.Errorf("search requires exactly one argument")
}
+ if searchOptions.Limit > 100 {
+ return errors.Errorf("Limit %d is outside the range of [1, 100]", searchOptions.Limit)
+ }
+
// TLS verification in c/image is controlled via a `types.OptionalBool`
// which allows for distinguishing among set-true, set-false, unspecified
// which is important to implement a sane way of dealing with defaults of
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/trust.go b/cmd/podman/images/trust.go
new file mode 100644
index 000000000..88a567871
--- /dev/null
+++ b/cmd/podman/images/trust.go
@@ -0,0 +1,27 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/cmd/podman/validate"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ trustDescription = `Manages which registries you trust as a source of container images based on their location.
+ The location is determined by the transport and the registry host of the image. Using this container image docker://quay.io/podman/stable as an example, docker is the transport and quay.io is the registry host.`
+ trustCmd = &cobra.Command{
+ Use: "trust",
+ Short: "Manage container image trust policy",
+ Long: trustDescription,
+ RunE: validate.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: trustCmd,
+ Parent: imageCmd,
+ })
+}
diff --git a/cmd/podman/images/trust_set.go b/cmd/podman/images/trust_set.go
new file mode 100644
index 000000000..5868f5546
--- /dev/null
+++ b/cmd/podman/images/trust_set.go
@@ -0,0 +1,56 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ setTrustDescription = "Set default trust policy or add a new trust policy for a registry"
+ setTrustCommand = &cobra.Command{
+ Use: "set [flags] REGISTRY",
+ Short: "Set default trust policy or a new trust policy for a registry",
+ Long: setTrustDescription,
+ Example: "",
+ RunE: setTrust,
+ Args: cobra.ExactArgs(1),
+ }
+)
+
+var (
+ setOptions entities.SetTrustOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: setTrustCommand,
+ Parent: trustCmd,
+ })
+ setFlags := setTrustCommand.Flags()
+ setFlags.StringVar(&setOptions.PolicyPath, "policypath", "", "")
+ _ = setFlags.MarkHidden("policypath")
+ setFlags.StringSliceVarP(&setOptions.PubKeysFile, "pubkeysfile", "f", []string{}, `Path of installed public key(s) to trust for TARGET.
+Absolute path to keys is added to policy.json. May
+used multiple times to define multiple public keys.
+File(s) must exist before using this command`)
+ setFlags.StringVarP(&setOptions.Type, "type", "t", "signedBy", "Trust type, accept values: signedBy(default), accept, reject")
+}
+
+func setTrust(cmd *cobra.Command, args []string) error {
+ validTrustTypes := []string{"accept", "insecureAcceptAnything", "reject", "signedBy"}
+
+ valid, err := image.IsValidImageURI(args[0])
+ if err != nil || !valid {
+ return errors.Wrapf(err, "invalid image uri %s", args[0])
+ }
+
+ if !util.StringInSlice(setOptions.Type, validTrustTypes) {
+ return errors.Errorf("invalid choice: %s (choose from 'accept', 'reject', 'signedBy')", setOptions.Type)
+ }
+ return registry.ImageEngine().SetTrust(registry.Context(), args, setOptions)
+}
diff --git a/cmd/podman/images/trust_show.go b/cmd/podman/images/trust_show.go
new file mode 100644
index 000000000..23ee6c709
--- /dev/null
+++ b/cmd/podman/images/trust_show.go
@@ -0,0 +1,77 @@
+package images
+
+import (
+ "fmt"
+ "os"
+ "text/tabwriter"
+ "text/template"
+
+ "github.com/containers/libpod/cmd/podman/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ showTrustDescription = "Display trust policy for the system"
+ showTrustCommand = &cobra.Command{
+ Use: "show [flags] [REGISTRY]",
+ Short: "Display trust policy for the system",
+ Long: showTrustDescription,
+ RunE: showTrust,
+ Example: "",
+ }
+)
+
+var (
+ showTrustOptions entities.ShowTrustOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: showTrustCommand,
+ Parent: trustCmd,
+ })
+ showFlags := showTrustCommand.Flags()
+ showFlags.BoolVarP(&showTrustOptions.JSON, "json", "j", false, "Output as json")
+ showFlags.StringVar(&showTrustOptions.PolicyPath, "policypath", "", "")
+ showFlags.BoolVar(&showTrustOptions.Raw, "raw", false, "Output raw policy file")
+ _ = showFlags.MarkHidden("policypath")
+ showFlags.StringVar(&showTrustOptions.RegistryPath, "registrypath", "", "")
+ _ = showFlags.MarkHidden("registrypath")
+
+}
+
+func showTrust(cmd *cobra.Command, args []string) error {
+ report, err := registry.ImageEngine().ShowTrust(registry.Context(), args, showTrustOptions)
+ if err != nil {
+ return err
+ }
+ if showTrustOptions.Raw {
+ fmt.Println(report.Raw)
+ return nil
+ }
+ if showTrustOptions.JSON {
+ b, err := json.MarshalIndent(report.Policies, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+ }
+
+ row := "{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n"
+ format := "{{range . }}" + row + "{{end}}"
+ tmpl, err := template.New("listContainers").Parse(format)
+ if err != nil {
+ return err
+ }
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ if err := tmpl.Execute(w, report.Policies); err != nil {
+ return err
+ }
+ if err := w.Flush(); err != nil {
+ return err
+ }
+ return nil
+}
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 {