refactor info

the current implementation of info, while typed, is very loosely done so.  we need stronger types for our apiv2 implmentation and bindings.

Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
Brent Baude
2020-03-15 11:53:59 -05:00
parent e318b09b68
commit e20ecc733c
27 changed files with 609 additions and 309 deletions

View File

@ -2,6 +2,8 @@ package main
import ( import (
"fmt" "fmt"
"html/template"
"os"
rt "runtime" rt "runtime"
"strings" "strings"
@ -11,7 +13,6 @@ import (
"github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/version" "github.com/containers/libpod/version"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -49,42 +50,32 @@ func init() {
} }
func infoCmd(c *cliconfig.InfoValues) error { func infoCmd(c *cliconfig.InfoValues) error {
info := map[string]interface{}{}
remoteClientInfo := map[string]interface{}{}
runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
if err != nil { if err != nil {
return errors.Wrapf(err, "could not get runtime") return errors.Wrapf(err, "could not get runtime")
} }
defer runtime.DeferredShutdown(false) defer runtime.DeferredShutdown(false)
infoArr, err := runtime.Info() i, err := runtime.Info()
if err != nil { if err != nil {
return errors.Wrapf(err, "error getting info") return errors.Wrapf(err, "error getting info")
} }
info := infoWithExtra{Info: i}
if runtime.Remote { if runtime.Remote {
endpoint, err := runtime.RemoteEndpoint() endpoint, err := runtime.RemoteEndpoint()
if err != nil { if err != nil {
logrus.Errorf("Failed to obtain server connection: %s", err.Error()) return err
} else {
remoteClientInfo["Connection"] = endpoint.Connection
remoteClientInfo["Connection Type"] = endpoint.Type.String()
} }
info.Remote = getRemote(endpoint)
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})
} }
if !runtime.Remote && c.Debug { if !runtime.Remote && c.Debug {
debugInfo := debugInfo(c) d, err := getDebug()
infoArr = append(infoArr, define.InfoData{Type: "debug", Data: debugInfo}) if err != nil {
} return err
}
for _, currInfo := range infoArr { info.Debug = d
info[currInfo.Type] = currInfo.Data
} }
var out formats.Writer var out formats.Writer
@ -98,19 +89,58 @@ func infoCmd(c *cliconfig.InfoValues) error {
case "": case "":
out = formats.YAMLStruct{Output: info} out = formats.YAMLStruct{Output: info}
default: 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() return out.Out()
} }
// top-level "debug" info // top-level "debug" info
func debugInfo(c *cliconfig.InfoValues) map[string]interface{} { func getDebug() (*debugInfo, error) {
info := map[string]interface{}{} v, err := define.GetVersion()
info["compiler"] = rt.Compiler if err != nil {
info["go version"] = rt.Version() return nil, err
info["podman version"] = version.Version }
version, _ := define.GetVersion() return &debugInfo{
info["git commit"] = version.GitCommit Compiler: rt.Compiler,
return info 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"`
} }

View File

@ -156,7 +156,7 @@ Run podman info with JSON formatted response:
``` ```
Run podman info and only get the registries information. 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]] map[registries:[docker.io quay.io registry.fedoraproject.org registry.access.redhat.com]]
``` ```

101
libpod/define/info.go Normal file
View File

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

View File

