V2 podman command

Signed-off-by: Jhon Honce <jhonce@redhat.com>
This commit is contained in:
Jhon Honce 2020-03-12 10:49:03 -07:00
parent bd9386ddac
commit fbe743501e
39 changed files with 2195 additions and 2 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,
})
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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
}

84
cmd/podmanV2/main.go Normal file
View File

@ -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
}

View File

@ -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
}

BIN
cmd/podmanV2/podmanV2 Executable file

Binary file not shown.

33
cmd/podmanV2/pods/pod.go Normal file
View File

@ -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
}

32
cmd/podmanV2/pods/ps.go Normal file
View File

@ -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
}

View File

@ -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())
}

View File

@ -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
}

35
cmd/podmanV2/root.go Normal file
View File

@ -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(&registry.PodmanTunnel, "remote", "r", false, "Access service via SSH tunnel")
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -28,7 +28,7 @@ func HistoryImage(w http.ResponseWriter, r *http.Request) {
for _, h := range history {
l := handlers.HistoryResponse{
ID: h.ID,
Created: h.Created.UnixNano(),
Created: h.Created.Unix(),
CreatedBy: h.CreatedBy,
Tags: h.Tags,
Size: h.Size,

View File

@ -109,7 +109,7 @@ func NewConnection(ctx context.Context, uri string, identity ...string) (context
}
client, err = tcpClient(_url)
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 {
return nil, errors.Wrapf(err, "Failed to create %sClient", _url.Scheme)

View File

@ -0,0 +1 @@
package entities

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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 }

View File

@ -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
// }

View File

@ -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)
// }

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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")
}

41
pkg/domain/utils/utils.go Normal file
View File

@ -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
}