mirror of https://github.com/containers/podman.git
V2 podman command
Signed-off-by: Jhon Honce <jhonce@redhat.com>
This commit is contained in:
parent
bd9386ddac
commit
fbe743501e
|
@ -0,0 +1,33 @@
|
||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Command: podman _container_
|
||||||
|
containerCmd = &cobra.Command{
|
||||||
|
Use: "container",
|
||||||
|
Short: "Manage containers",
|
||||||
|
Long: "Manage containers",
|
||||||
|
TraverseChildren: true,
|
||||||
|
PersistentPreRunE: preRunE,
|
||||||
|
RunE: registry.SubCommandExists,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: containerCmd,
|
||||||
|
})
|
||||||
|
containerCmd.SetHelpTemplate(registry.HelpTemplate())
|
||||||
|
containerCmd.SetUsageTemplate(registry.UsageTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
func preRunE(cmd *cobra.Command, args []string) error {
|
||||||
|
_, err := registry.NewContainerEngine(cmd, args)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// podman container _inspect_
|
||||||
|
inspectCmd = &cobra.Command{
|
||||||
|
Use: "inspect [flags] CONTAINER",
|
||||||
|
Short: "Display the configuration of a container",
|
||||||
|
Long: `Displays the low-level information on a container identified by name or ID.`,
|
||||||
|
PreRunE: inspectPreRunE,
|
||||||
|
RunE: inspect,
|
||||||
|
Example: `podman container inspect myCtr
|
||||||
|
podman container inspect -l --format '{{.Id}} {{.Config.Labels}}'`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: inspectCmd,
|
||||||
|
Parent: containerCmd,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspectPreRunE(cmd *cobra.Command, args []string) (err error) {
|
||||||
|
err = preRunE(cmd, args)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = registry.NewImageEngine(cmd, args)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspect(cmd *cobra.Command, args []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// podman container _list_
|
||||||
|
listCmd = &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Short: "List containers",
|
||||||
|
Long: "Prints out information about the containers",
|
||||||
|
RunE: containers,
|
||||||
|
Example: `podman container list -a
|
||||||
|
podman container list -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}"
|
||||||
|
podman container list --size --sort names`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: listCmd,
|
||||||
|
Parent: containerCmd,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func containers(cmd *cobra.Command, args []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// podman _ps_
|
||||||
|
psCmd = &cobra.Command{
|
||||||
|
Use: "ps",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
Short: listCmd.Short,
|
||||||
|
Long: listCmd.Long,
|
||||||
|
PersistentPreRunE: preRunE,
|
||||||
|
RunE: containers,
|
||||||
|
Example: strings.Replace(listCmd.Example, "container list", "ps", -1),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: psCmd,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package images
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
long = `Displays the history of an image.
|
||||||
|
|
||||||
|
The information can be printed out in an easy to read, or user specified format, and can be truncated.`
|
||||||
|
|
||||||
|
// podman _history_
|
||||||
|
historyCmd = &cobra.Command{
|
||||||
|
Use: "history [flags] IMAGE",
|
||||||
|
Short: "Show history of a specified image",
|
||||||
|
Long: long,
|
||||||
|
Example: "podman history quay.io/fedora/fedora",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
PersistentPreRunE: preRunE,
|
||||||
|
RunE: history,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdFlags = struct {
|
||||||
|
Human bool
|
||||||
|
NoTrunc bool
|
||||||
|
Quiet bool
|
||||||
|
Format string
|
||||||
|
}{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: historyCmd,
|
||||||
|
})
|
||||||
|
|
||||||
|
historyCmd.SetHelpTemplate(registry.HelpTemplate())
|
||||||
|
historyCmd.SetUsageTemplate(registry.UsageTemplate())
|
||||||
|
flags := historyCmd.Flags()
|
||||||
|
flags.StringVar(&cmdFlags.Format, "format", "", "Change the output to JSON or a Go template")
|
||||||
|
flags.BoolVarP(&cmdFlags.Human, "human", "H", true, "Display sizes and dates in human readable format")
|
||||||
|
flags.BoolVar(&cmdFlags.NoTrunc, "no-trunc", false, "Do not truncate the output")
|
||||||
|
flags.BoolVar(&cmdFlags.NoTrunc, "notruncate", false, "Do not truncate the output")
|
||||||
|
flags.BoolVarP(&cmdFlags.Quiet, "quiet", "q", false, "Display the numeric IDs only")
|
||||||
|
}
|
||||||
|
|
||||||
|
func history(cmd *cobra.Command, args []string) error {
|
||||||
|
results, err := registry.ImageEngine().History(context.Background(), args[0], entities.ImageHistoryOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
row := "{{slice $x.ID 0 12}}\t{{toRFC3339 $x.Created}}\t{{ellipsis $x.CreatedBy 45}}\t{{$x.Size}}\t{{$x.Comment}}\n"
|
||||||
|
if cmdFlags.Human {
|
||||||
|
row = "{{slice $x.ID 0 12}}\t{{toHumanDuration $x.Created}}\t{{ellipsis $x.CreatedBy 45}}\t{{toHumanSize $x.Size}}\t{{$x.Comment}}\n"
|
||||||
|
}
|
||||||
|
format := "{{range $y, $x := . }}" + row + "{{end}}"
|
||||||
|
|
||||||
|
tmpl := template.Must(template.New("report").Funcs(report.PodmanTemplateFuncs()).Parse(format))
|
||||||
|
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
|
||||||
|
|
||||||
|
_, _ = w.Write(report.ReportHeader("id", "created", "created by", "size", "comment"))
|
||||||
|
err = tmpl.Execute(w, results.Layers)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, errors.Wrapf(err, "Failed to print report"))
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
// Command: podman _image_
|
||||||
|
imageCmd = &cobra.Command{
|
||||||
|
Use: "image",
|
||||||
|
Short: "Manage images",
|
||||||
|
Long: "Manage images",
|
||||||
|
TraverseChildren: true,
|
||||||
|
PersistentPreRunE: preRunE,
|
||||||
|
RunE: registry.SubCommandExists,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: imageCmd,
|
||||||
|
})
|
||||||
|
imageCmd.SetHelpTemplate(registry.HelpTemplate())
|
||||||
|
imageCmd.SetUsageTemplate(registry.UsageTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
func preRunE(cmd *cobra.Command, args []string) error {
|
||||||
|
_, err := registry.NewImageEngine(cmd, args)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package images
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// podman _images_
|
||||||
|
imagesCmd = &cobra.Command{
|
||||||
|
Use: strings.Replace(listCmd.Use, "list", "images", 1),
|
||||||
|
Short: listCmd.Short,
|
||||||
|
Long: listCmd.Long,
|
||||||
|
PersistentPreRunE: preRunE,
|
||||||
|
RunE: images,
|
||||||
|
Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1),
|
||||||
|
}
|
||||||
|
|
||||||
|
imagesOpts = entities.ImageListOptions{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: imagesCmd,
|
||||||
|
})
|
||||||
|
imagesCmd.SetHelpTemplate(registry.HelpTemplate())
|
||||||
|
imagesCmd.SetUsageTemplate(registry.UsageTemplate())
|
||||||
|
|
||||||
|
flags := imagesCmd.Flags()
|
||||||
|
flags.BoolVarP(&imagesOpts.All, "all", "a", false, "Show all images (default hides intermediate images)")
|
||||||
|
flags.BoolVar(&imagesOpts.Digests, "digests", false, "Show digests")
|
||||||
|
flags.StringSliceVarP(&imagesOpts.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])")
|
||||||
|
flags.StringVar(&imagesOpts.Format, "format", "", "Change the output format to JSON or a Go template")
|
||||||
|
flags.BoolVarP(&imagesOpts.Noheading, "noheading", "n", false, "Do not print column headings")
|
||||||
|
// TODO Need to learn how to deal with second name being a string instead of a char.
|
||||||
|
// This needs to be "no-trunc, notruncate"
|
||||||
|
flags.BoolVar(&imagesOpts.NoTrunc, "no-trunc", false, "Do not truncate output")
|
||||||
|
flags.BoolVar(&imagesOpts.NoTrunc, "notruncate", false, "Do not truncate output")
|
||||||
|
flags.BoolVarP(&imagesOpts.Quiet, "quiet", "q", false, "Display only image IDs")
|
||||||
|
flags.StringVar(&imagesOpts.Sort, "sort", "created", "Sort by created, id, repository, size, or tag")
|
||||||
|
flags.BoolVarP(&imagesOpts.History, "history", "", false, "Display the image name history")
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package images
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/buildah/pkg/formats"
|
||||||
|
"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,
|
||||||
|
Example: `podman image inspect alpine`,
|
||||||
|
}
|
||||||
|
|
||||||
|
containerEngine entities.ContainerEngine
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.GlobalFlags.EngineMode == entities.ABIMode {
|
||||||
|
// TODO: This is the same as V1. We could skip creating the flag altogether in V2...
|
||||||
|
_ = flags.MarkHidden("latest")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
inspectTypeContainer = "container"
|
||||||
|
inspectTypeImage = "image"
|
||||||
|
inspectAll = "all"
|
||||||
|
)
|
||||||
|
|
||||||
|
func imageInspect(cmd *cobra.Command, args []string) error {
|
||||||
|
inspectType := inspectTypeImage
|
||||||
|
latestContainer := inspectOpts.Latest
|
||||||
|
|
||||||
|
if len(args) == 0 && !latestContainer {
|
||||||
|
return errors.Errorf("container or image name must be specified: podman inspect [options [...]] name")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 0 && latestContainer {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 strings.Contains(outputFormat, ".Dst") {
|
||||||
|
outputFormat = strings.Replace(outputFormat, ".Dst", ".Destination", -1)
|
||||||
|
}
|
||||||
|
if strings.Contains(outputFormat, ".ImageID") {
|
||||||
|
outputFormat = strings.Replace(outputFormat, ".ImageID", ".Image", -1)
|
||||||
|
}
|
||||||
|
_ = 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
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
// Command: podman image _list_
|
||||||
|
listCmd = &cobra.Command{
|
||||||
|
Use: "list [flag] [IMAGE]",
|
||||||
|
Aliases: []string{"ls"},
|
||||||
|
Short: "List images in local storage",
|
||||||
|
Long: "Lists images previously pulled to the system or created on the system.",
|
||||||
|
RunE: images,
|
||||||
|
Example: `podman image list --format json
|
||||||
|
podman image list --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}"
|
||||||
|
podman image list --filter dangling=true`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: listCmd,
|
||||||
|
Parent: imageCmd,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func images(cmd *cobra.Command, args []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
_ "github.com/containers/libpod/cmd/podmanV2/containers"
|
||||||
|
_ "github.com/containers/libpod/cmd/podmanV2/images"
|
||||||
|
_ "github.com/containers/libpod/cmd/podmanV2/networks"
|
||||||
|
_ "github.com/containers/libpod/cmd/podmanV2/pods"
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
_ "github.com/containers/libpod/cmd/podmanV2/volumes"
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if err := libpod.SetXdgDirs(); err != nil {
|
||||||
|
logrus.Errorf(err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
initCobra()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initCobra() {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
fallthrough
|
||||||
|
case "windows":
|
||||||
|
registry.GlobalFlags.EngineMode = entities.TunnelMode
|
||||||
|
case "linux":
|
||||||
|
registry.GlobalFlags.EngineMode = entities.ABIMode
|
||||||
|
default:
|
||||||
|
logrus.Errorf("%s is not a supported OS", runtime.GOOS)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Is there a Cobra way to "peek" at os.Args?
|
||||||
|
if ok := Contains("--remote", os.Args); ok {
|
||||||
|
registry.GlobalFlags.EngineMode = entities.TunnelMode
|
||||||
|
}
|
||||||
|
|
||||||
|
cobra.OnInitialize(func() {})
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Fprintf(os.Stderr, "Number of commands: %d\n", len(registry.Commands))
|
||||||
|
for _, c := range registry.Commands {
|
||||||
|
if Contains(registry.GlobalFlags.EngineMode, c.Mode) {
|
||||||
|
parent := rootCmd
|
||||||
|
if c.Parent != nil {
|
||||||
|
parent = c.Parent
|
||||||
|
}
|
||||||
|
parent.AddCommand(c.Command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Execute()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Contains(item interface{}, slice interface{}) bool {
|
||||||
|
s := reflect.ValueOf(slice)
|
||||||
|
|
||||||
|
switch s.Kind() {
|
||||||
|
case reflect.Array:
|
||||||
|
fallthrough
|
||||||
|
case reflect.Slice:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < s.Len(); i++ {
|
||||||
|
if s.Index(i).Interface() == item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
// Command: podman _network_
|
||||||
|
cmd = &cobra.Command{
|
||||||
|
Use: "network",
|
||||||
|
Short: "Manage networks",
|
||||||
|
Long: "Manage networks",
|
||||||
|
TraverseChildren: true,
|
||||||
|
PersistentPreRunE: preRunE,
|
||||||
|
RunE: registry.SubCommandExists,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode},
|
||||||
|
Command: cmd,
|
||||||
|
})
|
||||||
|
cmd.SetHelpTemplate(registry.HelpTemplate())
|
||||||
|
cmd.SetUsageTemplate(registry.UsageTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
func preRunE(cmd *cobra.Command, args []string) error {
|
||||||
|
_, err := registry.NewContainerEngine(cmd, args)
|
||||||
|
return err
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,33 @@
|
||||||
|
package pods
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Command: podman _pod_
|
||||||
|
podCmd = &cobra.Command{
|
||||||
|
Use: "pod",
|
||||||
|
Short: "Manage pods",
|
||||||
|
Long: "Manage pods",
|
||||||
|
TraverseChildren: true,
|
||||||
|
PersistentPreRunE: preRunE,
|
||||||
|
RunE: registry.SubCommandExists,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: podCmd,
|
||||||
|
})
|
||||||
|
podCmd.SetHelpTemplate(registry.HelpTemplate())
|
||||||
|
podCmd.SetUsageTemplate(registry.UsageTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
func preRunE(cmd *cobra.Command, args []string) error {
|
||||||
|
_, err := registry.NewContainerEngine(cmd, args)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package pods
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
psDescription = "List all pods on system including their names, ids and current state."
|
||||||
|
|
||||||
|
// Command: podman pod _ps_
|
||||||
|
psCmd = &cobra.Command{
|
||||||
|
Use: "ps",
|
||||||
|
Aliases: []string{"ls", "list"},
|
||||||
|
Short: "list pods",
|
||||||
|
Long: psDescription,
|
||||||
|
RunE: pods,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: psCmd,
|
||||||
|
Parent: podCmd,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func pods(cmd *cobra.Command, args []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/domain/infra"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CliCommand struct {
|
||||||
|
Mode []entities.EngineMode
|
||||||
|
Command *cobra.Command
|
||||||
|
Parent *cobra.Command
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
Commands []CliCommand
|
||||||
|
GlobalFlags entities.EngineFlags
|
||||||
|
imageEngine entities.ImageEngine
|
||||||
|
containerEngine entities.ContainerEngine
|
||||||
|
PodmanTunnel bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// HelpTemplate returns the help template for podman commands
|
||||||
|
// This uses the short and long options.
|
||||||
|
// command should not use this.
|
||||||
|
func HelpTemplate() string {
|
||||||
|
return `{{.Short}}
|
||||||
|
|
||||||
|
Description:
|
||||||
|
{{.Long}}
|
||||||
|
|
||||||
|
{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsageTemplate returns the usage template for podman commands
|
||||||
|
// This blocks the displaying of the global options. The main podman
|
||||||
|
// command should not use this.
|
||||||
|
func UsageTemplate() string {
|
||||||
|
return `Usage(v2):{{if (and .Runnable (not .HasAvailableSubCommands))}}
|
||||||
|
{{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
|
||||||
|
{{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
|
||||||
|
|
||||||
|
Aliases:
|
||||||
|
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
|
||||||
|
|
||||||
|
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||||
|
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||||
|
{{end}}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImageEngine() entities.ImageEngine {
|
||||||
|
return imageEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageEngine is a wrapper for building an ImageEngine to be used for PreRunE functions
|
||||||
|
func NewImageEngine(cmd *cobra.Command, args []string) (entities.ImageEngine, error) {
|
||||||
|
if imageEngine == nil {
|
||||||
|
engine, err := infra.NewImageEngine(GlobalFlags.EngineMode, entities.EngineOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
imageEngine = engine
|
||||||
|
}
|
||||||
|
return imageEngine, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContainerEngine() entities.ContainerEngine {
|
||||||
|
return containerEngine
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContainerEngine is a wrapper for building an ContainerEngine to be used for PreRunE functions
|
||||||
|
func NewContainerEngine(cmd *cobra.Command, args []string) (entities.ContainerEngine, error) {
|
||||||
|
if containerEngine == nil {
|
||||||
|
engine, err := infra.NewContainerEngine(GlobalFlags.EngineMode, entities.EngineOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
containerEngine = engine
|
||||||
|
}
|
||||||
|
return containerEngine, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubCommandExists(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) > 0 {
|
||||||
|
return errors.Errorf("unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information.", cmd.CommandPath(), args[0])
|
||||||
|
}
|
||||||
|
return errors.Errorf("missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information.", cmd.CommandPath())
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package report
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/go-units"
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultFuncMap = template.FuncMap{
|
||||||
|
"ellipsis": func(s string, length int) string {
|
||||||
|
if len(s) > length {
|
||||||
|
return s[:length-3] + "..."
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
},
|
||||||
|
// TODO: Remove on Go 1.14 port
|
||||||
|
"slice": func(s string, i, j int) string {
|
||||||
|
if i > j || len(s) < i {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
if len(s) < j {
|
||||||
|
return s[i:]
|
||||||
|
}
|
||||||
|
return s[i:j]
|
||||||
|
},
|
||||||
|
"toRFC3339": func(t int64) string {
|
||||||
|
return time.Unix(t, 0).Format(time.RFC3339)
|
||||||
|
},
|
||||||
|
"toHumanDuration": func(t int64) string {
|
||||||
|
return units.HumanDuration(time.Since(time.Unix(t, 0))) + " ago"
|
||||||
|
},
|
||||||
|
"toHumanSize": func(sz int64) string {
|
||||||
|
return units.HumanSize(float64(sz))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReportHeader(columns ...string) []byte {
|
||||||
|
hdr := make([]string, len(columns))
|
||||||
|
for i, h := range columns {
|
||||||
|
hdr[i] = strings.ToUpper(h)
|
||||||
|
}
|
||||||
|
return []byte(strings.Join(hdr, "\t") + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppendFuncMap(funcMap template.FuncMap) template.FuncMap {
|
||||||
|
merged := PodmanTemplateFuncs()
|
||||||
|
for k, v := range funcMap {
|
||||||
|
merged[k] = v
|
||||||
|
}
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
func PodmanTemplateFuncs() template.FuncMap {
|
||||||
|
merged := make(template.FuncMap)
|
||||||
|
for k, v := range defaultFuncMap {
|
||||||
|
merged[k] = v
|
||||||
|
}
|
||||||
|
return merged
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/cmd/podmanV2/registry"
|
||||||
|
"github.com/containers/libpod/version"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: path.Base(os.Args[0]),
|
||||||
|
Long: "Manage pods, containers and images",
|
||||||
|
SilenceUsage: true,
|
||||||
|
SilenceErrors: true,
|
||||||
|
TraverseChildren: true,
|
||||||
|
RunE: registry.SubCommandExists,
|
||||||
|
Version: version.Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Override default --help information of `--version` global flag}
|
||||||
|
var dummyVersion bool
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&dummyVersion, "version", "v", false, "Version of podman")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(®istry.PodmanTunnel, "remote", "r", false, "Access service via SSH tunnel")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Execute() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
// Command: podman _system_
|
||||||
|
cmd = &cobra.Command{
|
||||||
|
Use: "system",
|
||||||
|
Short: "Manage podman",
|
||||||
|
Long: "Manage podman",
|
||||||
|
TraverseChildren: true,
|
||||||
|
PersistentPreRunE: preRunE,
|
||||||
|
RunE: registry.SubCommandExists,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: cmd,
|
||||||
|
})
|
||||||
|
cmd.SetHelpTemplate(registry.HelpTemplate())
|
||||||
|
cmd.SetUsageTemplate(registry.UsageTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
func preRunE(cmd *cobra.Command, args []string) error {
|
||||||
|
_, err := registry.NewContainerEngine(cmd, args)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
// Command: podman _volume_
|
||||||
|
cmd = &cobra.Command{
|
||||||
|
Use: "volume",
|
||||||
|
Short: "Manage volumes",
|
||||||
|
Long: "Volumes are created in and can be shared between containers",
|
||||||
|
TraverseChildren: true,
|
||||||
|
PersistentPreRunE: preRunE,
|
||||||
|
RunE: registry.SubCommandExists,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
|
Command: cmd,
|
||||||
|
})
|
||||||
|
cmd.SetHelpTemplate(registry.HelpTemplate())
|
||||||
|
cmd.SetUsageTemplate(registry.UsageTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
func preRunE(cmd *cobra.Command, args []string) error {
|
||||||
|
_, err := registry.NewContainerEngine(cmd, args)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ func HistoryImage(w http.ResponseWriter, r *http.Request) {
|
||||||
for _, h := range history {
|
for _, h := range history {
|
||||||
l := handlers.HistoryResponse{
|
l := handlers.HistoryResponse{
|
||||||
ID: h.ID,
|
ID: h.ID,
|
||||||
Created: h.Created.UnixNano(),
|
Created: h.Created.Unix(),
|
||||||
CreatedBy: h.CreatedBy,
|
CreatedBy: h.CreatedBy,
|
||||||
Tags: h.Tags,
|
Tags: h.Tags,
|
||||||
Size: h.Size,
|
Size: h.Size,
|
||||||
|
|
|
@ -109,7 +109,7 @@ func NewConnection(ctx context.Context, uri string, identity ...string) (context
|
||||||
}
|
}
|
||||||
client, err = tcpClient(_url)
|
client, err = tcpClient(_url)
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("%s is not a support schema", _url.Scheme)
|
return nil, errors.Errorf("'%s' is not a supported schema", _url.Scheme)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "Failed to create %sClient", _url.Scheme)
|
return nil, errors.Wrapf(err, "Failed to create %sClient", _url.Scheme)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
package entities
|
|
@ -0,0 +1,97 @@
|
||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EngineMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ABIMode = EngineMode("abi")
|
||||||
|
TunnelMode = EngineMode("tunnel")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m EngineMode) String() string {
|
||||||
|
return string(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EngineOptions struct {
|
||||||
|
Uri *url.URL
|
||||||
|
Identities []string
|
||||||
|
FlagSet pflag.FlagSet
|
||||||
|
Flags EngineFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
type EngineFlags struct {
|
||||||
|
CGroupManager string
|
||||||
|
CniConfigDir string
|
||||||
|
ConmonPath string
|
||||||
|
DefaultMountsFile string
|
||||||
|
EventsBackend string
|
||||||
|
HooksDir []string
|
||||||
|
MaxWorks int
|
||||||
|
Namespace string
|
||||||
|
Root string
|
||||||
|
Runroot string
|
||||||
|
Runtime string
|
||||||
|
StorageDriver string
|
||||||
|
StorageOpts []string
|
||||||
|
Syslog bool
|
||||||
|
Trace bool
|
||||||
|
NetworkCmdPath string
|
||||||
|
|
||||||
|
Config string
|
||||||
|
CpuProfile string
|
||||||
|
LogLevel string
|
||||||
|
TmpDir string
|
||||||
|
|
||||||
|
RemoteUserName string
|
||||||
|
RemoteHost string
|
||||||
|
VarlinkAddress string
|
||||||
|
ConnectionName string
|
||||||
|
RemoteConfigFilePath string
|
||||||
|
Port int
|
||||||
|
IdentityFile string
|
||||||
|
IgnoreHosts bool
|
||||||
|
|
||||||
|
EngineMode EngineMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEngineOptions() (EngineFlags, error) {
|
||||||
|
u, _ := user.Current()
|
||||||
|
return EngineFlags{
|
||||||
|
CGroupManager: define.SystemdCgroupsManager,
|
||||||
|
CniConfigDir: "",
|
||||||
|
Config: "",
|
||||||
|
ConmonPath: filepath.Join("usr", "bin", "conmon"),
|
||||||
|
ConnectionName: "",
|
||||||
|
CpuProfile: "",
|
||||||
|
DefaultMountsFile: "",
|
||||||
|
EventsBackend: "",
|
||||||
|
HooksDir: nil,
|
||||||
|
IdentityFile: "",
|
||||||
|
IgnoreHosts: false,
|
||||||
|
LogLevel: "",
|
||||||
|
MaxWorks: 0,
|
||||||
|
Namespace: "",
|
||||||
|
NetworkCmdPath: "",
|
||||||
|
Port: 0,
|
||||||
|
RemoteConfigFilePath: "",
|
||||||
|
RemoteHost: "",
|
||||||
|
RemoteUserName: "",
|
||||||
|
Root: "",
|
||||||
|
Runroot: filepath.Join("run", "user", u.Uid),
|
||||||
|
Runtime: "",
|
||||||
|
StorageDriver: "overlayfs",
|
||||||
|
StorageOpts: nil,
|
||||||
|
Syslog: false,
|
||||||
|
TmpDir: filepath.Join("run", "user", u.Uid, "libpod", "tmp"),
|
||||||
|
Trace: false,
|
||||||
|
VarlinkAddress: "",
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContainerEngine interface {
|
||||||
|
ContainerRuntime
|
||||||
|
PodRuntime
|
||||||
|
VolumeRuntime
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerRuntime interface {
|
||||||
|
ContainerDelete(ctx context.Context, opts ContainerDeleteOptions) (*ContainerDeleteReport, error)
|
||||||
|
ContainerPrune(ctx context.Context) (*ContainerPruneReport, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PodRuntime interface {
|
||||||
|
PodDelete(ctx context.Context, opts PodPruneOptions) (*PodDeleteReport, error)
|
||||||
|
PodPrune(ctx context.Context) (*PodPruneReport, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type VolumeRuntime interface {
|
||||||
|
VolumeDelete(ctx context.Context, opts VolumeDeleteOptions) (*VolumeDeleteReport, error)
|
||||||
|
VolumePrune(ctx context.Context) (*VolumePruneReport, error)
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ImageEngine interface {
|
||||||
|
Delete(ctx context.Context, nameOrId string, opts ImageDeleteOptions) (*ImageDeleteReport, error)
|
||||||
|
History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error)
|
||||||
|
List(ctx context.Context, opts ImageListOptions) (*ImageListReport, error)
|
||||||
|
Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Identifier interface allows filters to access ID() of object
|
||||||
|
type Identifier interface {
|
||||||
|
Id() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Named interface allows filters to access Name() of object
|
||||||
|
type Named interface {
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Named interface allows filters to access Name() of object
|
||||||
|
type Names interface {
|
||||||
|
Names() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdOrName interface allows filters to access ID() or Name() of object
|
||||||
|
type IdOrNamed interface {
|
||||||
|
Identifier
|
||||||
|
Named
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdOrName interface allows filters to access ID() or Names() of object
|
||||||
|
type IdOrNames interface {
|
||||||
|
Identifier
|
||||||
|
Names
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageFilter func(Image) bool
|
||||||
|
type VolumeFilter func(Volume) bool
|
||||||
|
type ContainerFilter func(Container) bool
|
||||||
|
|
||||||
|
func CompileImageFilters(filters url.Values) ImageFilter {
|
||||||
|
var fns []interface{}
|
||||||
|
|
||||||
|
for name, targets := range filters {
|
||||||
|
switch name {
|
||||||
|
case "id":
|
||||||
|
fns = append(fns, FilterIdFn(targets))
|
||||||
|
case "name":
|
||||||
|
fns = append(fns, FilterNamesFn(targets))
|
||||||
|
case "idOrName":
|
||||||
|
fns = append(fns, FilterIdOrNameFn(targets))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(image Image) bool {
|
||||||
|
for _, fn := range fns {
|
||||||
|
if !fn.(ImageFilter)(image) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompileContainerFilters(filters url.Values) ContainerFilter {
|
||||||
|
var fns []interface{}
|
||||||
|
|
||||||
|
for name, targets := range filters {
|
||||||
|
switch name {
|
||||||
|
case "id":
|
||||||
|
fns = append(fns, FilterIdFn(targets))
|
||||||
|
case "name":
|
||||||
|
fns = append(fns, FilterNameFn(targets))
|
||||||
|
case "idOrName":
|
||||||
|
fns = append(fns, FilterIdOrNameFn(targets))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(ctnr Container) bool {
|
||||||
|
for _, fn := range fns {
|
||||||
|
if !fn.(ContainerFilter)(ctnr) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompileVolumeFilters(filters url.Values) VolumeFilter {
|
||||||
|
var fns []interface{}
|
||||||
|
|
||||||
|
for name, targets := range filters {
|
||||||
|
if name == "id" {
|
||||||
|
fns = append(fns, FilterIdFn(targets))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(volume Volume) bool {
|
||||||
|
for _, fn := range fns {
|
||||||
|
if !fn.(VolumeFilter)(volume) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterIdFn(id []string) func(Identifier) bool {
|
||||||
|
return func(obj Identifier) bool {
|
||||||
|
for _, v := range id {
|
||||||
|
if strings.Contains(obj.Id(), v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterNameFn(name []string) func(Named) bool {
|
||||||
|
return func(obj Named) bool {
|
||||||
|
for _, v := range name {
|
||||||
|
if strings.Contains(obj.Name(), v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterNamesFn(name []string) func(Names) bool {
|
||||||
|
return func(obj Names) bool {
|
||||||
|
for _, v := range name {
|
||||||
|
for _, n := range obj.Names() {
|
||||||
|
if strings.Contains(n, v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FilterIdOrNameFn(id []string) func(IdOrNamed) bool {
|
||||||
|
return func(obj IdOrNamed) bool {
|
||||||
|
for _, v := range id {
|
||||||
|
if strings.Contains(obj.Id(), v) || strings.Contains(obj.Name(), v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/containers/image/v5/manifest"
|
||||||
|
docker "github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Image struct {
|
||||||
|
IdOrNamed
|
||||||
|
ID string `json:"Id"`
|
||||||
|
RepoTags []string `json:",omitempty"`
|
||||||
|
RepoDigests []string `json:",omitempty"`
|
||||||
|
Parent string `json:",omitempty"`
|
||||||
|
Comment string `json:",omitempty"`
|
||||||
|
Created string `json:",omitempty"`
|
||||||
|
Container string `json:",omitempty"`
|
||||||
|
ContainerConfig *container.Config `json:",omitempty"`
|
||||||
|
DockerVersion string `json:",omitempty"`
|
||||||
|
Author string `json:",omitempty"`
|
||||||
|
Config *container.Config `json:",omitempty"`
|
||||||
|
Architecture string `json:",omitempty"`
|
||||||
|
Variant string `json:",omitempty"`
|
||||||
|
Os string `json:",omitempty"`
|
||||||
|
OsVersion string `json:",omitempty"`
|
||||||
|
Size int64 `json:",omitempty"`
|
||||||
|
VirtualSize int64 `json:",omitempty"`
|
||||||
|
GraphDriver docker.GraphDriverData `json:",omitempty"`
|
||||||
|
RootFS docker.RootFS `json:",omitempty"`
|
||||||
|
Metadata docker.ImageMetadata `json:",omitempty"`
|
||||||
|
|
||||||
|
// Podman extensions
|
||||||
|
Digest digest.Digest `json:",omitempty"`
|
||||||
|
PodmanVersion string `json:",omitempty"`
|
||||||
|
ManifestType string `json:",omitempty"`
|
||||||
|
User string `json:",omitempty"`
|
||||||
|
History []v1.History `json:",omitempty"`
|
||||||
|
NamesHistory []string `json:",omitempty"`
|
||||||
|
HealthCheck *manifest.Schema2HealthConfig `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) Id() string {
|
||||||
|
return i.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageSummary struct {
|
||||||
|
Identifier
|
||||||
|
ID string `json:"Id"`
|
||||||
|
ParentId string `json:",omitempty"`
|
||||||
|
RepoTags []string `json:",omitempty"`
|
||||||
|
Created int `json:",omitempty"`
|
||||||
|
Size int `json:",omitempty"`
|
||||||
|
SharedSize int `json:",omitempty"`
|
||||||
|
VirtualSize int `json:",omitempty"`
|
||||||
|
Labels string `json:",omitempty"`
|
||||||
|
Containers int `json:",omitempty"`
|
||||||
|
ReadOnly bool `json:",omitempty"`
|
||||||
|
Dangling bool `json:",omitempty"`
|
||||||
|
|
||||||
|
// Podman extensions
|
||||||
|
Digest digest.Digest `json:",omitempty"`
|
||||||
|
ConfigDigest digest.Digest `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ImageSummary) Id() string {
|
||||||
|
return i.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ImageSummary) IsReadOnly() bool {
|
||||||
|
return i.ReadOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ImageSummary) IsDangling() bool {
|
||||||
|
return i.Dangling
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageOptions struct {
|
||||||
|
All bool
|
||||||
|
Digests bool
|
||||||
|
Filter []string
|
||||||
|
Format string
|
||||||
|
Noheading bool
|
||||||
|
NoTrunc bool
|
||||||
|
Quiet bool
|
||||||
|
Sort string
|
||||||
|
History bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageDeleteOptions struct {
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImageDeleteResponse is the response for removing an image from storage and containers
|
||||||
|
// what was untagged vs actually removed
|
||||||
|
type ImageDeleteReport struct {
|
||||||
|
Untagged []string `json:"untagged"`
|
||||||
|
Deleted string `json:"deleted"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageHistoryOptions struct{}
|
||||||
|
|
||||||
|
type ImageHistoryLayer struct {
|
||||||
|
ID string `json:"Id"`
|
||||||
|
Created int64 `json:"Created,omitempty"`
|
||||||
|
CreatedBy string `json:",omitempty"`
|
||||||
|
Tags []string `json:",omitempty"`
|
||||||
|
Size int64 `json:",omitempty"`
|
||||||
|
Comment string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageHistoryReport struct {
|
||||||
|
Layers []ImageHistoryLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageInspectOptions struct {
|
||||||
|
TypeObject string `json:",omitempty"`
|
||||||
|
Format string `json:",omitempty"`
|
||||||
|
Size bool `json:",omitempty"`
|
||||||
|
Latest bool `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageListOptions struct {
|
||||||
|
All bool `json:"all" schema:"all"`
|
||||||
|
Digests bool `json:"digests" schema:"digests"`
|
||||||
|
Filter []string `json:",omitempty"`
|
||||||
|
Filters url.Values `json:"filters" schema:"filters"`
|
||||||
|
Format string `json:",omitempty"`
|
||||||
|
History bool `json:",omitempty"`
|
||||||
|
Noheading bool `json:",omitempty"`
|
||||||
|
NoTrunc bool `json:",omitempty"`
|
||||||
|
Quiet bool `json:",omitempty"`
|
||||||
|
Sort string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageListReport struct {
|
||||||
|
Images []ImageSummary
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImagePruneOptions struct {
|
||||||
|
All bool
|
||||||
|
Filter ImageFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImagePruneReport struct {
|
||||||
|
Report Report
|
||||||
|
Size int64
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package entities
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
IdOrNamed
|
||||||
|
}
|
||||||
|
|
||||||
|
type Volume struct {
|
||||||
|
Identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
type Report struct {
|
||||||
|
Id []string
|
||||||
|
Err map[string]error
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerDeleteOptions struct{}
|
||||||
|
type ContainerDeleteReport struct{ Report }
|
||||||
|
type ContainerPruneReport struct{ Report }
|
||||||
|
|
||||||
|
type PodDeleteReport struct{ Report }
|
||||||
|
type PodPruneOptions struct{}
|
||||||
|
type PodPruneReport struct{ Report }
|
||||||
|
type VolumeDeleteOptions struct{}
|
||||||
|
type VolumeDeleteReport struct{ Report }
|
||||||
|
type VolumePruneReport struct{ Report }
|
|
@ -0,0 +1,131 @@
|
||||||
|
// +build ABISupport
|
||||||
|
|
||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
libpodImage "github.com/containers/libpod/libpod/image"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/domain/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
|
||||||
|
image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := ir.Libpod.RemoveImage(ctx, image, opts.Force)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
report := entities.ImageDeleteReport{}
|
||||||
|
if err := utils.DeepCopy(&report, results); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &report, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
|
||||||
|
results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, []string{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
report := entities.ImagePruneReport{}
|
||||||
|
copy(report.Report.Id, results)
|
||||||
|
return &report, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) (*entities.ImageListReport, error) {
|
||||||
|
var (
|
||||||
|
images []*libpodImage.Image
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
filters := utils.ToLibpodFilters(opts.Filters)
|
||||||
|
if len(filters) > 0 {
|
||||||
|
images, err = ir.Libpod.ImageRuntime().GetImagesWithFilters(filters)
|
||||||
|
} else {
|
||||||
|
images, err = ir.Libpod.ImageRuntime().GetImages()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
report := entities.ImageListReport{
|
||||||
|
Images: make([]entities.ImageSummary, len(images)),
|
||||||
|
}
|
||||||
|
for i, img := range images {
|
||||||
|
hold := entities.ImageSummary{}
|
||||||
|
if err := utils.DeepCopy(&hold, img); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
report.Images[i] = hold
|
||||||
|
}
|
||||||
|
return &report, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) {
|
||||||
|
image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
results, err := image.History(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
history := entities.ImageHistoryReport{
|
||||||
|
Layers: make([]entities.ImageHistoryLayer, len(results)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, layer := range results {
|
||||||
|
history.Layers[i] = ToDomainHistoryLayer(layer)
|
||||||
|
}
|
||||||
|
return &history, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer {
|
||||||
|
l := entities.ImageHistoryLayer{}
|
||||||
|
l.ID = layer.ID
|
||||||
|
l.Created = layer.Created.Unix()
|
||||||
|
l.CreatedBy = layer.CreatedBy
|
||||||
|
copy(l.Tags, layer.Tags)
|
||||||
|
l.Size = layer.Size
|
||||||
|
l.Comment = layer.Comment
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// results, err := r.libpod.RemoveImage(ctx, image, opts.Force)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// report := entities.ImageDeleteReport{}
|
||||||
|
// if err := utils.DeepCopy(&report, results); err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// return &report, nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (r *imageRuntime) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
|
||||||
|
// // TODO: map FilterOptions
|
||||||
|
// id, err := r.libpod.ImageEngine().PruneImages(ctx, opts.All, []string{})
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // TODO: Determine Size
|
||||||
|
// report := entities.ImagePruneReport{}
|
||||||
|
// copy(report.Report.Id, id)
|
||||||
|
// return &report, nil
|
||||||
|
// }
|
|
@ -0,0 +1,37 @@
|
||||||
|
package abi
|
||||||
|
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "context"
|
||||||
|
// "testing"
|
||||||
|
//
|
||||||
|
// "github.com/stretchr/testify/mock"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// type MockImageRuntime struct {
|
||||||
|
// mock.Mock
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (m *MockImageRuntime) Delete(ctx context.Context, renderer func() interface{}, name string) error {
|
||||||
|
// _ = m.Called(ctx, renderer, name)
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func TestImageSuccess(t *testing.T) {
|
||||||
|
// actual := func() interface{} { return nil }
|
||||||
|
//
|
||||||
|
// m := new(MockImageRuntime)
|
||||||
|
// m.On(
|
||||||
|
// "Delete",
|
||||||
|
// mock.AnythingOfType("*context.emptyCtx"),
|
||||||
|
// mock.AnythingOfType("func() interface {}"),
|
||||||
|
// "fedora").
|
||||||
|
// Return(nil)
|
||||||
|
//
|
||||||
|
// r := DirectImageRuntime{m}
|
||||||
|
// err := r.Delete(context.TODO(), actual, "fedora")
|
||||||
|
// if err != nil {
|
||||||
|
// t.Errorf("error should be nil, got: %v", err)
|
||||||
|
// }
|
||||||
|
// m.AssertExpectations(t)
|
||||||
|
// }
|
|
@ -0,0 +1,19 @@
|
||||||
|
// +build ABISupport
|
||||||
|
|
||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Image-related runtime linked against libpod library
|
||||||
|
type ImageEngine struct {
|
||||||
|
Libpod *libpod.Runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container-related runtime linked against libpod library
|
||||||
|
type ContainerEngine struct {
|
||||||
|
entities.ContainerEngine
|
||||||
|
Libpod *libpod.Runtime
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// +build ABISupport
|
||||||
|
|
||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/pkg/bindings"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/domain/infra/abi"
|
||||||
|
"github.com/containers/libpod/pkg/domain/infra/tunnel"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewContainerEngine factory provides a libpod runtime for container-related operations
|
||||||
|
func NewContainerEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ContainerEngine, error) {
|
||||||
|
switch mode {
|
||||||
|
case entities.ABIMode:
|
||||||
|
r, err := NewLibpodRuntime(opts.FlagSet, opts.Flags)
|
||||||
|
return &abi.ContainerEngine{ContainerEngine: r}, err
|
||||||
|
case entities.TunnelMode:
|
||||||
|
ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
|
||||||
|
return &tunnel.ContainerEngine{ClientCxt: ctx}, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContainerEngine factory provides a libpod runtime for image-related operations
|
||||||
|
func NewImageEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ImageEngine, error) {
|
||||||
|
switch mode {
|
||||||
|
case entities.ABIMode:
|
||||||
|
r, err := NewLibpodImageRuntime(opts.FlagSet, opts.Flags)
|
||||||
|
return r, err
|
||||||
|
case entities.TunnelMode:
|
||||||
|
ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
|
||||||
|
return &tunnel.ImageEngine{ClientCxt: ctx}, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
// +build ABISupport
|
||||||
|
|
||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/domain/infra/abi"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerEngine Image Proxy will be EOL'ed after podmanV2 is separated from libpod repo
|
||||||
|
|
||||||
|
func NewLibpodImageRuntime(flags pflag.FlagSet, opts entities.EngineFlags) (entities.ImageEngine, error) {
|
||||||
|
r, err := GetRuntime(context.Background(), flags, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &abi.ImageEngine{Libpod: r}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ir *runtime) ShutdownImageRuntime(force bool) error {
|
||||||
|
return ir.Libpod.Shutdown(force)
|
||||||
|
}
|
|
@ -0,0 +1,331 @@
|
||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/pkg/cgroups"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/namespaces"
|
||||||
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
|
"github.com/containers/storage"
|
||||||
|
"github.com/containers/storage/pkg/idtools"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
flag "github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
type engineOpts struct {
|
||||||
|
name string
|
||||||
|
renumber bool
|
||||||
|
migrate bool
|
||||||
|
noStore bool
|
||||||
|
withFDS bool
|
||||||
|
flags entities.EngineFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers
|
||||||
|
func GetRuntimeMigrate(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags, newRuntime string) (*libpod.Runtime, error) {
|
||||||
|
return getRuntime(ctx, fs, &engineOpts{
|
||||||
|
name: newRuntime,
|
||||||
|
renumber: false,
|
||||||
|
migrate: true,
|
||||||
|
noStore: false,
|
||||||
|
withFDS: true,
|
||||||
|
flags: ef,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify
|
||||||
|
func GetRuntimeDisableFDs(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
|
||||||
|
return getRuntime(ctx, fs, &engineOpts{
|
||||||
|
renumber: false,
|
||||||
|
migrate: false,
|
||||||
|
noStore: false,
|
||||||
|
withFDS: false,
|
||||||
|
flags: ef,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber
|
||||||
|
func GetRuntimeRenumber(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
|
||||||
|
return getRuntime(ctx, fs, &engineOpts{
|
||||||
|
renumber: true,
|
||||||
|
migrate: false,
|
||||||
|
noStore: false,
|
||||||
|
withFDS: true,
|
||||||
|
flags: ef,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRuntime generates a new libpod runtime configured by command line options
|
||||||
|
func GetRuntime(ctx context.Context, flags flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
|
||||||
|
return getRuntime(ctx, flags, &engineOpts{
|
||||||
|
renumber: false,
|
||||||
|
migrate: false,
|
||||||
|
noStore: false,
|
||||||
|
withFDS: true,
|
||||||
|
flags: ef,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRuntimeNoStore generates a new libpod runtime configured by command line options
|
||||||
|
func GetRuntimeNoStore(ctx context.Context, fs flag.FlagSet, ef entities.EngineFlags) (*libpod.Runtime, error) {
|
||||||
|
return getRuntime(ctx, fs, &engineOpts{
|
||||||
|
renumber: false,
|
||||||
|
migrate: false,
|
||||||
|
noStore: true,
|
||||||
|
withFDS: true,
|
||||||
|
flags: ef,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRuntime(ctx context.Context, fs flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) {
|
||||||
|
options := []libpod.RuntimeOption{}
|
||||||
|
storageOpts := storage.StoreOptions{}
|
||||||
|
storageSet := false
|
||||||
|
|
||||||
|
uidmapFlag := fs.Lookup("uidmap")
|
||||||
|
gidmapFlag := fs.Lookup("gidmap")
|
||||||
|
subuidname := fs.Lookup("subuidname")
|
||||||
|
subgidname := fs.Lookup("subgidname")
|
||||||
|
if (uidmapFlag != nil && gidmapFlag != nil && subuidname != nil && subgidname != nil) &&
|
||||||
|
(uidmapFlag.Changed || gidmapFlag.Changed || subuidname.Changed || subgidname.Changed) {
|
||||||
|
userns, _ := fs.GetString("userns")
|
||||||
|
uidmapVal, _ := fs.GetStringSlice("uidmap")
|
||||||
|
gidmapVal, _ := fs.GetStringSlice("gidmap")
|
||||||
|
subuidVal, _ := fs.GetString("subuidname")
|
||||||
|
subgidVal, _ := fs.GetString("subgidname")
|
||||||
|
mappings, err := ParseIDMapping(namespaces.UsernsMode(userns), uidmapVal, gidmapVal, subuidVal, subgidVal)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
storageOpts.UIDMap = mappings.UIDMap
|
||||||
|
storageOpts.GIDMap = mappings.GIDMap
|
||||||
|
|
||||||
|
storageSet = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs.Changed("root") {
|
||||||
|
storageSet = true
|
||||||
|
storageOpts.GraphRoot = opts.flags.Root
|
||||||
|
}
|
||||||
|
if fs.Changed("runroot") {
|
||||||
|
storageSet = true
|
||||||
|
storageOpts.RunRoot = opts.flags.Runroot
|
||||||
|
}
|
||||||
|
if len(storageOpts.RunRoot) > 50 {
|
||||||
|
return nil, errors.New("the specified runroot is longer than 50 characters")
|
||||||
|
}
|
||||||
|
if fs.Changed("storage-driver") {
|
||||||
|
storageSet = true
|
||||||
|
storageOpts.GraphDriverName = opts.flags.StorageDriver
|
||||||
|
// Overriding the default storage driver caused GraphDriverOptions from storage.conf to be ignored
|
||||||
|
storageOpts.GraphDriverOptions = []string{}
|
||||||
|
}
|
||||||
|
// This should always be checked after storage-driver is checked
|
||||||
|
if len(opts.flags.StorageOpts) > 0 {
|
||||||
|
storageSet = true
|
||||||
|
storageOpts.GraphDriverOptions = opts.flags.StorageOpts
|
||||||
|
}
|
||||||
|
if opts.migrate {
|
||||||
|
options = append(options, libpod.WithMigrate())
|
||||||
|
if opts.name != "" {
|
||||||
|
options = append(options, libpod.WithMigrateRuntime(opts.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.renumber {
|
||||||
|
options = append(options, libpod.WithRenumber())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only set this if the user changes storage config on the command line
|
||||||
|
if storageSet {
|
||||||
|
options = append(options, libpod.WithStorageConfig(storageOpts))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !storageSet && opts.noStore {
|
||||||
|
options = append(options, libpod.WithNoStore())
|
||||||
|
}
|
||||||
|
// TODO CLI flags for image config?
|
||||||
|
// TODO CLI flag for signature policy?
|
||||||
|
|
||||||
|
if len(opts.flags.Namespace) > 0 {
|
||||||
|
options = append(options, libpod.WithNamespace(opts.flags.Namespace))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs.Changed("runtime") {
|
||||||
|
options = append(options, libpod.WithOCIRuntime(opts.flags.Runtime))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs.Changed("conmon") {
|
||||||
|
options = append(options, libpod.WithConmonPath(opts.flags.ConmonPath))
|
||||||
|
}
|
||||||
|
if fs.Changed("tmpdir") {
|
||||||
|
options = append(options, libpod.WithTmpDir(opts.flags.TmpDir))
|
||||||
|
}
|
||||||
|
if fs.Changed("network-cmd-path") {
|
||||||
|
options = append(options, libpod.WithNetworkCmdPath(opts.flags.NetworkCmdPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs.Changed("events-backend") {
|
||||||
|
options = append(options, libpod.WithEventsLogger(opts.flags.EventsBackend))
|
||||||
|
}
|
||||||
|
|
||||||
|
if fs.Changed("cgroup-manager") {
|
||||||
|
options = append(options, libpod.WithCgroupManager(opts.flags.CGroupManager))
|
||||||
|
} else {
|
||||||
|
unified, err := cgroups.IsCgroup2UnifiedMode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rootless.IsRootless() && !unified {
|
||||||
|
options = append(options, libpod.WithCgroupManager("cgroupfs"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO flag to set libpod static dir?
|
||||||
|
// TODO flag to set libpod tmp dir?
|
||||||
|
|
||||||
|
if fs.Changed("cni-config-dir") {
|
||||||
|
options = append(options, libpod.WithCNIConfigDir(opts.flags.CniConfigDir))
|
||||||
|
}
|
||||||
|
if fs.Changed("default-mounts-file") {
|
||||||
|
options = append(options, libpod.WithDefaultMountsFile(opts.flags.DefaultMountsFile))
|
||||||
|
}
|
||||||
|
if fs.Changed("hooks-dir") {
|
||||||
|
options = append(options, libpod.WithHooksDir(opts.flags.HooksDir...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO flag to set CNI plugins dir?
|
||||||
|
|
||||||
|
// TODO I don't think these belong here?
|
||||||
|
// Will follow up with a different PR to address
|
||||||
|
//
|
||||||
|
// Pod create options
|
||||||
|
|
||||||
|
infraImageFlag := fs.Lookup("infra-image")
|
||||||
|
if infraImageFlag != nil && infraImageFlag.Changed {
|
||||||
|
infraImage, _ := fs.GetString("infra-image")
|
||||||
|
options = append(options, libpod.WithDefaultInfraImage(infraImage))
|
||||||
|
}
|
||||||
|
|
||||||
|
infraCommandFlag := fs.Lookup("infra-command")
|
||||||
|
if infraCommandFlag != nil && infraImageFlag.Changed {
|
||||||
|
infraCommand, _ := fs.GetString("infra-command")
|
||||||
|
options = append(options, libpod.WithDefaultInfraCommand(infraCommand))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !opts.withFDS {
|
||||||
|
options = append(options, libpod.WithEnableSDNotify())
|
||||||
|
}
|
||||||
|
if fs.Changed("config") {
|
||||||
|
return libpod.NewRuntimeFromConfig(ctx, opts.flags.Config, options...)
|
||||||
|
}
|
||||||
|
return libpod.NewRuntime(ctx, options...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
|
||||||
|
func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []string, subUIDMap, subGIDMap string) (*storage.IDMappingOptions, error) {
|
||||||
|
options := storage.IDMappingOptions{
|
||||||
|
HostUIDMapping: true,
|
||||||
|
HostGIDMapping: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if mode.IsKeepID() {
|
||||||
|
if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 {
|
||||||
|
return nil, errors.New("cannot specify custom mappings with --userns=keep-id")
|
||||||
|
}
|
||||||
|
if len(subUIDMap) > 0 || len(subGIDMap) > 0 {
|
||||||
|
return nil, errors.New("cannot specify subuidmap or subgidmap with --userns=keep-id")
|
||||||
|
}
|
||||||
|
if rootless.IsRootless() {
|
||||||
|
min := func(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
uid := rootless.GetRootlessUID()
|
||||||
|
gid := rootless.GetRootlessGID()
|
||||||
|
|
||||||
|
uids, gids, err := rootless.GetConfiguredMappings()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "cannot read mappings")
|
||||||
|
}
|
||||||
|
maxUID, maxGID := 0, 0
|
||||||
|
for _, u := range uids {
|
||||||
|
maxUID += u.Size
|
||||||
|
}
|
||||||
|
for _, g := range gids {
|
||||||
|
maxGID += g.Size
|
||||||
|
}
|
||||||
|
|
||||||
|
options.UIDMap, options.GIDMap = nil, nil
|
||||||
|
|
||||||
|
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(uid, maxUID)})
|
||||||
|
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid, HostID: 0, Size: 1})
|
||||||
|
if maxUID > uid {
|
||||||
|
options.UIDMap = append(options.UIDMap, idtools.IDMap{ContainerID: uid + 1, HostID: uid + 1, Size: maxUID - uid})
|
||||||
|
}
|
||||||
|
|
||||||
|
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: 0, HostID: 1, Size: min(gid, maxGID)})
|
||||||
|
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid, HostID: 0, Size: 1})
|
||||||
|
if maxGID > gid {
|
||||||
|
options.GIDMap = append(options.GIDMap, idtools.IDMap{ContainerID: gid + 1, HostID: gid + 1, Size: maxGID - gid})
|
||||||
|
}
|
||||||
|
|
||||||
|
options.HostUIDMapping = false
|
||||||
|
options.HostGIDMapping = false
|
||||||
|
}
|
||||||
|
// Simply ignore the setting and do not setup an inner namespace for root as it is a no-op
|
||||||
|
return &options, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if subGIDMap == "" && subUIDMap != "" {
|
||||||
|
subGIDMap = subUIDMap
|
||||||
|
}
|
||||||
|
if subUIDMap == "" && subGIDMap != "" {
|
||||||
|
subUIDMap = subGIDMap
|
||||||
|
}
|
||||||
|
if len(gidMapSlice) == 0 && len(uidMapSlice) != 0 {
|
||||||
|
gidMapSlice = uidMapSlice
|
||||||
|
}
|
||||||
|
if len(uidMapSlice) == 0 && len(gidMapSlice) != 0 {
|
||||||
|
uidMapSlice = gidMapSlice
|
||||||
|
}
|
||||||
|
if len(uidMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 {
|
||||||
|
uidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())}
|
||||||
|
}
|
||||||
|
if len(gidMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 {
|
||||||
|
gidMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())}
|
||||||
|
}
|
||||||
|
|
||||||
|
if subUIDMap != "" && subGIDMap != "" {
|
||||||
|
mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
options.UIDMap = mappings.UIDs()
|
||||||
|
options.GIDMap = mappings.GIDs()
|
||||||
|
}
|
||||||
|
parsedUIDMap, err := idtools.ParseIDMap(uidMapSlice, "UID")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parsedGIDMap, err := idtools.ParseIDMap(gidMapSlice, "GID")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
options.UIDMap = append(options.UIDMap, parsedUIDMap...)
|
||||||
|
options.GIDMap = append(options.GIDMap, parsedGIDMap...)
|
||||||
|
if len(options.UIDMap) > 0 {
|
||||||
|
options.HostUIDMapping = false
|
||||||
|
}
|
||||||
|
if len(options.GIDMap) > 0 {
|
||||||
|
options.HostGIDMapping = false
|
||||||
|
}
|
||||||
|
return &options, nil
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
// +build ABISupport
|
||||||
|
|
||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
flag "github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerEngine Proxy will be EOL'ed after podmanV2 is separated from libpod repo
|
||||||
|
|
||||||
|
type runtime struct {
|
||||||
|
entities.ContainerEngine
|
||||||
|
Libpod *libpod.Runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLibpodRuntime(flags flag.FlagSet, opts entities.EngineFlags) (entities.ContainerEngine, error) {
|
||||||
|
r, err := GetRuntime(context.Background(), flags, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &runtime{Libpod: r}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runtime) ShutdownRuntime(force bool) error {
|
||||||
|
return r.Libpod.Shutdown(force)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runtime) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runtime) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runtime) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runtime) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runtime) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runtime) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// +build !ABISupport
|
||||||
|
|
||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/pkg/bindings"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/domain/infra/tunnel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewContainerEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ContainerEngine, error) {
|
||||||
|
switch mode {
|
||||||
|
case entities.ABIMode:
|
||||||
|
return nil, fmt.Errorf("direct runtime not supported")
|
||||||
|
case entities.TunnelMode:
|
||||||
|
ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
|
||||||
|
return &tunnel.ContainerEngine{ClientCxt: ctx}, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewImageEngine factory provides a libpod runtime for image-related operations
|
||||||
|
func NewImageEngine(mode entities.EngineMode, opts entities.EngineOptions) (entities.ImageEngine, error) {
|
||||||
|
switch mode {
|
||||||
|
case entities.ABIMode:
|
||||||
|
return nil, fmt.Errorf("direct image runtime not supported")
|
||||||
|
case entities.TunnelMode:
|
||||||
|
ctx, err := bindings.NewConnection(context.Background(), opts.Uri.String(), opts.Identities...)
|
||||||
|
return &tunnel.ImageEngine{ClientCxt: ctx}, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("runtime mode '%v' is not supported", mode)
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package tunnel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
images "github.com/containers/libpod/pkg/bindings/images"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/domain/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
|
||||||
|
results, err := images.Remove(ir.ClientCxt, nameOrId, &opts.Force)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
report := entities.ImageDeleteReport{
|
||||||
|
Untagged: nil,
|
||||||
|
Deleted: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range results {
|
||||||
|
if a, ok := e["Deleted"]; ok {
|
||||||
|
report.Deleted = a
|
||||||
|
}
|
||||||
|
|
||||||
|
if a, ok := e["Untagged"]; ok {
|
||||||
|
report.Untagged = append(report.Untagged, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &report, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) (*entities.ImageListReport, error) {
|
||||||
|
images, err := images.List(ir.ClientCxt, &opts.All, opts.Filters)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
report := entities.ImageListReport{
|
||||||
|
Images: make([]entities.ImageSummary, len(images)),
|
||||||
|
}
|
||||||
|
for i, img := range images {
|
||||||
|
hold := entities.ImageSummary{}
|
||||||
|
if err := utils.DeepCopy(&hold, img); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
report.Images[i] = hold
|
||||||
|
}
|
||||||
|
return &report, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) {
|
||||||
|
results, err := images.History(ir.ClientCxt, nameOrId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
history := entities.ImageHistoryReport{
|
||||||
|
Layers: make([]entities.ImageHistoryLayer, len(results)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, layer := range results {
|
||||||
|
hold := entities.ImageHistoryLayer{}
|
||||||
|
_ = utils.DeepCopy(&hold, layer)
|
||||||
|
history.Layers[i] = hold
|
||||||
|
}
|
||||||
|
return &history, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
|
||||||
|
results, err := images.Prune(ir.ClientCxt, url.Values{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
report := entities.ImagePruneReport{}
|
||||||
|
copy(report.Report.Id, results)
|
||||||
|
return &report, nil
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package tunnel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Image-related runtime using an ssh-tunnel to utilize Podman service
|
||||||
|
type ImageEngine struct {
|
||||||
|
ClientCxt context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// Container-related runtime using an ssh-tunnel to utilize Podman service
|
||||||
|
type ContainerEngine struct {
|
||||||
|
ClientCxt context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ContainerEngine) Shutdown(force bool) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ContainerEngine) ContainerDelete(ctx context.Context, opts entities.ContainerDeleteOptions) (*entities.ContainerDeleteReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ContainerEngine) ContainerPrune(ctx context.Context) (*entities.ContainerPruneReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ContainerEngine) PodDelete(ctx context.Context, opts entities.PodPruneOptions) (*entities.PodDeleteReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ContainerEngine) PodPrune(ctx context.Context) (*entities.PodPruneReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ContainerEngine) VolumeDelete(ctx context.Context, opts entities.VolumeDeleteOptions) (*entities.VolumeDeleteReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ContainerEngine) VolumePrune(ctx context.Context) (*entities.VolumePruneReport, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
|
||||||
|
// DeepCopy does a deep copy of a structure
|
||||||
|
// Error checking of parameters delegated to json engine
|
||||||
|
var DeepCopy = func(dst interface{}, src interface{}) error {
|
||||||
|
payload, err := json.Marshal(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(payload, dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToLibpodFilters(f url.Values) (filters []string) {
|
||||||
|
for k, v := range f {
|
||||||
|
filters = append(filters, k+"="+v[0])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToUrlValues(f []string) (filters url.Values) {
|
||||||
|
filters = make(url.Values)
|
||||||
|
for _, v := range f {
|
||||||
|
t := strings.SplitN(v, "=", 2)
|
||||||
|
filters.Add(t[0], t[1])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
Loading…
Reference in New Issue