@ -13,7 +13,9 @@ import (
"time" "time"
"github.com/containers/buildah" "github.com/containers/buildah"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/pkg/cgroups" "github.com/containers/libpod/pkg/cgroups"
registries2 "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage" "github.com/containers/storage"
"github.com/containers/storage/pkg/system" "github.com/containers/storage/pkg/system"
@ -21,14 +23,80 @@ import (
"github.com/sirupsen/logrus" "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 // 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 // lets say OS, arch, number of cpus, amount of memory, maybe os distribution/version, hostname, kernel version, uptime
info := map[string]interface{}{} mi, err := system.ReadMemInfo()
info["os"] = runtime.GOOS if err != nil {
info["arch"] = runtime.GOARCH return nil, errors.Wrapf(err, "error reading memory info")
info["cpus"] = runtime.NumCPU() }
info["rootless"] = rootless.IsRootless()
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() unified, err := cgroups.IsCgroup2UnifiedMode()
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error reading cgroups mode") return nil, errors.Wrapf(err, "error reading cgroups mode")
@ -37,17 +105,8 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
if unified { if unified {
cgroupVersion = "v2" cgroupVersion = "v2"
} }
info["CgroupVersion"] = cgroupVersion info.CGroupsVersion = 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()
if rootless.IsRootless() { if rootless.IsRootless() {
if path, err := exec.LookPath("slirp4netns"); err == nil { if path, err := exec.LookPath("slirp4netns"); err == nil {
logrus.Warnf("Failed to retrieve program version for %s: %v", path, err) 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 { if err != nil {
logrus.Warnf("Failed to retrieve program version for %s: %v", path, err) logrus.Warnf("Failed to retrieve program version for %s: %v", path, err)
} }
program := map[string]interface{}{} program := define.SlirpInfo{
program["Executable"] = path Executable: path,
program["Version"] = version Package: packageVersion(path),
program["Package"] = packageVersion(path) Version: version,
info["slirp4netns"] = program }
info.Slirp4NetNS = program
} }
uidmappings, err := rootless.ReadMappingsProc("/proc/self/uid_map") uidmappings, err := rootless.ReadMappingsProc("/proc/self/uid_map")
if err != nil { if err != nil {
@ -69,29 +129,19 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "error reading gid mappings") return nil, errors.Wrapf(err, "error reading gid mappings")
} }
idmappings := make(map[string]interface{}) idmappings := define.IDMappings{
idmappings["uidmap"] = uidmappings GIDMap: gidmappings,
idmappings["gidmap"] = gidmappings UIDMap: uidmappings,
info["IDMappings"] = idmappings }
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")
}
info["kernel"] = kv
runtimeInfo, err := r.defaultOCIRuntime.RuntimeInfo() conmonInfo, ociruntimeInfo, err := r.defaultOCIRuntime.RuntimeInfo()
if err != nil { if err != nil {
logrus.Errorf("Error getting info on OCI runtime %s: %v", r.defaultOCIRuntime.Name(), err) logrus.Errorf("Error getting info on OCI runtime %s: %v", r.defaultOCIRuntime.Name(), err)
} else { } else {
for k, v := range runtimeInfo { info.Conmon = conmonInfo
info[k] = v info.OCIRuntime = ociruntimeInfo
}
} }
up, err := readUptime() up, err := readUptime()
@ -105,6 +155,7 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) {
return nil, errors.Wrapf(err, "error parsing system uptime") return nil, errors.Wrapf(err, "error parsing system uptime")
} }
// TODO Isnt there a simple lib for this, something like humantime?
hoursFound := false hoursFound := false
var timeBuffer bytes.Buffer var timeBuffer bytes.Buffer
var hoursBuffer 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 { if hoursFound {
hours, err := strconv.ParseFloat(hoursBuffer.String(), 64) hours, err := strconv.ParseFloat(hoursBuffer.String(), 64)
if err == nil { if err == nil {
days := hours / 24 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
if err != nil { }
return nil, errors.Wrapf(err, "error getting hostname")
}
info["hostname"] = host
info["eventlogger"] = r.eventer.String()
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 cs, err
}
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 // 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 // lets say storage driver in use, number of images, number of containers
info := map[string]interface{}{} configFile, err := storage.DefaultConfigFile(rootless.IsRootless())
info["GraphRoot"] = r.store.GraphRoot() if err != nil {
info["RunRoot"] = r.store.RunRoot() return nil, err
info["GraphDriverName"] = r.store.GraphDriverName() }
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{}{} graphOptions := map[string]interface{}{}
for _, o := range r.store.GraphOptions() { for _, o := range r.store.GraphOptions() {
split := strings.SplitN(o, "=", 2) split := strings.SplitN(o, "=", 2)
@ -164,14 +258,8 @@ func (r *Runtime) storeInfo() (map[string]interface{}, error) {
graphOptions[split[0]] = split[1] graphOptions[split[0]] = split[1]
} }
} }
info["GraphOptions"] = graphOptions info.GraphOptions = graphOptions
info["VolumePath"] = r.config.Engine.VolumePath
configFile, err := storage.DefaultConfigFile(rootless.IsRootless())
if err != nil {
return nil, err
}
info["ConfigFile"] = configFile
statusPairs, err := r.store.Status() statusPairs, err := r.store.Status()
if err != nil { if err != nil {
return nil, err return nil, err
@ -180,24 +268,8 @@ func (r *Runtime) storeInfo() (map[string]interface{}, error) {
for _, pair := range statusPairs { for _, pair := range statusPairs {
status[pair[0]] = pair[1] status[pair[0]] = pair[1]
} }
info["GraphStatus"] = status info.GraphStatus = status
images, err := r.store.Images() return &info, nil
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
} }
func readKernelVersion() (string, error) { func readKernelVersion() (string, error) {
@ -225,14 +297,13 @@ func readUptime() (string, error) {
} }
// GetHostDistributionInfo returns a map containing the host's distribution and version // GetHostDistributionInfo returns a map containing the host's distribution and version
func (r *Runtime) GetHostDistributionInfo() map[string]string { func (r *Runtime) GetHostDistributionInfo() define.DistributionInfo {
dist := make(map[string]string)
// Populate values in case we cannot find the values // Populate values in case we cannot find the values
// or the file // or the file
dist["Distribution"] = "unknown" dist := define.DistributionInfo{
dist["Version"] = "unknown" Distribution: "unknown",
Version: "unknown",
}
f, err := os.Open("/etc/os-release") f, err := os.Open("/etc/os-release")
if err != nil { if err != nil {
return dist return dist
@ -242,10 +313,10 @@ func (r *Runtime) GetHostDistributionInfo() map[string]string {
l := bufio.NewScanner(f) l := bufio.NewScanner(f)
for l.Scan() { for l.Scan() {
if strings.HasPrefix(l.Text(), "ID=") { 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=") { 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 return dist

View File

@ -5,7 +5,6 @@ import (
"net" "net"
"github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/define"
"k8s.io/client-go/tools/remotecommand" "k8s.io/client-go/tools/remotecommand"
) )
@ -122,7 +121,7 @@ type OCIRuntime interface {
ExitFilePath(ctr *Container) (string, error) ExitFilePath(ctr *Container) (string, error)
// RuntimeInfo returns verbose information about the runtime. // 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 // ExecOptions are options passed into ExecContainer. They control the command

View File

@ -999,32 +999,30 @@ func (r *ConmonOCIRuntime) ExitFilePath(ctr *Container) (string, error) {
} }
// RuntimeInfo provides information on the runtime. // 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) runtimePackage := packageVersion(r.path)
conmonPackage := packageVersion(r.conmonPath) conmonPackage := packageVersion(r.conmonPath)
runtimeVersion, err := r.getOCIRuntimeVersion() runtimeVersion, err := r.getOCIRuntimeVersion()
if err != nil { 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() conmonVersion, err := r.getConmonVersion()
if err != nil { 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{}) conmon := define.ConmonInfo{
info["Conmon"] = map[string]interface{}{ Package: conmonPackage,
"path": r.conmonPath, Path: r.conmonPath,
"package": conmonPackage, Version: conmonVersion,
"version": conmonVersion,
} }
info["OCIRuntime"] = map[string]interface{}{ ocirt := define.OCIRuntimeInfo{
"name": r.name, Name: r.name,
"path": r.path, Path: r.path,
"package": runtimePackage, Package: runtimePackage,
"version": runtimeVersion, Version: runtimeVersion,
} }
return &conmon, &ocirt, nil
return info, nil
} }
// makeAccessible changes the path permission and each parent directory to have --x--x--x // makeAccessible changes the path permission and each parent directory to have --x--x--x

View File

@ -117,8 +117,8 @@ func (r *ConmonOCIRuntime) ExitFilePath(ctr *Container) (string, error) {
} }
// RuntimeInfo is not supported on this OS. // RuntimeInfo is not supported on this OS.
func (r *ConmonOCIRuntime) RuntimeInfo() (map[string]interface{}, error) { func (r *ConmonOCIRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) {
return nil, define.ErrNotImplemented return nil, nil, define.ErrNotImplemented
} }
// Package is not supported on this OS. // Package is not supported on this OS.

View File

@ -195,15 +195,14 @@ func (r *MissingRuntime) ExitFilePath(ctr *Container) (string, error) {
} }
// RuntimeInfo returns information on the missing runtime // RuntimeInfo returns information on the missing runtime
func (r *MissingRuntime) RuntimeInfo() (map[string]interface{}, error) { func (r *MissingRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) {
info := make(map[string]interface{}) ocirt := define.OCIRuntimeInfo{
info["OCIRuntime"] = map[string]interface{}{ Name: r.name,
"name": r.name, Path: "missing",
"path": "missing", Package: "missing",
"package": "missing", Version: "missing",
"version": "missing",
} }
return info, nil return nil, &ocirt, nil
} }
// Return an error indicating the runtime is missing // Return an error indicating the runtime is missing

View File

@ -9,16 +9,14 @@ import (
"sync" "sync"
"syscall" "syscall"
"github.com/containers/common/pkg/config"
is "github.com/containers/image/v5/storage" is "github.com/containers/image/v5/storage"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/events"
"github.com/containers/libpod/libpod/image" "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/libpod/lock" "github.com/containers/libpod/libpod/lock"
"github.com/containers/libpod/pkg/cgroups" "github.com/containers/libpod/pkg/cgroups"
sysreg "github.com/containers/libpod/pkg/registries"
"github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util" "github.com/containers/libpod/pkg/util"
"github.com/containers/storage" "github.com/containers/storage"
@ -675,40 +673,8 @@ func (r *Runtime) refresh(alivePath string) error {
} }
// Info returns the store and host information // Info returns the store and host information
func (r *Runtime) Info() ([]define.InfoData, error) { func (r *Runtime) Info() (*define.Info, error) {
info := []define.InfoData{} return r.info()
// 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
} }
// generateName generates a unique name for a container or pod. // generateName generates a unique name for a container or pod.

View File

@ -3,51 +3,62 @@
package adapter package adapter
import ( import (
"encoding/json"
"github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/define"
iopodman "github.com/containers/libpod/pkg/varlink" iopodman "github.com/containers/libpod/pkg/varlink"
) )
// Info returns information for the host system and its components // 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 // TODO the varlink implementation for info should be updated to match the output for regular info
var ( var (
reply []define.InfoData reply define.Info
regInfo map[string]interface{}
hostInfo map[string]interface{}
store map[string]interface{}
) )
info, err := iopodman.GetInfo().Call(r.Conn) info, err := iopodman.GetInfo().Call(r.Conn)
if err != nil { if err != nil {
return nil, err return nil, err
} }
hostInfo := define.HostInfo{
// info.host -> map[string]interface{} Arch: info.Host.Arch,
h, err := json.Marshal(info.Host) BuildahVersion: info.Host.Buildah_version,
if err != nil { CPUs: int(info.Host.Cpus),
return nil, err 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) storeInfo := define.StoreInfo{
ContainerStore: define.ContainerStore{
// info.store -> map[string]interface{} Number: int(info.Store.Containers),
s, err := json.Marshal(info.Store) },
if err != nil { GraphDriverName: info.Store.Graph_driver_name,
return nil, err GraphRoot: info.Store.Graph_root,
ImageStore: define.ImageStore{
Number: int(info.Store.Images),
},
RunRoot: info.Store.Run_root,
} }
json.Unmarshal(s, &store) reply.Host = &hostInfo
reply.Store = &storeInfo
// info.Registries -> map[string]interface{} regs := make(map[string]interface{})
reg, err := json.Marshal(info.Registries) if len(info.Registries.Search) > 0 {
if err != nil { regs["search"] = info.Registries.Search
return nil, err
} }
json.Unmarshal(reg, &regInfo) if len(info.Registries.Blocked) > 0 {
regs["blocked"] = info.Registries.Blocked
// Add everything to the reply }
reply = append(reply, define.InfoData{Type: "host", Data: hostInfo}) if len(info.Registries.Insecure) > 0 {
reply = append(reply, define.InfoData{Type: "registries", Data: regInfo}) regs["insecure"] = info.Registries.Insecure
reply = append(reply, define.InfoData{Type: "store", Data: store}) }
return reply, nil reply.Registries = regs
return &reply, nil
} }

View File

@ -94,15 +94,9 @@ func ListContainers(w http.ResponseWriter, r *http.Request) {
} }
} }
// TODO filters still need to be applied // 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)) var list = make([]*handlers.Container, len(containers))
for i, ctnr := range containers { for i, ctnr := range containers {
api, err := handlers.LibpodToContainer(ctnr, infoData, query.Size) api, err := handlers.LibpodToContainer(ctnr, query.Size)
if err != nil { if err != nil {
utils.InternalServerError(w, err) utils.InternalServerError(w, err)
return return

View File

@ -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")) utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to obtain system memory info"))
return return
} }
hostInfo := infoData[0].Data
storeInfo := infoData[1].Data
configInfo, err := runtime.GetConfig() configInfo, err := runtime.GetConfig()
if err != nil { if err != nil {
@ -64,44 +62,44 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
ClusterAdvertise: "", ClusterAdvertise: "",
ClusterStore: "", ClusterStore: "",
ContainerdCommit: docker.Commit{}, ContainerdCommit: docker.Commit{},
Containers: storeInfo["ContainerStore"].(map[string]interface{})["number"].(int), Containers: infoData.Store.ContainerStore.Number,
ContainersPaused: stateInfo[define.ContainerStatePaused], ContainersPaused: stateInfo[define.ContainerStatePaused],
ContainersRunning: stateInfo[define.ContainerStateRunning], ContainersRunning: stateInfo[define.ContainerStateRunning],
ContainersStopped: stateInfo[define.ContainerStateStopped] + stateInfo[define.ContainerStateExited], ContainersStopped: stateInfo[define.ContainerStateStopped] + stateInfo[define.ContainerStateExited],
Debug: log.IsLevelEnabled(log.DebugLevel), Debug: log.IsLevelEnabled(log.DebugLevel),
DefaultRuntime: configInfo.Engine.OCIRuntime, DefaultRuntime: configInfo.Engine.OCIRuntime,
DockerRootDir: storeInfo["GraphRoot"].(string), DockerRootDir: infoData.Store.GraphRoot,
Driver: storeInfo["GraphDriverName"].(string), Driver: infoData.Store.GraphDriverName,
DriverStatus: getGraphStatus(storeInfo), DriverStatus: getGraphStatus(infoData.Store.GraphStatus),
ExperimentalBuild: true, ExperimentalBuild: true,
GenericResources: nil, GenericResources: nil,
HTTPProxy: getEnv("http_proxy"), HTTPProxy: getEnv("http_proxy"),
HTTPSProxy: getEnv("https_proxy"), HTTPSProxy: getEnv("https_proxy"),
ID: uuid.New().String(), ID: uuid.New().String(),
IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled, IPv4Forwarding: !sysInfo.IPv4ForwardingDisabled,
Images: storeInfo["ImageStore"].(map[string]interface{})["number"].(int), Images: infoData.Store.ImageStore.Number,
IndexServerAddress: "", IndexServerAddress: "",
InitBinary: "", InitBinary: "",
InitCommit: docker.Commit{}, InitCommit: docker.Commit{},
Isolation: "", Isolation: "",
KernelMemory: sysInfo.KernelMemory, KernelMemory: sysInfo.KernelMemory,
KernelMemoryTCP: false, KernelMemoryTCP: false,
KernelVersion: hostInfo["kernel"].(string), KernelVersion: infoData.Host.Kernel,
Labels: nil, Labels: nil,
LiveRestoreEnabled: false, LiveRestoreEnabled: false,
LoggingDriver: "", LoggingDriver: "",
MemTotal: hostInfo["MemTotal"].(int64), MemTotal: infoData.Host.MemTotal,
MemoryLimit: sysInfo.MemoryLimit, MemoryLimit: sysInfo.MemoryLimit,
NCPU: goRuntime.NumCPU(), NCPU: goRuntime.NumCPU(),
NEventsListener: 0, NEventsListener: 0,
NFd: getFdCount(), NFd: getFdCount(),
NGoroutines: goRuntime.NumGoroutine(), NGoroutines: goRuntime.NumGoroutine(),
Name: hostInfo["hostname"].(string), Name: infoData.Host.Hostname,
NoProxy: getEnv("no_proxy"), NoProxy: getEnv("no_proxy"),
OSType: goRuntime.GOOS, OSType: goRuntime.GOOS,
OSVersion: hostInfo["Distribution"].(map[string]interface{})["version"].(string), OSVersion: infoData.Host.Distribution.Version,
OomKillDisable: sysInfo.OomKillDisable, OomKillDisable: sysInfo.OomKillDisable,
OperatingSystem: hostInfo["Distribution"].(map[string]interface{})["distribution"].(string), OperatingSystem: infoData.Host.Distribution.Distribution,
PidsLimit: sysInfo.PidsLimit, PidsLimit: sysInfo.PidsLimit,
Plugins: docker.PluginsInfo{}, Plugins: docker.PluginsInfo{},
ProductLicense: "Apache-2.0", ProductLicense: "Apache-2.0",
@ -118,21 +116,21 @@ func GetInfo(w http.ResponseWriter, r *http.Request) {
SystemTime: time.Now().Format(time.RFC3339Nano), SystemTime: time.Now().Format(time.RFC3339Nano),
Warnings: []string{}, Warnings: []string{},
}, },
BuildahVersion: hostInfo["BuildahVersion"].(string), BuildahVersion: infoData.Host.BuildahVersion,
CPURealtimePeriod: sysInfo.CPURealtimePeriod, CPURealtimePeriod: sysInfo.CPURealtimePeriod,
CPURealtimeRuntime: sysInfo.CPURealtimeRuntime, CPURealtimeRuntime: sysInfo.CPURealtimeRuntime,
CgroupVersion: hostInfo["CgroupVersion"].(string), CgroupVersion: infoData.Host.CGroupsVersion,
Rootless: rootless.IsRootless(), Rootless: rootless.IsRootless(),
SwapFree: hostInfo["SwapFree"].(int64), SwapFree: infoData.Host.SwapFree,
SwapTotal: hostInfo["SwapTotal"].(int64), SwapTotal: infoData.Host.SwapTotal,
Uptime: hostInfo["uptime"].(string), Uptime: infoData.Host.Uptime,
} }
utils.WriteResponse(w, http.StatusOK, info) 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 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}) graphStatus = append(graphStatus, [2]string{k, v})
} }
return graphStatus return graphStatus

