summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/cliconfig/config.go1
-rw-r--r--cmd/podman/info.go88
-rw-r--r--cmd/podman/main_local.go21
-rw-r--r--cmd/podman/play_kube.go1
-rw-r--r--cmd/podmanV2/common/create.go1
-rw-r--r--cmd/podmanV2/containers/ps.go370
-rw-r--r--cmd/podmanV2/containers/run.go125
-rw-r--r--cmd/podmanV2/containers/start.go87
-rw-r--r--cmd/podmanV2/pods/ps.go4
-rw-r--r--cmd/podmanV2/system/info.go74
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/source/markdown/podman-create.1.md6
-rw-r--r--docs/source/markdown/podman-info.1.md2
-rw-r--r--docs/source/markdown/podman-play-kube.1.md14
-rw-r--r--docs/source/markdown/podman-run.1.md6
-rw-r--r--go.mod6
-rw-r--r--go.sum6
-rw-r--r--libpod/container_internal.go40
-rw-r--r--libpod/container_internal_linux.go14
-rw-r--r--libpod/container_internal_unsupported.go5
-rw-r--r--libpod/define/info.go101
-rw-r--r--libpod/filters/containers.go157
-rw-r--r--libpod/filters/pods.go (renamed from libpod/podfilters/pods.go)2
-rw-r--r--libpod/info.go243
-rw-r--r--libpod/oci.go3
-rw-r--r--libpod/oci_conmon_linux.go28
-rw-r--r--libpod/oci_conmon_unsupported.go4
-rw-r--r--libpod/oci_missing.go15
-rw-r--r--libpod/runtime.go40
-rw-r--r--libpod/storage.go5
-rw-r--r--pkg/adapter/info_remote.go73
-rw-r--r--pkg/adapter/pods.go18
-rw-r--r--pkg/api/handlers/compat/containers.go8
-rw-r--r--pkg/api/handlers/compat/info.go36
-rw-r--r--pkg/api/handlers/compat/version.go6
-rw-r--r--pkg/api/handlers/libpod/containers.go185
-rw-r--r--pkg/api/handlers/libpod/info.go18
-rw-r--r--pkg/api/handlers/libpod/swagger.go10
-rw-r--r--pkg/api/handlers/libpod/types.go82
-rw-r--r--pkg/api/handlers/types.go2
-rw-r--r--pkg/api/server/register_info.go19
-rw-r--r--pkg/bindings/containers/containers.go6
-rw-r--r--pkg/bindings/info.go3
-rw-r--r--pkg/bindings/system/info.go23
-rw-r--r--pkg/bindings/test/info_test.go73
-rw-r--r--pkg/cgroups/pids.go6
-rw-r--r--pkg/domain/entities/container_ps.go174
-rw-r--r--pkg/domain/entities/containers.go59
-rw-r--r--pkg/domain/entities/engine_container.go5
-rw-r--r--pkg/domain/infra/abi/containers.go187
-rw-r--r--pkg/domain/infra/abi/pods.go5
-rw-r--r--pkg/domain/infra/abi/system.go13
-rw-r--r--pkg/domain/infra/tunnel/containers.go17
-rw-r--r--pkg/domain/infra/tunnel/helpers.go5
-rw-r--r--pkg/domain/infra/tunnel/system.go11
-rw-r--r--pkg/namespaces/namespaces.go54
-rw-r--r--pkg/ps/ps.go189
-rw-r--r--pkg/spec/namespaces.go4
-rw-r--r--pkg/util/utils.go12
-rw-r--r--pkg/varlinkapi/system.go73
-rw-r--r--test/e2e/info_test.go8
-rw-r--r--test/e2e/run_userns_test.go131
-rw-r--r--test/system/005-info.bats32
-rw-r--r--test/system/030-run.bats2
-rw-r--r--test/system/065-cp.bats2
-rw-r--r--test/system/400-unprivileged-access.bats4
-rw-r--r--test/system/helpers.bash2
-rw-r--r--vendor/github.com/containers/storage/VERSION2
-rw-r--r--vendor/github.com/containers/storage/go.mod2
-rw-r--r--vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go88
-rw-r--r--vendor/modules.txt6
71 files changed, 2483 insertions, 642 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 99f389799..6d98aaf0e 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -321,6 +321,7 @@ type KubePlayValues struct {
Authfile string
CertDir string
Creds string
+ Network string
Quiet bool
SignaturePolicy string
TlsVerify bool
diff --git a/cmd/podman/info.go b/cmd/podman/info.go
index 7361525ce..79417b85d 100644
--- a/cmd/podman/info.go
+++ b/cmd/podman/info.go
@@ -2,6 +2,8 @@ package main
import (
"fmt"
+ "html/template"
+ "os"
rt "runtime"
"strings"
@@ -11,7 +13,6 @@ import (
"github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/version"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -49,42 +50,32 @@ func init() {
}
func infoCmd(c *cliconfig.InfoValues) error {
- info := map[string]interface{}{}
- remoteClientInfo := map[string]interface{}{}
-
runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.DeferredShutdown(false)
- infoArr, err := runtime.Info()
+ i, err := runtime.Info()
if err != nil {
return errors.Wrapf(err, "error getting info")
}
+ info := infoWithExtra{Info: i}
if runtime.Remote {
endpoint, err := runtime.RemoteEndpoint()
if err != nil {
- logrus.Errorf("Failed to obtain server connection: %s", err.Error())
- } else {
- remoteClientInfo["Connection"] = endpoint.Connection
- remoteClientInfo["Connection Type"] = endpoint.Type.String()
+ return err
}
-
- remoteClientInfo["RemoteAPI Version"] = version.RemoteAPIVersion
- remoteClientInfo["Podman Version"] = version.Version
- remoteClientInfo["OS Arch"] = fmt.Sprintf("%s/%s", rt.GOOS, rt.GOARCH)
- infoArr = append(infoArr, define.InfoData{Type: "client", Data: remoteClientInfo})
+ info.Remote = getRemote(endpoint)
}
if !runtime.Remote && c.Debug {
- debugInfo := debugInfo(c)
- infoArr = append(infoArr, define.InfoData{Type: "debug", Data: debugInfo})
- }
-
- for _, currInfo := range infoArr {
- info[currInfo.Type] = currInfo.Data
+ d, err := getDebug()
+ if err != nil {
+ return err
+ }
+ info.Debug = d
}
var out formats.Writer
@@ -98,19 +89,58 @@ func infoCmd(c *cliconfig.InfoValues) error {
case "":
out = formats.YAMLStruct{Output: info}
default:
- out = formats.StdoutTemplate{Output: info, Template: infoOutputFormat}
+ tmpl, err := template.New("info").Parse(c.Format)
+ if err != nil {
+ return err
+ }
+ err = tmpl.Execute(os.Stdout, info)
+ return err
}
return out.Out()
}
// top-level "debug" info
-func debugInfo(c *cliconfig.InfoValues) map[string]interface{} {
- info := map[string]interface{}{}
- info["compiler"] = rt.Compiler
- info["go version"] = rt.Version()
- info["podman version"] = version.Version
- version, _ := define.GetVersion()
- info["git commit"] = version.GitCommit
- return info
+func getDebug() (*debugInfo, error) {
+ v, err := define.GetVersion()
+ if err != nil {
+ return nil, err
+ }
+ return &debugInfo{
+ Compiler: rt.Compiler,
+ GoVersion: rt.Version(),
+ PodmanVersion: v.Version,
+ GitCommit: v.GitCommit,
+ }, nil
+}
+
+func getRemote(endpoint *adapter.Endpoint) *remoteInfo {
+ return &remoteInfo{
+ Connection: endpoint.Connection,
+ ConnectionType: endpoint.Type.String(),
+ RemoteAPIVersion: string(version.RemoteAPIVersion),
+ PodmanVersion: version.Version,
+ OSArch: fmt.Sprintf("%s/%s", rt.GOOS, rt.GOARCH),
+ }
+}
+
+type infoWithExtra struct {
+ *define.Info
+ Remote *remoteInfo `json:"remote,omitempty"`
+ Debug *debugInfo `json:"debug,omitempty"`
+}
+
+type remoteInfo struct {
+ Connection string `json:"connection"`
+ ConnectionType string `json:"connectionType"`
+ RemoteAPIVersion string `json:"remoteAPIVersion"`
+ PodmanVersion string `json:"podmanVersion"`
+ OSArch string `json:"OSArch"`
+}
+
+type debugInfo struct {
+ Compiler string `json:"compiler"`
+ GoVersion string `json:"goVersion"`
+ PodmanVersion string `json:"podmanVersion"`
+ GitCommit string `json:"gitCommit"`
}
diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go
index 23b3f5ae7..e71dbbf97 100644
--- a/cmd/podman/main_local.go
+++ b/cmd/podman/main_local.go
@@ -11,7 +11,6 @@ import (
"os"
"runtime/pprof"
"strconv"
- "strings"
"syscall"
"github.com/containers/common/pkg/config"
@@ -192,7 +191,7 @@ func setupRootless(cmd *cobra.Command, args []string) error {
}
}
- if os.Geteuid() == 0 || cmd == _searchCommand || cmd == _versionCommand || cmd == _mountCommand || cmd == _migrateCommand || strings.HasPrefix(cmd.Use, "help") {
+ if !executeCommandInUserNS(cmd) {
return nil
}
@@ -243,6 +242,24 @@ func setupRootless(cmd *cobra.Command, args []string) error {
return nil
}
+// Most podman commands when run in rootless mode, need to be executed in the
+// users usernamespace. This function is updated with a list of commands that
+// should NOT be run within the user namespace.
+func executeCommandInUserNS(cmd *cobra.Command) bool {
+ if os.Geteuid() == 0 {
+ return false
+ }
+ switch cmd {
+ case _migrateCommand,
+ _mountCommand,
+ _renumberCommand,
+ _searchCommand,
+ _versionCommand:
+ return false
+ }
+ return true
+}
+
func setRLimits() error {
rlimits := new(syscall.Rlimit)
rlimits.Cur = 1048576
diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go
index 2028d2ef4..a5669c595 100644
--- a/cmd/podman/play_kube.go
+++ b/cmd/podman/play_kube.go
@@ -51,6 +51,7 @@ func init() {
flags.StringVar(&playKubeCommand.SeccompProfileRoot, "seccomp-profile-root", defaultSeccompRoot, "Directory path for seccomp profiles")
markFlagHidden(flags, "signature-policy")
}
+ flags.StringVar(&playKubeCommand.Network, "network", "", "Connect pod to CNI network(s)")
}
func playKubeCmd(c *cliconfig.KubePlayValues) error {
diff --git a/cmd/podmanV2/common/create.go b/cmd/podmanV2/common/create.go
index f81d021c8..e2eb8cbda 100644
--- a/cmd/podmanV2/common/create.go
+++ b/cmd/podmanV2/common/create.go
@@ -29,7 +29,6 @@ func getDefaultContainerConfig() *config.Config {
}
func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
- //createFlags := c.Flags()
createFlags := pflag.FlagSet{}
createFlags.StringSliceVar(
&cf.Annotation,
diff --git a/cmd/podmanV2/containers/ps.go b/cmd/podmanV2/containers/ps.go
index ce3d66c51..2397eb8c0 100644
--- a/cmd/podmanV2/containers/ps.go
+++ b/cmd/podmanV2/containers/ps.go
@@ -1,29 +1,379 @@
package containers
import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "sort"
+ "strconv"
"strings"
+ "text/tabwriter"
+ "text/template"
+ "time"
+ tm "github.com/buger/goterm"
+ "github.com/containers/buildah/pkg/formats"
+ "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/report"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/docker/go-units"
+ "github.com/pkg/errors"
"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),
+ psDescription = "Prints out information about the containers"
+ psCommand = &cobra.Command{
+ Use: "ps",
+ Args: checkFlags,
+ Short: "List containers",
+ Long: psDescription,
+ RunE: ps,
+ PreRunE: preRunE,
+ Example: `podman ps -a
+ podman ps -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}"
+ podman ps --size --sort names`,
}
)
+var (
+ listOpts = entities.ContainerListOptions{
+ Filters: make(map[string][]string),
+ }
+ filters []string
+ noTrunc bool
+ defaultHeaders string = "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES"
+
+// CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
+
+)
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
- Command: psCmd,
+ Command: psCommand,
+ })
+ flags := psCommand.Flags()
+ flags.BoolVarP(&listOpts.All, "all", "a", false, "Show all the containers, default is only running containers")
+ flags.StringSliceVarP(&filters, "filter", "f", []string{}, "Filter output based on conditions given")
+ flags.StringVar(&listOpts.Format, "format", "", "Pretty-print containers to JSON or using a Go template")
+ flags.IntVarP(&listOpts.Last, "last", "n", -1, "Print the n last created containers (all states)")
+ flags.BoolVarP(&listOpts.Latest, "latest", "l", false, "Show the latest container created (all states)")
+ flags.BoolVar(&listOpts.Namespace, "namespace", false, "Display namespace information")
+ flags.BoolVar(&listOpts.Namespace, "ns", false, "Display namespace information")
+ flags.BoolVar(&noTrunc, "no-trunc", false, "Display the extended information")
+ flags.BoolVarP(&listOpts.Pod, "pod", "p", false, "Print the ID and name of the pod the containers are associated with")
+ flags.BoolVarP(&listOpts.Quiet, "quiet", "q", false, "Print the numeric IDs of the containers only")
+ flags.BoolVarP(&listOpts.Size, "size", "s", false, "Display the total file sizes")
+ flags.StringVar(&listOpts.Sort, "sort", "created", "Sort output by command, created, id, image, names, runningfor, size, or status")
+ flags.BoolVar(&listOpts.Sync, "sync", false, "Sync container state with OCI runtime")
+ flags.UintVarP(&listOpts.Watch, "watch", "w", 0, "Watch the ps output on an interval in seconds")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ }
+}
+func checkFlags(c *cobra.Command, args []string) error {
+ // latest, and last are mutually exclusive.
+ if listOpts.Last >= 0 && listOpts.Latest {
+ return errors.Errorf("last and latest are mutually exclusive")
+ }
+ // Filter on status forces all
+ for _, filter := range filters {
+ splitFilter := strings.SplitN(filter, "=", 2)
+ if strings.ToLower(splitFilter[0]) == "status" {
+ listOpts.All = true
+ break
+ }
+ }
+ // Quiet conflicts with size and namespace and is overridden by a Go
+ // template.
+ if listOpts.Quiet {
+ if listOpts.Size || listOpts.Namespace {
+ return errors.Errorf("quiet conflicts with size and namespace")
+ }
+ if c.Flag("format").Changed && listOpts.Format != formats.JSONString {
+ // Quiet is overridden by Go template output.
+ listOpts.Quiet = false
+ }
+ }
+ // Size and namespace conflict with each other
+ if listOpts.Size && listOpts.Namespace {
+ return errors.Errorf("size and namespace options conflict")
+ }
+
+ if listOpts.Watch > 0 && listOpts.Latest {
+ return errors.New("the watch and latest flags cannot be used together")
+ }
+ return nil
+}
+
+func jsonOut(responses []entities.ListContainer) error {
+ b, err := json.MarshalIndent(responses, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+}
+
+func quietOut(responses []entities.ListContainer) error {
+ for _, r := range responses {
+ id := r.ID
+ if !noTrunc {
+ id = id[0:12]
+ }
+ fmt.Println(id)
+ }
+ return nil
+}
+
+func getResponses() ([]entities.ListContainer, error) {
+ responses, err := registry.ContainerEngine().ContainerList(registry.GetContext(), listOpts)
+ if err != nil {
+ return nil, err
+ }
+ if len(listOpts.Sort) > 0 {
+ responses, err = entities.SortPsOutput(listOpts.Sort, responses)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return responses, nil
+}
+
+func ps(cmd *cobra.Command, args []string) error {
+ // []string to map[string][]string
+ for _, f := range filters {
+ split := strings.SplitN(f, "=", 2)
+ if len(split) == 1 {
+ return errors.Errorf("invalid filter %q", f)
+ }
+ listOpts.Filters[split[0]] = append(listOpts.Filters[split[0]], split[1])
+ }
+ responses, err := getResponses()
+ if err != nil {
+ return err
+ }
+ if len(listOpts.Sort) > 0 {
+ responses, err = entities.SortPsOutput(listOpts.Sort, responses)
+ if err != nil {
+ return err
+ }
+ }
+ if listOpts.Format == "json" {
+ return jsonOut(responses)
+ }
+ if listOpts.Quiet {
+ return quietOut(responses)
+ }
+ headers, row := createPsOut()
+ if cmd.Flag("format").Changed {
+ row = listOpts.Format
+ if !strings.HasPrefix(row, "\n") {
+ row += "\n"
+ }
+ }
+ format := "{{range . }}" + row + "{{end}}"
+ if !listOpts.Quiet && !cmd.Flag("format").Changed {
+ format = headers + format
+ }
+ funcs := report.AppendFuncMap(psFuncMap)
+ tmpl, err := template.New("listPods").Funcs(funcs).Parse(format)
+ if err != nil {
+ return err
+ }
+ w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
+ if listOpts.Watch > 0 {
+ for {
+ tm.Clear()
+ tm.MoveCursor(1, 1)
+ tm.Flush()
+ responses, err := getResponses()
+ if err != nil {
+ return err
+ }
+ if err := tmpl.Execute(w, responses); err != nil {
+ return nil
+ }
+ if err := w.Flush(); err != nil {
+ return err
+ }
+ time.Sleep(time.Duration(listOpts.Watch) * time.Second)
+ tm.Clear()
+ tm.MoveCursor(1, 1)
+ tm.Flush()
+ }
+ } else if listOpts.Watch < 1 {
+ if err := tmpl.Execute(w, responses); err != nil {
+ return err
+ }
+ return w.Flush()
+ }
+ return nil
+}
+
+func createPsOut() (string, string) {
+ var row string
+ if listOpts.Namespace {
+ headers := "CONTAINER ID\tNAMES\tPID\tCGROUPNS\tIPC\tMNT\tNET\tPIDN\tUSERNS\tUTS\n"
+ row := "{{.ID}}\t{{names .Names}}\t{{.Pid}}\t{{.Namespaces.Cgroup}}\t{{.Namespaces.IPC}}\t{{.Namespaces.MNT}}\t{{.Namespaces.NET}}\t{{.Namespaces.PIDNS}}\t{{.Namespaces.User}}\t{{.Namespaces.UTS}}\n"
+ return headers, row
+ }
+ headers := defaultHeaders
+ if noTrunc {
+ row += "{{.ID}}"
+ } else {
+ row += "{{slice .ID 0 12}}"
+ }
+ row += "\t{{.Image}}\t{{cmd .Command}}\t{{humanDuration .Created}}\t{{state .}}\t{{ports .Ports}}\t{{names .Names}}"
+
+ if listOpts.Pod {
+ headers += "\tPOD ID\tPODNAME"
+ if noTrunc {
+ row += "\t{{.Pod}}"
+ } else {
+ row += "\t{{slice .Pod 0 12}}"
+ }
+ row += "\t{{.PodName}}"
+ }
+
+ if listOpts.Size {
+ headers += "\tSIZE"
+ row += "\t{{consize .Size}}"
+ }
+ if !strings.HasSuffix(headers, "\n") {
+ headers += "\n"
+ }
+ if !strings.HasSuffix(row, "\n") {
+ row += "\n"
+ }
+ return headers, row
+}
+
+var psFuncMap = template.FuncMap{
+ "cmd": func(conCommand []string) string {
+ return strings.Join(conCommand, " ")
+ },
+ "state": func(con entities.ListContainer) string {
+ var state string
+ switch con.State {
+ case "running":
+ t := units.HumanDuration(time.Since(time.Unix(con.StartedAt, 0)))
+ state = "Up " + t + " ago"
+ case "configured":
+ state = "Created"
+ case "exited":
+ t := units.HumanDuration(time.Since(time.Unix(con.ExitedAt, 0)))
+ state = fmt.Sprintf("Exited (%d) %s ago", con.ExitCode, t)
+ default:
+ state = con.State
+ }
+ return state
+ },
+ "ports": func(ports []ocicni.PortMapping) string {
+ if len(ports) == 0 {
+ return ""
+ }
+ return portsToString(ports)
+ },
+ "names": func(names []string) string {
+ return names[0]
+ },
+ "consize": func(csize shared.ContainerSize) string {
+ virt := units.HumanSizeWithPrecision(float64(csize.RootFsSize), 3)
+ s := units.HumanSizeWithPrecision(float64(csize.RwSize), 3)
+ return fmt.Sprintf("%s (virtual %s)", s, virt)
+ },
+}
+
+// portsToString converts the ports used to a string of the from "port1, port2"
+// and also groups a continuous list of ports into a readable format.
+func portsToString(ports []ocicni.PortMapping) string {
+ type portGroup struct {
+ first int32
+ last int32
+ }
+ var portDisplay []string
+ if len(ports) == 0 {
+ return ""
+ }
+ //Sort the ports, so grouping continuous ports become easy.
+ sort.Slice(ports, func(i, j int) bool {
+ return comparePorts(ports[i], ports[j])
})
+
+ // portGroupMap is used for grouping continuous ports.
+ portGroupMap := make(map[string]*portGroup)
+ var groupKeyList []string
+
+ for _, v := range ports {
+
+ hostIP := v.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ // If hostPort and containerPort are not same, consider as individual port.
+ if v.ContainerPort != v.HostPort {
+ portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol))
+ continue
+ }
+
+ portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol)
+
+ portgroup, ok := portGroupMap[portMapKey]
+ if !ok {
+ portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort}
+ // This list is required to traverse portGroupMap.
+ groupKeyList = append(groupKeyList, portMapKey)
+ continue
+ }
+
+ if portgroup.last == (v.ContainerPort - 1) {
+ portgroup.last = v.ContainerPort
+ continue
+ }
+ }
+ // For each portMapKey, format group list and appned to output string.
+ for _, portKey := range groupKeyList {
+ group := portGroupMap[portKey]
+ portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last))
+ }
+ return strings.Join(portDisplay, ", ")
+}
+
+func comparePorts(i, j ocicni.PortMapping) bool {
+ if i.ContainerPort != j.ContainerPort {
+ return i.ContainerPort < j.ContainerPort
+ }
+
+ if i.HostIP != j.HostIP {
+ return i.HostIP < j.HostIP
+ }
+
+ if i.HostPort != j.HostPort {
+ return i.HostPort < j.HostPort
+ }
+
+ return i.Protocol < j.Protocol
+}
+
+// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto>
+// e.g 0.0.0.0:1000-1006->1000-1006/tcp.
+func formatGroup(key string, start, last int32) string {
+ parts := strings.Split(key, "/")
+ groupType := parts[0]
+ var ip string
+ if len(parts) > 1 {
+ ip = parts[0]
+ groupType = parts[1]
+ }
+ group := strconv.Itoa(int(start))
+ if start != last {
+ group = fmt.Sprintf("%s-%d", group, last)
+ }
+ if ip != "" {
+ group = fmt.Sprintf("%s:%s->%s", ip, group, group)
+ }
+ return fmt.Sprintf("%s/%s", group, groupType)
}
diff --git a/cmd/podmanV2/containers/run.go b/cmd/podmanV2/containers/run.go
new file mode 100644
index 000000000..bd90aee2f
--- /dev/null
+++ b/cmd/podmanV2/containers/run.go
@@ -0,0 +1,125 @@
+package containers
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/sirupsen/logrus"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/specgen"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ runDescription = "Runs a command in a new container from the given image"
+ runCommand = &cobra.Command{
+ Use: "run [flags] IMAGE [COMMAND [ARG...]]",
+ Short: "Run a command in a new container",
+ Long: runDescription,
+ PreRunE: preRunE,
+ RunE: run,
+ Example: `podman run imageID ls -alF /etc
+ podman run --network=host imageID dnf -y install java
+ podman run --volume /var/hostdir:/var/ctrdir -i -t fedora /bin/bash`,
+ }
+)
+
+var (
+ runOpts = entities.ContainerRunOptions{
+ OutputStream: os.Stdout,
+ InputStream: os.Stdin,
+ ErrorStream: os.Stderr,
+ }
+ runRmi bool
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: runCommand,
+ })
+ flags := runCommand.Flags()
+ flags.AddFlagSet(common.GetCreateFlags(&cliVals))
+ flags.AddFlagSet(common.GetNetFlags())
+ flags.SetNormalizeFunc(common.AliasFlags)
+ flags.BoolVar(&runOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process")
+ flags.BoolVar(&runRmi, "rmi", false, "Remove container image unless used by other containers")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("authfile")
+ }
+}
+
+func run(cmd *cobra.Command, args []string) error {
+ var (
+ err error
+ )
+ cliVals.Net, err = common.NetFlagsToNetOptions(cmd)
+ if err != nil {
+ return err
+ }
+ if af := cliVals.Authfile; len(af) > 0 {
+ if _, err := os.Stat(af); err != nil {
+ return errors.Wrapf(err, "error checking authfile path %s", af)
+ }
+ }
+ runOpts.Rm = cliVals.Rm
+ if err := createInit(cmd); err != nil {
+ return err
+ }
+
+ // If -i is not set, clear stdin
+ if !cliVals.Interactive {
+ runOpts.InputStream = nil
+ }
+
+ // If attach is set, clear stdin/stdout/stderr and only attach requested
+ if cmd.Flag("attach").Changed {
+ runOpts.OutputStream = nil
+ runOpts.ErrorStream = nil
+ if !cliVals.Interactive {
+ runOpts.InputStream = nil
+ }
+
+ for _, stream := range cliVals.Attach {
+ switch strings.ToLower(stream) {
+ case "stdout":
+ runOpts.OutputStream = os.Stdout
+ case "stderr":
+ runOpts.ErrorStream = os.Stderr
+ case "stdin":
+ runOpts.InputStream = os.Stdin
+ default:
+ return errors.Wrapf(define.ErrInvalidArg, "invalid stream %q for --attach - must be one of stdin, stdout, or stderr", stream)
+ }
+ }
+ }
+ runOpts.Detach = cliVals.Detach
+ runOpts.DetachKeys = cliVals.DetachKeys
+ s := specgen.NewSpecGenerator(args[0])
+ if err := common.FillOutSpecGen(s, &cliVals, args); err != nil {
+ return err
+ }
+ runOpts.Spec = s
+ report, err := registry.ContainerEngine().ContainerRun(registry.GetContext(), runOpts)
+ if err != nil {
+ return err
+ }
+ if cliVals.Detach {
+ fmt.Println(report.Id)
+ }
+ registry.SetExitCode(report.ExitCode)
+ if runRmi {
+ _, err := registry.ImageEngine().Delete(registry.GetContext(), []string{report.Id}, entities.ImageDeleteOptions{})
+ if err != nil {
+ logrus.Errorf("%s", errors.Wrapf(err, "failed removing image"))
+ }
+ }
+ return nil
+}
diff --git a/cmd/podmanV2/containers/start.go b/cmd/podmanV2/containers/start.go
new file mode 100644
index 000000000..0ae2f6d50
--- /dev/null
+++ b/cmd/podmanV2/containers/start.go
@@ -0,0 +1,87 @@
+package containers
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/containers/libpod/cmd/podmanV2/common"
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/cmd/podmanV2/utils"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ startDescription = `Starts one or more containers. The container name or ID can be used.`
+ startCommand = &cobra.Command{
+ Use: "start [flags] CONTAINER [CONTAINER...]",
+ Short: "Start one or more containers",
+ Long: startDescription,
+ RunE: start,
+ PreRunE: preRunE,
+ Args: cobra.MinimumNArgs(1),
+ Example: `podman start --latest
+ podman start 860a4b231279 5421ab43b45
+ podman start --interactive --attach imageID`,
+ }
+)
+
+var (
+ startOptions entities.ContainerStartOptions
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode},
+ Command: startCommand,
+ })
+ flags := startCommand.Flags()
+ flags.BoolVarP(&startOptions.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR")
+ flags.StringVar(&startOptions.DetachKeys, "detach-keys", common.GetDefaultDetachKeys(), "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`")
+ flags.BoolVarP(&startOptions.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
+ flags.BoolVarP(&startOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ flags.BoolVar(&startOptions.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)")
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("latest")
+ _ = flags.MarkHidden("sig-proxy")
+ }
+
+}
+
+func start(cmd *cobra.Command, args []string) error {
+ var errs utils.OutputErrors
+ if len(args) > 1 && startOptions.Attach {
+ return errors.Errorf("you cannot start and attach multiple containers at once")
+ }
+
+ sigProxy := startOptions.SigProxy || startOptions.Attach
+ if cmd.Flag("sig-proxy").Changed {
+ sigProxy = startOptions.SigProxy
+ }
+
+ if sigProxy && !startOptions.Attach {
+ return errors.Wrapf(define.ErrInvalidArg, "you cannot use sig-proxy without --attach")
+ }
+ if startOptions.Attach {
+ startOptions.Stdin = os.Stdin
+ startOptions.Stderr = os.Stderr
+ startOptions.Stdout = os.Stdout
+ }
+
+ responses, err := registry.ContainerEngine().ContainerStart(registry.GetContext(), args, startOptions)
+ if err != nil {
+ return err
+ }
+
+ for _, r := range responses {
+ if r.Err == nil {
+ fmt.Println(r.Id)
+ } else {
+ errs = append(errs, r.Err)
+ }
+ }
+ // TODO need to understand an implement exitcodes
+ return errs.PrintErrors()
+}
diff --git a/cmd/podmanV2/pods/ps.go b/cmd/podmanV2/pods/ps.go
index 9546dff9e..9875263ac 100644
--- a/cmd/podmanV2/pods/ps.go
+++ b/cmd/podmanV2/pods/ps.go
@@ -88,7 +88,7 @@ func pods(cmd *cobra.Command, args []string) error {
fmt.Println(string(b))
return nil
}
- headers, row := createPodPsOut(cmd)
+ headers, row := createPodPsOut()
if psInput.Quiet {
if noTrunc {
row = "{{.Id}}\n"
@@ -123,7 +123,7 @@ func pods(cmd *cobra.Command, args []string) error {
return nil
}
-func createPodPsOut(cmd *cobra.Command) (string, string) {
+func createPodPsOut() (string, string) {
var row string
headers := defaultHeaders
if noTrunc {
diff --git a/cmd/podmanV2/system/info.go b/cmd/podmanV2/system/info.go
new file mode 100644
index 000000000..69b2871b7
--- /dev/null
+++ b/cmd/podmanV2/system/info.go
@@ -0,0 +1,74 @@
+package system
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "text/template"
+
+ "github.com/containers/libpod/cmd/podmanV2/registry"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/cobra"
+ "gopkg.in/yaml.v2"
+)
+
+var (
+ infoDescription = `Display information pertaining to the host, current storage stats, and build of podman.
+
+ Useful for the user and when reporting issues.
+`
+ infoCommand = &cobra.Command{
+ Use: "info",
+ Args: cobra.NoArgs,
+ Long: infoDescription,
+ Short: "Display podman system information",
+ PreRunE: preRunE,
+ RunE: info,
+ Example: `podman info`,
+ }
+)
+
+var (
+ inFormat string
+ debug bool
+)
+
+func init() {
+ registry.Commands = append(registry.Commands, registry.CliCommand{
+ Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
+ Command: infoCommand,
+ })
+ flags := infoCommand.Flags()
+ flags.BoolVarP(&debug, "debug", "D", false, "Display additional debug information")
+ flags.StringVarP(&inFormat, "format", "f", "", "Change the output format to JSON or a Go template")
+}
+
+func info(cmd *cobra.Command, args []string) error {
+ info, err := registry.ContainerEngine().Info(registry.GetContext())
+ if err != nil {
+ return err
+ }
+
+ if inFormat == "json" {
+ b, err := json.MarshalIndent(info, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+ }
+ if !cmd.Flag("format").Changed {
+ b, err := yaml.Marshal(info)
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(b))
+ return nil
+ }
+ tmpl, err := template.New("info").Parse(inFormat)
+ if err != nil {
+ return err
+ }
+ err = tmpl.Execute(os.Stdout, info)
+ return err
+}
diff --git a/completions/bash/podman b/completions/bash/podman
index 77f881d53..6997db3b5 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -2681,6 +2681,7 @@ _podman_play_kube() {
--authfile
--cert-dir
--creds
+ --network
"
local boolean_options="
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 38b95edc3..f0494ca7d 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -823,6 +823,7 @@ The following examples are all valid:
Without this argument the command will be run as root in the container.
+**--userns**=*auto*[:OPTIONS]
**--userns**=*host*
**--userns**=*keep-id*
**--userns**=container:container
@@ -831,6 +832,11 @@ Without this argument the command will be run as root in the container.
Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value means user namespaces are disabled.
+
+- `auto`: automatically create a namespace. It is possible to specify other options to `auto`. The supported options are
+ **size=SIZE** to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will guess a size for the user namespace.
+ **uidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a UID mapping to be present in the user namespace.
+ **gidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a GID mapping to be present in the user namespace.
- `container`: join the user namespace of the specified container.
- `host`: run in the user namespace of the caller. This is the default if no user namespace options are set. The processes running in the container will have the same privileges on the host as any other process launched by the calling user.
- `keep-id`: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user.
diff --git a/docs/source/markdown/podman-info.1.md b/docs/source/markdown/podman-info.1.md
index b539f1d3c..d9da4c3f8 100644
--- a/docs/source/markdown/podman-info.1.md
+++ b/docs/source/markdown/podman-info.1.md
@@ -156,7 +156,7 @@ Run podman info with JSON formatted response:
```
Run podman info and only get the registries information.
```
-$ podman info --format={{".registries"}}
+$ podman info --format={{".Registries"}}
map[registries:[docker.io quay.io registry.fedoraproject.org registry.access.redhat.com]]
```
diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md
index 2367ff7fe..dd9441800 100644
--- a/docs/source/markdown/podman-play-kube.1.md
+++ b/docs/source/markdown/podman-play-kube.1.md
@@ -36,6 +36,10 @@ The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
+**--network**=*cni networks*
+
+A comma-separated list of the names of CNI networks the pod should join.
+
**--quiet**, **-q**
Suppress output information when pulling images
@@ -62,8 +66,16 @@ $ podman play kube demo.yml
52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6
```
+CNI network(s) can be specified as comma-separated list using ``--network``
+```
+$ podman play kube demo.yml --network cni1,cni2
+52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6
+```
+
+Please take into account that CNI networks must be created first using podman-network-create(1).
+
## SEE ALSO
-podman(1), podman-container(1), podman-pod(1), podman-generate-kube(1), podman-play(1)
+podman(1), podman-container(1), podman-pod(1), podman-generate-kube(1), podman-play(1), podman-network-create(1)
## HISTORY
December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index e8b7d56b7..b21eb9da9 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -862,10 +862,14 @@ Sets the username or UID used and optionally the groupname or GID for the specif
Without this argument the command will be run as root in the container.
-**--userns**=**host**|**keep-id**|**container:**_id_|**ns:**_namespace_
+**--userns**=**auto**|**host**|**keep-id**|**container:**_id_|**ns:**_namespace_
Set the user namespace mode for the container. It defaults to the **PODMAN_USERNS** environment variable. An empty value means user namespaces are disabled.
+- **auto**: automatically create a namespace. It is possible to specify other options to `auto`. The supported options are
+ **size=SIZE** to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will guess a size for the user namespace.
+ **uidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a UID mapping to be present in the user namespace.
+ **gidmapping=HOST_UID:CONTAINER_UID:SIZE** to force a GID mapping to be present in the user namespace.
- **host**: run in the user namespace of the caller. This is the default if no user namespace options are set. The processes running in the container will have the same privileges on the host as any other process launched by the calling user.
- **keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user.
- **ns**: run the container in the given existing user namespace.
diff --git a/go.mod b/go.mod
index 96108b370..36ec8b931 100644
--- a/go.mod
+++ b/go.mod
@@ -10,11 +10,11 @@ require (
github.com/containernetworking/cni v0.7.2-0.20200304161608-4fae32b84921
github.com/containernetworking/plugins v0.8.5
github.com/containers/buildah v1.14.6-0.20200402210551-e9a6703edee2
- github.com/containers/common v0.8.0
+ github.com/containers/common v0.8.1
github.com/containers/conmon v2.0.14+incompatible
github.com/containers/image/v5 v5.3.1
github.com/containers/psgo v1.4.0
- github.com/containers/storage v1.18.1
+ github.com/containers/storage v1.18.2
github.com/coreos/go-systemd/v22 v22.0.0
github.com/cri-o/ocicni v0.1.1-0.20190920040751-deac903fd99b
github.com/cyphar/filepath-securejoin v0.2.2
@@ -42,7 +42,7 @@ require (
github.com/opencontainers/runc v1.0.0-rc9
github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7
github.com/opencontainers/runtime-tools v0.9.0
- github.com/opencontainers/selinux v1.4.0
+ github.com/opencontainers/selinux v1.5.0
github.com/opentracing/opentracing-go v1.1.0
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
diff --git a/go.sum b/go.sum
index 5b150c690..c90c410b7 100644
--- a/go.sum
+++ b/go.sum
@@ -71,6 +71,8 @@ github.com/containers/common v0.6.1/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaT
github.com/containers/common v0.7.0/go.mod h1:UmhIdvSkhTR0hWR01AnuZGNufm80+A0s8isb05eTmz0=
github.com/containers/common v0.8.0 h1:C+wjkcmR4gooeKCXZpyjsHSFARm5AZRegflGz0x0MMw=
github.com/containers/common v0.8.0/go.mod h1:QJTx9+SvhHKP6e+p7Nxqc8oNnS5rSf0KVhxudIbDslU=
+github.com/containers/common v0.8.1 h1:1IUwAtZ4mC7GYRr4AC23cHf2oXCuoLzTUoSzIkSgnYw=
+github.com/containers/common v0.8.1/go.mod h1:VxDJbaA1k6N1TNv9Rt6bQEF4hyKVHNfOfGA5L91ADEs=
github.com/containers/common v1.0.0 h1:sZB48LzGP4bP1CmrkQIFUzdUVBysqRv3kWVk4+qbaVA=
github.com/containers/common v1.0.0/go.mod h1:m62kenckrWi5rZx32kaLje2Og0hpf6NsaTBn6+b+Oys=
github.com/containers/conmon v2.0.14+incompatible h1:knU1O1QxXy5YxtjMQVKEyCajROaehizK9FHaICl+P5Y=
@@ -92,6 +94,8 @@ github.com/containers/storage v1.16.6/go.mod h1:Fws4I+U+C4DmJxDbBs1z9SKk50DzN4Lt
github.com/containers/storage v1.18.0/go.mod h1:gbFeFybWhlVCk3buJ0sovNKs8MzWEBTrk8/sbJw8irQ=
github.com/containers/storage v1.18.1 h1:W134oYa8ALd78yo6DKiDp6n7EWXrc+fCnYmJi6o49vo=
github.com/containers/storage v1.18.1/go.mod h1:6NtCgnUeYsRlyZyrZ5qKkXYC560GRgvA7YrKRSAYSlo=
+github.com/containers/storage v1.18.2 h1:4cgFbrrgr9nR9xCeOmfpyxk1MtXYZGr7XGPJfAVkGmc=
+github.com/containers/storage v1.18.2/go.mod h1:WTBMf+a9ZZ/LbmEVeLHH2TX4CikWbO1Bt+/m58ZHVPg=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
@@ -351,6 +355,8 @@ github.com/opencontainers/selinux v1.3.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwy
github.com/opencontainers/selinux v1.3.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/opencontainers/selinux v1.4.0 h1:cpiX/2wWIju/6My60T6/z9CxNG7c8xTQyEmA9fChpUo=
github.com/opencontainers/selinux v1.4.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
+github.com/opencontainers/selinux v1.5.0 h1:giFN+hbiSqvKWPyagmNk9sABaH7VUZ/+XS7tInqDQ6c=
+github.com/opencontainers/selinux v1.5.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316 h1:enQG2QUGwug4fR1yM6hL0Fjzx6Km/exZY6RbSPwMu3o=
github.com/openshift/api v0.0.0-20200106203948-7ab22a2c8316/go.mod h1:dv+J0b/HWai0QnMVb37/H0v36klkLBi2TNpPeWDxX10=
github.com/openshift/imagebuilder v1.1.3 h1:8TiphsD2wboU7tygtGZ5ZBfCP9FH2ZtvEAli67V2PJ4=
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 4e18819b8..c930017a4 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -339,6 +339,29 @@ func (c *Container) syncContainer() error {
return nil
}
+func (c *Container) setupStorageMapping(dest, from *storage.IDMappingOptions) {
+ if c.config.Rootfs != "" {
+ return
+ }
+ *dest = *from
+ if dest.AutoUserNs {
+ overrides := c.getUserOverrides()
+ dest.AutoUserNsOpts.PasswdFile = overrides.ContainerEtcPasswdPath
+ dest.AutoUserNsOpts.GroupFile = overrides.ContainerEtcGroupPath
+ if c.config.User != "" {
+ initialSize := uint32(0)
+ parts := strings.Split(c.config.User, ":")
+ for _, p := range parts {
+ s, err := strconv.ParseUint(p, 10, 32)
+ if err == nil && uint32(s) > initialSize {
+ initialSize = uint32(s)
+ }
+ }
+ dest.AutoUserNsOpts.InitialSize = initialSize + 1
+ }
+ }
+}
+
// Create container root filesystem for use
func (c *Container) setupStorage(ctx context.Context) error {
span, _ := opentracing.StartSpanFromContext(ctx, "setupStorage")
@@ -398,14 +421,20 @@ func (c *Container) setupStorage(ctx context.Context) error {
options.MountOpts = newOptions
}
- if c.config.Rootfs == "" {
- options.IDMappingOptions = c.config.IDMappings
- }
+ c.setupStorageMapping(&options.IDMappingOptions, &c.config.IDMappings)
+
containerInfo, err := c.runtime.storageService.CreateContainerStorage(ctx, c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, options)
if err != nil {
return errors.Wrapf(err, "error creating container storage")
}
+ c.config.IDMappings.UIDMap = containerInfo.UIDMap
+ c.config.IDMappings.GIDMap = containerInfo.GIDMap
+ c.config.ProcessLabel = containerInfo.ProcessLabel
+ c.config.MountLabel = containerInfo.MountLabel
+ c.config.StaticDir = containerInfo.Dir
+ c.state.RunDir = containerInfo.RunDir
+
if len(c.config.IDMappings.UIDMap) != 0 || len(c.config.IDMappings.GIDMap) != 0 {
if err := os.Chown(containerInfo.RunDir, c.RootUID(), c.RootGID()); err != nil {
return err
@@ -416,11 +445,6 @@ func (c *Container) setupStorage(ctx context.Context) error {
}
}
- c.config.ProcessLabel = containerInfo.ProcessLabel
- c.config.MountLabel = containerInfo.MountLabel
- c.config.StaticDir = containerInfo.Dir
- c.state.RunDir = containerInfo.RunDir
-
// Set the default Entrypoint and Command
if containerInfo.Config != nil {
if c.config.Entrypoint == nil {
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index a3f97f2a6..c40ad45b9 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -396,6 +396,20 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
}
+ if c.config.IDMappings.AutoUserNs {
+ if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
+ return nil, err
+ }
+ g.ClearLinuxUIDMappings()
+ for _, uidmap := range c.config.IDMappings.UIDMap {
+ g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
+ }
+ g.ClearLinuxGIDMappings()
+ for _, gidmap := range c.config.IDMappings.GIDMap {
+ g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
+ }
+ }
+
g.SetRootPath(c.state.Mountpoint)
g.AddAnnotation(annotations.Created, c.config.CreatedTime.Format(time.RFC3339Nano))
g.AddAnnotation("org.opencontainers.image.stopSignal", fmt.Sprintf("%d", c.config.StopSignal))
diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go
index 395271b2a..2a611c2d9 100644
--- a/libpod/container_internal_unsupported.go
+++ b/libpod/container_internal_unsupported.go
@@ -6,6 +6,7 @@ import (
"context"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/lookup"
spec "github.com/opencontainers/runtime-spec/specs-go"
)
@@ -44,3 +45,7 @@ func (c *Container) copyOwnerAndPerms(source, dest string) error {
func (c *Container) getOCICgroupPath() (string, error) {
return "", define.ErrNotImplemented
}
+
+func (c *Container) getUserOverrides() *lookup.Overrides {
+ return nil
+}
diff --git a/libpod/define/info.go b/libpod/define/info.go
new file mode 100644
index 000000000..e9809c367
--- /dev/null
+++ b/libpod/define/info.go
@@ -0,0 +1,101 @@
+package define
+
+import "github.com/containers/storage/pkg/idtools"
+
+// Info is the overall struct that describes the host system
+// running libpod/podman
+type Info struct {
+ Host *HostInfo `json:"host"`
+ Store *StoreInfo `json:"store"`
+ Registries map[string]interface{} `json:"registries"`
+}
+
+//HostInfo describes the libpod host
+type HostInfo struct {
+ Arch string `json:"arch"`
+ BuildahVersion string `json:"buildahVersion"`
+ CGroupsVersion string `json:"cgroupVersion"`
+ Conmon *ConmonInfo `json:"conmon"`
+ CPUs int `json:"cpus"`
+ Distribution DistributionInfo `json:"distribution"`
+ EventLogger string `json:"eventLogger"`
+ Hostname string `json:"hostname"`
+ IDMappings IDMappings `json:"idMappings,omitempty"`
+ Kernel string `json:"kernel"`
+ MemFree int64 `json:"memFree"`
+ MemTotal int64 `json:"memTotal"`
+ OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"`
+ OS string `json:"os"`
+ Rootless bool `json:"rootless"`
+ RuntimeInfo map[string]interface{} `json:"runtimeInfo,omitempty"`
+ Slirp4NetNS SlirpInfo `json:"slirp4netns,omitempty"`
+ SwapFree int64 `json:"swapFree"`
+ SwapTotal int64 `json:"swapTotal"`
+ Uptime string `json:"uptime"`
+}
+
+// SlirpInfo describes the slirp exectuable that
+// is being being used.
+type SlirpInfo struct {
+ Executable string `json:"executable"`
+ Package string `json:"package"`
+ Version string `json:"version"`
+}
+
+// IDMappings describe the GID and UID mappings
+type IDMappings struct {
+ GIDMap []idtools.IDMap `json:"gidmap"`
+ UIDMap []idtools.IDMap `json:"uidmap"`
+}
+
+// DistributionInfo describes the host distribution
+// for libpod
+type DistributionInfo struct {
+ Distribution string `json:"distribution"`
+ Version string `json:"version"`
+}
+
+// ConmonInfo describes the conmon executable being used
+type ConmonInfo struct {
+ Package string `json:"package"`
+ Path string `json:"path"`
+ Version string `json:"version"`
+}
+
+// OCIRuntimeInfo describes the runtime (crun or runc) being
+// used with podman
+type OCIRuntimeInfo struct {
+ Name string `json:"name"`
+ Package string `json:"package"`
+ Path string `json:"path"`
+ Version string `json:"version"`
+}
+
+// StoreInfo describes the container storage and its
+// attributes
+type StoreInfo struct {
+ ConfigFile string `json:"configFile"`
+ ContainerStore ContainerStore `json:"containerStore"`
+ GraphDriverName string `json:"graphDriverName"`
+ GraphOptions map[string]interface{} `json:"graphOptions"`
+ GraphRoot string `json:"graphRoot"`
+ GraphStatus map[string]string `json:"graphStatus"`
+ ImageStore ImageStore `json:"imageStore"`
+ RunRoot string `json:"runRoot"`
+ VolumePath string `json:"volumePath"`
+}
+
+// ImageStore describes the image store. Right now only the number
+// of images present
+type ImageStore struct {
+ Number int `json:"number"`
+}
+
+// ContainerStore describes the quantity of containers in the
+// store by status
+type ContainerStore struct {
+ Number int `json:"number"`
+ Paused int `json:"paused"`
+ Running int `json:"running"`
+ Stopped int `json:"stopped"`
+}
diff --git a/libpod/filters/containers.go b/libpod/filters/containers.go
new file mode 100644
index 000000000..c13372f91
--- /dev/null
+++ b/libpod/filters/containers.go
@@ -0,0 +1,157 @@
+package lpfilters
+
+import (
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/timetype"
+ "github.com/containers/libpod/pkg/util"
+ "github.com/pkg/errors"
+)
+
+// GenerateContainerFilterFuncs return ContainerFilter functions based of filter.
+func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) {
+ switch filter {
+ case "id":
+ return func(c *libpod.Container) bool {
+ return strings.Contains(c.ID(), filterValue)
+ }, nil
+ case "label":
+ var filterArray = strings.SplitN(filterValue, "=", 2)
+ var filterKey = filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ return func(c *libpod.Container) bool {
+ for labelKey, labelValue := range c.Labels() {
+ if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "name":
+ return func(c *libpod.Container) bool {
+ match, err := regexp.MatchString(filterValue, c.Name())
+ if err != nil {
+ return false
+ }
+ return match
+ }, nil
+ case "exited":
+ exitCode, err := strconv.ParseInt(filterValue, 10, 32)
+ if err != nil {
+ return nil, errors.Wrapf(err, "exited code out of range %q", filterValue)
+ }
+ return func(c *libpod.Container) bool {
+ ec, exited, err := c.ExitCode()
+ if ec == int32(exitCode) && err == nil && exited {
+ return true
+ }
+ return false
+ }, nil
+ case "status":
+ if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) {
+ return nil, errors.Errorf("%s is not a valid status", filterValue)
+ }
+ return func(c *libpod.Container) bool {
+ status, err := c.State()
+ if err != nil {
+ return false
+ }
+ if filterValue == "stopped" {
+ filterValue = "exited"
+ }
+ state := status.String()
+ if status == define.ContainerStateConfigured {
+ state = "created"
+ } else if status == define.ContainerStateStopped {
+ state = "exited"
+ }
+ return state == filterValue
+ }, nil
+ case "ancestor":
+ // This needs to refine to match docker
+ // - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant.
+ return func(c *libpod.Container) bool {
+ containerConfig := c.Config()
+ if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) {
+ return true
+ }
+ return false
+ }, nil
+ case "before":
+ ctr, err := r.LookupContainer(filterValue)
+ if err != nil {
+ return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
+ }
+ containerConfig := ctr.Config()
+ createTime := containerConfig.CreatedTime
+ return func(c *libpod.Container) bool {
+ cc := c.Config()
+ return createTime.After(cc.CreatedTime)
+ }, nil
+ case "since":
+ ctr, err := r.LookupContainer(filterValue)
+ if err != nil {
+ return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
+ }
+ containerConfig := ctr.Config()
+ createTime := containerConfig.CreatedTime
+ return func(c *libpod.Container) bool {
+ cc := c.Config()
+ return createTime.Before(cc.CreatedTime)
+ }, nil
+ case "volume":
+ //- volume=(<volume-name>|<mount-point-destination>)
+ return func(c *libpod.Container) bool {
+ containerConfig := c.Config()
+ var dest string
+ arr := strings.Split(filterValue, ":")
+ source := arr[0]
+ if len(arr) == 2 {
+ dest = arr[1]
+ }
+ for _, mount := range containerConfig.Spec.Mounts {
+ if dest != "" && (mount.Source == source && mount.Destination == dest) {
+ return true
+ }
+ if dest == "" && mount.Source == source {
+ return true
+ }
+ }
+ return false
+ }, nil
+ case "health":
+ return func(c *libpod.Container) bool {
+ hcStatus, err := c.HealthCheckStatus()
+ if err != nil {
+ return false
+ }
+ return hcStatus == filterValue
+ }, nil
+ case "until":
+ ts, err := timetype.GetTimestamp(filterValue, time.Now())
+ if err != nil {
+ return nil, err
+ }
+ seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
+ if err != nil {
+ return nil, err
+ }
+ until := time.Unix(seconds, nanoseconds)
+ return func(c *libpod.Container) bool {
+ if !until.IsZero() && c.CreatedTime().After((until)) {
+ return true
+ }
+ return false
+ }, nil
+ }
+ return nil, errors.Errorf("%s is an invalid filter", filter)
+}
diff --git a/libpod/podfilters/pods.go b/libpod/filters/pods.go
index 54fa85edc..9bf436eab 100644
--- a/libpod/podfilters/pods.go
+++ b/libpod/filters/pods.go
@@ -1,4 +1,4 @@
-package podfilters
+package lpfilters
import (
"strconv"
diff --git a/libpod/info.go b/libpod/info.go
index 8d411f0d4..3cc767be6 100644
--- a/libpod/info.go
+++ b/libpod/info.go
@@ -13,7 +13,9 @@ import (
"time"
"github.com/containers/buildah"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/cgroups"
+ registries2 "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/system"
@@ -21,14 +23,80 @@ import (
"github.com/sirupsen/logrus"
)
+// Info returns the store and host information
+func (r *Runtime) info() (*define.Info, error) {
+ info := define.Info{}
+ // get host information
+ hostInfo, err := r.hostInfo()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting host info")
+ }
+ info.Host = hostInfo
+
+ // get store information
+ storeInfo, err := r.storeInfo()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting store info")
+ }
+ info.Store = storeInfo
+ registries := make(map[string]interface{})
+ data, err := registries2.GetRegistriesData()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting registries")
+ }
+ for _, reg := range data {
+ registries[reg.Prefix] = reg
+ }
+ regs, err := registries2.GetRegistries()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting registries")
+ }
+ if len(regs) > 0 {
+ registries["search"] = regs
+ }
+
+ info.Registries = registries
+ return &info, nil
+}
+
// top-level "host" info
-func (r *Runtime) hostInfo() (map[string]interface{}, error) {
+func (r *Runtime) hostInfo() (*define.HostInfo, error) {
// lets say OS, arch, number of cpus, amount of memory, maybe os distribution/version, hostname, kernel version, uptime
- info := map[string]interface{}{}
- info["os"] = runtime.GOOS
- info["arch"] = runtime.GOARCH
- info["cpus"] = runtime.NumCPU()
- info["rootless"] = rootless.IsRootless()
+ mi, err := system.ReadMemInfo()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading memory info")
+ }
+
+ hostDistributionInfo := r.GetHostDistributionInfo()
+
+ kv, err := readKernelVersion()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error reading kernel version")
+ }
+
+ host, err := os.Hostname()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting hostname")
+ }
+ info := define.HostInfo{
+ Arch: runtime.GOARCH,
+ BuildahVersion: buildah.Version,
+ CPUs: runtime.NumCPU(),
+ Distribution: hostDistributionInfo,
+ EventLogger: r.eventer.String(),
+ Hostname: host,
+ IDMappings: define.IDMappings{},
+ Kernel: kv,
+ MemFree: mi.MemFree,
+ MemTotal: mi.MemTotal,
+ OS: runtime.GOOS,
+ Rootless: rootless.IsRootless(),
+ Slirp4NetNS: define.SlirpInfo{},
+ SwapFree: mi.SwapFree,
+ SwapTotal: mi.SwapTotal,
+ }
+
+ // CGroups version
unified, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, errors.Wrapf(err, "error reading cgroups mode")
@@ -37,17 +105,8 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
if unified {
cgroupVersion = "v2"
}
- info["CgroupVersion"] = cgroupVersion
- mi, err := system.ReadMemInfo()
- if err != nil {
- return nil, errors.Wrapf(err, "error reading memory info")
- }
- // TODO this might be a place for github.com/dustin/go-humanize
- info["MemTotal"] = mi.MemTotal
- info["MemFree"] = mi.MemFree
- info["SwapTotal"] = mi.SwapTotal
- info["SwapFree"] = mi.SwapFree
- hostDistributionInfo := r.GetHostDistributionInfo()
+ info.CGroupsVersion = cgroupVersion
+
if rootless.IsRootless() {
if path, err := exec.LookPath("slirp4netns"); err == nil {
logrus.Warnf("Failed to retrieve program version for %s: %v", path, err)
@@ -55,11 +114,12 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
if err != nil {
logrus.Warnf("Failed to retrieve program version for %s: %v", path, err)
}
- program := map[string]interface{}{}
- program["Executable"] = path
- program["Version"] = version
- program["Package"] = packageVersion(path)
- info["slirp4netns"] = program
+ program := define.SlirpInfo{
+ Executable: path,
+ Package: packageVersion(path),
+ Version: version,
+ }
+ info.Slirp4NetNS = program
}
uidmappings, err := rootless.ReadMappingsProc("/proc/self/uid_map")
if err != nil {
@@ -69,29 +129,19 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
if err != nil {
return nil, errors.Wrapf(err, "error reading gid mappings")
}
- idmappings := make(map[string]interface{})
- idmappings["uidmap"] = uidmappings
- idmappings["gidmap"] = gidmappings
- info["IDMappings"] = idmappings
- }
- info["Distribution"] = map[string]interface{}{
- "distribution": hostDistributionInfo["Distribution"],
- "version": hostDistributionInfo["Version"],
- }
- info["BuildahVersion"] = buildah.Version
- kv, err := readKernelVersion()
- if err != nil {
- return nil, errors.Wrapf(err, "error reading kernel version")
+ idmappings := define.IDMappings{
+ GIDMap: gidmappings,
+ UIDMap: uidmappings,
+ }
+ info.IDMappings = idmappings
}
- info["kernel"] = kv
- runtimeInfo, err := r.defaultOCIRuntime.RuntimeInfo()
+ conmonInfo, ociruntimeInfo, err := r.defaultOCIRuntime.RuntimeInfo()
if err != nil {
logrus.Errorf("Error getting info on OCI runtime %s: %v", r.defaultOCIRuntime.Name(), err)
} else {
- for k, v := range runtimeInfo {
- info[k] = v
- }
+ info.Conmon = conmonInfo
+ info.OCIRuntime = ociruntimeInfo
}
up, err := readUptime()
@@ -105,6 +155,7 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
return nil, errors.Wrapf(err, "error parsing system uptime")
}
+ // TODO Isnt there a simple lib for this, something like humantime?
hoursFound := false
var timeBuffer bytes.Buffer
var hoursBuffer bytes.Buffer
@@ -121,32 +172,75 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
}
}
- info["uptime"] = timeBuffer.String()
+ info.Uptime = timeBuffer.String()
if hoursFound {
hours, err := strconv.ParseFloat(hoursBuffer.String(), 64)
if err == nil {
days := hours / 24
- info["uptime"] = fmt.Sprintf("%s (Approximately %.2f days)", info["uptime"], days)
+ info.Uptime = fmt.Sprintf("%s (Approximately %.2f days)", info.Uptime, days)
}
}
- host, err := os.Hostname()
+ return &info, nil
+}
+
+func (r *Runtime) getContainerStoreInfo() (define.ContainerStore, error) {
+ var (
+ paused, running, stopped int
+ )
+ cs := define.ContainerStore{}
+ cons, err := r.GetAllContainers()
if err != nil {
- return nil, errors.Wrapf(err, "error getting hostname")
+ return cs, err
}
- info["hostname"] = host
- info["eventlogger"] = r.eventer.String()
-
- return info, nil
+ for _, con := range cons {
+ state, err := con.State()
+ if err != nil {
+ return cs, err
+ }
+ switch state {
+ case define.ContainerStateRunning:
+ running += 1
+ case define.ContainerStatePaused:
+ paused += 1
+ default:
+ stopped += 1
+ }
+ }
+ cs.Number = len(cons)
+ cs.Paused = paused
+ cs.Stopped = stopped
+ cs.Running = running
+ return cs, nil
}
// top-level "store" info
-func (r *Runtime) storeInfo() (map[string]interface{}, error) {
+func (r *Runtime) storeInfo() (*define.StoreInfo, error) {
// lets say storage driver in use, number of images, number of containers
- info := map[string]interface{}{}
- info["GraphRoot"] = r.store.GraphRoot()
- info["RunRoot"] = r.store.RunRoot()
- info["GraphDriverName"] = r.store.GraphDriverName()
+ configFile, err := storage.DefaultConfigFile(rootless.IsRootless())
+ if err != nil {
+ return nil, err
+ }
+ images, err := r.store.Images()
+ if err != nil {
+ return nil, errors.Wrapf(err, "error getting number of images")
+ }
+ conInfo, err := r.getContainerStoreInfo()
+ if err != nil {
+ return nil, err
+ }
+ imageInfo := define.ImageStore{Number: len(images)}
+
+ info := define.StoreInfo{
+ ImageStore: imageInfo,
+ ContainerStore: conInfo,
+ GraphRoot: r.store.GraphRoot(),
+ RunRoot: r.store.RunRoot(),
+ GraphDriverName: r.store.GraphDriverName(),
+ GraphOptions: nil,
+ VolumePath: r.config.Engine.VolumePath,
+ ConfigFile: configFile,
+ }
graphOptions := map[string]interface{}{}
for _, o := range r.store.GraphOptions() {
split := strings.SplitN(o, "=", 2)
@@ -164,14 +258,8 @@ func (r *Runtime) storeInfo() (map[string]interface{}, error) {
graphOptions[split[0]] = split[1]
}
}
- info["GraphOptions"] = graphOptions
- info["VolumePath"] = r.config.Engine.VolumePath
+ info.GraphOptions = graphOptions
- configFile, err := storage.DefaultConfigFile(rootless.IsRootless())
- if err != nil {
- return nil, err
- }
- info["ConfigFile"] = configFile
statusPairs, err := r.store.Status()
if err != nil {
return nil, err
@@ -180,24 +268,8 @@ func (r *Runtime) storeInfo() (map[string]interface{}, error) {
for _, pair := range statusPairs {
status[pair[0]] = pair[1]
}
- info["GraphStatus"] = status
- images, err := r.store.Images()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting number of images")
- }
- info["ImageStore"] = map[string]interface{}{
- "number": len(images),
- }
-
- containers, err := r.store.Containers()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting number of containers")
- }
- info["ContainerStore"] = map[string]interface{}{
- "number": len(containers),
- }
-
- return info, nil
+ info.GraphStatus = status
+ return &info, nil
}
func readKernelVersion() (string, error) {
@@ -225,14 +297,13 @@ func readUptime() (string, error) {
}
// GetHostDistributionInfo returns a map containing the host's distribution and version
-func (r *Runtime) GetHostDistributionInfo() map[string]string {
- dist := make(map[string]string)
-
+func (r *Runtime) GetHostDistributionInfo() define.DistributionInfo {
// Populate values in case we cannot find the values
// or the file
- dist["Distribution"] = "unknown"
- dist["Version"] = "unknown"
-
+ dist := define.DistributionInfo{
+ Distribution: "unknown",
+ Version: "unknown",
+ }
f, err := os.Open("/etc/os-release")
if err != nil {
return dist
@@ -242,10 +313,10 @@ func (r *Runtime) GetHostDistributionInfo() map[string]string {
l := bufio.NewScanner(f)
for l.Scan() {
if strings.HasPrefix(l.Text(), "ID=") {
- dist["Distribution"] = strings.TrimPrefix(l.Text(), "ID=")
+ dist.Distribution = strings.TrimPrefix(l.Text(), "ID=")
}
if strings.HasPrefix(l.Text(), "VERSION_ID=") {
- dist["Version"] = strings.Trim(strings.TrimPrefix(l.Text(), "VERSION_ID="), "\"")
+ dist.Version = strings.Trim(strings.TrimPrefix(l.Text(), "VERSION_ID="), "\"")
}
}
return dist
diff --git a/libpod/oci.go b/libpod/oci.go
index e4fbcb62e..6adf42497 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -5,7 +5,6 @@ import (
"net"
"github.com/containers/libpod/libpod/define"
-
"k8s.io/client-go/tools/remotecommand"
)
@@ -122,7 +121,7 @@ type OCIRuntime interface {
ExitFilePath(ctr *Container) (string, error)
// RuntimeInfo returns verbose information about the runtime.
- RuntimeInfo() (map[string]interface{}, error)
+ RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error)
}
// ExecOptions are options passed into ExecContainer. They control the command
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 2e96dbe57..c20e3f0b4 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -999,32 +999,30 @@ func (r *ConmonOCIRuntime) ExitFilePath(ctr *Container) (string, error) {
}
// RuntimeInfo provides information on the runtime.
-func (r *ConmonOCIRuntime) RuntimeInfo() (map[string]interface{}, error) {
+func (r *ConmonOCIRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) {
runtimePackage := packageVersion(r.path)
conmonPackage := packageVersion(r.conmonPath)
runtimeVersion, err := r.getOCIRuntimeVersion()
if err != nil {
- return nil, errors.Wrapf(err, "error getting version of OCI runtime %s", r.name)
+ return nil, nil, errors.Wrapf(err, "error getting version of OCI runtime %s", r.name)
}
conmonVersion, err := r.getConmonVersion()
if err != nil {
- return nil, errors.Wrapf(err, "error getting conmon version")
+ return nil, nil, errors.Wrapf(err, "error getting conmon version")
}
- info := make(map[string]interface{})
- info["Conmon"] = map[string]interface{}{
- "path": r.conmonPath,
- "package": conmonPackage,
- "version": conmonVersion,
+ conmon := define.ConmonInfo{
+ Package: conmonPackage,
+ Path: r.conmonPath,
+ Version: conmonVersion,
}
- info["OCIRuntime"] = map[string]interface{}{
- "name": r.name,
- "path": r.path,
- "package": runtimePackage,
- "version": runtimeVersion,
+ ocirt := define.OCIRuntimeInfo{
+ Name: r.name,
+ Path: r.path,
+ Package: runtimePackage,
+ Version: runtimeVersion,
}
-
- return info, nil
+ return &conmon, &ocirt, nil
}
// makeAccessible changes the path permission and each parent directory to have --x--x--x
diff --git a/libpod/oci_conmon_unsupported.go b/libpod/oci_conmon_unsupported.go
index 395b6f6d9..1f9d89ff6 100644
--- a/libpod/oci_conmon_unsupported.go
+++ b/libpod/oci_conmon_unsupported.go
@@ -117,8 +117,8 @@ func (r *ConmonOCIRuntime) ExitFilePath(ctr *Container) (string, error) {
}
// RuntimeInfo is not supported on this OS.
-func (r *ConmonOCIRuntime) RuntimeInfo() (map[string]interface{}, error) {
- return nil, define.ErrNotImplemented
+func (r *ConmonOCIRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) {
+ return nil, nil, define.ErrNotImplemented
}
// Package is not supported on this OS.
diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go
index a5d589255..5284fb4b7 100644
--- a/libpod/oci_missing.go
+++ b/libpod/oci_missing.go
@@ -195,15 +195,14 @@ func (r *MissingRuntime) ExitFilePath(ctr *Container) (string, error) {
}
// RuntimeInfo returns information on the missing runtime
-func (r *MissingRuntime) RuntimeInfo() (map[string]interface{}, error) {
- info := make(map[string]interface{})
- info["OCIRuntime"] = map[string]interface{}{
- "name": r.name,
- "path": "missing",
- "package": "missing",
- "version": "missing",
+func (r *MissingRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) {
+ ocirt := define.OCIRuntimeInfo{
+ Name: r.name,
+ Path: "missing",
+ Package: "missing",
+ Version: "missing",
}
- return info, nil
+ return nil, &ocirt, nil
}
// Return an error indicating the runtime is missing
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 422b79359..a6032ad23 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -9,16 +9,14 @@ import (
"sync"
"syscall"
+ "github.com/containers/common/pkg/config"
is "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/types"
-
- "github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/libpod/lock"
"github.com/containers/libpod/pkg/cgroups"
- sysreg "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
@@ -675,40 +673,8 @@ func (r *Runtime) refresh(alivePath string) error {
}
// Info returns the store and host information
-func (r *Runtime) Info() ([]define.InfoData, error) {
- info := []define.InfoData{}
- // get host information
- hostInfo, err := r.hostInfo()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting host info")
- }
- info = append(info, define.InfoData{Type: "host", Data: hostInfo})
-
- // get store information
- storeInfo, err := r.storeInfo()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting store info")
- }
- info = append(info, define.InfoData{Type: "store", Data: storeInfo})
-
- registries := make(map[string]interface{})
- data, err := sysreg.GetRegistriesData()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting registries")
- }
- for _, reg := range data {
- registries[reg.Prefix] = reg
- }
- regs, err := sysreg.GetRegistries()
- if err != nil {
- return nil, errors.Wrapf(err, "error getting registries")
- }
- if len(regs) > 0 {
- registries["search"] = regs
- }
-
- info = append(info, define.InfoData{Type: "registries", Data: registries})
- return info, nil
+func (r *Runtime) Info() (*define.Info, error) {
+ return r.info()
}
// generateName generates a unique name for a container or pod.
diff --git a/libpod/storage.go b/libpod/storage.go
index d675f4ffe..34e40f699 100644
--- a/libpod/storage.go
+++ b/libpod/storage.go
@@ -8,6 +8,7 @@ import (
"github.com/containers/image/v5/types"
"github.com/containers/libpod/libpod/define"
"github.com/containers/storage"
+ "github.com/containers/storage/pkg/idtools"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
@@ -35,6 +36,8 @@ type ContainerInfo struct {
Config *v1.Image
ProcessLabel string
MountLabel string
+ UIDMap []idtools.IDMap
+ GIDMap []idtools.IDMap
}
// RuntimeContainerMetadata is the structure that we encode as JSON and store
@@ -166,6 +169,8 @@ func (r *storageService) CreateContainerStorage(ctx context.Context, systemConte
logrus.Debugf("container %q has run directory %q", container.ID, containerRunDir)
return ContainerInfo{
+ UIDMap: options.UIDMap,
+ GIDMap: options.GIDMap,
Dir: containerDir,
RunDir: containerRunDir,
Config: imageConfig,
diff --git a/pkg/adapter/info_remote.go b/pkg/adapter/info_remote.go
index 0e8fb06d1..549b01f54 100644
--- a/pkg/adapter/info_remote.go
+++ b/pkg/adapter/info_remote.go
@@ -3,51 +3,62 @@
package adapter
import (
- "encoding/json"
-
"github.com/containers/libpod/libpod/define"
iopodman "github.com/containers/libpod/pkg/varlink"
)
// Info returns information for the host system and its components
-func (r RemoteRuntime) Info() ([]define.InfoData, error) {
+func (r RemoteRuntime) Info() (*define.Info, error) {
// TODO the varlink implementation for info should be updated to match the output for regular info
var (
- reply []define.InfoData
- regInfo map[string]interface{}
- hostInfo map[string]interface{}
- store map[string]interface{}
+ reply define.Info
)
info, err := iopodman.GetInfo().Call(r.Conn)
if err != nil {
return nil, err
}
-
- // info.host -> map[string]interface{}
- h, err := json.Marshal(info.Host)
- if err != nil {
- return nil, err
+ hostInfo := define.HostInfo{
+ Arch: info.Host.Arch,
+ BuildahVersion: info.Host.Buildah_version,
+ CPUs: int(info.Host.Cpus),
+ Distribution: define.DistributionInfo{
+ Distribution: info.Host.Distribution.Distribution,
+ Version: info.Host.Distribution.Version,
+ },
+ EventLogger: info.Host.Eventlogger,
+ Hostname: info.Host.Hostname,
+ Kernel: info.Host.Kernel,
+ MemFree: info.Host.Mem_free,
+ MemTotal: info.Host.Mem_total,
+ OS: info.Host.Os,
+ SwapFree: info.Host.Swap_free,
+ SwapTotal: info.Host.Swap_total,
+ Uptime: info.Host.Uptime,
}
- json.Unmarshal(h, &hostInfo)
-
- // info.store -> map[string]interface{}
- s, err := json.Marshal(info.Store)
- if err != nil {
- return nil, err
+ storeInfo := define.StoreInfo{
+ ContainerStore: define.ContainerStore{
+ Number: int(info.Store.Containers),
+ },
+ GraphDriverName: info.Store.Graph_driver_name,
+ GraphRoot: info.Store.Graph_root,
+ ImageStore: define.ImageStore{
+ Number: int(info.Store.Images),
+ },
+ RunRoot: info.Store.Run_root,
}
- json.Unmarshal(s, &store)
-
- // info.Registries -> map[string]interface{}
- reg, err := json.Marshal(info.Registries)
- if err != nil {
- return nil, err
+ reply.Host = &hostInfo
+ reply.Store = &storeInfo
+ regs := make(map[string]interface{})
+ if len(info.Registries.Search) > 0 {
+ regs["search"] = info.Registries.Search
}
- json.Unmarshal(reg, &regInfo)
-
- // Add everything to the reply
- reply = append(reply, define.InfoData{Type: "host", Data: hostInfo})
- reply = append(reply, define.InfoData{Type: "registries", Data: regInfo})
- reply = append(reply, define.InfoData{Type: "store", Data: store})
- return reply, nil
+ if len(info.Registries.Blocked) > 0 {
+ regs["blocked"] = info.Registries.Blocked
+ }
+ if len(info.Registries.Insecure) > 0 {
+ regs["insecure"] = info.Registries.Insecure
+ }
+ reply.Registries = regs
+ return &reply, nil
}
diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go
index 102eabd8b..7c2a84cc7 100644
--- a/pkg/adapter/pods.go
+++ b/pkg/adapter/pods.go
@@ -343,7 +343,7 @@ func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateVa
logrus.Debugf("Pod will use host networking")
options = append(options, libpod.WithPodHostNetwork())
case "":
- return "", errors.Errorf("invalid value passed to --net: must provide a comma-separated list of CNI networks or host")
+ return "", errors.Errorf("invalid value passed to --network: must provide a comma-separated list of CNI networks or host")
default:
// We'll assume this is a comma-separated list of CNI
// networks.
@@ -595,6 +595,22 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa
podPorts := getPodPorts(podYAML.Spec.Containers)
podOptions = append(podOptions, libpod.WithInfraContainerPorts(podPorts))
+ if c.Flag("network").Changed {
+ netValue := c.String("network")
+ switch strings.ToLower(netValue) {
+ case "bridge", "host":
+ return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML")
+ case "":
+ return nil, errors.Errorf("invalid value passed to --network: must provide a comma-separated list of CNI networks")
+ default:
+ // We'll assume this is a comma-separated list of CNI
+ // networks.
+ networks := strings.Split(netValue, ",")
+ logrus.Debugf("Pod joining CNI networks: %v", networks)
+ podOptions = append(podOptions, libpod.WithPodNetworks(networks))
+ }
+ }
+
// Create the Pod
pod, err = r.NewPod(ctx, podOptions...)
if err != nil {
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 2ce113d30..c53af0f26 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -94,15 +94,9 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
}
}
// TODO filters still need to be applied
- infoData, err := runtime.Info()
- if err != nil {
- utils.InternalServerError(w, errors.Wrapf(err, "Failed to obtain system info"))
- return
- }
-
var list = make([]*handlers.Container, len(containers))
for i, ctnr := range containers {
- api, err := handlers.LibpodToContainer(ctnr, infoData, query.Size)
+ api, err := handlers.LibpodToContainer(ctnr, query.Size)
if err != nil {
utils.InternalServerError(w, err)
return
diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go
index 104d0793b..179b4a3e0 100644
--- a/pkg/api/handlers/compat/info.go
+++ b/pkg/api/handlers/compat/info.go
@@ -33,8 +33,6 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to obtain system memory info"))
return
}
- hostInfo := infoData[0].Data
- storeInfo := infoData[1].Data
configInfo, err := runtime.GetConfig()
if err != nil {
@@ -64,44 +62,44 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
ClusterAdvertise: "",
ClusterStore: "",
ContainerdCommit: docker.Commit{},
- Containers: storeInfo["ContainerStore"].(map[string]interface{})["number"].(int),
+ Containers: infoData.Store.ContainerStore.Number,
ContainersPaused: stateInfo[define.ContainerStatePaused],
ContainersRunning: stateInfo[define.ContainerStateRunning],
ContainersStopped: stateInfo[define.ContainerStateStopped] + stateInfo[define.ContainerStateExited],
Debug: log.IsLevelEnabled(log.DebugLevel),
DefaultRuntime: configInfo.Engine.OCIRuntime,
- DockerRootDir: storeInfo["GraphRoot"].(string),
- Driver: storeInfo["GraphDriverName"].(string),
- DriverStatus: getGraphStatus(storeInfo),
+ DockerRootDir: infoData.Store.GraphRoot,
+ Driver: infoData.Store.GraphDriverName,
+ DriverStatus: getGraphStatus(infoData.Store.GraphStatus),
ExperimentalBuild: true,
GenericResources: nil,
HTTPProxy: getEnv("http_proxy"),
HTTPSProxy: getEnv("https_proxy"),
ID: uuid.New().String(),
IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled,
- Images: storeInfo["ImageStore"].(map[string]interface{})["number"].(int),
+ Images: infoData.Store.ImageStore.Number,
IndexServerAddress: "",
InitBinary: "",
InitCommit: docker.Commit{},
Isolation: "",
KernelMemory: sysInfo.KernelMemory,
KernelMemoryTCP: false,
- KernelVersion: hostInfo["kernel"].(string),
+ KernelVersion: infoData.Host.Kernel,
Labels: nil,
LiveRestoreEnabled: false,
LoggingDriver: "",
- MemTotal: hostInfo["MemTotal"].(int64),
+ MemTotal: infoData.Host.MemTotal,
MemoryLimit: sysInfo.MemoryLimit,
NCPU: goRuntime.NumCPU(),
NEventsListener: 0,
NFd: getFdCount(),
NGoroutines: goRuntime.NumGoroutine(),
- Name: hostInfo["hostname"].(string),
+ Name: infoData.Host.Hostname,
NoProxy: getEnv("no_proxy"),
OSType: goRuntime.GOOS,
- OSVersion: hostInfo["Distribution"].(map[string]interface{})["version"].(string),
+ OSVersion: infoData.Host.Distribution.Version,
OomKillDisable: sysInfo.OomKillDisable,
- OperatingSystem: hostInfo["Distribution"].(map[string]interface{})["distribution"].(string),
+ OperatingSystem: infoData.Host.Distribution.Distribution,
PidsLimit: sysInfo.PidsLimit,
Plugins: docker.PluginsInfo{},
ProductLicense: "Apache-2.0",
@@ -118,21 +116,21 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
SystemTime: time.Now().Format(time.RFC3339Nano),
Warnings: []string{},
},
- BuildahVersion: hostInfo["BuildahVersion"].(string),
+ BuildahVersion: infoData.Host.BuildahVersion,
CPURealtimePeriod: sysInfo.CPURealtimePeriod,
CPURealtimeRuntime: sysInfo.CPURealtimeRuntime,
- CgroupVersion: hostInfo["CgroupVersion"].(string),
+ CgroupVersion: infoData.Host.CGroupsVersion,
Rootless: rootless.IsRootless(),
- SwapFree: hostInfo["SwapFree"].(int64),
- SwapTotal: hostInfo["SwapTotal"].(int64),
- Uptime: hostInfo["uptime"].(string),
+ SwapFree: infoData.Host.SwapFree,
+ SwapTotal: infoData.Host.SwapTotal,
+ Uptime: infoData.Host.Uptime,
}
utils.WriteResponse(w, http.StatusOK, info)
}
-func getGraphStatus(storeInfo map[string]interface{}) [][2]string {
+func getGraphStatus(storeInfo map[string]string) [][2]string {
var graphStatus [][2]string
- for k, v := range storeInfo["GraphStatus"].(map[string]string) {
+ for k, v := range storeInfo {
graphStatus = append(graphStatus, [2]string{k, v})
}
return graphStatus
diff --git a/pkg/api/handlers/compat/version.go b/pkg/api/handlers/compat/version.go
index c7f7917ac..35a95b562 100644
--- a/pkg/api/handlers/compat/version.go
+++ b/pkg/api/handlers/compat/version.go
@@ -30,8 +30,6 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to obtain system memory info"))
return
}
- hostInfo := infoData[0].Data
-
components := []docker.ComponentVersion{{
Name: "Podman Engine",
Version: versionInfo.Version,
@@ -42,7 +40,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
"Experimental": "true",
"GitCommit": versionInfo.GitCommit,
"GoVersion": versionInfo.GoVersion,
- "KernelVersion": hostInfo["kernel"].(string),
+ "KernelVersion": infoData.Host.Kernel,
"MinAPIVersion": handlers.MinimalApiVersion,
"Os": goRuntime.GOOS,
},
@@ -52,7 +50,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
Platform: struct {
Name string
}{
- Name: fmt.Sprintf("%s/%s/%s", goRuntime.GOOS, goRuntime.GOARCH, hostInfo["Distribution"].(map[string]interface{})["distribution"].(string)),
+ Name: fmt.Sprintf("%s/%s/%s-%s", goRuntime.GOOS, goRuntime.GOARCH, infoData.Host.Distribution.Distribution, infoData.Host.Distribution.Version),
},
APIVersion: components[0].Details["APIVersion"],
Arch: components[0].Details["Arch"],
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index fde72552b..5cbfb11eb 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -4,21 +4,16 @@ import (
"io/ioutil"
"net/http"
"os"
- "path/filepath"
- "sort"
"strconv"
- "time"
- "github.com/containers/libpod/pkg/api/handlers/compat"
-
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/api/handlers/compat"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/ps"
"github.com/gorilla/schema"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
)
func ContainerExists(w http.ResponseWriter, r *http.Request) {
@@ -38,8 +33,8 @@ func ContainerExists(w http.ResponseWriter, r *http.Request) {
func ListContainers(w http.ResponseWriter, r *http.Request) {
var (
- filterFuncs []libpod.ContainerFilter
- pss []ListContainer
+ //filterFuncs []libpod.ContainerFilter
+ //pss []entities.ListContainer
)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
@@ -61,66 +56,19 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
}
runtime := r.Context().Value("runtime").(*libpod.Runtime)
- opts := shared.PsOptions{
+ opts := entities.ContainerListOptions{
All: query.All,
Last: query.Last,
Size: query.Size,
Sort: "",
Namespace: query.Namespace,
- NoTrunc: true,
Pod: query.Pod,
Sync: query.Sync,
}
-
- all := query.All
- if len(query.Filters) > 0 {
- for k, v := range query.Filters {
- for _, val := range v {
- generatedFunc, err := shared.GenerateContainerFilterFuncs(k, val, runtime)
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filterFuncs = append(filterFuncs, generatedFunc)
- }
- }
- }
-
- // Docker thinks that if status is given as an input, then we should override
- // the all setting and always deal with all containers.
- if len(query.Filters["status"]) > 0 {
- all = true
- }
- if !all {
- runningOnly, err := shared.GenerateContainerFilterFuncs("status", define.ContainerStateRunning.String(), runtime)
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- filterFuncs = append(filterFuncs, runningOnly)
- }
-
- cons, err := runtime.GetContainers(filterFuncs...)
+ pss, err := ps.GetContainerLists(runtime, opts)
if err != nil {
utils.InternalServerError(w, err)
- }
- if query.Last > 0 {
- // Sort the containers we got
- sort.Sort(psSortCreateTime{cons})
- // we should perform the lopping before we start getting
- // the expensive information on containers
- if query.Last < len(cons) {
- cons = cons[len(cons)-query.Last:]
- }
- }
- for _, con := range cons {
- listCon, err := ListContainerBatch(runtime, con, opts)
- if err != nil {
- utils.InternalServerError(w, err)
- return
- }
- pss = append(pss, listCon)
-
+ return
}
utils.WriteResponse(w, http.StatusOK, pss)
}
@@ -212,125 +160,6 @@ func ShowMountedContainers(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, response)
}
-// BatchContainerOp is used in ps to reduce performance hits by "batching"
-// locks.
-func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts shared.PsOptions) (ListContainer, error) {
- var (
- conConfig *libpod.ContainerConfig
- conState define.ContainerStatus
- err error
- exitCode int32
- exited bool
- pid int
- size *shared.ContainerSize
- startedTime time.Time
- exitedTime time.Time
- cgroup, ipc, mnt, net, pidns, user, uts string
- )
-
- batchErr := ctr.Batch(func(c *libpod.Container) error {
- conConfig = c.Config()
- conState, err = c.State()
- if err != nil {
- return errors.Wrapf(err, "unable to obtain container state")
- }
-
- exitCode, exited, err = c.ExitCode()
- if err != nil {
- return errors.Wrapf(err, "unable to obtain container exit code")
- }
- startedTime, err = c.StartedTime()
- if err != nil {
- logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
- }
- exitedTime, err = c.FinishedTime()
- if err != nil {
- logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
- }
-
- if !opts.Size && !opts.Namespace {
- return nil
- }
-
- if opts.Namespace {
- pid, err = c.PID()
- if err != nil {
- return errors.Wrapf(err, "unable to obtain container pid")
- }
- ctrPID := strconv.Itoa(pid)
- cgroup, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
- ipc, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
- mnt, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
- net, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
- pidns, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
- user, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
- uts, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
- }
- if opts.Size {
- size = new(shared.ContainerSize)
-
- rootFsSize, err := c.RootFsSize()
- if err != nil {
- logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
- }
-
- rwSize, err := c.RWSize()
- if err != nil {
- logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
- }
-
- size.RootFsSize = rootFsSize
- size.RwSize = rwSize
- }
- return nil
- })
-
- if batchErr != nil {
- return ListContainer{}, batchErr
- }
-
- ps := ListContainer{
- Command: conConfig.Command,
- Created: conConfig.CreatedTime.Unix(),
- Exited: exited,
- ExitCode: exitCode,
- ExitedAt: exitedTime.Unix(),
- ID: conConfig.ID,
- Image: conConfig.RootfsImageName,
- IsInfra: conConfig.IsInfra,
- Labels: conConfig.Labels,
- Mounts: ctr.UserVolumes(),
- Names: []string{conConfig.Name},
- Pid: pid,
- Pod: conConfig.Pod,
- Ports: conConfig.PortMappings,
- Size: size,
- StartedAt: startedTime.Unix(),
- State: conState.String(),
- }
- if opts.Pod && len(conConfig.Pod) > 0 {
- pod, err := rt.GetPod(conConfig.Pod)
- if err != nil {
- return ListContainer{}, err
- }
- ps.PodName = pod.Name()
- }
-
- if opts.Namespace {
- ns := ListContainerNamespaces{
- Cgroup: cgroup,
- IPC: ipc,
- MNT: mnt,
- NET: net,
- PIDNS: pidns,
- User: user,
- UTS: uts,
- }
- ps.Namespaces = ns
- }
- return ps, nil
-}
-
func Checkpoint(w http.ResponseWriter, r *http.Request) {
var targetFile string
decoder := r.Context().Value("decoder").(*schema.Decoder)
diff --git a/pkg/api/handlers/libpod/info.go b/pkg/api/handlers/libpod/info.go
new file mode 100644
index 000000000..cbf03aa17
--- /dev/null
+++ b/pkg/api/handlers/libpod/info.go
@@ -0,0 +1,18 @@
+package libpod
+
+import (
+ "net/http"
+
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/api/handlers/utils"
+)
+
+func GetInfo(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ info, err := runtime.Info()
+ if err != nil {
+ utils.InternalServerError(w, err)
+ return
+ }
+ utils.WriteResponse(w, http.StatusOK, info)
+}
diff --git a/pkg/api/handlers/libpod/swagger.go b/pkg/api/handlers/libpod/swagger.go
index 1fad2dd1a..ed19462c6 100644
--- a/pkg/api/handlers/libpod/swagger.go
+++ b/pkg/api/handlers/libpod/swagger.go
@@ -5,6 +5,7 @@ import (
"os"
"github.com/containers/image/v5/manifest"
+ "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
@@ -17,7 +18,7 @@ const DefaultPodmanSwaggerSpec = "/usr/share/containers/podman/swagger.yaml"
// swagger:response ListContainers
type swagInspectPodResponse struct {
// in:body
- Body []ListContainer
+ Body []entities.ListContainer
}
// Inspect Manifest
@@ -76,6 +77,13 @@ type swagRmPodResponse struct {
Body entities.PodRmReport
}
+// Info
+// swagger:response InfoResponse
+type swagInfoResponse struct {
+ // in:body
+ Body define.Info
+}
+
func ServeSwagger(w http.ResponseWriter, r *http.Request) {
path := DefaultPodmanSwaggerSpec
if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {
diff --git a/pkg/api/handlers/libpod/types.go b/pkg/api/handlers/libpod/types.go
deleted file mode 100644
index 0949b2a72..000000000
--- a/pkg/api/handlers/libpod/types.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package libpod
-
-import (
- "github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/libpod"
- "github.com/cri-o/ocicni/pkg/ocicni"
-)
-
-// Listcontainer describes a container suitable for listing
-type ListContainer struct {
- // Container command
- Command []string
- // Container creation time
- Created int64
- // If container has exited/stopped
- Exited bool
- // Time container exited
- ExitedAt int64
- // If container has exited, the return code from the command
- ExitCode int32
- // The unique identifier for the container
- ID string `json:"Id"`
- // Container image
- Image string
- // If this container is a Pod infra container
- IsInfra bool
- // Labels for container
- Labels map[string]string
- // User volume mounts
- Mounts []string
- // The names assigned to the container
- Names []string
- // Namespaces the container belongs to. Requires the
- // namespace boolean to be true
- Namespaces ListContainerNamespaces
- // The process id of the container
- Pid int
- // If the container is part of Pod, the Pod ID. Requires the pod
- // boolean to be set
- Pod string
- // If the container is part of Pod, the Pod name. Requires the pod
- // boolean to be set
- PodName string
- // Port mappings
- Ports []ocicni.PortMapping
- // Size of the container rootfs. Requires the size boolean to be true
- Size *shared.ContainerSize
- // Time when container started
- StartedAt int64
- // State of container
- State string
-}
-
-// ListContainer Namespaces contains the identifiers of the container's Linux namespaces
-type ListContainerNamespaces struct {
- // Mount namespace
- MNT string `json:"Mnt,omitempty"`
- // Cgroup namespace
- Cgroup string `json:"Cgroup,omitempty"`
- // IPC namespace
- IPC string `json:"Ipc,omitempty"`
- // Network namespace
- NET string `json:"Net,omitempty"`
- // PID namespace
- PIDNS string `json:"Pidns,omitempty"`
- // UTS namespace
- UTS string `json:"Uts,omitempty"`
- // User namespace
- User string `json:"User,omitempty"`
-}
-
-// sortContainers helps us set-up ability to sort by createTime
-type sortContainers []*libpod.Container
-
-func (a sortContainers) Len() int { return len(a) }
-func (a sortContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-
-type psSortCreateTime struct{ sortContainers }
-
-func (a psSortCreateTime) Less(i, j int) bool {
- return a.sortContainers[i].CreatedTime().Before(a.sortContainers[j].CreatedTime())
-}
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 496512f2e..f1c932ebc 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -353,7 +353,7 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI
}
-func LibpodToContainer(l *libpod.Container, infoData []define.InfoData, sz bool) (*Container, error) {
+func LibpodToContainer(l *libpod.Container, sz bool) (*Container, error) {
imageId, imageName := l.Image()
var (
diff --git a/pkg/api/server/register_info.go b/pkg/api/server/register_info.go
index b4ab8871c..75aaa957b 100644
--- a/pkg/api/server/register_info.go
+++ b/pkg/api/server/register_info.go
@@ -4,14 +4,15 @@ import (
"net/http"
"github.com/containers/libpod/pkg/api/handlers/compat"
+ "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/gorilla/mux"
)
func (s *APIServer) registerInfoHandlers(r *mux.Router) error {
- // swagger:operation GET /info libpod libpodGetInfo
+ // swagger:operation GET /info compat getInfo
// ---
// tags:
- // - system
+ // - system (compat)
// summary: Get info
// description: Returns information on the system and libpod configuration
// produces:
@@ -24,5 +25,19 @@ func (s *APIServer) registerInfoHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/info"), s.APIHandler(compat.GetInfo)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths
r.Handle("/info", s.APIHandler(compat.GetInfo)).Methods(http.MethodGet)
+ // swagger:operation GET /libpod/info libpod libpodGetInfo
+ // ---
+ // tags:
+ // - system
+ // summary: Get info
+ // description: Returns information on the system and libpod configuration
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/InfoResponse"
+ // 500:
+ // $ref: "#/responses/InternalError"
+ r.Handle(VersionedPath("/libpod/info"), s.APIHandler(libpod.GetInfo)).Methods(http.MethodGet)
return nil
}
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index 49a2dfd58..a188d73a0 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -10,8 +10,8 @@ import (
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/api/handlers"
- lpapiv2 "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/domain/entities"
)
// List obtains a list of containers in local storage. All parameters to this method are optional.
@@ -19,12 +19,12 @@ import (
// the most recent number of containers. The pod and size booleans indicate that pod information and rootfs
// size information should also be included. Finally, the sync bool synchronizes the OCI runtime and
// container state.
-func List(ctx context.Context, filters map[string][]string, all *bool, last *int, pod, size, sync *bool) ([]lpapiv2.ListContainer, error) { // nolint:typecheck
+func List(ctx context.Context, filters map[string][]string, all *bool, last *int, pod, size, sync *bool) ([]entities.ListContainer, error) { // nolint:typecheck
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
- var containers []lpapiv2.ListContainer
+ var containers []entities.ListContainer
params := url.Values{}
if all != nil {
params.Set("all", strconv.FormatBool(*all))
diff --git a/pkg/bindings/info.go b/pkg/bindings/info.go
deleted file mode 100644
index 5f318d652..000000000
--- a/pkg/bindings/info.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package bindings
-
-func (c Connection) Info() {}
diff --git a/pkg/bindings/system/info.go b/pkg/bindings/system/info.go
new file mode 100644
index 000000000..13e12645d
--- /dev/null
+++ b/pkg/bindings/system/info.go
@@ -0,0 +1,23 @@
+package system
+
+import (
+ "context"
+ "net/http"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/bindings"
+)
+
+// Info returns information about the libpod environment and its stores
+func Info(ctx context.Context) (*define.Info, error) {
+ info := define.Info{}
+ conn, err := bindings.GetClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ response, err := conn.DoRequest(nil, http.MethodGet, "/info", nil)
+ if err != nil {
+ return nil, err
+ }
+ return &info, response.Process(&info)
+}
diff --git a/pkg/bindings/test/info_test.go b/pkg/bindings/test/info_test.go
new file mode 100644
index 000000000..d0e651134
--- /dev/null
+++ b/pkg/bindings/test/info_test.go
@@ -0,0 +1,73 @@
+package test_bindings
+
+import (
+ "runtime"
+ "time"
+
+ "github.com/containers/libpod/pkg/bindings/containers"
+ "github.com/containers/libpod/pkg/bindings/images"
+ "github.com/containers/libpod/pkg/bindings/system"
+ "github.com/containers/libpod/pkg/specgen"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("Podman info", func() {
+ var (
+ bt *bindingTest
+ s *gexec.Session
+ t bool = true
+ )
+
+ BeforeEach(func() {
+ bt = newBindingTest()
+ bt.RestoreImagesFromCache()
+ s = bt.startAPIService()
+ time.Sleep(1 * time.Second)
+ err := bt.NewConnection()
+ Expect(err).To(BeNil())
+ })
+
+ AfterEach(func() {
+ s.Kill()
+ bt.cleanup()
+ })
+
+ It("podman info", func() {
+ info, err := system.Info(bt.conn)
+ Expect(err).To(BeNil())
+ Expect(info.Host.Arch).To(Equal(runtime.GOARCH))
+ Expect(info.Host.OS).To(Equal(runtime.GOOS))
+ i, err := images.List(bt.conn, &t, nil)
+ Expect(err).To(BeNil())
+ Expect(info.Store.ImageStore.Number).To(Equal(len(i)))
+ })
+
+ It("podman info container counts", func() {
+ s := specgen.NewSpecGenerator(alpine.name)
+ _, err := containers.CreateWithSpec(bt.conn, s)
+ Expect(err).To(BeNil())
+
+ idPause, err := bt.RunTopContainer(nil, nil, nil)
+ Expect(err).To(BeNil())
+ err = containers.Pause(bt.conn, idPause)
+ Expect(err).To(BeNil())
+
+ idStop, err := bt.RunTopContainer(nil, nil, nil)
+ Expect(err).To(BeNil())
+ err = containers.Stop(bt.conn, idStop, nil)
+ Expect(err).To(BeNil())
+
+ _, err = bt.RunTopContainer(nil, nil, nil)
+ Expect(err).To(BeNil())
+
+ info, err := system.Info(bt.conn)
+ Expect(err).To(BeNil())
+
+ Expect(info.Store.ContainerStore.Number).To(BeNumerically("==", 4))
+ Expect(info.Store.ContainerStore.Paused).To(Equal(1))
+ Expect(info.Store.ContainerStore.Stopped).To(Equal(2))
+ Expect(info.Store.ContainerStore.Running).To(Equal(1))
+ })
+})
diff --git a/pkg/cgroups/pids.go b/pkg/cgroups/pids.go
index 65b9b5b34..92f82553b 100644
--- a/pkg/cgroups/pids.go
+++ b/pkg/cgroups/pids.go
@@ -44,8 +44,12 @@ func (c *pidHandler) Destroy(ctr *CgroupControl) error {
// Stat fills a metrics structure with usage stats for the controller
func (c *pidHandler) Stat(ctr *CgroupControl, m *Metrics) error {
- var PIDRoot string
+ if ctr.path != "" {
+ // nothing we can do to retrieve the pids.current path
+ return nil
+ }
+ var PIDRoot string
if ctr.cgroup2 {
PIDRoot = filepath.Join(cgroupRoot, ctr.path)
} else {
diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go
new file mode 100644
index 000000000..ceafecebc
--- /dev/null
+++ b/pkg/domain/entities/container_ps.go
@@ -0,0 +1,174 @@
+package entities
+
+import (
+ "sort"
+ "strings"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/pkg/errors"
+)
+
+// Listcontainer describes a container suitable for listing
+type ListContainer struct {
+ // Container command
+ Command []string
+ // Container creation time
+ Created int64
+ // If container has exited/stopped
+ Exited bool
+ // Time container exited
+ ExitedAt int64
+ // If container has exited, the return code from the command
+ ExitCode int32
+ // The unique identifier for the container
+ ID string `json:"Id"`
+ // Container image
+ Image string
+ // If this container is a Pod infra container
+ IsInfra bool
+ // Labels for container
+ Labels map[string]string
+ // User volume mounts
+ Mounts []string
+ // The names assigned to the container
+ Names []string
+ // Namespaces the container belongs to. Requires the
+ // namespace boolean to be true
+ Namespaces ListContainerNamespaces
+ // The process id of the container
+ Pid int
+ // If the container is part of Pod, the Pod ID. Requires the pod
+ // boolean to be set
+ Pod string
+ // If the container is part of Pod, the Pod name. Requires the pod
+ // boolean to be set
+ PodName string
+ // Port mappings
+ Ports []ocicni.PortMapping
+ // Size of the container rootfs. Requires the size boolean to be true
+ Size *shared.ContainerSize
+ // Time when container started
+ StartedAt int64
+ // State of container
+ State string
+}
+
+// ListContainer Namespaces contains the identifiers of the container's Linux namespaces
+type ListContainerNamespaces struct {
+ // Mount namespace
+ MNT string `json:"Mnt,omitempty"`
+ // Cgroup namespace
+ Cgroup string `json:"Cgroup,omitempty"`
+ // IPC namespace
+ IPC string `json:"Ipc,omitempty"`
+ // Network namespace
+ NET string `json:"Net,omitempty"`
+ // PID namespace
+ PIDNS string `json:"Pidns,omitempty"`
+ // UTS namespace
+ UTS string `json:"Uts,omitempty"`
+ // User namespace
+ User string `json:"User,omitempty"`
+}
+
+// SortContainers helps us set-up ability to sort by createTime
+type SortContainers []*libpod.Container
+
+func (a SortContainers) Len() int { return len(a) }
+func (a SortContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type SortCreateTime struct{ SortContainers }
+
+func (a SortCreateTime) Less(i, j int) bool {
+ return a.SortContainers[i].CreatedTime().Before(a.SortContainers[j].CreatedTime())
+}
+
+type SortListContainers []ListContainer
+
+func (a SortListContainers) Len() int { return len(a) }
+func (a SortListContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type psSortedCommand struct{ SortListContainers }
+
+func (a psSortedCommand) Less(i, j int) bool {
+ return strings.Join(a.SortListContainers[i].Command, " ") < strings.Join(a.SortListContainers[j].Command, " ")
+}
+
+type psSortedId struct{ SortListContainers }
+
+func (a psSortedId) Less(i, j int) bool {
+ return a.SortListContainers[i].ID < a.SortListContainers[j].ID
+}
+
+type psSortedImage struct{ SortListContainers }
+
+func (a psSortedImage) Less(i, j int) bool {
+ return a.SortListContainers[i].Image < a.SortListContainers[j].Image
+}
+
+type psSortedNames struct{ SortListContainers }
+
+func (a psSortedNames) Less(i, j int) bool {
+ return a.SortListContainers[i].Names[0] < a.SortListContainers[j].Names[0]
+}
+
+type psSortedPod struct{ SortListContainers }
+
+func (a psSortedPod) Less(i, j int) bool {
+ return a.SortListContainers[i].Pod < a.SortListContainers[j].Pod
+}
+
+type psSortedRunningFor struct{ SortListContainers }
+
+func (a psSortedRunningFor) Less(i, j int) bool {
+ return a.SortListContainers[i].StartedAt < a.SortListContainers[j].StartedAt
+}
+
+type psSortedStatus struct{ SortListContainers }
+
+func (a psSortedStatus) Less(i, j int) bool {
+ return a.SortListContainers[i].State < a.SortListContainers[j].State
+}
+
+type psSortedSize struct{ SortListContainers }
+
+func (a psSortedSize) Less(i, j int) bool {
+ if a.SortListContainers[i].Size == nil || a.SortListContainers[j].Size == nil {
+ return false
+ }
+ return a.SortListContainers[i].Size.RootFsSize < a.SortListContainers[j].Size.RootFsSize
+}
+
+type PsSortedCreateTime struct{ SortListContainers }
+
+func (a PsSortedCreateTime) Less(i, j int) bool {
+ return a.SortListContainers[i].Created < a.SortListContainers[j].Created
+}
+
+func SortPsOutput(sortBy string, psOutput SortListContainers) (SortListContainers, error) {
+ switch sortBy {
+ case "id":
+ sort.Sort(psSortedId{psOutput})
+ case "image":
+ sort.Sort(psSortedImage{psOutput})
+ case "command":
+ sort.Sort(psSortedCommand{psOutput})
+ case "runningfor":
+ sort.Sort(psSortedRunningFor{psOutput})
+ case "status":
+ sort.Sort(psSortedStatus{psOutput})
+ case "size":
+ sort.Sort(psSortedSize{psOutput})
+ case "names":
+ sort.Sort(psSortedNames{psOutput})
+ case "created":
+ sort.Sort(PsSortedCreateTime{psOutput})
+ case "pod":
+ sort.Sort(psSortedPod{psOutput})
+ default:
+ return nil, errors.Errorf("invalid option for --sort, options are: command, created, id, image, names, runningfor, size, or status")
+ }
+ return psOutput, nil
+}
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index cf907eb1b..5d302058b 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -6,6 +6,7 @@ import (
"time"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/specgen"
)
type WaitOptions struct {
@@ -186,3 +187,61 @@ type ExecOptions struct {
User string
WorkDir string
}
+
+// ContainerStartOptions describes the val from the
+// CLI needed to start a container
+type ContainerStartOptions struct {
+ Attach bool
+ DetachKeys string
+ Interactive bool
+ Latest bool
+ SigProxy bool
+ Stdout *os.File
+ Stderr *os.File
+ Stdin *os.File
+}
+
+// ContainerStartReport describes the response from starting
+// containers from the cli
+type ContainerStartReport struct {
+ Id string
+ Err error
+ ExitCode int
+}
+
+// ContainerListOptions describes the CLI options
+// for listing containers
+type ContainerListOptions struct {
+ All bool
+ Filters map[string][]string
+ Format string
+ Last int
+ Latest bool
+ Namespace bool
+ Pod bool
+ Quiet bool
+ Size bool
+ Sort string
+ Sync bool
+ Watch uint
+}
+
+// ContainerRunOptions describes the options needed
+// to run a container from the CLI
+type ContainerRunOptions struct {
+ Detach bool
+ DetachKeys string
+ ErrorStream *os.File
+ InputStream *os.File
+ OutputStream *os.File
+ Rm bool
+ SigProxy bool
+ Spec *specgen.SpecGenerator
+}
+
+// ContainerRunReport describes the results of running
+//a container
+type ContainerRunReport struct {
+ ExitCode int
+ Id string
+}
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index 9bf3d51de..0fc390ea2 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -19,8 +19,11 @@ type ContainerEngine interface {
ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error
ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error)
ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
+ ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error)
ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error)
ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error)
+ ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error)
+ ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error)
ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error)
ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error)
ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error)
@@ -45,4 +48,6 @@ type ContainerEngine interface {
VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error)
VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error)
VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error)
+
+ Info(ctx context.Context) (*define.Info, error)
}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index b93e7665a..828ee56f0 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -12,10 +12,12 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/checkpoint"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/domain/infra/abi/terminal"
+ "github.com/containers/libpod/pkg/ps"
"github.com/containers/libpod/pkg/signal"
"github.com/containers/libpod/pkg/specgen"
"github.com/containers/libpod/pkg/specgen/generate"
@@ -509,3 +511,188 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, o
ec, err = terminal.ExecAttachCtr(ctx, ctr, options.Tty, options.Privileged, options.Envs, options.Cmd, options.User, options.WorkDir, &options.Streams, options.PreserveFDs, options.DetachKeys)
return define.TranslateExecErrorToExitCode(ec, err), err
}
+
+func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
+ var reports []*entities.ContainerStartReport
+ var exitCode = define.ExecErrorCodeGeneric
+ ctrs, err := getContainersByContext(false, options.Latest, namesOrIds, ic.Libpod)
+ if err != nil {
+ return nil, err
+ }
+ // There can only be one container if attach was used
+ for _, ctr := range ctrs {
+ ctrState, err := ctr.State()
+ if err != nil {
+ return nil, err
+ }
+ ctrRunning := ctrState == define.ContainerStateRunning
+
+ if options.Attach {
+ err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning, ctr.PodID() != "")
+ if errors.Cause(err) == define.ErrDetach {
+ // User manually detached
+ // Exit cleanly immediately
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: nil,
+ ExitCode: 0,
+ })
+ return reports, nil
+ }
+
+ if errors.Cause(err) == define.ErrWillDeadlock {
+ logrus.Debugf("Deadlock error: %v", err)
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: err,
+ ExitCode: define.ExitCode(err),
+ })
+ return reports, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID())
+ }
+
+ if ctrRunning {
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: nil,
+ ExitCode: 0,
+ })
+ return reports, err
+ }
+
+ if err != nil {
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: err,
+ ExitCode: exitCode,
+ })
+ return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID())
+ }
+
+ if ecode, err := ctr.Wait(); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ // Check events
+ event, err := ic.Libpod.GetLastContainerEvent(ctr.ID(), events.Exited)
+ if err != nil {
+ logrus.Errorf("Cannot get exit code: %v", err)
+ exitCode = define.ExecErrorCodeNotFound
+ } else {
+ exitCode = event.ContainerExitCode
+ }
+ }
+ } else {
+ exitCode = int(ecode)
+ }
+ reports = append(reports, &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ Err: err,
+ ExitCode: exitCode,
+ })
+ return reports, nil
+ } // end attach
+
+ // Start the container if it's not running already.
+ if !ctrRunning {
+ // Handle non-attach start
+ // If the container is in a pod, also set to recursively start dependencies
+ report := &entities.ContainerStartReport{
+ Id: ctr.ID(),
+ ExitCode: 125,
+ }
+ if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil {
+ //if lastError != nil {
+ // fmt.Fprintln(os.Stderr, lastError)
+ //}
+ report.Err = err
+ if errors.Cause(err) == define.ErrWillDeadlock {
+ report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks")
+ reports = append(reports, report)
+ continue
+ }
+ report.Err = errors.Wrapf(err, "unable to start container %q", ctr.ID())
+ reports = append(reports, report)
+ continue
+ }
+ report.ExitCode = 0
+ reports = append(reports, report)
+ }
+ }
+ return reports, nil
+}
+
+func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) {
+ return ps.GetContainerLists(ic.Libpod, options)
+}
+
+func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) {
+ var (
+ joinPod bool
+ )
+ if err := generate.CompleteSpec(ctx, ic.Libpod, opts.Spec); err != nil {
+ return nil, err
+ }
+ ctr, err := generate.MakeContainer(ic.Libpod, opts.Spec)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(ctr.PodID()) > 0 {
+ joinPod = true
+ }
+ report := entities.ContainerRunReport{Id: ctr.ID()}
+
+ if logrus.GetLevel() == logrus.DebugLevel {
+ cgroupPath, err := ctr.CGroupPath()
+ if err == nil {
+ logrus.Debugf("container %q has CgroupParent %q", ctr.ID(), cgroupPath)
+ }
+ }
+ if opts.Detach {
+ // if the container was created as part of a pod, also start its dependencies, if any.
+ if err := ctr.Start(ctx, joinPod); err != nil {
+ // This means the command did not exist
+ report.ExitCode = define.ExitCode(err)
+ return &report, err
+ }
+
+ return &report, nil
+ }
+
+ // if the container was created as part of a pod, also start its dependencies, if any.
+ if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true, joinPod); err != nil {
+ // We've manually detached from the container
+ // Do not perform cleanup, or wait for container exit code
+ // Just exit immediately
+ if errors.Cause(err) == define.ErrDetach {
+ report.ExitCode = 0
+ return &report, nil
+ }
+ if opts.Rm {
+ if deleteError := ic.Libpod.RemoveContainer(ctx, ctr, true, false); deleteError != nil {
+ logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID())
+ }
+ }
+ if errors.Cause(err) == define.ErrWillDeadlock {
+ logrus.Debugf("Deadlock error on %q: %v", ctr.ID(), err)
+ report.ExitCode = define.ExitCode(err)
+ return &report, errors.Errorf("attempting to start container %s would cause a deadlock; please run 'podman system renumber' to resolve", ctr.ID())
+ }
+ report.ExitCode = define.ExitCode(err)
+ return &report, err
+ }
+
+ if ecode, err := ctr.Wait(); err != nil {
+ if errors.Cause(err) == define.ErrNoSuchCtr {
+ // Check events
+ event, err := ic.Libpod.GetLastContainerEvent(ctr.ID(), events.Exited)
+ if err != nil {
+ logrus.Errorf("Cannot get exit code: %v", err)
+ report.ExitCode = define.ExecErrorCodeNotFound
+ } else {
+ report.ExitCode = event.ContainerExitCode
+ }
+ }
+ } else {
+ report.ExitCode = int(ecode)
+ }
+ return &report, nil
+}
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index 073cd8d5c..c3e5d59bc 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -5,9 +5,10 @@ package abi
import (
"context"
+ lpfilters "github.com/containers/libpod/libpod/filters"
+
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
- "github.com/containers/libpod/libpod/podfilters"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/signal"
"github.com/containers/libpod/pkg/specgen"
@@ -281,7 +282,7 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti
)
for k, v := range options.Filters {
for _, filter := range v {
- f, err := podfilters.GeneratePodFilterFunc(k, filter)
+ f, err := lpfilters.GeneratePodFilterFunc(k, filter)
if err != nil {
return nil, err
}
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
new file mode 100644
index 000000000..8aaa69847
--- /dev/null
+++ b/pkg/domain/infra/abi/system.go
@@ -0,0 +1,13 @@
+// +build ABISupport
+
+package abi
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod/define"
+)
+
+func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
+ return ic.Libpod.Info()
+}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 1e71cba2c..e96200c5b 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -7,7 +7,6 @@ import (
"github.com/containers/image/v5/docker/reference"
"github.com/containers/libpod/libpod/define"
- "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/specgen"
@@ -233,7 +232,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [
var (
reports []*entities.CheckpointReport
err error
- ctrs []libpod.ListContainer
+ ctrs []entities.ListContainer
)
if options.All {
@@ -268,7 +267,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
var (
reports []*entities.RestoreReport
err error
- ctrs []libpod.ListContainer
+ ctrs []entities.ListContainer
)
if options.All {
allCtrs, err := getContainersByContext(ic.ClientCxt, true, []string{})
@@ -313,3 +312,15 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string,
func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) {
return 125, errors.New("not implemented")
}
+
+func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) {
+ return nil, errors.New("not implemented")
+}
+
+func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) {
+ return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Pod, &options.Size, &options.Sync)
+}
+
+func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) {
+ return nil, errors.New("not implemented")
+}
diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go
index f9183c955..682d60d6a 100644
--- a/pkg/domain/infra/tunnel/helpers.go
+++ b/pkg/domain/infra/tunnel/helpers.go
@@ -5,7 +5,6 @@ import (
"strings"
"github.com/containers/libpod/libpod/define"
- "github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/containers/libpod/pkg/bindings"
"github.com/containers/libpod/pkg/bindings/containers"
"github.com/containers/libpod/pkg/bindings/pods"
@@ -14,9 +13,9 @@ import (
"github.com/pkg/errors"
)
-func getContainersByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]libpod.ListContainer, error) {
+func getContainersByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]entities.ListContainer, error) {
var (
- cons []libpod.ListContainer
+ cons []entities.ListContainer
)
if all && len(namesOrIds) > 0 {
return nil, errors.New("cannot lookup containers and all")
diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go
index 5bafef1fe..5d6346234 100644
--- a/pkg/domain/infra/tunnel/system.go
+++ b/pkg/domain/infra/tunnel/system.go
@@ -1 +1,12 @@
package tunnel
+
+import (
+ "context"
+
+ "github.com/containers/libpod/libpod/define"
+ "github.com/containers/libpod/pkg/bindings/system"
+)
+
+func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
+ return system.Info(ic.ClientCxt)
+}
diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go
index 14453e7f4..2cb3c3f20 100644
--- a/pkg/namespaces/namespaces.go
+++ b/pkg/namespaces/namespaces.go
@@ -1,7 +1,11 @@
package namespaces
import (
+ "fmt"
+ "strconv"
"strings"
+
+ "github.com/containers/storage"
)
const (
@@ -92,6 +96,54 @@ func (n UsernsMode) IsKeepID() bool {
return n == "keep-id"
}
+// IsAuto indicates whether container uses the "auto" userns mode.
+func (n UsernsMode) IsAuto() bool {
+ parts := strings.Split(string(n), ":")
+ return parts[0] == "auto"
+}
+
+// GetAutoOptions returns a AutoUserNsOptions with the settings to setup automatically
+// a user namespace.
+func (n UsernsMode) GetAutoOptions() (*storage.AutoUserNsOptions, error) {
+ parts := strings.SplitN(string(n), ":", 2)
+ if parts[0] != "auto" {
+ return nil, fmt.Errorf("wrong user namespace mode")
+ }
+ options := storage.AutoUserNsOptions{}
+ if len(parts) == 1 {
+ return &options, nil
+ }
+ for _, o := range strings.Split(parts[1], ",") {
+ v := strings.SplitN(o, "=", 2)
+ if len(v) != 2 {
+ return nil, fmt.Errorf("invalid option specified: %q", o)
+ }
+ switch v[0] {
+ case "size":
+ s, err := strconv.ParseUint(v[1], 10, 32)
+ if err != nil {
+ return nil, err
+ }
+ options.Size = uint32(s)
+ case "uidmapping":
+ mapping, err := storage.ParseIDMapping([]string{v[1]}, nil, "", "")
+ if err != nil {
+ return nil, err
+ }
+ options.AdditionalUIDMappings = append(options.AdditionalUIDMappings, mapping.UIDMap...)
+ case "gidmapping":
+ mapping, err := storage.ParseIDMapping(nil, []string{v[1]}, "", "")
+ if err != nil {
+ return nil, err
+ }
+ options.AdditionalGIDMappings = append(options.AdditionalGIDMappings, mapping.GIDMap...)
+ default:
+ return nil, fmt.Errorf("unknown option specified: %q", v[0])
+ }
+ }
+ return &options, nil
+}
+
// IsPrivate indicates whether the container uses the a private userns.
func (n UsernsMode) IsPrivate() bool {
return !(n.IsHost() || n.IsContainer())
@@ -101,7 +153,7 @@ func (n UsernsMode) IsPrivate() bool {
func (n UsernsMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
- case "", privateType, hostType, "keep-id", nsType:
+ case "", privateType, hostType, "keep-id", nsType, "auto":
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go
new file mode 100644
index 000000000..58fcc2c21
--- /dev/null
+++ b/pkg/ps/ps.go
@@ -0,0 +1,189 @@
+package ps
+
+import (
+ "path/filepath"
+ "sort"
+ "strconv"
+ "time"
+
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/define"
+ lpfilters "github.com/containers/libpod/libpod/filters"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOptions) ([]entities.ListContainer, error) {
+ var (
+ filterFuncs []libpod.ContainerFilter
+ pss []entities.ListContainer
+ )
+ all := options.All
+ if len(options.Filters) > 0 {
+ for k, v := range options.Filters {
+ for _, val := range v {
+ generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, val, runtime)
+ if err != nil {
+ return nil, err
+ }
+ filterFuncs = append(filterFuncs, generatedFunc)
+ }
+ }
+ }
+
+ // Docker thinks that if status is given as an input, then we should override
+ // the all setting and always deal with all containers.
+ if len(options.Filters["status"]) > 0 {
+ all = true
+ }
+ if !all {
+ runningOnly, err := lpfilters.GenerateContainerFilterFuncs("status", define.ContainerStateRunning.String(), runtime)
+ if err != nil {
+ return nil, err
+ }
+ filterFuncs = append(filterFuncs, runningOnly)
+ }
+
+ cons, err := runtime.GetContainers(filterFuncs...)
+ if err != nil {
+ return nil, err
+ }
+ if options.Last > 0 {
+ // Sort the containers we got
+ sort.Sort(entities.SortCreateTime{SortContainers: cons})
+ // we should perform the lopping before we start getting
+ // the expensive information on containers
+ if options.Last < len(cons) {
+ cons = cons[len(cons)-options.Last:]
+ }
+ }
+ for _, con := range cons {
+ listCon, err := ListContainerBatch(runtime, con, options)
+ if err != nil {
+ return nil, err
+ }
+ pss = append(pss, listCon)
+
+ }
+ return pss, nil
+}
+
+// BatchContainerOp is used in ps to reduce performance hits by "batching"
+// locks.
+func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities.ContainerListOptions) (entities.ListContainer, error) {
+ var (
+ conConfig *libpod.ContainerConfig
+ conState define.ContainerStatus
+ err error
+ exitCode int32
+ exited bool
+ pid int
+ size *shared.ContainerSize
+ startedTime time.Time
+ exitedTime time.Time
+ cgroup, ipc, mnt, net, pidns, user, uts string
+ )
+
+ batchErr := ctr.Batch(func(c *libpod.Container) error {
+ conConfig = c.Config()
+ conState, err = c.State()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container state")
+ }
+
+ exitCode, exited, err = c.ExitCode()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container exit code")
+ }
+ startedTime, err = c.StartedTime()
+ if err != nil {
+ logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
+ }
+ exitedTime, err = c.FinishedTime()
+ if err != nil {
+ logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
+ }
+
+ if !opts.Size && !opts.Namespace {
+ return nil
+ }
+
+ if opts.Namespace {
+ pid, err = c.PID()
+ if err != nil {
+ return errors.Wrapf(err, "unable to obtain container pid")
+ }
+ ctrPID := strconv.Itoa(pid)
+ cgroup, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
+ ipc, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
+ mnt, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
+ net, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
+ pidns, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
+ user, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
+ uts, _ = shared.GetNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
+ }
+ if opts.Size {
+ size = new(shared.ContainerSize)
+
+ rootFsSize, err := c.RootFsSize()
+ if err != nil {
+ logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
+ }
+
+ rwSize, err := c.RWSize()
+ if err != nil {
+ logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
+ }
+
+ size.RootFsSize = rootFsSize
+ size.RwSize = rwSize
+ }
+ return nil
+ })
+
+ if batchErr != nil {
+ return entities.ListContainer{}, batchErr
+ }
+
+ ps := entities.ListContainer{
+ Command: conConfig.Command,
+ Created: conConfig.CreatedTime.Unix(),
+ Exited: exited,
+ ExitCode: exitCode,
+ ExitedAt: exitedTime.Unix(),
+ ID: conConfig.ID,
+ Image: conConfig.RootfsImageName,
+ IsInfra: conConfig.IsInfra,
+ Labels: conConfig.Labels,
+ Mounts: ctr.UserVolumes(),
+ Names: []string{conConfig.Name},
+ Pid: pid,
+ Pod: conConfig.Pod,
+ Ports: conConfig.PortMappings,
+ Size: size,
+ StartedAt: startedTime.Unix(),
+ State: conState.String(),
+ }
+ if opts.Pod && len(conConfig.Pod) > 0 {
+ pod, err := rt.GetPod(conConfig.Pod)
+ if err != nil {
+ return entities.ListContainer{}, err
+ }
+ ps.PodName = pod.Name()
+ }
+
+ if opts.Namespace {
+ ps.Namespaces = entities.ListContainerNamespaces{
+ Cgroup: cgroup,
+ IPC: ipc,
+ MNT: mnt,
+ NET: net,
+ PIDNS: pidns,
+ User: user,
+ UTS: uts,
+ }
+ }
+ return ps, nil
+}
diff --git a/pkg/spec/namespaces.go b/pkg/spec/namespaces.go
index 838d95c54..aebc90f68 100644
--- a/pkg/spec/namespaces.go
+++ b/pkg/spec/namespaces.go
@@ -277,7 +277,7 @@ func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error {
}
func (c *UserConfig) getPostConfigureNetNS() bool {
- hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
+ hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
return postConfigureNetNS
}
@@ -285,7 +285,7 @@ func (c *UserConfig) getPostConfigureNetNS() bool {
// InNS returns true if the UserConfig indicates to be in a dedicated user
// namespace.
func (c *UserConfig) InNS(isRootless bool) bool {
- hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
+ hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
return isRootless || (hasUserns && !c.UsernsMode.IsHost())
}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 0c055745d..372c7c53b 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -327,6 +327,18 @@ func ParseIDMapping(mode namespaces.UsernsMode, uidMapSlice, gidMapSlice []strin
HostGIDMapping: true,
}
+ if mode.IsAuto() {
+ var err error
+ options.HostUIDMapping = false
+ options.HostGIDMapping = false
+ options.AutoUserNs = true
+ opts, err := mode.GetAutoOptions()
+ if err != nil {
+ return nil, err
+ }
+ options.AutoUserNsOpts = *opts
+ return &options, nil
+ }
if mode.IsKeepID() {
if len(uidMapSlice) > 0 || len(gidMapSlice) > 0 {
return nil, errors.New("cannot specify custom mappings with --userns=keep-id")
diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go
index 04fb9f648..7bee643c2 100644
--- a/pkg/varlinkapi/system.go
+++ b/pkg/varlinkapi/system.go
@@ -44,28 +44,26 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error {
return call.ReplyErrorOccurred(err.Error())
}
- host := info[0].Data
distribution := iopodman.InfoDistribution{
- Distribution: host["Distribution"].(map[string]interface{})["distribution"].(string),
- Version: host["Distribution"].(map[string]interface{})["version"].(string),
+ Distribution: info.Host.Distribution.Distribution,
+ Version: info.Host.Distribution.Version,
}
infoHost := iopodman.InfoHost{
- Buildah_version: host["BuildahVersion"].(string),
+ Buildah_version: info.Host.BuildahVersion,
Distribution: distribution,
- Mem_free: host["MemFree"].(int64),
- Mem_total: host["MemTotal"].(int64),
- Swap_free: host["SwapFree"].(int64),
- Swap_total: host["SwapTotal"].(int64),
- Arch: host["arch"].(string),
- Cpus: int64(host["cpus"].(int)),
- Hostname: host["hostname"].(string),
- Kernel: host["kernel"].(string),
- Os: host["os"].(string),
- Uptime: host["uptime"].(string),
- Eventlogger: host["eventlogger"].(string),
+ Mem_free: info.Host.MemFree,
+ Mem_total: info.Host.MemTotal,
+ Swap_free: info.Host.SwapFree,
+ Swap_total: info.Host.SwapTotal,
+ Arch: info.Host.Arch,
+ Cpus: int64(info.Host.CPUs),
+ Hostname: info.Host.Hostname,
+ Kernel: info.Host.Kernel,
+ Os: info.Host.OS,
+ Uptime: info.Host.Uptime,
+ Eventlogger: info.Host.EventLogger,
}
podmanInfo.Host = infoHost
- store := info[1].Data
pmaninfo := iopodman.InfoPodmanBinary{
Compiler: goruntime.Compiler,
Go_version: goruntime.Version(),
@@ -74,36 +72,33 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error {
}
graphStatus := iopodman.InfoGraphStatus{
- Backing_filesystem: store["GraphStatus"].(map[string]string)["Backing Filesystem"],
- Native_overlay_diff: store["GraphStatus"].(map[string]string)["Native Overlay Diff"],
- Supports_d_type: store["GraphStatus"].(map[string]string)["Supports d_type"],
+ Backing_filesystem: info.Store.GraphStatus["Backing Filesystem"],
+ Native_overlay_diff: info.Store.GraphStatus["Native Overlay Diff"],
+ Supports_d_type: info.Store.GraphStatus["Supports d_type"],
}
infoStore := iopodman.InfoStore{
- Graph_driver_name: store["GraphDriverName"].(string),
- Containers: int64(store["ContainerStore"].(map[string]interface{})["number"].(int)),
- Images: int64(store["ImageStore"].(map[string]interface{})["number"].(int)),
- Run_root: store["RunRoot"].(string),
- Graph_root: store["GraphRoot"].(string),
- Graph_driver_options: fmt.Sprintf("%v", store["GraphOptions"]),
+ Graph_driver_name: info.Store.GraphDriverName,
+ Containers: int64(info.Store.ContainerStore.Number),
+ Images: int64(info.Store.ImageStore.Number),
+ Run_root: info.Store.RunRoot,
+ Graph_root: info.Store.GraphRoot,
+ Graph_driver_options: fmt.Sprintf("%v", info.Store.GraphOptions),
Graph_status: graphStatus,
}
// Registry information if any is stored as the second list item
- if len(info) > 2 {
- for key, val := range info[2].Data {
- if key == "search" {
- podmanInfo.Registries.Search = val.([]string)
- continue
- }
- regData := val.(sysregistriesv2.Registry)
- if regData.Insecure {
- podmanInfo.Registries.Insecure = append(podmanInfo.Registries.Insecure, key)
- }
- if regData.Blocked {
- podmanInfo.Registries.Blocked = append(podmanInfo.Registries.Blocked, key)
- }
+ for key, val := range info.Registries {
+ if key == "search" {
+ podmanInfo.Registries.Search = val.([]string)
+ continue
+ }
+ regData := val.(sysregistriesv2.Registry)
+ if regData.Insecure {
+ podmanInfo.Registries.Insecure = append(podmanInfo.Registries.Insecure, key)
+ }
+ if regData.Blocked {
+ podmanInfo.Registries.Blocked = append(podmanInfo.Registries.Blocked, key)
}
-
}
podmanInfo.Store = infoStore
podmanInfo.Podman = pmaninfo
diff --git a/test/e2e/info_test.go b/test/e2e/info_test.go
index d16661d5b..446dbc16e 100644
--- a/test/e2e/info_test.go
+++ b/test/e2e/info_test.go
@@ -43,10 +43,16 @@ var _ = Describe("Podman Info", func() {
Expect(session.ExitCode()).To(Equal(0))
})
- It("podman info --format GO template", func() {
+ It("podman info --format JSON GO template", func() {
session := podmanTest.Podman([]string{"info", "--format", "{{ json .}}"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.IsJSONOutputValid()).To(BeTrue())
})
+
+ It("podman info --format GO template", func() {
+ session := podmanTest.Podman([]string{"info", "--format", "{{ .Store.GraphRoot }}"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
})
diff --git a/test/e2e/run_userns_test.go b/test/e2e/run_userns_test.go
index e873f5abe..25f12ec2e 100644
--- a/test/e2e/run_userns_test.go
+++ b/test/e2e/run_userns_test.go
@@ -4,7 +4,10 @@ package integration
import (
"fmt"
+ "io/ioutil"
"os"
+ "os/user"
+ "strings"
. "github.com/containers/libpod/test/utils"
. "github.com/onsi/ginkgo"
@@ -86,6 +89,134 @@ var _ = Describe("Podman UserNS support", func() {
Expect(ok).To(BeTrue())
})
+ It("podman --userns=auto", func() {
+ u, err := user.Current()
+ Expect(err).To(BeNil())
+ name := u.Name
+ if name == "root" {
+ name = "containers"
+ }
+
+ content, err := ioutil.ReadFile("/etc/subuid")
+ if err != nil {
+ Skip("cannot read /etc/subuid")
+ }
+ if !strings.Contains(string(content), name) {
+ Skip("cannot find mappings for the current user")
+ }
+
+ m := make(map[string]string)
+ for i := 0; i < 5; i++ {
+ session := podmanTest.Podman([]string{"run", "--userns=auto", "alpine", "cat", "/proc/self/uid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ l := session.OutputToString()
+ Expect(strings.Contains(l, "1024")).To(BeTrue())
+ m[l] = l
+ }
+ // check for no duplicates
+ Expect(len(m)).To(Equal(5))
+ })
+
+ It("podman --userns=auto:size=%d", func() {
+ u, err := user.Current()
+ Expect(err).To(BeNil())
+
+ name := u.Name
+ if name == "root" {
+ name = "containers"
+ }
+
+ content, err := ioutil.ReadFile("/etc/subuid")
+ if err != nil {
+ Skip("cannot read /etc/subuid")
+ }
+ if !strings.Contains(string(content), name) {
+ Skip("cannot find mappings for the current user")
+ }
+
+ session := podmanTest.Podman([]string{"run", "--userns=auto:size=500", "alpine", "cat", "/proc/self/uid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ ok, _ := session.GrepString("500")
+
+ session = podmanTest.Podman([]string{"run", "--userns=auto:size=3000", "alpine", "cat", "/proc/self/uid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ ok, _ = session.GrepString("3000")
+
+ session = podmanTest.Podman([]string{"run", "--userns=auto", "--user=2000:3000", "alpine", "cat", "/proc/self/uid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ ok, _ = session.GrepString("3001")
+
+ session = podmanTest.Podman([]string{"run", "--userns=auto", "--user=4000:1000", "alpine", "cat", "/proc/self/uid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ ok, _ = session.GrepString("4001")
+ Expect(ok).To(BeTrue())
+ })
+
+ It("podman --userns=auto:uidmapping=", func() {
+ u, err := user.Current()
+ Expect(err).To(BeNil())
+
+ name := u.Name
+ if name == "root" {
+ name = "containers"
+ }
+
+ content, err := ioutil.ReadFile("/etc/subuid")
+ if err != nil {
+ Skip("cannot read /etc/subuid")
+ }
+ if !strings.Contains(string(content), name) {
+ Skip("cannot find mappings for the current user")
+ }
+
+ session := podmanTest.Podman([]string{"run", "--userns=auto:uidmapping=0:0:1", "alpine", "cat", "/proc/self/uid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ output := session.OutputToString()
+ Expect(output).To(MatchRegexp("\\s0\\s0\\s1"))
+
+ session = podmanTest.Podman([]string{"run", "--userns=auto:size=8192,uidmapping=0:0:1", "alpine", "cat", "/proc/self/uid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ ok, _ := session.GrepString("8191")
+ Expect(ok).To(BeTrue())
+ })
+
+ It("podman --userns=auto:gidmapping=", func() {
+ u, err := user.Current()
+ Expect(err).To(BeNil())
+
+ name := u.Name
+ if name == "root" {
+ name = "containers"
+ }
+
+ content, err := ioutil.ReadFile("/etc/subuid")
+ if err != nil {
+ Skip("cannot read /etc/subuid")
+ }
+ if !strings.Contains(string(content), name) {
+ Skip("cannot find mappings for the current user")
+ }
+
+ session := podmanTest.Podman([]string{"run", "--userns=auto:gidmapping=0:0:1", "alpine", "cat", "/proc/self/gid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ output := session.OutputToString()
+ Expect(output).To(MatchRegexp("\\s0\\s0\\s1"))
+
+ session = podmanTest.Podman([]string{"run", "--userns=auto:size=8192,gidmapping=0:0:1", "alpine", "cat", "/proc/self/gid_map"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ ok, _ := session.GrepString("8191")
+ Expect(ok).To(BeTrue())
+ })
+
It("podman --userns=container:CTR", func() {
ctrName := "userns-ctr"
session := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:0:1", "--uidmap=1:1:4998", "--name", ctrName, "alpine", "top"})
diff --git a/test/system/005-info.bats b/test/system/005-info.bats
index f229b0886..c53ba8125 100644
--- a/test/system/005-info.bats
+++ b/test/system/005-info.bats
@@ -8,19 +8,19 @@ load helpers
run_podman info
expected_keys="
-BuildahVersion: *[0-9.]\\\+
-Conmon:\\\s\\\+package:
-Distribution:
-OCIRuntime:\\\s\\\+name:
+buildahVersion: *[0-9.]\\\+
+conmon:\\\s\\\+package:
+distribution:
+ociRuntime:\\\s\\\+name:
os:
rootless:
registries:
store:
-GraphDriverName:
-GraphRoot:
-GraphStatus:
-ImageStore:\\\s\\\+number: 1
-RunRoot:
+graphDriverName:
+graphRoot:
+graphStatus:
+imageStore:\\\s\\\+number: 1
+runRoot:
"
while read expect; do
is "$output" ".*$expect" "output includes '$expect'"
@@ -36,13 +36,13 @@ RunRoot:
expr_path="/[a-z0-9\\\/.-]\\\+\\\$"
tests="
-host.BuildahVersion | [0-9.]
-host.Conmon.path | $expr_path
-host.OCIRuntime.path | $expr_path
-store.ConfigFile | $expr_path
-store.GraphDriverName | [a-z0-9]\\\+\\\$
-store.GraphRoot | $expr_path
-store.ImageStore.number | 1
+host.buildahVersion | [0-9.]
+host.conmon.path | $expr_path
+host.ociRuntime.path | $expr_path
+store.configFile | $expr_path
+store.graphDriverName | [a-z0-9]\\\+\\\$
+store.graphRoot | $expr_path
+store.imageStore.number | 1
"
parse_table "$tests" | while read field expect; do
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index 98c65f788..56e9fed3b 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -12,7 +12,7 @@ load helpers
err_no_exec_dir="Error: .*: starting container process caused .*exec:.* permission denied"
# ...but check the configured runtime engine, and switch to crun as needed
- run_podman info --format '{{ .host.OCIRuntime.path }}'
+ run_podman info --format '{{ .Host.OCIRuntime.Path }}'
if expr "$output" : ".*/crun"; then
err_no_such_cmd="Error: executable file not found in \$PATH: No such file or directory: OCI runtime command not found error"
err_no_exec_dir="Error: open executable: Operation not permitted: OCI runtime permission denied error"
diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats
index 0701055f9..a350c2173 100644
--- a/test/system/065-cp.bats
+++ b/test/system/065-cp.bats
@@ -187,7 +187,7 @@ load helpers
chmod 644 $srcdir/$rand_filename
# Determine path to podman storage (eg /var/lib/c/s, or $HOME/.local/...)
- run_podman info --format '{{.store.GraphRoot}}'
+ run_podman info --format '{{.Store.GraphRoot}}'
graphroot=$output
# Create that directory in the container, and sleep (to keep container
diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats
index 56c40e9c8..98f8b8211 100644
--- a/test/system/400-unprivileged-access.bats
+++ b/test/system/400-unprivileged-access.bats
@@ -70,10 +70,10 @@ EOF
chmod 755 $PODMAN_TMPDIR $test_script
# get podman image and container storage directories
- run_podman info --format '{{.store.GraphRoot}}'
+ run_podman info --format '{{.Store.GraphRoot}}'
is "$output" "/var/lib/containers/storage" "GraphRoot in expected place"
GRAPH_ROOT="$output"
- run_podman info --format '{{.store.RunRoot}}'
+ run_podman info --format '{{.Store.RunRoot}}'
is "$output" "/var/run/containers/storage" "RunRoot in expected place"
RUN_ROOT="$output"
diff --git a/test/system/helpers.bash b/test/system/helpers.bash
index 2e856930e..51240edc9 100644
--- a/test/system/helpers.bash
+++ b/test/system/helpers.bash
@@ -391,7 +391,7 @@ function random_string() {
# Return exec_pid hash files if exists, otherwise, return nothing
#
function find_exec_pid_files() {
- run_podman info --format '{{.store.RunRoot}}'
+ run_podman info --format '{{.Store.RunRoot}}'
local storage_path="$output"
if [ -d $storage_path ]; then
find $storage_path -type f -iname 'exec_pid_*'
diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION
index ec6d649be..b57fc7228 100644
--- a/vendor/github.com/containers/storage/VERSION
+++ b/vendor/github.com/containers/storage/VERSION
@@ -1 +1 @@
-1.18.1
+1.18.2
diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod
index b2426c9f9..f18f84f2f 100644
--- a/vendor/github.com/containers/storage/go.mod
+++ b/vendor/github.com/containers/storage/go.mod
@@ -16,7 +16,7 @@ require (
github.com/opencontainers/selinux v1.4.0
github.com/pkg/errors v0.9.1
github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7
- github.com/sirupsen/logrus v1.5.0
+ github.com/sirupsen/logrus v1.4.2
github.com/stretchr/testify v1.5.1
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
github.com/tchap/go-patricia v2.3.0+incompatible
diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
index 599bdb6e2..9c979e5e2 100644
--- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
+++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
@@ -31,6 +31,7 @@ const (
// Disabled constant to indicate SELinux is disabled
Disabled = -1
+ contextFile = "/usr/share/containers/selinux/contexts"
selinuxDir = "/etc/selinux/"
selinuxConfig = selinuxDir + "config"
selinuxfsMount = "/sys/fs/selinux"
@@ -684,23 +685,26 @@ func ROFileLabel() string {
return roFileLabel
}
-/*
-ContainerLabels returns an allocated processLabel and fileLabel to be used for
-container labeling by the calling process.
-*/
-func ContainerLabels() (processLabel string, fileLabel string) {
+func openContextFile() (*os.File, error) {
+ if f, err := os.Open(contextFile); err == nil {
+ return f, nil
+ }
+ lxcPath := filepath.Join(getSELinuxPolicyRoot(), "/contexts/lxc_contexts")
+ return os.Open(lxcPath)
+}
+
+var labels = loadLabels()
+
+func loadLabels() map[string]string {
var (
val, key string
bufin *bufio.Reader
)
- if !GetEnabled() {
- return "", ""
- }
- lxcPath := fmt.Sprintf("%s/contexts/lxc_contexts", getSELinuxPolicyRoot())
- in, err := os.Open(lxcPath)
+ labels := make(map[string]string)
+ in, err := openContextFile()
if err != nil {
- return "", ""
+ return labels
}
defer in.Close()
@@ -712,7 +716,7 @@ func ContainerLabels() (processLabel string, fileLabel string) {
if err == io.EOF {
done = true
} else {
- goto exit
+ break
}
}
line = strings.TrimSpace(line)
@@ -726,26 +730,64 @@ func ContainerLabels() (processLabel string, fileLabel string) {
}
if groups := assignRegex.FindStringSubmatch(line); groups != nil {
key, val = strings.TrimSpace(groups[1]), strings.TrimSpace(groups[2])
- if key == "process" {
- processLabel = strings.Trim(val, "\"")
- }
- if key == "file" {
- fileLabel = strings.Trim(val, "\"")
- }
- if key == "ro_file" {
- roFileLabel = strings.Trim(val, "\"")
- }
+ labels[key] = strings.Trim(val, "\"")
}
}
- if processLabel == "" || fileLabel == "" {
+ return labels
+}
+
+/*
+KVMContainerLabels returns the default processLabel and mountLabel to be used
+for kvm containers by the calling process.
+*/
+func KVMContainerLabels() (string, string) {
+ processLabel := labels["kvm_process"]
+ if processLabel == "" {
+ processLabel = labels["process"]
+ }
+
+ return addMcs(processLabel, labels["file"])
+}
+
+/*
+InitContainerLabels returns the default processLabel and file labels to be
+used for containers running an init system like systemd by the calling process.
+*/
+func InitContainerLabels() (string, string) {
+ processLabel := labels["init_process"]
+ if processLabel == "" {
+ processLabel = labels["process"]
+ }
+
+ return addMcs(processLabel, labels["file"])
+}
+
+/*
+ContainerLabels returns an allocated processLabel and fileLabel to be used for
+container labeling by the calling process.
+*/
+func ContainerLabels() (processLabel string, fileLabel string) {
+ if !GetEnabled() {
return "", ""
}
+ processLabel = labels["process"]
+ fileLabel = labels["file"]
+ roFileLabel = labels["ro_file"]
+
+ if processLabel == "" || fileLabel == "" {
+ return "", fileLabel
+ }
+
if roFileLabel == "" {
roFileLabel = fileLabel
}
-exit:
+
+ return addMcs(processLabel, fileLabel)
+}
+
+func addMcs(processLabel, fileLabel string) (string, string) {
scon, _ := NewContext(processLabel)
if scon["level"] != "" {
mcs := uniqMcs(1024)
diff --git a/vendor/modules.txt b/vendor/modules.txt
index f474a12cf..f4663b04d 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -82,7 +82,7 @@ github.com/containers/buildah/pkg/secrets
github.com/containers/buildah/pkg/supplemented
github.com/containers/buildah/pkg/umask
github.com/containers/buildah/util
-# github.com/containers/common v0.8.0
+# github.com/containers/common v0.8.1
github.com/containers/common/pkg/apparmor
github.com/containers/common/pkg/capabilities
github.com/containers/common/pkg/cgroupv2
@@ -150,7 +150,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process
-# github.com/containers/storage v1.18.1
+# github.com/containers/storage v1.18.2
github.com/containers/storage
github.com/containers/storage/drivers
github.com/containers/storage/drivers/aufs
@@ -411,7 +411,7 @@ github.com/opencontainers/runtime-tools/generate
github.com/opencontainers/runtime-tools/generate/seccomp
github.com/opencontainers/runtime-tools/specerror
github.com/opencontainers/runtime-tools/validate
-# github.com/opencontainers/selinux v1.4.0
+# github.com/opencontainers/selinux v1.5.0
github.com/opencontainers/selinux/go-selinux
github.com/opencontainers/selinux/go-selinux/label
github.com/opencontainers/selinux/pkg/pwalk