aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml2
-rw-r--r--cmd/podmanV2/Makefile2
-rw-r--r--cmd/podmanV2/common/inspect.go18
-rw-r--r--cmd/podmanV2/containers/export.go57
-rw-r--r--cmd/podmanV2/containers/inspect.go32
-rw-r--r--cmd/podmanV2/healthcheck/healthcheck.go33
-rw-r--r--cmd/podmanV2/healthcheck/run.go42
-rw-r--r--cmd/podmanV2/images/inspect.go141
-rw-r--r--cmd/podmanV2/images/list.go2
-rw-r--r--cmd/podmanV2/images/load.go61
-rw-r--r--cmd/podmanV2/images/pull.go140
-rw-r--r--cmd/podmanV2/images/tag.go34
-rw-r--r--cmd/podmanV2/images/untag.go33
-rw-r--r--cmd/podmanV2/inspect.go62
-rw-r--r--cmd/podmanV2/main.go7
-rw-r--r--cmd/podmanV2/pods/pod.go30
-rw-r--r--cmd/podmanV2/pods/ps.go134
-rw-r--r--cmd/podmanV2/registry/registry.go17
-rw-r--r--cmd/podmanV2/report/templates.go3
-rw-r--r--cmd/podmanV2/root.go5
-rw-r--r--cmd/podmanV2/system/system.go2
-rwxr-xr-xcontrib/build_rpm.sh13
-rw-r--r--contrib/cirrus/README.md4
-rw-r--r--contrib/spec/podman.spec.in4
-rw-r--r--go.sum2
-rwxr-xr-xhack/podmanv2-retry37
-rw-r--r--libpod/image/image.go6
-rw-r--r--libpod/image/image_test.go2
-rw-r--r--libpod/podfilters/pods.go115
-rw-r--r--pkg/api/handlers/compat/containers_export.go42
-rw-r--r--pkg/api/handlers/libpod/images.go46
-rw-r--r--pkg/api/handlers/swagger.go2
-rw-r--r--pkg/api/handlers/utils/pods.go5
-rw-r--r--pkg/api/server/register_containers.go45
-rw-r--r--pkg/api/server/register_images.go34
-rw-r--r--pkg/bindings/containers/containers.go20
-rw-r--r--pkg/bindings/images/images.go70
-rw-r--r--pkg/bindings/test/images_test.go25
-rw-r--r--pkg/domain/entities/containers.go10
-rw-r--r--pkg/domain/entities/engine_container.go20
-rw-r--r--pkg/domain/entities/engine_image.go5
-rw-r--r--pkg/domain/entities/healthcheck.go3
-rw-r--r--pkg/domain/entities/images.go67
-rw-r--r--pkg/domain/entities/pods.go13
-rw-r--r--pkg/domain/entities/types.go7
-rw-r--r--pkg/domain/infra/abi/containers.go10
-rw-r--r--pkg/domain/infra/abi/healthcheck.go26
-rw-r--r--pkg/domain/infra/abi/images.go174
-rw-r--r--pkg/domain/infra/abi/pods.go59
-rw-r--r--pkg/domain/infra/tunnel/containers.go18
-rw-r--r--pkg/domain/infra/tunnel/healthcheck.go13
-rw-r--r--pkg/domain/infra/tunnel/images.go82
-rw-r--r--pkg/domain/infra/tunnel/pods.go4
53 files changed, 1677 insertions, 163 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 0770e0702..a0340dbbd 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -39,7 +39,7 @@ env:
UBUNTU_NAME: "ubuntu-19"
PRIOR_UBUNTU_NAME: "ubuntu-18"
- _BUILT_IMAGE_SUFFIX: "libpod-5633729662025728"
+ _BUILT_IMAGE_SUFFIX: "libpod-6465271544152064"
FEDORA_CACHE_IMAGE_NAME: "${FEDORA_NAME}-${_BUILT_IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "${PRIOR_FEDORA_NAME}-${_BUILT_IMAGE_SUFFIX}"
UBUNTU_CACHE_IMAGE_NAME: "${UBUNTU_NAME}-${_BUILT_IMAGE_SUFFIX}"
diff --git a/cmd/podmanV2/Makefile b/cmd/podmanV2/Makefile
index 147a78d9c..f2f7bd73c 100644
--- a/cmd/podmanV2/Makefile
+++ b/cmd/podmanV2/Makefile
@@ -1,2 +1,2 @@
all:
- GO111MODULE=off go build -tags 'ABISupport'
+ GO111MODULE=off go build -tags 'ABISupport systemd'
diff --git a/cmd/podmanV2/common/inspect.go b/cmd/podmanV2/common/inspect.go
new file mode 100644
index 000000000..dfc6fe679
--- /dev/null
+++ b/cmd/podmanV2/common/inspect.go
@@ -0,0 +1,18 @@
+package common
+
+import (
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+// AddInspectFlagSet takes a command and adds the inspect flags and returns an InspectOptions object
+// Since this cannot live in `package main` it lives here until a better home is found
+func AddInspectFlagSet(cmd *cobra.Command) *entities.InspectOptions {
+ opts := entities.InspectOptions{}
+
+ flags := cmd.Flags()
+ flags.BoolVarP(&opts.Size, "size", "s", false, "Display total file size")
+ flags.StringVarP(&opts.Format, "format", "f", "", "Change the output format to a Go template")
+
+ return &opts
+}
diff --git a/cmd/podmanV2/containers/export.go b/cmd/podmanV2/containers/export.go
new file mode 100644
index 000000000..b93b60878
--- /dev/null
+++ b/cmd/podmanV2/containers/export.go
@@ -0,0 +1,57 @@
+package containers
+
+import (
+ "context"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/parse"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+var (
+ exportDescription = "Exports container's filesystem contents as a tar archive" +
+ " and saves it on the local machine."
+
+ exportCommand = &cobra.Command{
+ Use: "export [flags] CONTAINER",
+ Short: "Export container's filesystem contents as a tar archive",
+ Long: exportDescription,
+ PersistentPreRunE: preRunE,
+ RunE: export,
+ Args: cobra.ExactArgs(1),
+ Example: `podman export ctrID > myCtr.tar
+ podman export --output="myCtr.tar" ctrID`,
+ }
+)
+
+var (
+ exportOpts entities.ContainerExportOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: exportCommand,
+ })
+ exportCommand.SetHelpTemplate(registry.HelpTemplate())
+ exportCommand.SetUsageTemplate(registry.UsageTemplate())
+ flags := exportCommand.Flags()
+ flags.StringVarP(&exportOpts.Output, "output", "o", "", "Write to a specified file (default: stdout, which must be redirected)")
+}
+
+func export(cmd *cobra.Command, args []string) error {
+ if len(exportOpts.Output) == 0 {
+ file := os.Stdout
+ if terminal.IsTerminal(int(file.Fd())) {
+ return errors.Errorf("refusing to export to terminal. Use -o flag or redirect")
+ }
+ exportOpts.Output = "/dev/stdout"
+ } else if err := parse.ValidateFileName(exportOpts.Output); err != nil {
+ return err
+ }
+ return registry.ContainerEngine().ContainerExport(context.Background(), args[0], exportOpts)
+}
diff --git a/cmd/podmanV2/containers/inspect.go b/cmd/podmanV2/containers/inspect.go
index 648289f0b..3147426cb 100644
--- a/cmd/podmanV2/containers/inspect.go
+++ b/cmd/podmanV2/containers/inspect.go
@@ -7,9 +7,11 @@ import (
"strings"
"text/template"
+ "github.com/containers/libpod/cmd/podmanV2/common"
"github.com/containers/libpod/cmd/podmanV2/registry"
+
"github.com/containers/libpod/pkg/domain/entities"
- jsoniter "github.com/json-iterator/go"
+ json "github.com/json-iterator/go"
"github.com/spf13/cobra"
)
@@ -24,10 +26,7 @@ var (
Example: `podman container inspect myCtr
podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`,
}
-)
-
-var (
- inspectOptions entities.ContainerInspectOptions
+ inspectOpts *entities.InspectOptions
)
func init() {
@@ -36,29 +35,29 @@ func init() {
Command: inspectCmd,
Parent: containerCmd,
})
+ inspectOpts = common.AddInspectFlagSet(inspectCmd)
flags := inspectCmd.Flags()
- flags.StringVarP(&inspectOptions.Format, "format", "f", "", "Change the output format to a Go template")
- flags.BoolVarP(&inspectOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
- flags.BoolVarP(&inspectOptions.Size, "size", "s", false, "Display total file size")
- if registry.IsRemote() {
- _ = flags.MarkHidden("latest")
+
+ if !registry.IsRemote() {
+ flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
}
+
}
func inspect(cmd *cobra.Command, args []string) error {
- responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, inspectOptions)
+ responses, err := registry.ContainerEngine().ContainerInspect(context.Background(), args, *inspectOpts)
if err != nil {
return err
}
- if inspectOptions.Format == "" {
- b, err := jsoniter.MarshalIndent(responses, "", " ")
+ if inspectOpts.Format == "" {
+ b, err := json.MarshalIndent(responses, "", " ")
if err != nil {
return err
}
fmt.Println(string(b))
return nil
}
- format := inspectOptions.Format
+ format := inspectOpts.Format
if !strings.HasSuffix(format, "\n") {
format += "\n"
}
@@ -73,3 +72,8 @@ func inspect(cmd *cobra.Command, args []string) error {
}
return nil
}
+
+func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error {
+ inspectOpts = options
+ return inspect(cmd, args)
+}
diff --git a/cmd/podmanV2/healthcheck/healthcheck.go b/cmd/podmanV2/healthcheck/healthcheck.go
new file mode 100644
index 000000000..2af398ff0
--- /dev/null
+++ b/cmd/podmanV2/healthcheck/healthcheck.go
@@ -0,0 +1,33 @@
+package healthcheck
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ // Command: healthcheck
+ healthCmd = &cobra.Command{
+ Use: "healthcheck",
+ Short: "Manage Healthcheck",
+ Long: "Manage Healthcheck",
+ TraverseChildren: true,
+ PersistentPreRunE: preRunE,
+ RunE: registry.SubCommandExists,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: healthCmd,
+ })
+ healthCmd.SetHelpTemplate(registry.HelpTemplate())
+ healthCmd.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func preRunE(cmd *cobra.Command, args []string) error {
+ _, err := registry.NewContainerEngine(cmd, args)
+ return err
+}
diff --git a/cmd/podmanV2/healthcheck/run.go b/cmd/podmanV2/healthcheck/run.go
new file mode 100644
index 000000000..bb2962eaf
--- /dev/null
+++ b/cmd/podmanV2/healthcheck/run.go
@@ -0,0 +1,42 @@
+package healthcheck
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ healthcheckRunDescription = "run the health check of a container"
+ healthcheckrunCommand = &cobra.Command{
+ Use: "run [flags] CONTAINER",
+ Short: "run the health check of a container",
+ Long: healthcheckRunDescription,
+ Example: `podman healthcheck run mywebapp`,
+ RunE: run,
+ Args: cobra.ExactArgs(1),
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: healthcheckrunCommand,
+ Parent: healthCmd,
+ })
+}
+
+func run(cmd *cobra.Command, args []string) error {
+ response, err := registry.ContainerEngine().HealthCheckRun(context.Background(), args[0], entities.HealthCheckOptions{})
+ if err != nil {
+ return err
+ }
+ if response.Status == "unhealthy" {
+ registry.SetExitCode(1)
+ }
+ fmt.Println(response.Status)
+ return err
+}
diff --git a/cmd/podmanV2/images/inspect.go b/cmd/podmanV2/images/inspect.go
index f8fd44571..d7f6b0ee1 100644
--- a/cmd/podmanV2/images/inspect.go
+++ b/cmd/podmanV2/images/inspect.go
@@ -1,71 +1,44 @@
package images
import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "os"
"strings"
+ "text/tabwriter"
+ "text/template"
"github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podmanV2/common"
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
var (
- inspectOpts = entities.ImageInspectOptions{}
-
// Command: podman image _inspect_
inspectCmd = &cobra.Command{
Use: "inspect [flags] IMAGE",
Short: "Display the configuration of an image",
Long: `Displays the low-level information on an image identified by name or ID.`,
- PreRunE: populateEngines,
- RunE: imageInspect,
+ RunE: inspect,
Example: `podman image inspect alpine`,
}
-
- containerEngine entities.ContainerEngine
+ inspectOpts *entities.InspectOptions
)
-// Inspect is unique in that it needs both an ImageEngine and a ContainerEngine
-func populateEngines(cmd *cobra.Command, args []string) (err error) {
- // Populate registry.ImageEngine
- err = preRunE(cmd, args)
- if err != nil {
- return
- }
-
- // Populate registry.ContainerEngine
- containerEngine, err = registry.NewContainerEngine(cmd, args)
- return
-}
-
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: inspectCmd,
Parent: imageCmd,
})
-
- flags := inspectCmd.Flags()
- flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
- flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size")
- flags.StringVarP(&inspectOpts.Format, "format", "f", "", "Change the output format to a Go template")
-
- if registry.EngineOptions.EngineMode == entities.ABIMode {
- // TODO: This is the same as V1. We could skip creating the flag altogether in V2...
- _ = flags.MarkHidden("latest")
- }
+ inspectOpts = common.AddInspectFlagSet(inspectCmd)
}
-const (
- inspectTypeContainer = "container"
- inspectTypeImage = "image"
- inspectAll = "all"
-)
-
-func imageInspect(cmd *cobra.Command, args []string) error {
- inspectType := inspectTypeImage
+func inspect(cmd *cobra.Command, args []string) error {
latestContainer := inspectOpts.Latest
if len(args) == 0 && !latestContainer {
@@ -76,49 +49,61 @@ func imageInspect(cmd *cobra.Command, args []string) error {
return errors.Errorf("you cannot provide additional arguments with --latest")
}
- if !util.StringInSlice(inspectType, []string{inspectTypeContainer, inspectTypeImage, inspectAll}) {
- return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll)
+ results, err := registry.ImageEngine().Inspect(context.Background(), args, *inspectOpts)
+ if err != nil {
+ return err
}
- outputFormat := inspectOpts.Format
- if strings.Contains(outputFormat, "{{.Id}}") {
- outputFormat = strings.Replace(outputFormat, "{{.Id}}", formats.IDString, -1)
- }
- // These fields were renamed, so we need to provide backward compat for
- // the old names.
- if strings.Contains(outputFormat, ".Src") {
- outputFormat = strings.Replace(outputFormat, ".Src", ".Source", -1)
+ if len(results.Images) > 0 {
+ if inspectOpts.Format == "" {
+ buf, err := json.MarshalIndent(results.Images, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(buf))
+
+ for id, e := range results.Errors {
+ fmt.Fprintf(os.Stderr, "%s: %s\n", id, e.Error())
+ }
+ return nil
+ }
+
+ row := inspectFormat(inspectOpts.Format)
+ format := "{{range . }}" + row + "{{end}}"
+ tmpl, err := template.New("inspect").Parse(format)
+ if err != nil {
+ return err
+ }
+
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ defer func() { _ = w.Flush() }()
+ err = tmpl.Execute(w, results)
+ if err != nil {
+ return err
+ }
}
- if strings.Contains(outputFormat, ".Dst") {
- outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1)
- }
- if strings.Contains(outputFormat, ".ImageID") {
- outputFormat = strings.Replace(outputFormat, ".ImageID", ".Image", -1)
+
+ for id, e := range results.Errors {
+ fmt.Fprintf(os.Stderr, "%s: %s\n", id, e.Error())
}
- _ = outputFormat
- // if latestContainer {
- // lc, err := ctnrRuntime.GetLatestContainer()
- // if err != nil {
- // return err
- // }
- // args = append(args, lc.ID())
- // inspectType = inspectTypeContainer
- // }
-
- // inspectedObjects, iterateErr := iterateInput(getContext(), c.Size, args, runtime, inspectType)
- // if iterateErr != nil {
- // return iterateErr
- // }
- //
- // var out formats.Writer
- // if outputFormat != "" && outputFormat != formats.JSONString {
- // // template
- // out = formats.StdoutTemplateArray{Output: inspectedObjects, Template: outputFormat}
- // } else {
- // // default is json output
- // out = formats.JSONStructArray{Output: inspectedObjects}
- // }
- //
- // return out.Out()
return nil
}
+
+func inspectFormat(row string) string {
+ r := strings.NewReplacer("{{.Id}}", formats.IDString,
+ ".Src", ".Source",
+ ".Dst", ".Destination",
+ ".ImageID", ".Image",
+ )
+ row = r.Replace(row)
+
+ if !strings.HasSuffix(row, "\n") {
+ row += "\n"
+ }
+ return row
+}
+
+func Inspect(cmd *cobra.Command, args []string, options *entities.InspectOptions) error {
+ inspectOpts = options
+ return inspect(cmd, args)
+}
diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go
index 9a5b47299..2d6cb3596 100644
--- a/cmd/podmanV2/images/list.go
+++ b/cmd/podmanV2/images/list.go
@@ -152,7 +152,7 @@ func writeTemplate(imageS []*entities.ImageSummary, err error) error {
hdr, row := imageListFormat(listFlag)
format := hdr + "{{range . }}" + row + "{{end}}"
- tmpl := template.Must(template.New("report").Funcs(report.PodmanTemplateFuncs()).Parse(format))
+ tmpl := template.Must(template.New("list").Funcs(report.PodmanTemplateFuncs()).Parse(format))
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
defer w.Flush()
return tmpl.Execute(w, imgs)
diff --git a/cmd/podmanV2/images/load.go b/cmd/podmanV2/images/load.go
new file mode 100644
index 000000000..f60dc4908
--- /dev/null
+++ b/cmd/podmanV2/images/load.go
@@ -0,0 +1,61 @@
+package images
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ loadDescription = "Loads an image from a locally stored archive (tar file) into container storage."
+ loadCommand = &cobra.Command{
+ Use: "load [flags] [NAME[:TAG]]",
+ Short: "Load an image from container archive",
+ Long: loadDescription,
+ RunE: load,
+ Args: cobra.MaximumNArgs(1),
+ PersistentPreRunE: preRunE,
+ }
+)
+
+var (
+ loadOpts entities.ImageLoadOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: loadCommand,
+ })
+
+ loadCommand.SetHelpTemplate(registry.HelpTemplate())
+ loadCommand.SetUsageTemplate(registry.UsageTemplate())
+ flags := loadCommand.Flags()
+ 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")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("signature-policy")
+ }
+
+}
+
+func load(cmd *cobra.Command, args []string) error {
+ if len(args) > 0 {
+ repo, err := image.NormalizedTag(args[0])
+ if err != nil {
+ return err
+ }
+ loadOpts.Name = repo.Name()
+ }
+ response, err := registry.ImageEngine().Load(context.Background(), loadOpts)
+ if err != nil {
+ return err
+ }
+ fmt.Println("Loaded image: " + response.Name)
+ return nil
+}
diff --git a/cmd/podmanV2/images/pull.go b/cmd/podmanV2/images/pull.go
new file mode 100644
index 000000000..c7e325409
--- /dev/null
+++ b/cmd/podmanV2/images/pull.go
@@ -0,0 +1,140 @@
+package images
+
+import (
+ "fmt"
+
+ buildahcli "github.com/containers/buildah/pkg/cli"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/opentracing/opentracing-go"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+ "github.com/spf13/pflag"
+)
+
+// pullOptionsWrapper wraps entities.ImagePullOptions and prevents leaking
+// CLI-only fields into the API types.
+type pullOptionsWrapper struct {
+ entities.ImagePullOptions
+ TLSVerifyCLI bool // CLI only
+}
+
+var (
+ pullOptions = pullOptionsWrapper{}
+ pullDescription = `Pulls an image from a registry and stores it locally.
+
+ An image can be pulled by tag or digest. If a tag is not specified, the image with the 'latest' tag is pulled.`
+
+ // Command: podman pull
+ pullCmd = &cobra.Command{
+ Use: "pull [flags] IMAGE",
+ Short: "Pull an image from a registry",
+ Long: pullDescription,
+ PreRunE: preRunE,
+ RunE: imagePull,
+ Example: `podman pull imageName
+ podman pull fedora:latest`,
+ }
+
+ // Command: podman image pull
+ // It's basically a clone of `pullCmd` with the exception of being a
+ // child of the images command.
+ imagesPullCmd = &cobra.Command{
+ Use: pullCmd.Use,
+ Short: pullCmd.Short,
+ Long: pullCmd.Long,
+ PreRunE: pullCmd.PreRunE,
+ RunE: pullCmd.RunE,
+ Example: `podman image pull imageName
+ podman image pull fedora:latest`,
+ }
+)
+
+func init() {
+ // pull
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: pullCmd,
+ })
+
+ pullCmd.SetHelpTemplate(registry.HelpTemplate())
+ pullCmd.SetUsageTemplate(registry.UsageTemplate())
+
+ flags := pullCmd.Flags()
+ pullFlags(flags)
+
+ // images pull
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: imagesPullCmd,
+ Parent: imageCmd,
+ })
+
+ imagesPullCmd.SetHelpTemplate(registry.HelpTemplate())
+ imagesPullCmd.SetUsageTemplate(registry.UsageTemplate())
+ imagesPullFlags := imagesPullCmd.Flags()
+ pullFlags(imagesPullFlags)
+}
+
+// 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.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")
+ flags.StringVar(&pullOptions.OverrideOS, "override-os", "", "Use `OS` instead of the running OS for choosing images")
+ flags.BoolVarP(&pullOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
+ flags.StringVar(&pullOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
+ flags.BoolVar(&pullOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
+
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("authfile")
+ _ = flags.MarkHidden("cert-dir")
+ _ = flags.MarkHidden("signature-policy")
+ _ = flags.MarkHidden("tls-verify")
+ }
+}
+
+// imagePull is implement the command for pulling images.
+func imagePull(cmd *cobra.Command, args []string) error {
+ // Sanity check input.
+ if len(args) == 0 {
+ return errors.Errorf("an image name must be specified")
+ }
+ if len(args) > 1 {
+ return errors.Errorf("too many arguments. Requires exactly 1")
+ }
+
+ // Start tracing if requested.
+ if cmd.Flags().Changed("trace") {
+ span, _ := opentracing.StartSpanFromContext(registry.GetContext(), "pullCmd")
+ defer span.Finish()
+ }
+
+ pullOptsAPI := pullOptions.ImagePullOptions
+ // 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
+ // boolean CLI flags.
+ if cmd.Flags().Changed("tls-verify") {
+ pullOptsAPI.TLSVerify = types.NewOptionalBool(pullOptions.TLSVerifyCLI)
+ }
+
+ // Let's do all the remaining Yoga in the API to prevent us from
+ // scattering logic across (too) many parts of the code.
+ pullReport, err := registry.ImageEngine().Pull(registry.GetContext(), args[0], pullOptsAPI)
+ if err != nil {
+ return err
+ }
+
+ if len(pullReport.Images) > 1 {
+ fmt.Println("Pulled Images:")
+ }
+ for _, img := range pullReport.Images {
+ fmt.Println(img)
+ }
+
+ return nil
+}
diff --git a/cmd/podmanV2/images/tag.go b/cmd/podmanV2/images/tag.go
new file mode 100644
index 000000000..f66fe7857
--- /dev/null
+++ b/cmd/podmanV2/images/tag.go
@@ -0,0 +1,34 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ tagDescription = "Adds one or more additional names to locally-stored image."
+ tagCommand = &cobra.Command{
+ Use: "tag [flags] IMAGE TARGET_NAME [TARGET_NAME...]",
+ Short: "Add an additional name to a local image",
+ Long: tagDescription,
+ RunE: tag,
+ Args: cobra.MinimumNArgs(2),
+ Example: `podman tag 0e3bbc2 fedora:latest
+ podman tag imageID:latest myNewImage:newTag
+ podman tag httpd myregistryhost:5000/fedora/httpd:v2`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: tagCommand,
+ })
+ tagCommand.SetHelpTemplate(registry.HelpTemplate())
+ tagCommand.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func tag(cmd *cobra.Command, args []string) error {
+ return registry.ImageEngine().Tag(registry.GetContext(), args[0], args[1:], entities.ImageTagOptions{})
+}
diff --git a/cmd/podmanV2/images/untag.go b/cmd/podmanV2/images/untag.go
new file mode 100644
index 000000000..c84827bb3
--- /dev/null
+++ b/cmd/podmanV2/images/untag.go
@@ -0,0 +1,33 @@
+package images
+
+import (
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+var (
+ untagCommand = &cobra.Command{
+ Use: "untag [flags] IMAGE [NAME...]",
+ Short: "Remove a name from a local image",
+ Long: "Removes one or more names from a locally-stored image.",
+ RunE: untag,
+ Args: cobra.MinimumNArgs(1),
+ Example: `podman untag 0e3bbc2
+ podman untag imageID:latest otherImageName:latest
+ podman untag httpd myregistryhost:5000/fedora/httpd:v2`,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: untagCommand,
+ })
+ untagCommand.SetHelpTemplate(registry.HelpTemplate())
+ untagCommand.SetUsageTemplate(registry.UsageTemplate())
+}
+
+func untag(cmd *cobra.Command, args []string) error {
+ return registry.ImageEngine().Untag(registry.GetContext(), args[0], args[1:], entities.ImageUntagOptions{})
+}
diff --git a/cmd/podmanV2/inspect.go b/cmd/podmanV2/inspect.go
new file mode 100644
index 000000000..4975cf632
--- /dev/null
+++ b/cmd/podmanV2/inspect.go
@@ -0,0 +1,62 @@
+package main
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/containers"
+ "github.com/containers/libpod/cmd/podmanV2/images"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+)
+
+// Inspect is one of the out layer commands in that it operates on images/containers/...
+
+var (
+ inspectOpts *entities.InspectOptions
+
+ // Command: podman _inspect_ Object_ID
+ inspectCmd = &cobra.Command{
+ Use: "inspect [flags] {CONTAINER_ID | IMAGE_ID}",
+ Args: cobra.ExactArgs(1),
+ Short: "Display the configuration of object denoted by ID",
+ Long: "Displays the low-level information on an object identified by name or ID",
+ TraverseChildren: true,
+ RunE: inspect,
+ }
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: inspectCmd,
+ })
+ inspectOpts = common.AddInspectFlagSet(inspectCmd)
+}
+
+func inspect(cmd *cobra.Command, args []string) error {
+ ie, err := registry.NewImageEngine(cmd, args)
+ if err != nil {
+ return err
+ }
+
+ if found, err := ie.Exists(context.Background(), args[0]); err != nil {
+ return err
+ } else if found.Value {
+ return images.Inspect(cmd, args, inspectOpts)
+ }
+
+ ce, err := registry.NewContainerEngine(cmd, args)
+ if err != nil {
+ return err
+ }
+
+ if found, err := ce.ContainerExists(context.Background(), args[0]); err != nil {
+ return err
+ } else if found.Value {
+ return containers.Inspect(cmd, args, inspectOpts)
+ }
+ return fmt.Errorf("%s not found on system", args[0])
+}
diff --git a/cmd/podmanV2/main.go b/cmd/podmanV2/main.go
index bd9fbb25e..6781a7f06 100644
--- a/cmd/podmanV2/main.go
+++ b/cmd/podmanV2/main.go
@@ -7,6 +7,7 @@ import (
"strings"
_ "github.com/containers/libpod/cmd/podmanV2/containers"
+ _ "github.com/containers/libpod/cmd/podmanV2/healthcheck"
_ "github.com/containers/libpod/cmd/podmanV2/images"
_ "github.com/containers/libpod/cmd/podmanV2/networks"
_ "github.com/containers/libpod/cmd/podmanV2/pods"
@@ -14,6 +15,7 @@ import (
_ "github.com/containers/libpod/cmd/podmanV2/volumes"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/storage/pkg/reexec"
"github.com/sirupsen/logrus"
)
@@ -44,6 +46,11 @@ func init() {
}
func main() {
+ if reexec.Init() {
+ // We were invoked with a different argv[0] indicating that we
+ // had a specific job to do as a subprocess, and it's done.
+ return
+ }
for _, c := range registry.Commands {
if Contains(registry.EngineOptions.EngineMode, c.Mode) {
parent := rootCmd
diff --git a/cmd/podmanV2/pods/pod.go b/cmd/podmanV2/pods/pod.go
index 81c0d33e1..3766893bb 100644
--- a/cmd/podmanV2/pods/pod.go
+++ b/cmd/podmanV2/pods/pod.go
@@ -1,6 +1,9 @@
package pods
import (
+ "strings"
+ "text/template"
+
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/spf13/cobra"
@@ -18,6 +21,33 @@ var (
}
)
+var podFuncMap = template.FuncMap{
+ "numCons": func(cons []*entities.ListPodContainer) int {
+ return len(cons)
+ },
+ "podcids": func(cons []*entities.ListPodContainer) string {
+ var ctrids []string
+ for _, c := range cons {
+ ctrids = append(ctrids, c.Id[:12])
+ }
+ return strings.Join(ctrids, ",")
+ },
+ "podconnames": func(cons []*entities.ListPodContainer) string {
+ var ctrNames []string
+ for _, c := range cons {
+ ctrNames = append(ctrNames, c.Names[:12])
+ }
+ return strings.Join(ctrNames, ",")
+ },
+ "podconstatuses": func(cons []*entities.ListPodContainer) string {
+ var statuses []string
+ for _, c := range cons {
+ statuses = append(statuses, c.Status)
+ }
+ return strings.Join(statuses, ",")
+ },
+}
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
diff --git a/cmd/podmanV2/pods/ps.go b/cmd/podmanV2/pods/ps.go
index d4c625b2e..9546dff9e 100644
--- a/cmd/podmanV2/pods/ps.go
+++ b/cmd/podmanV2/pods/ps.go
@@ -1,8 +1,19 @@
package pods
import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "os"
+ "strings"
+ "text/tabwriter"
+ "text/template"
+
"github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/report"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -19,14 +30,137 @@ var (
}
)
+var (
+ defaultHeaders string = "POD ID\tNAME\tSTATUS\tCREATED"
+ inputFilters string
+ noTrunc bool
+ psInput entities.PodPSOptions
+)
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: psCmd,
Parent: podCmd,
})
+ flags := psCmd.Flags()
+ flags.BoolVar(&psInput.CtrNames, "ctr-names", false, "Display the container names")
+ flags.BoolVar(&psInput.CtrIds, "ctr-ids", false, "Display the container UUIDs. If no-trunc is not set they will be truncated")
+ flags.BoolVar(&psInput.CtrStatus, "ctr-status", false, "Display the container status")
+ // TODO should we make this a [] ?
+ flags.StringVarP(&inputFilters, "filter", "f", "", "Filter output based on conditions given")
+ flags.StringVar(&psInput.Format, "format", "", "Pretty-print pods to JSON or using a Go template")
+ flags.BoolVarP(&psInput.Latest, "latest", "l", false, "Act on the latest pod podman is aware of")
+ flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod")
+ flags.BoolVar(&psInput.Namespace, "ns", false, "Display namespace information of the pod")
+ flags.BoolVar(&noTrunc, "no-trunc", false, "Do not truncate pod and container IDs")
+ flags.BoolVarP(&psInput.Quiet, "quiet", "q", false, "Print the numeric IDs of the pods only")
+ flags.StringVar(&psInput.Sort, "sort", "created", "Sort output by created, id, name, or number")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
}
func pods(cmd *cobra.Command, args []string) error {
+ var (
+ w io.Writer = os.Stdout
+ row string
+ )
+ if cmd.Flag("filter").Changed {
+ for _, f := range strings.Split(inputFilters, ",") {
+ split := strings.Split(f, "=")
+ if len(split) < 2 {
+ return errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ }
+ psInput.Filters[split[0]] = append(psInput.Filters[split[0]], split[1])
+ }
+ }
+ responses, err := registry.ContainerEngine().PodPs(context.Background(), psInput)
+ if err != nil {
+ return err
+ }
+
+ if psInput.Format == "json" {
+ b, err := json.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+ }
+ headers, row := createPodPsOut(cmd)
+ if psInput.Quiet {
+ if noTrunc {
+ row = "{{.Id}}\n"
+ } else {
+ row = "{{slice .Id 0 12}}\n"
+ }
+ }
+ if cmd.Flag("format").Changed {
+ row = psInput.Format
+ if !strings.HasPrefix(row, "\n") {
+ row += "\n"
+ }
+ }
+ format := "{{range . }}" + row + "{{end}}"
+ if !psInput.Quiet && !cmd.Flag("format").Changed {
+ format = headers + format
+ }
+ funcs := report.AppendFuncMap(podFuncMap)
+ tmpl, err := template.New("listPods").Funcs(funcs).Parse(format)
+ if err != nil {
+ return err
+ }
+ if !psInput.Quiet {
+ w = tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ }
+ if err := tmpl.Execute(w, responses); err != nil {
+ return err
+ }
+ if flusher, ok := w.(interface{ Flush() error }); ok {
+ return flusher.Flush()
+ }
return nil
}
+
+func createPodPsOut(cmd *cobra.Command) (string, string) {
+ var row string
+ headers := defaultHeaders
+ if noTrunc {
+ row += "{{.Id}}"
+ } else {
+ row += "{{slice .Id 0 12}}"
+ }
+
+ row += "\t{{.Name}}\t{{.Status}}\t{{humanDurationFromTime .Created}}"
+
+ //rowFormat string = "{{slice .Id 0 12}}\t{{.Name}}\t{{.Status}}\t{{humanDurationFromTime .Created}}"
+ if psInput.CtrIds {
+ headers += "\tIDS"
+ row += "\t{{podcids .Containers}}"
+ }
+ if psInput.CtrNames {
+ headers += "\tNAMES"
+ row += "\t{{podconnames .Containers}}"
+ }
+ if psInput.CtrStatus {
+ headers += "\tSTATUS"
+ row += "\t{{podconstatuses .Containers}}"
+ }
+ if psInput.Namespace {
+ headers += "\tCGROUP\tNAMESPACES"
+ row += "\t{{.Cgroup}}\t{{.Namespace}}"
+ }
+ if !psInput.CtrStatus && !psInput.CtrNames && !psInput.CtrIds {
+ headers += "\t# OF CONTAINERS"
+ row += "\t{{numCons .Containers}}"
+
+ }
+ headers += "\tINFRA ID\n"
+ if noTrunc {
+ row += "\t{{.InfraId}}\n"
+ } else {
+ row += "\t{{slice .InfraId 0 12}}\n"
+ }
+ return headers, row
+}
diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go
index 5cdb8a840..401f82718 100644
--- a/cmd/podmanV2/registry/registry.go
+++ b/cmd/podmanV2/registry/registry.go
@@ -3,7 +3,6 @@ package registry
import (
"context"
- "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/infra"
"github.com/pkg/errors"
@@ -18,24 +17,24 @@ type CliCommand struct {
Parent *cobra.Command
}
-var (
- Commands []CliCommand
+const ExecErrorCodeGeneric = 125
- imageEngine entities.ImageEngine
- containerEngine entities.ContainerEngine
+var (
cliCtx context.Context
+ containerEngine entities.ContainerEngine
+ exitCode = ExecErrorCodeGeneric
+ imageEngine entities.ImageEngine
+ Commands []CliCommand
EngineOptions entities.EngineOptions
-
- ExitCode = define.ExecErrorCodeGeneric
)
func SetExitCode(code int) {
- ExitCode = code
+ exitCode = code
}
func GetExitCode() int {
- return ExitCode
+ return exitCode
}
// HelpTemplate returns the help template for podman commands
diff --git a/cmd/podmanV2/report/templates.go b/cmd/podmanV2/report/templates.go
index f3bc06405..e46048e97 100644
--- a/cmd/podmanV2/report/templates.go
+++ b/cmd/podmanV2/report/templates.go
@@ -19,6 +19,9 @@ var defaultFuncMap = template.FuncMap{
"humanDuration": func(t int64) string {
return units.HumanDuration(time.Since(time.Unix(t, 0))) + " ago"
},
+ "humanDurationFromTime": func(t time.Time) string {
+ return units.HumanDuration(time.Since(t)) + " ago"
+ },
"humanSize": func(sz int64) string {
s := units.HumanSizeWithPrecision(float64(sz), 3)
i := strings.LastIndexFunc(s, unicode.IsNumber)
diff --git a/cmd/podmanV2/root.go b/cmd/podmanV2/root.go
index cb4cb4e00..6fc12f57e 100644
--- a/cmd/podmanV2/root.go
+++ b/cmd/podmanV2/root.go
@@ -7,7 +7,6 @@ import (
"path"
"github.com/containers/libpod/cmd/podmanV2/registry"
- "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/version"
"github.com/sirupsen/logrus"
@@ -88,8 +87,8 @@ func Execute() {
o := registry.NewOptions(rootCmd.Context(), &registry.EngineOptions)
if err := rootCmd.ExecuteContext(o); err != nil {
fmt.Fprintln(os.Stderr, "Error:", err.Error())
- } else if registry.GetExitCode() == define.ExecErrorCodeGeneric {
- // The exitCode modified from define.ExecErrorCodeGeneric,
+ } else if registry.GetExitCode() == registry.ExecErrorCodeGeneric {
+ // The exitCode modified from registry.ExecErrorCodeGeneric,
// indicates an application
// running inside of a container failed, as opposed to the
// podman command failed. Must exit with that exit code
diff --git a/cmd/podmanV2/system/system.go b/cmd/podmanV2/system/system.go
index 30ed328e8..4e805c7bd 100644
--- a/cmd/podmanV2/system/system.go
+++ b/cmd/podmanV2/system/system.go
@@ -1,4 +1,4 @@
-package images
+package system
import (
"github.com/containers/libpod/cmd/podmanV2/registry"
diff --git a/contrib/build_rpm.sh b/contrib/build_rpm.sh
index de6941199..a9db029df 100755
--- a/contrib/build_rpm.sh
+++ b/contrib/build_rpm.sh
@@ -7,11 +7,18 @@ echo "Package manager binary: $pkg_manager"
if [[ $pkg_manager == *yum ]]; then
- echo "[virt7-container-common-candidate]
-name=virt7-container-common-candidate
-baseurl=https://cbs.centos.org/repos/virt7-container-common-candidate/x86_64/os/
+ echo "[virt7-container]
+name=virt7-container
+baseurl=https://buildlogs.centos.org/centos/7/virt/x86_64/container/
enabled=1
gpgcheck=0" > /etc/yum.repos.d/container_virt.repo
+ echo "[paas_openshift_origin311]
+name=paas_openshift_origin311
+# Use this if you need the newest *-testing packages
+# baseurl=https://buildlogs.centos.org/centos/7/paas/x86_64/openshift-origin311/
+baseurl=http://mirror.centos.org/centos/7/paas/x86_64/openshift-origin311/
+enabled=1
+gpgcheck=0" > /etc/yum.repos.d/paas_openshift_origin311.repo
fi
declare -a PKGS=(\
diff --git a/contrib/cirrus/README.md b/contrib/cirrus/README.md
index 3789965d6..709985b5b 100644
--- a/contrib/cirrus/README.md
+++ b/contrib/cirrus/README.md
@@ -80,7 +80,7 @@ having `SPECIALMODE` set to 'cgroupv2`
Modifying the contents of cache-images is tested by making changes to
one or more of the ``./contrib/cirrus/packer/*_setup.sh`` files. Then
-in the PR description, add the magic string: ``***CIRRUS: TEST IMAGES***``
+in the PR description, add the magic string: ``[CI:IMG]``
***N/B: Steps below are performed by automation***
@@ -153,7 +153,7 @@ env:
***NOTES:***
* If re-using the same PR with new images in `.cirrus.yml`,
take care to also *update the PR description* to remove
- the magic ``***CIRRUS: TEST IMAGES***`` string. Keeping it and
+ the magic ``[CI:IMG]`` string. Keeping it and
`--force` pushing would needlessly cause Cirrus-CI to build
and test images again.
* In the future, if you need to review the log from the build that produced
diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in
index 17adf9672..9641a52e6 100644
--- a/contrib/spec/podman.spec.in
+++ b/contrib/spec/podman.spec.in
@@ -392,7 +392,7 @@ popd
ln -s vendor src
export GO111MODULE=off
export GOPATH=$(pwd)/_build:$(pwd):$(pwd):%{gopath}
-export BUILDTAGS="varlink selinux seccomp $(%{hackdir}/hack/btrfs_installed_tag.sh) $(%{hackdir}/hack/btrfs_tag.sh) $(%{hackdir}/hack/libdm_tag.sh) exclude_graphdriver_devicemapper"
+export BUILDTAGS="varlink selinux seccomp systemd $(%{hackdir}/hack/btrfs_installed_tag.sh) $(%{hackdir}/hack/btrfs_tag.sh) $(%{hackdir}/hack/libdm_tag.sh) exclude_graphdriver_devicemapper"
GOPATH=$GOPATH go generate ./pkg/varlink/...
@@ -410,7 +410,7 @@ mkdir -p src/%{provider}.%{provider_tld}/{containers,opencontainers}
ln -s $(dirs +1 -l) src/%{import_path_conmon}
popd
-export BUILDTAGS="selinux seccomp $(%{hackdir}/hack/btrfs_installed_tag.sh) $(%{hackdir}/hack/btrfs_tag.sh)"
+export BUILDTAGS="selinux seccomp systemd $(%{hackdir}/hack/btrfs_installed_tag.sh) $(%{hackdir}/hack/btrfs_tag.sh)"
BUILDTAGS=$BUILDTAGS make
popd
diff --git a/go.sum b/go.sum
index c77bfb88b..ac6a4fca1 100644
--- a/go.sum
+++ b/go.sum
@@ -301,6 +301,7 @@ github.com/mtrmac/gpgme v0.1.2/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgi
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -596,6 +597,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
diff --git a/hack/podmanv2-retry b/hack/podmanv2-retry
new file mode 100755
index 000000000..ea77486ff
--- /dev/null
+++ b/hack/podmanv2-retry
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# podman-try - try running a command via PODMAN1; use PODMAN2 as fallback
+#
+# Intended for use with a podmanv2 client. If a command isn't yet
+# implemented, fall back to regular podman:
+#
+# Set PODMAN_V2 to the path to a podman v2 client
+# Set PODMAN_FALLBACK to the path to regular podman
+#
+# THIS IS IMPERFECT. In particular, it will not work if stdin is redirected
+# (e.g. 'podman ... < file' or 'something | podman'); nor for anything
+# that generates continuous output ('podman logs -f'); and probably more
+# situations.
+#
+
+die() {
+ echo "$(basename $0): $*" >&2
+ exit 1
+}
+
+test -n "$PODMAN_V2" || die "Please set \$PODMAN_V2 (path to podman v2)"
+test -n "$PODMAN_FALLBACK" || die "Please set \$PODMAN_FALLBACK (path to podman)"
+
+
+result=$(${PODMAN_V2} "$@" 2>&1)
+rc=$?
+
+if [ $rc == 125 ]; then
+ if [[ "$result" =~ unrecognized\ command|unknown\ flag|unknown\ shorthand ]]; then
+ result=$(${PODMAN_FALLBACK} "$@")
+ rc=$?
+ fi
+fi
+
+echo -n "$result"
+exit $rc
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 5f914ed79..80cc6f15a 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -512,8 +512,8 @@ func getImageDigest(ctx context.Context, src types.ImageReference, sc *types.Sys
return "@" + imageDigest.Hex(), nil
}
-// normalizedTag returns the canonical version of tag for use in Image.Names()
-func normalizedTag(tag string) (reference.Named, error) {
+// NormalizedTag returns the canonical version of tag for use in Image.Names()
+func NormalizedTag(tag string) (reference.Named, error) {
decomposedTag, err := decompose(tag)
if err != nil {
return nil, err
@@ -541,7 +541,7 @@ func (i *Image) TagImage(tag string) error {
if err := i.reloadImage(); err != nil {
return err
}
- ref, err := normalizedTag(tag)
+ ref, err := NormalizedTag(tag)
if err != nil {
return err
}
diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go
index 19f7eee1e..3cd368cdc 100644
--- a/libpod/image/image_test.go
+++ b/libpod/image/image_test.go
@@ -292,7 +292,7 @@ func TestNormalizedTag(t *testing.T) {
{"ns/busybox:latest", "localhost/ns/busybox:latest"}, // Unqualified with a dot-less namespace
{"docker.io/busybox:latest", "docker.io/library/busybox:latest"}, // docker.io without /library/
} {
- res, err := normalizedTag(c.input)
+ res, err := NormalizedTag(c.input)
if c.expected == "" {
assert.Error(t, err, c.input)
} else {
diff --git a/libpod/podfilters/pods.go b/libpod/podfilters/pods.go
new file mode 100644
index 000000000..54fa85edc
--- /dev/null
+++ b/libpod/podfilters/pods.go
@@ -0,0 +1,115 @@
+package podfilters
+
+import (
+ "strconv"
+ "strings"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+)
+
+// GeneratePodFilterFunc takes a filter and filtervalue (key, value)
+// and generates a libpod function that can be used to filter
+// pods
+func GeneratePodFilterFunc(filter, filterValue string) (
+ func(pod *libpod.Pod) bool, error) {
+ switch filter {
+ case "ctr-ids":
+ return func(p *libpod.Pod) bool {
+ ctrIds, err := p.AllContainersByID()
+ if err != nil {
+ return false
+ }
+ return util.StringInSlice(filterValue, ctrIds)
+ }, nil
+ case "ctr-names":
+ return func(p *libpod.Pod) bool {
+ ctrs, err := p.AllContainers()
+ if err != nil {
+ return false
+ }
+ for _, ctr := range ctrs {
+ if filterValue == ctr.Name() {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "ctr-number":
+ return func(p *libpod.Pod) bool {
+ ctrIds, err := p.AllContainersByID()
+ if err != nil {
+ return false
+ }
+
+ fVint, err2 := strconv.Atoi(filterValue)
+ if err2 != nil {
+ return false
+ }
+ return len(ctrIds) == fVint
+ }, nil
+ case "ctr-status":
+ if !util.StringInSlice(filterValue,
+ []string{"created", "restarting", "running", "paused",
+ "exited", "unknown"}) {
+ return nil, errors.Errorf("%s is not a valid status", filterValue)
+ }
+ return func(p *libpod.Pod) bool {
+ ctr_statuses, err := p.Status()
+ if err != nil {
+ return false
+ }
+ for _, ctr_status := range ctr_statuses {
+ state := ctr_status.String()
+ if ctr_status == define.ContainerStateConfigured {
+ state = "created"
+ }
+ if state == filterValue {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "id":
+ return func(p *libpod.Pod) bool {
+ return strings.Contains(p.ID(), filterValue)
+ }, nil
+ case "name":
+ return func(p *libpod.Pod) bool {
+ return strings.Contains(p.Name(), filterValue)
+ }, nil
+ case "status":
+ if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created"}) {
+ return nil, errors.Errorf("%s is not a valid pod status", filterValue)
+ }
+ return func(p *libpod.Pod) bool {
+ status, err := p.GetPodStatus()
+ if err != nil {
+ return false
+ }
+ if strings.ToLower(status) == filterValue {
+ return true
+ }
+ return false
+ }, nil
+ case "label":
+ var filterArray = strings.SplitN(filterValue, "=", 2)
+ var filterKey = filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ return func(p *libpod.Pod) bool {
+ for labelKey, labelValue := range p.Labels() {
+ if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
+ return true
+ }
+ }
+ return false
+ }, nil
+ }
+ return nil, errors.Errorf("%s is an invalid filter", filter)
+}
diff --git a/pkg/api/handlers/compat/containers_export.go b/pkg/api/handlers/compat/containers_export.go
new file mode 100644
index 000000000..37b9fbf2b
--- /dev/null
+++ b/pkg/api/handlers/compat/containers_export.go
@@ -0,0 +1,42 @@
+package compat
+
+import (
+ "io/ioutil"
+ "net/http"
+ "os"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/pkg/errors"
+)
+
+func ExportContainer(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ name := utils.GetName(r)
+ con, err := runtime.LookupContainer(name)
+ if err != nil {
+ utils.ContainerNotFound(w, name, err)
+ return
+ }
+ tmpfile, err := ioutil.TempFile("", "api.tar")
+ if err != nil {
+ utils.Error(w, "unable to create tarball tempfile", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
+ return
+ }
+ defer os.Remove(tmpfile.Name())
+ if err := tmpfile.Close(); err != nil {
+ utils.Error(w, "unable to close tempfile", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile"))
+ return
+ }
+ if err := con.Export(tmpfile.Name()); err != nil {
+ utils.Error(w, "failed to save the image", http.StatusInternalServerError, errors.Wrap(err, "failed to save image"))
+ return
+ }
+ rdr, err := os.Open(tmpfile.Name())
+ if err != nil {
+ utils.Error(w, "failed to read temp tarball", http.StatusInternalServerError, errors.Wrap(err, "failed to read the exported tarfile"))
+ return
+ }
+ defer rdr.Close()
+ utils.WriteResponse(w, http.StatusOK, rdr)
+}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 4b24d7d9f..19c6102be 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -254,7 +254,7 @@ func ImagesLoad(w http.ResponseWriter, r *http.Request) {
return
}
}
- utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesLoadReport{ID: loadedImage})
+ utils.WriteResponse(w, http.StatusOK, entities.ImageLoadReport{Name: loadedImage})
}
func ImagesImport(w http.ResponseWriter, r *http.Request) {
@@ -303,6 +303,10 @@ func ImagesImport(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, handlers.LibpodImagesImportReport{ID: importedImage})
}
+// ImagesPull is the v2 libpod endpoint for pulling images. Note that the
+// mandatory `reference` must be a reference to a registry (i.e., of docker
+// transport or be normalized to one). Other transports are rejected as they
+// do not make sense in a remote context.
func ImagesPull(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
@@ -328,10 +332,11 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
return
}
// Enforce the docker transport. This is just a precaution as some callers
- // might accustomed to using the "transport:reference" notation. Using
+ // might be accustomed to using the "transport:reference" notation. Using
// another than the "docker://" transport does not really make sense for a
// remote case. For loading tarballs, the load and import endpoints should
// be used.
+ dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
imageRef, err := alltransports.ParseImageName(query.Reference)
if err == nil && imageRef.Transport().Name() != docker.Transport.Name() {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
@@ -339,7 +344,7 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
return
} else if err != nil {
origErr := err
- imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s://%s", docker.Transport.Name(), query.Reference))
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, query.Reference))
if err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
errors.Wrapf(origErr, "reference %q must be a docker reference", query.Reference))
@@ -347,16 +352,19 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) {
}
}
+ // Trim the docker-transport prefix.
+ rawImage := strings.TrimPrefix(query.Reference, dockerPrefix)
+
// all-tags doesn't work with a tagged reference, so let's check early
- namedRef, err := reference.Parse(query.Reference)
+ namedRef, err := reference.Parse(rawImage)
if err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Wrapf(err, "error parsing reference %q", query.Reference))
+ errors.Wrapf(err, "error parsing reference %q", rawImage))
return
}
if _, isTagged := namedRef.(reference.Tagged); isTagged && query.AllTags {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
- errors.Errorf("reference %q must not have a tag for all-tags", query.Reference))
+ errors.Errorf("reference %q must not have a tag for all-tags", rawImage))
return
}
@@ -502,3 +510,29 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, handlers.IDResponse{ID: commitImage.ID()}) // nolint
}
+
+func UntagImage(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+
+ name := utils.GetName(r)
+ newImage, err := runtime.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ utils.ImageNotFound(w, name, errors.Wrapf(err, "Failed to find image %s", name))
+ return
+ }
+ tag := "latest"
+ if len(r.Form.Get("tag")) > 0 {
+ tag = r.Form.Get("tag")
+ }
+ if len(r.Form.Get("repo")) < 1 {
+ utils.Error(w, "repo tag is required", http.StatusBadRequest, errors.New("repo parameter is required to tag an image"))
+ return
+ }
+ repo := r.Form.Get("repo")
+ tagName := fmt.Sprintf("%s:%s", repo, tag)
+ if err := newImage.UntagImage(tagName); err != nil {
+ utils.Error(w, "failed to untag", http.StatusInternalServerError, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusCreated, "")
+}
diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go
index 52763a050..12de3a3bd 100644
--- a/pkg/api/handlers/swagger.go
+++ b/pkg/api/handlers/swagger.go
@@ -31,7 +31,7 @@ type swagImageInspect struct {
// swagger:response DocsLibpodImagesLoadResponse
type swagLibpodImagesLoadResponse struct {
// in:body
- Body []LibpodImagesLoadReport
+ Body entities.ImageLoadReport
}
// Import response
diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go
index 79d1a5090..d47053eda 100644
--- a/pkg/api/handlers/utils/pods.go
+++ b/pkg/api/handlers/utils/pods.go
@@ -59,6 +59,10 @@ func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport
if err != nil {
return nil, err
}
+ infraId, err := pod.InfraContainerID()
+ if err != nil {
+ return nil, err
+ }
lp := entities.ListPodsReport{
Cgroup: pod.CgroupParent(),
Created: pod.CreatedTime(),
@@ -66,6 +70,7 @@ func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport
Name: pod.Name(),
Namespace: pod.Namespace(),
Status: status,
+ InfraId: infraId,
}
for _, ctr := range ctrs {
state, err := ctr.State()
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 08834ff01..145c054c0 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -587,6 +587,29 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
r.HandleFunc(VersionedPath("/containers/{name}/resize"), s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
// Added non version path to URI to support docker non versioned paths
r.HandleFunc("/containers/{name}/resize", s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
+ // swagger:operation GET /containers/{name}/export compat exportContainer
+ // ---
+ // tags:
+ // - containers (compat)
+ // summary: Export a container
+ // description: Export the contents of a container as a tarball.
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/containers/{name}/export"), s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
+ r.HandleFunc("/containers/{name}/export", s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
/*
libpod endpoints
@@ -1237,5 +1260,27 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// 500:
// $ref: "#/responses/InternalError"
r.HandleFunc(VersionedPath("/libpod/containers/{name}/resize"), s.APIHandler(compat.ResizeContainer)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/containers/{name}/export libpod libpodExportContainer
+ // ---
+ // tags:
+ // - containers
+ // summary: Export a container
+ // description: Export the contents of a container as a tarball.
+ // parameters:
+ // - in: path
+ // name: name
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // description: tarball is returned in body
+ // 404:
+ // $ref: "#/responses/NoSuchContainer"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.HandleFunc(VersionedPath("/libpod/containers/{name}/export"), s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index e8dfe2fa8..74b245a77 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -1019,5 +1019,39 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/commit"), s.APIHandler(libpod.CommitContainer)).Methods(http.MethodPost)
+ // swagger:operation POST /libpod/images/{name:.*}/untag libpod libpodUntagImage
+ // ---
+ // tags:
+ // - images
+ // summary: Untag an image
+ // description: Untag an image
+ // parameters:
+ // - in: path
+ // name: name:.*
+ // type: string
+ // required: true
+ // description: the name or ID of the container
+ // - in: query
+ // name: repo
+ // type: string
+ // description: the repository to untag
+ // - in: query
+ // name: tag
+ // type: string
+ // description: the name of the tag to untag
+ // produces:
+ // - application/json
+ // responses:
+ // 201:
+ // description: no error
+ // 400:
+ // $ref: '#/responses/BadParamError'
+ // 404:
+ // $ref: '#/responses/NoSuchImage'
+ // 409:
+ // $ref: '#/responses/ConflictError'
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/libpod/images/{name:.*}/untag"), s.APIHandler(libpod.UntagImage)).Methods(http.MethodPost)
return nil
}
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index bad1294f4..49a2dfd58 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -2,6 +2,7 @@ package containers
import (
"context"
+ "io"
"net/http"
"net/url"
"strconv"
@@ -296,3 +297,22 @@ func Stop(ctx context.Context, nameOrID string, timeout *uint) error {
}
return response.Process(nil)
}
+
+// Export creates a tarball of the given name or ID of a container. It
+// requires an io.Writer be provided to write the tarball.
+func Export(ctx context.Context, nameOrID string, w io.Writer) error {
+ params := url.Values{}
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/export", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ if response.StatusCode/100 == 2 {
+ _, err = io.Copy(w, response.Body)
+ return err
+ }
+ return response.Process(nil)
+}
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index 5e3af7a60..6c739494b 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -8,10 +8,10 @@ import (
"net/url"
"strconv"
+ "github.com/containers/image/v5/types"
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/domain/entities"
- "github.com/containers/libpod/pkg/inspect"
)
// Exists a lightweight way to determine if an image exists in local storage. It returns a
@@ -56,7 +56,7 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*entit
// Get performs an image inspect. To have the on-disk size of the image calculated, you can
// use the optional size parameter.
-func GetImage(ctx context.Context, nameOrID string, size *bool) (*inspect.ImageData, error) {
+func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.ImageData, error) {
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
@@ -65,7 +65,7 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*inspect.ImageD
if size != nil {
params.Set("size", strconv.FormatBool(*size))
}
- inspectedData := inspect.ImageData{}
+ inspectedData := entities.ImageData{}
response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nameOrID)
if err != nil {
return &inspectedData, err
@@ -91,11 +91,11 @@ func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse,
return history, response.Process(&history)
}
-func Load(ctx context.Context, r io.Reader, name *string) (string, error) {
- var id handlers.IDResponse
+func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadReport, error) {
+ var report entities.ImageLoadReport
conn, err := bindings.GetClient(ctx)
if err != nil {
- return "", err
+ return nil, err
}
params := url.Values{}
if name != nil {
@@ -103,9 +103,9 @@ func Load(ctx context.Context, r io.Reader, name *string) (string, error) {
}
response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params)
if err != nil {
- return "", err
+ return nil, err
}
- return id.ID, response.Process(&id)
+ return &report, response.Process(&report)
}
// Remove deletes an image from local storage. The optional force parameter will forcibly remove
@@ -196,6 +196,22 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error {
return response.Process(nil)
}
+// Untag removes a name from locally-stored image. Both the tag and repo parameters are required.
+func Untag(ctx context.Context, nameOrID, tag, repo string) error {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return err
+ }
+ params := url.Values{}
+ params.Set("tag", tag)
+ params.Set("repo", repo)
+ response, err := conn.DoRequest(nil, http.MethodPost, "/images/%s/untag", params, nameOrID)
+ if err != nil {
+ return err
+ }
+ return response.Process(nil)
+}
+
func Build(nameOrId string) {}
// Imports adds the given image to the local image store. This can be done by file and the given reader
@@ -229,3 +245,41 @@ func Import(ctx context.Context, changes []string, message, reference, u *string
}
return id.ID, response.Process(&id)
}
+
+// Pull is the binding for libpod's v2 endpoints for pulling images. Note that
+// `rawImage` must be a reference to a registry (i.e., of docker transport or be
+// normalized to one). Other transports are rejected as they do not make sense
+// in a remote context.
+func Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) ([]string, error) {
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ params := url.Values{}
+ params.Set("reference", rawImage)
+ params.Set("credentials", options.Credentials)
+ params.Set("overrideArch", options.OverrideArch)
+ params.Set("overrideOS", options.OverrideOS)
+ if options.TLSVerify != types.OptionalBoolUndefined {
+ val := bool(options.TLSVerify == types.OptionalBoolTrue)
+ params.Set("tlsVerify", strconv.FormatBool(val))
+ }
+ params.Set("allTags", strconv.FormatBool(options.AllTags))
+
+ response, err := conn.DoRequest(nil, http.MethodPost, "/images/pull", params)
+ if err != nil {
+ return nil, err
+ }
+
+ reports := []handlers.LibpodImagesPullReport{}
+ if err := response.Process(&reports); err != nil {
+ return nil, err
+ }
+
+ pulledImages := []string{}
+ for _, r := range reports {
+ pulledImages = append(pulledImages, r.ID)
+ }
+
+ return pulledImages, nil
+}
diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go
index 13b6086c3..992720196 100644
--- a/pkg/bindings/test/images_test.go
+++ b/pkg/bindings/test/images_test.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/bindings/images"
+ "github.com/containers/libpod/pkg/domain/entities"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
@@ -218,7 +219,7 @@ var _ = Describe("Podman images", func() {
Expect(err).To(BeNil())
names, err := images.Load(bt.conn, f, nil)
Expect(err).To(BeNil())
- Expect(names).To(Equal(alpine.name))
+ Expect(names.Name).To(Equal(alpine.name))
exists, err = images.Exists(bt.conn, alpine.name)
Expect(err).To(BeNil())
Expect(exists).To(BeTrue())
@@ -234,7 +235,7 @@ var _ = Describe("Podman images", func() {
newName := "quay.io/newname:fizzle"
names, err = images.Load(bt.conn, f, &newName)
Expect(err).To(BeNil())
- Expect(names).To(Equal(alpine.name))
+ Expect(names.Name).To(Equal(alpine.name))
exists, err = images.Exists(bt.conn, newName)
Expect(err).To(BeNil())
Expect(exists).To(BeTrue())
@@ -353,4 +354,24 @@ var _ = Describe("Podman images", func() {
Expect(results).To(ContainElement("docker.io/library/alpine:latest"))
})
+ // TODO: we really need to extent to pull tests once we have a more sophisticated CI.
+ It("Image Pull", func() {
+ rawImage := "docker.io/library/busybox:latest"
+
+ pulledImages, err := images.Pull(bt.conn, rawImage, entities.ImagePullOptions{})
+ Expect(err).To(BeNil())
+ Expect(len(pulledImages)).To(Equal(1))
+
+ exists, err := images.Exists(bt.conn, rawImage)
+ Expect(err).To(BeNil())
+ Expect(exists).To(BeTrue())
+
+ // Make sure the normalization AND the full-transport reference works.
+ _, err = images.Pull(bt.conn, "docker://"+rawImage, entities.ImagePullOptions{})
+ Expect(err).To(BeNil())
+
+ // The v2 endpoint only supports the docker transport. Let's see if that's really true.
+ _, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", entities.ImagePullOptions{})
+ Expect(err).To(Not(BeNil()))
+ })
})
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index 3389e4db5..d51124f55 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -98,12 +98,6 @@ type RmReport struct {
Id string
}
-type ContainerInspectOptions struct {
- Format string
- Latest bool
- Size bool
-}
-
type ContainerInspectReport struct {
*define.InspectContainerData
}
@@ -123,3 +117,7 @@ type CommitOptions struct {
type CommitReport struct {
Id string
}
+
+type ContainerExportOptions struct {
+ Output string
+}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index dddaa6013..a122857cd 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -2,34 +2,38 @@ package entities
import (
"context"
+
+ "github.com/containers/libpod/libpod/define"
)
type ContainerEngine interface {
ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error)
ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error)
- ContainerInspect(ctx context.Context, namesOrIds []string, options ContainerInspectOptions) ([]*ContainerInspectReport, error)
+ ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error)
+ ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
- ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
- ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
+ ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
+ ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
+ HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error)
PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error)
+ PodPs(ctx context.Context, options PodPSOptions) ([]*ListPodsReport, error)
PodRestart(ctx context.Context, namesOrIds []string, options PodRestartOptions) ([]*PodRestartReport, error)
+ PodRm(ctx context.Context, namesOrIds []string, options PodRmOptions) ([]*PodRmReport, error)
PodStart(ctx context.Context, namesOrIds []string, options PodStartOptions) ([]*PodStartReport, error)
PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error)
- PodRm(ctx context.Context, namesOrIds []string, options PodRmOptions) ([]*PodRmReport, error)
- PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error)
PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error)
-
+ PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error)
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error)
VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error)
- VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
- VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error)
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
+ VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error)
+ VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
}
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index d0c860a04..4cc2a67b9 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -8,6 +8,11 @@ type ImageEngine interface {
Delete(ctx context.Context, nameOrId []string, opts ImageDeleteOptions) (*ImageDeleteReport, error)
Exists(ctx context.Context, nameOrId string) (*BoolReport, error)
History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error)
+ Inspect(ctx context.Context, names []string, opts InspectOptions) (*ImageInspectReport, error)
List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error)
Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
+ Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error)
+ Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error
+ Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error
+ Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error)
}
diff --git a/pkg/domain/entities/healthcheck.go b/pkg/domain/entities/healthcheck.go
new file mode 100644
index 000000000..a880805f9
--- /dev/null
+++ b/pkg/domain/entities/healthcheck.go
@@ -0,0 +1,3 @@
+package entities
+
+type HealthCheckOptions struct{}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 20af0356f..a6c7baebd 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -4,6 +4,8 @@ import (
"net/url"
"github.com/containers/image/v5/manifest"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/pkg/inspect"
docker "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/opencontainers/go-digest"
@@ -11,7 +13,6 @@ import (
)
type Image struct {
- IdOrNamed
ID string `json:"Id"`
RepoTags []string `json:",omitempty"`
RepoDigests []string `json:",omitempty"`
@@ -110,11 +111,37 @@ type ImageHistoryReport struct {
Layers []ImageHistoryLayer
}
-type ImageInspectOptions struct {
- TypeObject string `json:",omitempty"`
- Format string `json:",omitempty"`
- Size bool `json:",omitempty"`
- Latest bool `json:",omitempty"`
+// ImagePullOptions are the arguments for pulling images.
+type ImagePullOptions struct {
+ // AllTags can be specified to pull all tags of the spiecifed image. Note
+ // that this only works if the specified image does not include a tag.
+ AllTags bool
+ // Authfile is the path to the authentication file. Ignored for remote
+ // calls.
+ Authfile string
+ // CertDir is the path to certificate directories. Ignored for remote
+ // calls.
+ CertDir string
+ // Credentials for authenticating against the registry in the format
+ // USERNAME:PASSWORD.
+ Credentials string
+ // OverrideArch will overwrite the local architecture for image pulls.
+ OverrideArch string
+ // OverrideOS will overwrite the local operating system (OS) for image
+ // pulls.
+ OverrideOS string
+ // Quiet can be specified to suppress pull progress when pulling. Ignored
+ // for remote calls.
+ Quiet bool
+ // SignaturePolicy to use when pulling. Ignored for remote calls.
+ SignaturePolicy string
+ // TLSVerify to enable/disable HTTPS and certificate verification.
+ TLSVerify types.OptionalBool
+}
+
+// ImagePullReport is the response from pulling one or more images.
+type ImagePullReport struct {
+ Images []string
}
type ImageListOptions struct {
@@ -123,10 +150,6 @@ type ImageListOptions struct {
Filters url.Values `json:"filters" schema:"filters"`
}
-// type ImageListReport struct {
-// Images []ImageSummary
-// }
-
type ImagePruneOptions struct {
All bool `json:"all" schema:"all"`
Filter []string `json:"filter" schema:"filter"`
@@ -137,3 +160,27 @@ type ImagePruneReport struct {
Report Report
Size int64
}
+
+type ImageTagOptions struct{}
+type ImageUntagOptions struct{}
+
+type ImageData struct {
+ *inspect.ImageData
+}
+
+type ImageInspectReport struct {
+ Images []*ImageData
+ Errors map[string]error
+}
+
+type ImageLoadOptions struct {
+ Name string
+ Tag string
+ Input string
+ Quiet bool
+ SignaturePolicy string
+}
+
+type ImageLoadReport struct {
+ Name string
+}
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index d92d1bc7a..a0b2c6cec 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -22,6 +22,7 @@ type ListPodsReport struct {
Containers []*ListPodContainer
Created time.Time
Id string
+ InfraId string
Name string
Namespace string
Status string
@@ -151,3 +152,15 @@ type PodTopOptions struct {
Descriptors []string
NameOrID string
}
+
+type PodPSOptions struct {
+ CtrNames bool
+ CtrIds bool
+ CtrStatus bool
+ Filters map[string][]string
+ Format string
+ Latest bool
+ Namespace bool
+ Quiet bool
+ Sort string
+}
diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go
index a1a729584..dd7aaa07f 100644
--- a/pkg/domain/entities/types.go
+++ b/pkg/domain/entities/types.go
@@ -42,3 +42,10 @@ type NetOptions struct {
StaticIP *net.IP
StaticMAC *net.HardwareAddr
}
+
+// All CLI inspect commands and inspect sub-commands use the same options
+type InspectOptions struct {
+ Format string `json:",omitempty"`
+ Latest bool `json:",omitempty"`
+ Size bool `json:",omitempty"`
+}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index d25af24c5..d4c5ac311 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -243,7 +243,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
return reports, nil
}
-func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.ContainerInspectOptions) ([]*entities.ContainerInspectReport, error) {
+func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
var reports []*entities.ContainerInspectReport
ctrs, err := shortcuts.GetContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
if err != nil {
@@ -325,3 +325,11 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string,
}
return &entities.CommitReport{Id: newImage.ID()}, nil
}
+
+func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrId string, options entities.ContainerExportOptions) error {
+ ctr, err := ic.Libpod.LookupContainer(nameOrId)
+ if err != nil {
+ return err
+ }
+ return ctr.Export(options.Output)
+}
diff --git a/pkg/domain/infra/abi/healthcheck.go b/pkg/domain/infra/abi/healthcheck.go
new file mode 100644
index 000000000..699483243
--- /dev/null
+++ b/pkg/domain/infra/abi/healthcheck.go
@@ -0,0 +1,26 @@
+// +build ABISupport
+
+package abi
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrId string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) {
+ status, err := ic.Libpod.HealthCheck(nameOrId)
+ if err != nil {
+ return nil, err
+ }
+ hcStatus := "unhealthy"
+ if status == libpod.HealthCheckSuccess {
+ hcStatus = "healthy"
+ }
+ report := define.HealthCheckResults{
+ Status: hcStatus,
+ }
+ return &report, nil
+}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 44420c1e1..c3a2bb288 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -5,11 +5,23 @@ package abi
import (
"context"
"fmt"
+ "io"
+ "os"
+ "strings"
+ "github.com/containers/image/v5/docker"
+ dockerarchive "github.com/containers/image/v5/docker/archive"
+ "github.com/containers/image/v5/docker/reference"
+ "github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/image/v5/types"
+ "github.com/containers/libpod/libpod/image"
libpodImage "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/domain/entities"
+ domainUtils "github.com/containers/libpod/pkg/domain/utils"
+ "github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) {
@@ -134,6 +146,120 @@ func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer
return l
}
+func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) {
+ var writer io.Writer
+ if !options.Quiet {
+ writer = os.Stderr
+ }
+
+ dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name())
+ imageRef, err := alltransports.ParseImageName(rawImage)
+ if err != nil {
+ imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, rawImage))
+ if err != nil {
+ return nil, errors.Errorf("invalid image reference %q", rawImage)
+ }
+ }
+
+ // Special-case for docker-archive which allows multiple tags.
+ if imageRef.Transport().Name() == dockerarchive.Transport.Name() {
+ newImage, err := ir.Libpod.ImageRuntime().LoadFromArchiveReference(ctx, imageRef, options.SignaturePolicy, writer)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error pulling image %q", rawImage)
+ }
+ return &entities.ImagePullReport{Images: []string{newImage[0].ID()}}, nil
+ }
+
+ var registryCreds *types.DockerAuthConfig
+ if options.Credentials != "" {
+ creds, err := util.ParseRegistryCreds(options.Credentials)
+ if err != nil {
+ return nil, err
+ }
+ registryCreds = creds
+ }
+ dockerRegistryOptions := image.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ DockerCertPath: options.CertDir,
+ OSChoice: options.OverrideOS,
+ ArchitectureChoice: options.OverrideArch,
+ DockerInsecureSkipTLSVerify: options.TLSVerify,
+ }
+
+ if !options.AllTags {
+ newImage, err := ir.Libpod.ImageRuntime().New(ctx, rawImage, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error pulling image %q", rawImage)
+ }
+ return &entities.ImagePullReport{Images: []string{newImage.ID()}}, nil
+ }
+
+ // --all-tags requires the docker transport
+ if imageRef.Transport().Name() != docker.Transport.Name() {
+ return nil, errors.New("--all-tags requires docker transport")
+ }
+
+ // Trim the docker-transport prefix.
+ rawImage = strings.TrimPrefix(rawImage, docker.Transport.Name())
+
+ // all-tags doesn't work with a tagged reference, so let's check early
+ namedRef, err := reference.Parse(rawImage)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing %q", rawImage)
+ }
+ if _, isTagged := namedRef.(reference.Tagged); isTagged {
+ return nil, errors.New("--all-tags requires a reference without a tag")
+
+ }
+
+ systemContext := image.GetSystemContext("", options.Authfile, false)
+ tags, err := docker.GetRepositoryTags(ctx, systemContext, imageRef)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting repository tags")
+ }
+
+ var foundIDs []string
+ for _, tag := range tags {
+ name := rawImage + ":" + tag
+ newImage, err := ir.Libpod.ImageRuntime().New(ctx, name, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, util.PullImageAlways)
+ if err != nil {
+ logrus.Errorf("error pulling image %q", name)
+ continue
+ }
+ foundIDs = append(foundIDs, newImage.ID())
+ }
+
+ if len(tags) != len(foundIDs) {
+ return nil, errors.Errorf("error pulling image %q", rawImage)
+ }
+ return &entities.ImagePullReport{Images: foundIDs}, nil
+}
+
+func (ir *ImageEngine) Inspect(ctx context.Context, names []string, opts entities.InspectOptions) (*entities.ImageInspectReport, error) {
+ report := entities.ImageInspectReport{
+ Errors: make(map[string]error),
+ }
+
+ for _, id := range names {
+ img, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
+ if err != nil {
+ report.Errors[id] = err
+ continue
+ }
+
+ results, err := img.Inspect(ctx)
+ if err != nil {
+ report.Errors[id] = err
+ continue
+ }
+
+ cookedResults := entities.ImageData{}
+ _ = domainUtils.DeepCopy(&cookedResults, results)
+ report.Images = append(report.Images, &cookedResults)
+ }
+ return &report, nil
+}
+
// func (r *imageRuntime) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
// image, err := r.libpod.ImageEngine().NewFromLocal(nameOrId)
// if err != nil {
@@ -146,7 +272,7 @@ func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer
// }
//
// report := entities.ImageDeleteReport{}
-// if err := utils.DeepCopy(&report, results); err != nil {
+// if err := domainUtils.DeepCopy(&report, results); err != nil {
// return nil, err
// }
// return &report, nil
@@ -164,3 +290,49 @@ func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer
// copy(report.Report.Id, id)
// return &report, nil
// }
+
+func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, options entities.ImageTagOptions) error {
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return err
+ }
+ for _, tag := range tags {
+ if err := newImage.TagImage(tag); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error {
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
+ if err != nil {
+ return err
+ }
+ for _, tag := range tags {
+ if err := newImage.UntagImage(tag); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
+ var (
+ writer io.Writer
+ )
+ if !opts.Quiet {
+ writer = os.Stderr
+ }
+ name, err := ir.Libpod.LoadImage(ctx, opts.Name, opts.Input, writer, opts.SignaturePolicy)
+ if err != nil {
+ return nil, err
+ }
+ newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name)
+ if err != nil {
+ return nil, errors.Wrap(err, "image loaded but no additional tags were created")
+ }
+ if err := newImage.TagImage(opts.Name); err != nil {
+ return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName)
+ }
+ return &entities.ImageLoadReport{Name: name}, nil
+}
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index 8abcc6e4b..494a048ec 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -7,6 +7,7 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/libpod/podfilters"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/signal"
"github.com/containers/libpod/pkg/specgen"
@@ -272,3 +273,61 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp
report.Value, err = pod.GetPodPidInformation(options.Descriptors)
return report, err
}
+
+func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) {
+ var (
+ filters []libpod.PodFilter
+ reports []*entities.ListPodsReport
+ )
+ for k, v := range options.Filters {
+ for _, filter := range v {
+ f, err := podfilters.GeneratePodFilterFunc(k, filter)
+ if err != nil {
+ return nil, err
+ }
+ filters = append(filters, f)
+
+ }
+ }
+ pds, err := ic.Libpod.Pods(filters...)
+ if err != nil {
+ return nil, err
+ }
+ for _, p := range pds {
+ var lpcs []*entities.ListPodContainer
+ status, err := p.GetPodStatus()
+ if err != nil {
+ return nil, err
+ }
+ cons, err := p.AllContainers()
+ if err != nil {
+ return nil, err
+ }
+ for _, c := range cons {
+ state, err := c.State()
+ if err != nil {
+ return nil, err
+ }
+ lpcs = append(lpcs, &entities.ListPodContainer{
+ Id: c.ID(),
+ Names: c.Name(),
+ Status: state.String(),
+ })
+ }
+ infraId, err := p.InfraContainerID()
+ if err != nil {
+ return nil, err
+ }
+ reports = append(reports, &entities.ListPodsReport{
+ Cgroup: p.CgroupParent(),
+ Containers: lpcs,
+ Created: p.CreatedTime(),
+ Id: p.ID(),
+ InfraId: infraId,
+ Name: p.Name(),
+ Namespace: p.Namespace(),
+ Status: status,
+ })
+ }
+ return reports, nil
+}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 3c8be90dc..8885ae7c7 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -2,6 +2,8 @@ package tunnel
import (
"context"
+ "io"
+ "os"
"github.com/containers/image/v5/docker/reference"
@@ -142,7 +144,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
return reports, nil
}
-func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.ContainerInspectOptions) ([]*entities.ContainerInspectReport, error) {
+func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) {
var (
reports []*entities.ContainerInspectReport
)
@@ -210,3 +212,17 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string,
}
return &entities.CommitReport{Id: response.ID}, nil
}
+
+func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrId string, options entities.ContainerExportOptions) error {
+ var (
+ err error
+ w io.Writer
+ )
+ if len(options.Output) > 0 {
+ w, err = os.Create(options.Output)
+ if err != nil {
+ return err
+ }
+ }
+ return containers.Export(ic.ClientCxt, nameOrId, w)
+}
diff --git a/pkg/domain/infra/tunnel/healthcheck.go b/pkg/domain/infra/tunnel/healthcheck.go
new file mode 100644
index 000000000..e589489b3
--- /dev/null
+++ b/pkg/domain/infra/tunnel/healthcheck.go
@@ -0,0 +1,13 @@
+package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/bindings/containers"
+ "github.com/containers/libpod/pkg/domain/entities"
+)
+
+func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrId string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) {
+ return containers.RunHealthCheck(ic.ClientCxt, nameOrId)
+}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 6a3adc9ee..a4a0fccaf 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -2,10 +2,13 @@ package tunnel
import (
"context"
+ "os"
+ "github.com/containers/image/v5/docker/reference"
images "github.com/containers/libpod/pkg/bindings/images"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/utils"
+ "github.com/pkg/errors"
)
func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) {
@@ -85,3 +88,82 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
}
return &report, nil
}
+
+func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) (*entities.ImagePullReport, error) {
+ pulledImages, err := images.Pull(ir.ClientCxt, rawImage, options)
+ if err != nil {
+ return nil, err
+ }
+ return &entities.ImagePullReport{Images: pulledImages}, nil
+}
+
+func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, options entities.ImageTagOptions) error {
+ for _, newTag := range tags {
+ var (
+ tag, repo string
+ )
+ ref, err := reference.Parse(newTag)
+ if err != nil {
+ return err
+ }
+ if t, ok := ref.(reference.Tagged); ok {
+ tag = t.Tag()
+ }
+ if r, ok := ref.(reference.Named); ok {
+ repo = r.Name()
+ }
+ if len(repo) < 1 {
+ return errors.Errorf("invalid image name %q", nameOrId)
+ }
+ if err := images.Tag(ir.ClientCxt, nameOrId, tag, repo); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error {
+ for _, newTag := range tags {
+ var (
+ tag, repo string
+ )
+ ref, err := reference.Parse(newTag)
+ if err != nil {
+ return err
+ }
+ if t, ok := ref.(reference.Tagged); ok {
+ tag = t.Tag()
+ }
+ if r, ok := ref.(reference.Named); ok {
+ repo = r.Name()
+ }
+ if len(repo) < 1 {
+ return errors.Errorf("invalid image name %q", nameOrId)
+ }
+ if err := images.Untag(ir.ClientCxt, nameOrId, tag, repo); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (ir *ImageEngine) Inspect(_ context.Context, names []string, opts entities.InspectOptions) (*entities.ImageInspectReport, error) {
+ report := entities.ImageInspectReport{}
+ for _, id := range names {
+ r, err := images.GetImage(ir.ClientCxt, id, &opts.Size)
+ if err != nil {
+ report.Errors[id] = err
+ }
+ report.Images = append(report.Images, r)
+ }
+ return &report, nil
+}
+
+func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) (*entities.ImageLoadReport, error) {
+ f, err := os.Open(opts.Input)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return images.Load(ir.ClientCxt, f, &opts.Name)
+}
diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go
index 9561a9807..ad87a0a29 100644
--- a/pkg/domain/infra/tunnel/pods.go
+++ b/pkg/domain/infra/tunnel/pods.go
@@ -193,3 +193,7 @@ func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOp
}
return &entities.StringSliceReport{Value: topOutput}, nil
}
+
+func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) {
+ return pods.List(ic.ClientCxt, options.Filters)
+}