View File

@ -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")) utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to obtain system memory info"))
return return
} }
hostInfo := infoData[0].Data
components := []docker.ComponentVersion{{ components := []docker.ComponentVersion{{
Name: "Podman Engine", Name: "Podman Engine",
Version: versionInfo.Version, Version: versionInfo.Version,
@ -42,7 +40,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
"Experimental": "true", "Experimental": "true",
"GitCommit": versionInfo.GitCommit, "GitCommit": versionInfo.GitCommit,
"GoVersion": versionInfo.GoVersion, "GoVersion": versionInfo.GoVersion,
"KernelVersion": hostInfo["kernel"].(string), "KernelVersion": infoData.Host.Kernel,
"MinAPIVersion": handlers.MinimalApiVersion, "MinAPIVersion": handlers.MinimalApiVersion,
"Os": goRuntime.GOOS, "Os": goRuntime.GOOS,
}, },
@ -52,7 +50,7 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) {
Platform: struct { Platform: struct {
Name string 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"], APIVersion: components[0].Details["APIVersion"],
Arch: components[0].Details["Arch"], Arch: components[0].Details["Arch"],

View File

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

View File

@ -5,6 +5,7 @@ import (
"os" "os"
"github.com/containers/image/v5/manifest" "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/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -76,6 +77,13 @@ type swagRmPodResponse struct {
Body entities.PodRmReport Body entities.PodRmReport
} }
// Info
// swagger:response InfoResponse
type swagInfoResponse struct {
// in:body
Body define.Info
}
func ServeSwagger(w http.ResponseWriter, r *http.Request) { func ServeSwagger(w http.ResponseWriter, r *http.Request) {
path := DefaultPodmanSwaggerSpec path := DefaultPodmanSwaggerSpec
if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found { if p, found := os.LookupEnv("PODMAN_SWAGGER_SPEC"); found {

View File

@ -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() imageId, imageName := l.Image()
var ( var (

View File

@ -4,14 +4,15 @@ import (
"net/http" "net/http"
"github.com/containers/libpod/pkg/api/handlers/compat" "github.com/containers/libpod/pkg/api/handlers/compat"
"github.com/containers/libpod/pkg/api/handlers/libpod"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )
func (s *APIServer) registerInfoHandlers(r *mux.Router) error { func (s *APIServer) registerInfoHandlers(r *mux.Router) error {
// swagger:operation GET /info libpod libpodGetInfo // swagger:operation GET /info compat getInfo
// --- // ---
// tags: // tags:
// - system // - system (compat)
// summary: Get info // summary: Get info
// description: Returns information on the system and libpod configuration // description: Returns information on the system and libpod configuration
// produces: // produces:
@ -24,5 +25,19 @@ func (s *APIServer) registerInfoHandlers(r *mux.Router) error {
r.Handle(VersionedPath("/info"), s.APIHandler(compat.GetInfo)).Methods(http.MethodGet) r.Handle(VersionedPath("/info"), s.APIHandler(compat.GetInfo)).Methods(http.MethodGet)
// Added non version path to URI to support docker non versioned paths // Added non version path to URI to support docker non versioned paths
r.Handle("/info", s.APIHandler(compat.GetInfo)).Methods(http.MethodGet) 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 return nil
} }

View File

@ -1,3 +0,0 @@
package bindings
func (c Connection) Info() {}

View File

@ -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 info, err
}
response, err := conn.DoRequest(nil, http.MethodGet, "/info", nil)
if err != nil {
return info, err
}
return info, response.Process(&info)
}

View File

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

View File

@ -44,28 +44,26 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error {
return call.ReplyErrorOccurred(err.Error()) return call.ReplyErrorOccurred(err.Error())
} }
host := info[0].Data
distribution := iopodman.InfoDistribution{ distribution := iopodman.InfoDistribution{
Distribution: host["Distribution"].(map[string]interface{})["distribution"].(string), Distribution: info.Host.Distribution.Distribution,
Version: host["Distribution"].(map[string]interface{})["version"].(string), Version: info.Host.Distribution.Version,
} }
infoHost := iopodman.InfoHost{ infoHost := iopodman.InfoHost{
Buildah_version: host["BuildahVersion"].(string), Buildah_version: info.Host.BuildahVersion,
Distribution: distribution, Distribution: distribution,
Mem_free: host["MemFree"].(int64), Mem_free: info.Host.MemFree,
Mem_total: host["MemTotal"].(int64), Mem_total: info.Host.MemTotal,
Swap_free: host["SwapFree"].(int64), Swap_free: info.Host.SwapFree,
Swap_total: host["SwapTotal"].(int64), Swap_total: info.Host.SwapTotal,
Arch: host["arch"].(string), Arch: info.Host.Arch,
Cpus: int64(host["cpus"].(int)), Cpus: int64(info.Host.CPUs),
Hostname: host["hostname"].(string), Hostname: info.Host.Hostname,
Kernel: host["kernel"].(string), Kernel: info.Host.Kernel,
Os: host["os"].(string), Os: info.Host.OS,
Uptime: host["uptime"].(string), Uptime: info.Host.Uptime,
Eventlogger: host["eventlogger"].(string), Eventlogger: info.Host.EventLogger,
} }
podmanInfo.Host = infoHost podmanInfo.Host = infoHost
store := info[1].Data
pmaninfo := iopodman.InfoPodmanBinary{ pmaninfo := iopodman.InfoPodmanBinary{
Compiler: goruntime.Compiler, Compiler: goruntime.Compiler,
Go_version: goruntime.Version(), Go_version: goruntime.Version(),
@ -74,36 +72,33 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error {
} }
graphStatus := iopodman.InfoGraphStatus{ graphStatus := iopodman.InfoGraphStatus{
Backing_filesystem: store["GraphStatus"].(map[string]string)["Backing Filesystem"], Backing_filesystem: info.Store.GraphStatus["Backing Filesystem"],
Native_overlay_diff: store["GraphStatus"].(map[string]string)["Native Overlay Diff"], Native_overlay_diff: info.Store.GraphStatus["Native Overlay Diff"],
Supports_d_type: store["GraphStatus"].(map[string]string)["Supports d_type"], Supports_d_type: info.Store.GraphStatus["Supports d_type"],
} }
infoStore := iopodman.InfoStore{ infoStore := iopodman.InfoStore{
Graph_driver_name: store["GraphDriverName"].(string), Graph_driver_name: info.Store.GraphDriverName,
Containers: int64(store["ContainerStore"].(map[string]interface{})["number"].(int)), Containers: int64(info.Store.ContainerStore.Number),
Images: int64(store["ImageStore"].(map[string]interface{})["number"].(int)), Images: int64(info.Store.ImageStore.Number),
Run_root: store["RunRoot"].(string), Run_root: info.Store.RunRoot,
Graph_root: store["GraphRoot"].(string), Graph_root: info.Store.GraphRoot,
Graph_driver_options: fmt.Sprintf("%v", store["GraphOptions"]), Graph_driver_options: fmt.Sprintf("%v", info.Store.GraphOptions),
Graph_status: graphStatus, Graph_status: graphStatus,
} }
// Registry information if any is stored as the second list item // Registry information if any is stored as the second list item
if len(info) > 2 { for key, val := range info.Registries {
for key, val := range info[2].Data { if key == "search" {
if key == "search" { podmanInfo.Registries.Search = val.([]string)
podmanInfo.Registries.Search = val.([]string) continue
continue }
} regData := val.(sysregistriesv2.Registry)
regData := val.(sysregistriesv2.Registry) if regData.Insecure {
if regData.Insecure { podmanInfo.Registries.Insecure = append(podmanInfo.Registries.Insecure, key)
podmanInfo.Registries.Insecure = append(podmanInfo.Registries.Insecure, key) }
} if regData.Blocked {
if regData.Blocked { podmanInfo.Registries.Blocked = append(podmanInfo.Registries.Blocked, key)
podmanInfo.Registries.Blocked = append(podmanInfo.Registries.Blocked, key)
}
} }
} }
podmanInfo.Store = infoStore podmanInfo.Store = infoStore
podmanInfo.Podman = pmaninfo podmanInfo.Podman = pmaninfo

View File

@ -43,10 +43,16 @@ var _ = Describe("Podman Info", func() {
Expect(session.ExitCode()).To(Equal(0)) 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 := podmanTest.Podman([]string{"info", "--format", "{{ json .}}"})
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0)) Expect(session.ExitCode()).To(Equal(0))
Expect(session.IsJSONOutputValid()).To(BeTrue()) 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))
})
}) })

