summaryrefslogtreecommitdiff
path: root/cmd/podman/images
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/podman/images')
-rw-r--r--cmd/podman/images/build.go52
-rw-r--r--cmd/podman/images/mount.go139
-rw-r--r--cmd/podman/images/unmount.go71
3 files changed, 228 insertions, 34 deletions
diff --git a/cmd/podman/images/build.go b/cmd/podman/images/build.go
index de750d892..400f960cc 100644
--- a/cmd/podman/images/build.go
+++ b/cmd/podman/images/build.go
@@ -138,36 +138,9 @@ func build(cmd *cobra.Command, args []string) error {
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 {
+ for _, f := range buildOpts.File {
if f == "-" {
containerFiles = append(containerFiles, "/dev/stdin")
} else {
@@ -181,7 +154,7 @@ func extractContextAndFiles(args, files []string) (string, []string, error) {
// 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")
+ return errors.Wrapf(err, "error prepping temporary context directory")
}
if tempDir != "" {
// We had to download it to a temporary directory.
@@ -196,7 +169,7 @@ func extractContextAndFiles(args, files []string) (string, []string, error) {
// 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])
+ return errors.Wrapf(err, "error determining path to directory %q", args[0])
}
contextDir = absDir
}
@@ -212,7 +185,7 @@ func extractContextAndFiles(args, files []string) (string, []string, error) {
}
absFile, err := filepath.Abs(containerFiles[i])
if err != nil {
- return "", nil, errors.Wrapf(err, "error determining path to file %q", containerFiles[i])
+ return errors.Wrapf(err, "error determining path to file %q", containerFiles[i])
}
contextDir = filepath.Dir(absFile)
break
@@ -220,10 +193,10 @@ func extractContextAndFiles(args, files []string) (string, []string, error) {
}
if contextDir == "" {
- return "", nil, errors.Errorf("no context directory and no Containerfile specified")
+ return errors.Errorf("no context directory and no Containerfile specified")
}
if !utils.IsDir(contextDir) {
- return "", nil, errors.Errorf("context must be a directory: %q", contextDir)
+ return errors.Errorf("context must be a directory: %q", contextDir)
}
if len(containerFiles) == 0 {
if utils.FileExists(filepath.Join(contextDir, "Containerfile")) {
@@ -233,7 +206,18 @@ func extractContextAndFiles(args, files []string) (string, []string, error) {
}
}
- return contextDir, containerFiles, nil
+ 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
}
// buildFlagsWrapperToOptions converts the local build flags to the build options used
diff --git a/cmd/podman/images/mount.go b/cmd/podman/images/mount.go
new file mode 100644
index 000000000..fac06e324
--- /dev/null
+++ b/cmd/podman/images/mount.go
@@ -0,0 +1,139 @@
+package images
+
+import (
+ "fmt"
+ "os"
+ "text/tabwriter"
+ "text/template"
+
+ "github.com/containers/podman/v2/cmd/podman/registry"
+ "github.com/containers/podman/v2/cmd/podman/utils"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ mountDescription = `podman image mount
+ Lists all mounted images mount points if no images is specified
+
+ podman image mount IMAGE-NAME-OR-ID
+ Mounts the specified image and prints the mountpoint
+`
+
+ mountCommand = &cobra.Command{
+ Use: "mount [flags] [IMAGE...]",
+ Short: "Mount an images's root filesystem",
+ Long: mountDescription,
+ RunE: mount,
+ Example: `podman image mount imgID
+ podman image mount imgID1 imgID2 imgID3
+ podman image mount
+ podman image mount --all`,
+ Annotations: map[string]string{
+ registry.UnshareNSRequired: "",
+ registry.ParentNSRequired: "",
+ },
+ }
+)
+
+var (
+ mountOpts entities.ImageMountOptions
+)
+
+func mountFlags(flags *pflag.FlagSet) {
+ flags.BoolVarP(&mountOpts.All, "all", "a", false, "Mount all images")
+ flags.StringVar(&mountOpts.Format, "format", "", "Print the mounted images in specified format (json)")
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: mountCommand,
+ Parent: imageCmd,
+ })
+ mountFlags(mountCommand.Flags())
+}
+
+func mount(_ *cobra.Command, args []string) error {
+ var (
+ errs utils.OutputErrors
+ )
+ if len(args) > 0 && mountOpts.All {
+ return errors.New("when using the --all switch, you may not pass any image names or IDs")
+ }
+ reports, err := registry.ImageEngine().Mount(registry.GetContext(), args, mountOpts)
+ if err != nil {
+ return err
+ }
+ if len(args) > 0 || mountOpts.All {
+ for _, r := range reports {
+ if r.Err == nil {
+ fmt.Println(r.Path)
+ continue
+ }
+ errs = append(errs, r.Err)
+ }
+ return errs.PrintErrors()
+ }
+
+ switch mountOpts.Format {
+ case "json":
+ return printJSON(reports)
+ case "":
+ // do nothing
+ default:
+ return errors.Errorf("unknown --format argument: %s", mountOpts.Format)
+ }
+
+ mrs := make([]mountReporter, 0, len(reports))
+ for _, r := range reports {
+ mrs = append(mrs, mountReporter{r})
+ }
+ row := "{{.ID}} {{.Path}}\n"
+ format := "{{range . }}" + row + "{{end}}"
+ tmpl, err := template.New("mounts").Parse(format)
+ if err != nil {
+ return err
+ }
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ defer w.Flush()
+ return tmpl.Execute(w, mrs)
+}
+
+func printJSON(reports []*entities.ImageMountReport) error {
+ type jreport struct {
+ ID string `json:"id"`
+ Names []string
+ Repositories []string
+ Mountpoint string `json:"mountpoint"`
+ }
+ jreports := make([]jreport, 0, len(reports))
+
+ for _, r := range reports {
+ jreports = append(jreports, jreport{
+ ID: r.Id,
+ Names: []string{r.Name},
+ Repositories: r.Repositories,
+ Mountpoint: r.Path,
+ })
+ }
+ b, err := json.MarshalIndent(jreports, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
+
+type mountReporter struct {
+ *entities.ImageMountReport
+}
+
+func (m mountReporter) ID() string {
+ if len(m.Repositories) > 0 {
+ return m.Repositories[0]
+ }
+ return m.Id
+}
diff --git a/cmd/podman/images/unmount.go b/cmd/podman/images/unmount.go
new file mode 100644
index 000000000..f7f6cf8e5
--- /dev/null
+++ b/cmd/podman/images/unmount.go
@@ -0,0 +1,71 @@
+package images
+
+import (
+ "fmt"
+
+ "github.com/containers/podman/v2/cmd/podman/registry"
+ "github.com/containers/podman/v2/cmd/podman/utils"
+ "github.com/containers/podman/v2/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+var (
+ description = `Image storage increments a mount counter each time an image is mounted.
+
+ When an image is unmounted, the mount counter is decremented. The image's root filesystem is physically unmounted only when the mount counter reaches zero indicating no other processes are using the mount.
+
+ An unmount can be forced with the --force flag.
+`
+ unmountCommand = &cobra.Command{
+ Use: "unmount [flags] IMAGE [IMAGE...]",
+ Aliases: []string{"umount"},
+ Short: "Unmount an image's root filesystem",
+ Long: description,
+ RunE: unmount,
+ Example: `podman unmount imgID
+ podman unmount imgID1 imgID2 imgID3
+ podman unmount --all`,
+ }
+)
+
+var (
+ unmountOpts entities.ImageUnmountOptions
+)
+
+func unmountFlags(flags *pflag.FlagSet) {
+ flags.BoolVarP(&unmountOpts.All, "all", "a", false, "Unmount all of the currently mounted images")
+ flags.BoolVarP(&unmountOpts.Force, "force", "f", false, "Force the complete unmount of the specified mounted images")
+}
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Parent: imageCmd,
+ Command: unmountCommand,
+ })
+ unmountFlags(unmountCommand.Flags())
+}
+
+func unmount(cmd *cobra.Command, args []string) error {
+ var errs utils.OutputErrors
+ if len(args) < 1 && !unmountOpts.All {
+ return errors.New("image name or ID must be specified")
+ }
+ if len(args) > 0 && unmountOpts.All {
+ return errors.New("when using the --all switch, you may not pass any image names or IDs")
+ }
+ reports, err := registry.ImageEngine().Unmount(registry.GetContext(), args, unmountOpts)
+ if err != nil {
+ return err
+ }
+ for _, r := range reports {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ return errs.PrintErrors()
+}