View File

@ -8,19 +8,19 @@ load helpers
run_podman info run_podman info
expected_keys=" expected_keys="
BuildahVersion: *[0-9.]\\\+ buildahVersion: *[0-9.]\\\+
Conmon:\\\s\\\+package: conmon:\\\s\\\+package:
Distribution: distribution:
OCIRuntime:\\\s\\\+name: ociRuntime:\\\s\\\+name:
os: os:
rootless: rootless:
registries: registries:
store: store:
GraphDriverName: graphDriverName:
GraphRoot: graphRoot:
GraphStatus: graphStatus:
ImageStore:\\\s\\\+number: 1 imageStore:\\\s\\\+number: 1
RunRoot: runRoot:
" "
while read expect; do while read expect; do
is "$output" ".*$expect" "output includes '$expect'" is "$output" ".*$expect" "output includes '$expect'"
@ -36,13 +36,13 @@ RunRoot:
expr_path="/[a-z0-9\\\/.-]\\\+\\\$" expr_path="/[a-z0-9\\\/.-]\\\+\\\$"
tests=" tests="
host.BuildahVersion | [0-9.] host.buildahVersion | [0-9.]
host.Conmon.path | $expr_path host.conmon.path | $expr_path
host.OCIRuntime.path | $expr_path host.ociRuntime.path | $expr_path
store.ConfigFile | $expr_path store.configFile | $expr_path
store.GraphDriverName | [a-z0-9]\\\+\\\$ store.graphDriverName | [a-z0-9]\\\+\\\$
store.GraphRoot | $expr_path store.graphRoot | $expr_path
store.ImageStore.number | 1 store.imageStore.number | 1
" "
parse_table "$tests" | while read field expect; do parse_table "$tests" | while read field expect; do

View File

@ -12,7 +12,7 @@ load helpers
err_no_exec_dir="Error: .*: starting container process caused .*exec:.* permission denied" err_no_exec_dir="Error: .*: starting container process caused .*exec:.* permission denied"
# ...but check the configured runtime engine, and switch to crun as needed # ...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 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_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" err_no_exec_dir="Error: open executable: Operation not permitted: OCI runtime permission denied error"

View File

@ -187,7 +187,7 @@ load helpers
chmod 644 $srcdir/$rand_filename chmod 644 $srcdir/$rand_filename
# Determine path to podman storage (eg /var/lib/c/s, or $HOME/.local/...) # 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 graphroot=$output
# Create that directory in the container, and sleep (to keep container # Create that directory in the container, and sleep (to keep container

View File

@ -70,10 +70,10 @@ EOF
chmod 755 $PODMAN_TMPDIR $test_script chmod 755 $PODMAN_TMPDIR $test_script
# get podman image and container storage directories # 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" is "$output" "/var/lib/containers/storage" "GraphRoot in expected place"
GRAPH_ROOT="$output" 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" is "$output" "/var/run/containers/storage" "RunRoot in expected place"
RUN_ROOT="$output" RUN_ROOT="$output"

View File

@ -391,7 +391,7 @@ function random_string() {
# Return exec_pid hash files if exists, otherwise, return nothing # Return exec_pid hash files if exists, otherwise, return nothing
# #
function find_exec_pid_files() { function find_exec_pid_files() {
run_podman info --format '{{.store.RunRoot}}' run_podman info --format '{{.Store.RunRoot}}'
local storage_path="$output" local storage_path="$output"
if [ -d $storage_path ]; then if [ -d $storage_path ]; then
find $storage_path -type f -iname 'exec_pid_*' find $storage_path -type f -iname 'exec_pid_*'