varlink containers
first pass at adding in the container related endpoints/methods for the libpod backend. Couple of important notes: * endpoints that can use a console are not going to be done until we have "remote" console * several of the container methods should probably be able to stream as opposed to a one-off return Signed-off-by: baude <bbaude@redhat.com> Closes: #708 Approved by: baude
This commit is contained in:
parent
fae5033a01
commit
8dfebd4607
|
|
@ -0,0 +1,159 @@
|
|||
package batchcontainer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
"github.com/sirupsen/logrus"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PsOptions describes the struct being formed for ps
|
||||
type PsOptions struct {
|
||||
All bool
|
||||
Filter string
|
||||
Format string
|
||||
Last int
|
||||
Latest bool
|
||||
NoTrunc bool
|
||||
Quiet bool
|
||||
Size bool
|
||||
Label string
|
||||
Namespace bool
|
||||
}
|
||||
|
||||
// BatchContainerStruct is the return obkect from BatchContainer and contains
|
||||
// container related information
|
||||
type BatchContainerStruct struct {
|
||||
ConConfig *libpod.ContainerConfig
|
||||
ConState libpod.ContainerStatus
|
||||
ExitCode int32
|
||||
Pid int
|
||||
RootFsSize, RwSize int64
|
||||
StartedTime time.Time
|
||||
}
|
||||
|
||||
// Namespace describes output for ps namespace
|
||||
type Namespace struct {
|
||||
PID string `json:"pid,omitempty"`
|
||||
Cgroup string `json:"cgroup,omitempty"`
|
||||
IPC string `json:"ipc,omitempty"`
|
||||
MNT string `json:"mnt,omitempty"`
|
||||
NET string `json:"net,omitempty"`
|
||||
PIDNS string `json:"pidns,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
UTS string `json:"uts,omitempty"`
|
||||
}
|
||||
|
||||
// BatchContainer is used in ps to reduce performance hits by "batching"
|
||||
// locks.
|
||||
func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) {
|
||||
var (
|
||||
conConfig *libpod.ContainerConfig
|
||||
conState libpod.ContainerStatus
|
||||
err error
|
||||
exitCode int32
|
||||
pid int
|
||||
rootFsSize, rwSize int64
|
||||
startedTime time.Time
|
||||
)
|
||||
|
||||
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, 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)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
if opts.Size {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if batchErr != nil {
|
||||
return BatchContainerStruct{}, batchErr
|
||||
}
|
||||
return BatchContainerStruct{
|
||||
ConConfig: conConfig,
|
||||
ConState: conState,
|
||||
ExitCode: exitCode,
|
||||
Pid: pid,
|
||||
RootFsSize: rootFsSize,
|
||||
RwSize: rwSize,
|
||||
StartedTime: startedTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetNamespaces returns a populated namespace struct
|
||||
func GetNamespaces(pid int) *Namespace {
|
||||
ctrPID := strconv.Itoa(pid)
|
||||
cgroup, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
|
||||
ipc, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
|
||||
mnt, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
|
||||
net, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
|
||||
pidns, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
|
||||
user, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
|
||||
uts, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
|
||||
|
||||
return &Namespace{
|
||||
PID: ctrPID,
|
||||
Cgroup: cgroup,
|
||||
IPC: ipc,
|
||||
MNT: mnt,
|
||||
NET: net,
|
||||
PIDNS: pidns,
|
||||
User: user,
|
||||
UTS: uts,
|
||||
}
|
||||
}
|
||||
|
||||
func getNamespaceInfo(path string) (string, error) {
|
||||
val, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error getting info from %q", path)
|
||||
}
|
||||
return getStrFromSquareBrackets(val), nil
|
||||
}
|
||||
|
||||
// getStrFromSquareBrackets gets the string inside [] from a string
|
||||
func getStrFromSquareBrackets(cmd string) string {
|
||||
reg, err := regexp.Compile(".*\\[|\\].*")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
arr := strings.Split(reg.ReplaceAllLiteralString(cmd, ""), ",")
|
||||
return strings.Join(arr, ",")
|
||||
}
|
||||
290
cmd/podman/ps.go
290
cmd/podman/ps.go
|
|
@ -3,10 +3,7 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -15,6 +12,7 @@ import (
|
|||
"github.com/docker/go-units"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/cmd/podman/batchcontainer"
|
||||
"github.com/projectatomic/libpod/cmd/podman/formats"
|
||||
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
|
|
@ -26,19 +24,6 @@ import (
|
|||
|
||||
const mountTruncLength = 12
|
||||
|
||||
type psOptions struct {
|
||||
all bool
|
||||
filter string
|
||||
format string
|
||||
last int
|
||||
latest bool
|
||||
noTrunc bool
|
||||
quiet bool
|
||||
size bool
|
||||
label string
|
||||
namespace bool
|
||||
}
|
||||
|
||||
type psTemplateParams struct {
|
||||
ID string
|
||||
Image string
|
||||
|
|
@ -66,32 +51,21 @@ type psTemplateParams struct {
|
|||
// psJSONParams will be populated by data from libpod.Container,
|
||||
// the members of the struct are the sama data types as their sources.
|
||||
type psJSONParams struct {
|
||||
ID string `json:"id"`
|
||||
Image string `json:"image"`
|
||||
ImageID string `json:"image_id"`
|
||||
Command []string `json:"command"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
RunningFor time.Duration `json:"runningFor"`
|
||||
Status string `json:"status"`
|
||||
Ports []ocicni.PortMapping `json:"ports"`
|
||||
RootFsSize int64 `json:"rootFsSize"`
|
||||
RWSize int64 `json:"rwSize"`
|
||||
Names string `json:"names"`
|
||||
Labels fields.Set `json:"labels"`
|
||||
Mounts []specs.Mount `json:"mounts"`
|
||||
ContainerRunning bool `json:"ctrRunning"`
|
||||
Namespaces *namespace `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
type namespace struct {
|
||||
PID string `json:"pid,omitempty"`
|
||||
Cgroup string `json:"cgroup,omitempty"`
|
||||
IPC string `json:"ipc,omitempty"`
|
||||
MNT string `json:"mnt,omitempty"`
|
||||
NET string `json:"net,omitempty"`
|
||||
PIDNS string `json:"pidns,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
UTS string `json:"uts,omitempty"`
|
||||
ID string `json:"id"`
|
||||
Image string `json:"image"`
|
||||
ImageID string `json:"image_id"`
|
||||
Command []string `json:"command"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
RunningFor time.Duration `json:"runningFor"`
|
||||
Status string `json:"status"`
|
||||
Ports []ocicni.PortMapping `json:"ports"`
|
||||
RootFsSize int64 `json:"rootFsSize"`
|
||||
RWSize int64 `json:"rwSize"`
|
||||
Names string `json:"names"`
|
||||
Labels fields.Set `json:"labels"`
|
||||
Mounts []specs.Mount `json:"mounts"`
|
||||
ContainerRunning bool `json:"ctrRunning"`
|
||||
Namespaces *batchcontainer.Namespace `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -168,22 +142,22 @@ func psCmd(c *cli.Context) error {
|
|||
|
||||
format := genPsFormat(c.String("format"), c.Bool("quiet"), c.Bool("size"), c.Bool("namespace"))
|
||||
|
||||
opts := psOptions{
|
||||
all: c.Bool("all"),
|
||||
filter: c.String("filter"),
|
||||
format: format,
|
||||
last: c.Int("last"),
|
||||
latest: c.Bool("latest"),
|
||||
noTrunc: c.Bool("no-trunc"),
|
||||
quiet: c.Bool("quiet"),
|
||||
size: c.Bool("size"),
|
||||
namespace: c.Bool("namespace"),
|
||||
opts := batchcontainer.PsOptions{
|
||||
All: c.Bool("all"),
|
||||
Filter: c.String("filter"),
|
||||
Format: format,
|
||||
Last: c.Int("last"),
|
||||
Latest: c.Bool("latest"),
|
||||
NoTrunc: c.Bool("no-trunc"),
|
||||
Quiet: c.Bool("quiet"),
|
||||
Size: c.Bool("size"),
|
||||
Namespace: c.Bool("namespace"),
|
||||
}
|
||||
|
||||
var filterFuncs []libpod.ContainerFilter
|
||||
// When we are dealing with latest or last=n, we need to
|
||||
// get all containers.
|
||||
if !opts.all && !opts.latest && opts.last < 1 {
|
||||
if !opts.All && !opts.Latest && opts.Last < 1 {
|
||||
// only get running containers
|
||||
filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
|
||||
state, _ := c.State()
|
||||
|
|
@ -191,8 +165,8 @@ func psCmd(c *cli.Context) error {
|
|||
})
|
||||
}
|
||||
|
||||
if opts.filter != "" {
|
||||
filters := strings.Split(opts.filter, ",")
|
||||
if opts.Filter != "" {
|
||||
filters := strings.Split(opts.Filter, ",")
|
||||
for _, f := range filters {
|
||||
filterSplit := strings.Split(f, "=")
|
||||
if len(filterSplit) < 2 {
|
||||
|
|
@ -208,10 +182,10 @@ func psCmd(c *cli.Context) error {
|
|||
|
||||
containers, err := runtime.GetContainers(filterFuncs...)
|
||||
var outputContainers []*libpod.Container
|
||||
if opts.latest && len(containers) > 0 {
|
||||
if opts.Latest && len(containers) > 0 {
|
||||
outputContainers = append(outputContainers, containers[0])
|
||||
} else if opts.last > 0 && opts.last <= len(containers) {
|
||||
outputContainers = append(outputContainers, containers[:opts.last]...)
|
||||
} else if opts.Last > 0 && opts.Last <= len(containers) {
|
||||
outputContainers = append(outputContainers, containers[:opts.Last]...)
|
||||
} else {
|
||||
outputContainers = containers
|
||||
}
|
||||
|
|
@ -397,15 +371,15 @@ func (p *psTemplateParams) headerMap() map[string]string {
|
|||
}
|
||||
|
||||
// getTemplateOutput returns the modified container information
|
||||
func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemplateParams, error) {
|
||||
func getTemplateOutput(containers []*libpod.Container, opts batchcontainer.PsOptions) ([]psTemplateParams, error) {
|
||||
var (
|
||||
psOutput []psTemplateParams
|
||||
status, size string
|
||||
ns *namespace
|
||||
ns *batchcontainer.Namespace
|
||||
)
|
||||
|
||||
for _, ctr := range containers {
|
||||
batchInfo, err := batchContainerOp(ctr, opts)
|
||||
batchInfo, err := batchcontainer.BatchContainerOp(ctr, opts)
|
||||
if err != nil {
|
||||
// If the error was ErrNoSuchCtr, it was probably
|
||||
// removed sometime after we got the initial list.
|
||||
|
|
@ -421,11 +395,11 @@ func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemp
|
|||
runningFor := ""
|
||||
// If the container has not be started, the "zero" value of time is 0001-01-01 00:00:00 +0000 UTC
|
||||
// which would make the time elapsed about a few hundred of years. So checking for the "zero" value of time.Time
|
||||
if batchInfo.startedTime != (time.Time{}) {
|
||||
runningFor = units.HumanDuration(time.Since(batchInfo.startedTime))
|
||||
if batchInfo.StartedTime != (time.Time{}) {
|
||||
runningFor = units.HumanDuration(time.Since(batchInfo.StartedTime))
|
||||
}
|
||||
createdAt := batchInfo.conConfig.CreatedTime.Format("2006-01-02 15:04:05 -0700 MST")
|
||||
imageName := batchInfo.conConfig.RootfsImageName
|
||||
createdAt := batchInfo.ConConfig.CreatedTime.Format("2006-01-02 15:04:05 -0700 MST")
|
||||
imageName := batchInfo.ConConfig.RootfsImageName
|
||||
|
||||
var createArtifact createConfig
|
||||
artifact, err := ctr.GetArtifact("create-config")
|
||||
|
|
@ -436,27 +410,27 @@ func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemp
|
|||
} else {
|
||||
logrus.Errorf("couldn't get some ps information, error getting artifact %q: %v", ctr.ID(), err)
|
||||
}
|
||||
if opts.namespace {
|
||||
ns = getNamespaces(batchInfo.pid)
|
||||
if opts.Namespace {
|
||||
ns = batchcontainer.GetNamespaces(batchInfo.Pid)
|
||||
}
|
||||
if opts.size {
|
||||
if opts.Size {
|
||||
|
||||
size = units.HumanSizeWithPrecision(float64(batchInfo.rwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(batchInfo.rootFsSize), 3) + ")"
|
||||
size = units.HumanSizeWithPrecision(float64(batchInfo.RwSize), 3) + " (virtual " + units.HumanSizeWithPrecision(float64(batchInfo.RootFsSize), 3) + ")"
|
||||
}
|
||||
|
||||
command := strings.Join(batchInfo.conConfig.Spec.Process.Args, " ")
|
||||
if !opts.noTrunc {
|
||||
command := strings.Join(batchInfo.ConConfig.Spec.Process.Args, " ")
|
||||
if !opts.NoTrunc {
|
||||
if len(command) > 20 {
|
||||
command = command[:19] + "..."
|
||||
}
|
||||
}
|
||||
ports := portsToString(batchInfo.conConfig.PortMappings)
|
||||
mounts := getMounts(createArtifact.Volumes, opts.noTrunc)
|
||||
ports := portsToString(batchInfo.ConConfig.PortMappings)
|
||||
mounts := getMounts(createArtifact.Volumes, opts.NoTrunc)
|
||||
labels := formatLabels(ctr.Labels())
|
||||
|
||||
switch batchInfo.conState {
|
||||
switch batchInfo.ConState {
|
||||
case libpod.ContainerStateStopped:
|
||||
status = fmt.Sprintf("Exited (%d) %s ago", batchInfo.exitCode, runningFor)
|
||||
status = fmt.Sprintf("Exited (%d) %s ago", batchInfo.ExitCode, runningFor)
|
||||
case libpod.ContainerStateRunning:
|
||||
status = "Up " + runningFor + " ago"
|
||||
case libpod.ContainerStatePaused:
|
||||
|
|
@ -467,9 +441,9 @@ func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemp
|
|||
status = "Dead"
|
||||
}
|
||||
|
||||
if !opts.noTrunc {
|
||||
if !opts.NoTrunc {
|
||||
ctrID = shortID(ctr.ID())
|
||||
imageName = batchInfo.conConfig.RootfsImageName
|
||||
imageName = batchInfo.ConConfig.RootfsImageName
|
||||
}
|
||||
|
||||
params := psTemplateParams{
|
||||
|
|
@ -484,10 +458,10 @@ func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemp
|
|||
Names: ctr.Name(),
|
||||
Labels: labels,
|
||||
Mounts: mounts,
|
||||
PID: batchInfo.pid,
|
||||
PID: batchInfo.Pid,
|
||||
}
|
||||
|
||||
if opts.namespace {
|
||||
if opts.Namespace {
|
||||
params.Cgroup = ns.Cgroup
|
||||
params.IPC = ns.IPC
|
||||
params.MNT = ns.MNT
|
||||
|
|
@ -501,65 +475,35 @@ func getTemplateOutput(containers []*libpod.Container, opts psOptions) ([]psTemp
|
|||
return psOutput, nil
|
||||
}
|
||||
|
||||
func getNamespaces(pid int) *namespace {
|
||||
ctrPID := strconv.Itoa(pid)
|
||||
cgroup, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
|
||||
ipc, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
|
||||
mnt, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
|
||||
net, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
|
||||
pidns, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
|
||||
user, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
|
||||
uts, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
|
||||
|
||||
return &namespace{
|
||||
PID: ctrPID,
|
||||
Cgroup: cgroup,
|
||||
IPC: ipc,
|
||||
MNT: mnt,
|
||||
NET: net,
|
||||
PIDNS: pidns,
|
||||
User: user,
|
||||
UTS: uts,
|
||||
}
|
||||
}
|
||||
|
||||
func getNamespaceInfo(path string) (string, error) {
|
||||
val, err := os.Readlink(path)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error getting info from %q", path)
|
||||
}
|
||||
return getStrFromSquareBrackets(val), nil
|
||||
}
|
||||
|
||||
// getJSONOutput returns the container info in its raw form
|
||||
func getJSONOutput(containers []*libpod.Container, opts psOptions) ([]psJSONParams, error) {
|
||||
func getJSONOutput(containers []*libpod.Container, opts batchcontainer.PsOptions) ([]psJSONParams, error) {
|
||||
var (
|
||||
psOutput []psJSONParams
|
||||
ns *namespace
|
||||
ns *batchcontainer.Namespace
|
||||
)
|
||||
for _, ctr := range containers {
|
||||
batchInfo, err := batchContainerOp(ctr, opts)
|
||||
batchInfo, err := batchcontainer.BatchContainerOp(ctr, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.namespace {
|
||||
ns = getNamespaces(batchInfo.pid)
|
||||
if opts.Namespace {
|
||||
ns = batchcontainer.GetNamespaces(batchInfo.Pid)
|
||||
}
|
||||
params := psJSONParams{
|
||||
ID: ctr.ID(),
|
||||
Image: batchInfo.conConfig.RootfsImageName,
|
||||
ImageID: batchInfo.conConfig.RootfsImageID,
|
||||
Command: batchInfo.conConfig.Spec.Process.Args,
|
||||
CreatedAt: batchInfo.conConfig.CreatedTime,
|
||||
RunningFor: time.Since(batchInfo.conConfig.CreatedTime),
|
||||
Status: batchInfo.conState.String(),
|
||||
Ports: batchInfo.conConfig.PortMappings,
|
||||
RootFsSize: batchInfo.rootFsSize,
|
||||
RWSize: batchInfo.rwSize,
|
||||
Names: batchInfo.conConfig.Name,
|
||||
Labels: batchInfo.conConfig.Labels,
|
||||
Mounts: batchInfo.conConfig.Spec.Mounts,
|
||||
ContainerRunning: batchInfo.conState == libpod.ContainerStateRunning,
|
||||
Image: batchInfo.ConConfig.RootfsImageName,
|
||||
ImageID: batchInfo.ConConfig.RootfsImageID,
|
||||
Command: batchInfo.ConConfig.Spec.Process.Args,
|
||||
CreatedAt: batchInfo.ConConfig.CreatedTime,
|
||||
RunningFor: time.Since(batchInfo.ConConfig.CreatedTime),
|
||||
Status: batchInfo.ConState.String(),
|
||||
Ports: batchInfo.ConConfig.PortMappings,
|
||||
RootFsSize: batchInfo.RootFsSize,
|
||||
RWSize: batchInfo.RwSize,
|
||||
Names: batchInfo.ConConfig.Name,
|
||||
Labels: batchInfo.ConConfig.Labels,
|
||||
Mounts: batchInfo.ConConfig.Spec.Mounts,
|
||||
ContainerRunning: batchInfo.ConState == libpod.ContainerStateRunning,
|
||||
Namespaces: ns,
|
||||
}
|
||||
psOutput = append(psOutput, params)
|
||||
|
|
@ -567,13 +511,13 @@ func getJSONOutput(containers []*libpod.Container, opts psOptions) ([]psJSONPara
|
|||
return psOutput, nil
|
||||
}
|
||||
|
||||
func generatePsOutput(containers []*libpod.Container, opts psOptions) error {
|
||||
if len(containers) == 0 && opts.format != formats.JSONString {
|
||||
func generatePsOutput(containers []*libpod.Container, opts batchcontainer.PsOptions) error {
|
||||
if len(containers) == 0 && opts.Format != formats.JSONString {
|
||||
return nil
|
||||
}
|
||||
var out formats.Writer
|
||||
|
||||
switch opts.format {
|
||||
switch opts.Format {
|
||||
case formats.JSONString:
|
||||
psOutput, err := getJSONOutput(containers, opts)
|
||||
if err != nil {
|
||||
|
|
@ -585,22 +529,12 @@ func generatePsOutput(containers []*libpod.Container, opts psOptions) error {
|
|||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to create output")
|
||||
}
|
||||
out = formats.StdoutTemplateArray{Output: psToGeneric(psOutput, []psJSONParams{}), Template: opts.format, Fields: psOutput[0].headerMap()}
|
||||
out = formats.StdoutTemplateArray{Output: psToGeneric(psOutput, []psJSONParams{}), Template: opts.Format, Fields: psOutput[0].headerMap()}
|
||||
}
|
||||
|
||||
return formats.Writer(out).Out()
|
||||
}
|
||||
|
||||
// getStrFromSquareBrackets gets the string inside [] from a string
|
||||
func getStrFromSquareBrackets(cmd string) string {
|
||||
reg, err := regexp.Compile(".*\\[|\\].*")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
arr := strings.Split(reg.ReplaceAllLiteralString(cmd, ""), ",")
|
||||
return strings.Join(arr, ",")
|
||||
}
|
||||
|
||||
// getLabels converts the labels to a string of the form "key=value, key2=value2"
|
||||
func formatLabels(labels map[string]string) string {
|
||||
var arr []string
|
||||
|
|
@ -647,77 +581,3 @@ func portsToString(ports []ocicni.PortMapping) string {
|
|||
}
|
||||
return strings.Join(portDisplay, ", ")
|
||||
}
|
||||
|
||||
type batchContainerStruct struct {
|
||||
conConfig *libpod.ContainerConfig
|
||||
conState libpod.ContainerStatus
|
||||
exitCode int32
|
||||
pid int
|
||||
rootFsSize, rwSize int64
|
||||
startedTime time.Time
|
||||
}
|
||||
|
||||
func batchContainerOp(ctr *libpod.Container, opts psOptions) (batchContainerStruct, error) {
|
||||
var (
|
||||
conConfig *libpod.ContainerConfig
|
||||
conState libpod.ContainerStatus
|
||||
err error
|
||||
exitCode int32
|
||||
pid int
|
||||
rootFsSize, rwSize int64
|
||||
startedTime time.Time
|
||||
)
|
||||
|
||||
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, 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)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
if opts.size {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if batchErr != nil {
|
||||
return batchContainerStruct{}, batchErr
|
||||
}
|
||||
return batchContainerStruct{
|
||||
conConfig: conConfig,
|
||||
conState: conState,
|
||||
exitCode: exitCode,
|
||||
pid: pid,
|
||||
rootFsSize: rootFsSize,
|
||||
rwSize: rwSize,
|
||||
startedTime: startedTime,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
|
|
@ -64,7 +65,7 @@ func restartCmd(c *cli.Context) error {
|
|||
ctrTimeout = timeout
|
||||
}
|
||||
|
||||
lastError = restartCtr(ctrTimeout, lastCtr)
|
||||
lastError = lastCtr.RestartWithTimeout(context.TODO(), ctrTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +84,7 @@ func restartCmd(c *cli.Context) error {
|
|||
ctrTimeout = timeout
|
||||
}
|
||||
|
||||
if err := restartCtr(ctrTimeout, ctr); err != nil {
|
||||
if err := ctr.RestartWithTimeout(context.TODO(), ctrTimeout); err != nil {
|
||||
if lastError != nil {
|
||||
fmt.Fprintln(os.Stderr, lastError)
|
||||
}
|
||||
|
|
@ -93,24 +94,3 @@ func restartCmd(c *cli.Context) error {
|
|||
|
||||
return lastError
|
||||
}
|
||||
|
||||
// Restart a single container
|
||||
func restartCtr(timeout uint, ctr *libpod.Container) error {
|
||||
state, err := ctr.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if state == libpod.ContainerStateRunning {
|
||||
if err := ctr.StopWithTimeout(timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := ctr.Start(getContext()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", ctr.ID())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,32 +53,100 @@ type ImageSearch (
|
|||
star_count: int
|
||||
)
|
||||
|
||||
# ListContainer is the returned struct for an individual container
|
||||
type ListContainerData (
|
||||
id: string,
|
||||
image: string,
|
||||
imageid: string,
|
||||
command: []string,
|
||||
createdat: string,
|
||||
runningfor: string,
|
||||
status: string,
|
||||
ports: []ContainerPortMappings,
|
||||
rootfssize: int,
|
||||
rwsize: int,
|
||||
names: string,
|
||||
labels: [string]string,
|
||||
mounts: []ContainerMount,
|
||||
containerrunning: bool,
|
||||
namespaces: ContainerNameSpace
|
||||
)
|
||||
|
||||
# ContainerStats is the return struct for the stats of a container
|
||||
type ContainerStats (
|
||||
id: string,
|
||||
name: string,
|
||||
cpu: float,
|
||||
cpu_nano: int,
|
||||
system_nano: int,
|
||||
mem_usage: int,
|
||||
mem_limit: int,
|
||||
mem_perc: float,
|
||||
net_input: int,
|
||||
net_output: int,
|
||||
block_output: int,
|
||||
block_input: int,
|
||||
pids: int
|
||||
)
|
||||
|
||||
# ContainerMount describes the struct for mounts in a container
|
||||
type ContainerMount (
|
||||
destination: string,
|
||||
type: string,
|
||||
source: string,
|
||||
options: []string
|
||||
)
|
||||
|
||||
# ContainerPortMappings describes the struct for portmappings in an existing container
|
||||
type ContainerPortMappings (
|
||||
host_port: string,
|
||||
host_ip: string,
|
||||
protocol: string,
|
||||
container_port: string
|
||||
)
|
||||
|
||||
# ContainerNamespace describes the namespace structure for an existing container
|
||||
type ContainerNameSpace (
|
||||
user: string,
|
||||
uts: string,
|
||||
pidns: string,
|
||||
pid: string,
|
||||
cgroup: string,
|
||||
net: string,
|
||||
mnt: string,
|
||||
ipc: string
|
||||
)
|
||||
|
||||
# System
|
||||
method Ping() -> (ping: StringResponse)
|
||||
method GetVersion() -> (version: Version)
|
||||
|
||||
# Containers
|
||||
method ListContainers() -> (notimplemented: NotImplemented)
|
||||
method ListContainers() -> (containers: []ListContainerData)
|
||||
method GetContainer(name: string) -> (container: ListContainerData)
|
||||
method CreateContainer() -> (notimplemented: NotImplemented)
|
||||
method InspectContainer() -> (notimplemented: NotImplemented)
|
||||
method ListContainerProcesses() -> (notimplemented: NotImplemented)
|
||||
method GetContainerLogs() -> (notimplemented: NotImplemented)
|
||||
method ListContainerChanges() -> (notimplemented: NotImplemented)
|
||||
method ExportContainer() -> (notimplemented: NotImplemented)
|
||||
method GetContainerStats() -> (notimplemented: NotImplemented)
|
||||
method InspectContainer(name: string) -> (container: string)
|
||||
# TODO: Should this be made into a streaming response as opposed to a one off?
|
||||
method ListContainerProcesses(name: string, opts: []string) -> (container: []string)
|
||||
# TODO: Should this be made into a streaming response as opposed to a one off?
|
||||
method GetContainerLogs(name: string) -> (container: []string)
|
||||
method ListContainerChanges(name: string) -> (container: [string]string)
|
||||
# TODO: This should be made into a streaming response
|
||||
method ExportContainer(name: string, path: string) -> (tarfile: string)
|
||||
method GetContainerStats(name: string) -> (container: ContainerStats)
|
||||
method ResizeContainerTty() -> (notimplemented: NotImplemented)
|
||||
method StartContainer() -> (notimplemented: NotImplemented)
|
||||
method StopContainer() -> (notimplemented: NotImplemented)
|
||||
method RestartContainer() -> (notimplemented: NotImplemented)
|
||||
method KillContainer() -> (notimplemented: NotImplemented)
|
||||
method StopContainer(name: string, timeout: int) -> (container: string)
|
||||
method RestartContainer(name: string, timeout: int) -> (container: string)
|
||||
method KillContainer(name: string, signal: int) -> (container: string)
|
||||
method UpdateContainer() -> (notimplemented: NotImplemented)
|
||||
method RenameContainer() -> (notimplemented: NotImplemented)
|
||||
method PauseContainer() -> (notimplemented: NotImplemented)
|
||||
method UnpauseContainer() -> (notimplemented: NotImplemented)
|
||||
method PauseContainer(name: string) -> (container: string)
|
||||
method UnpauseContainer(name: string) -> (container: string)
|
||||
method AttachToContainer() -> (notimplemented: NotImplemented)
|
||||
method WaitContainer() -> (notimplemented: NotImplemented)
|
||||
method RemoveContainer() -> (notimplemented: NotImplemented)
|
||||
method DeleteStoppedContainers() -> (notimplemented: NotImplemented)
|
||||
method WaitContainer(name: string) -> (exitcode: int)
|
||||
method RemoveContainer(name: string, force: bool) -> (container: string)
|
||||
method DeleteStoppedContainers() -> (containers: []string)
|
||||
|
||||
# Images
|
||||
method ListImages() -> (images: []ImageInList)
|
||||
|
|
@ -99,6 +167,7 @@ method PullImage(name: string) -> (id: string)
|
|||
|
||||
# Something failed
|
||||
error ActionFailed (reason: string)
|
||||
error ImageNotFound (imagename: string)
|
||||
error ImageNotFound (name: string)
|
||||
error ContainerNotFound (name: string)
|
||||
error ErrorOccurred (reason: string)
|
||||
error RuntimeError (reason: string)
|
||||
error RuntimeError (reason: string)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -705,3 +705,52 @@ func (c *Container) Sync() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestartWithTimeout restarts a running container and takes a given timeout in uint
|
||||
func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) error {
|
||||
if !c.batched {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if err := c.syncContainer(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
notRunning, err := c.checkDependenciesRunning()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error checking dependencies for container %s")
|
||||
}
|
||||
if len(notRunning) > 0 {
|
||||
depString := strings.Join(notRunning, ",")
|
||||
return errors.Wrapf(ErrCtrStateInvalid, "some dependencies of container %s are not started: %s", c.ID(), depString)
|
||||
}
|
||||
if c.state.State == ContainerStateUnknown || c.state.State == ContainerStatePaused {
|
||||
return errors.Errorf("unable to restart a container in a paused or unknown state")
|
||||
}
|
||||
|
||||
if c.state.State == ContainerStateRunning {
|
||||
if err := c.stop(timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := c.prepare(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if err2 := c.cleanup(); err2 != nil {
|
||||
logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if c.state.State == ContainerStateStopped {
|
||||
// Reinitialize the container if we need to
|
||||
if err := c.reinit(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return c.start()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -459,7 +459,6 @@ func (c *Container) reinit(ctx context.Context) error {
|
|||
if err := c.runtime.ociRuntime.deleteContainer(c); err != nil {
|
||||
return errors.Wrapf(err, "error removing container %s from runtime", c.ID())
|
||||
}
|
||||
|
||||
// Our state is now Configured, as we've removed ourself from
|
||||
// the runtime
|
||||
// Set and save now to make sure that, if the init() below fails
|
||||
|
|
@ -545,7 +544,6 @@ func (c *Container) start() error {
|
|||
if err := c.runtime.ociRuntime.startContainer(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debugf("Started container %s", c.ID())
|
||||
|
||||
c.state.State = ContainerStateRunning
|
||||
|
|
|
|||
|
|
@ -1,12 +1,67 @@
|
|||
package varlinkapi
|
||||
|
||||
import (
|
||||
ioprojectatomicpodman "github.com/projectatomic/libpod/cmd/podman/varlink"
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectatomic/libpod/cmd/podman/batchcontainer"
|
||||
"github.com/projectatomic/libpod/cmd/podman/libpodruntime"
|
||||
"github.com/projectatomic/libpod/cmd/podman/varlink"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
)
|
||||
|
||||
// ListContainers ...
|
||||
func (i *LibpodAPI) ListContainers(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("ListContainers")
|
||||
var (
|
||||
listContainers []ioprojectatomicpodman.ListContainerData
|
||||
)
|
||||
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
containers, err := runtime.GetAllContainers()
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
opts := batchcontainer.PsOptions{
|
||||
Namespace: true,
|
||||
Size: true,
|
||||
}
|
||||
for _, ctr := range containers {
|
||||
batchInfo, err := batchcontainer.BatchContainerOp(ctr, opts)
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
|
||||
listContainers = append(listContainers, makeListContainer(ctr.ID(), batchInfo))
|
||||
}
|
||||
return call.ReplyListContainers(listContainers)
|
||||
}
|
||||
|
||||
// GetContainer ...
|
||||
func (i *LibpodAPI) GetContainer(call ioprojectatomicpodman.VarlinkCall, name string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
opts := batchcontainer.PsOptions{
|
||||
Namespace: true,
|
||||
Size: true,
|
||||
}
|
||||
batchInfo, err := batchcontainer.BatchContainerOp(ctr, opts)
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyGetContainer(makeListContainer(ctr.ID(), batchInfo))
|
||||
}
|
||||
|
||||
// CreateContainer ...
|
||||
|
|
@ -15,33 +70,158 @@ func (i *LibpodAPI) CreateContainer(call ioprojectatomicpodman.VarlinkCall) erro
|
|||
}
|
||||
|
||||
// InspectContainer ...
|
||||
func (i *LibpodAPI) InspectContainer(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("InspectContainer")
|
||||
func (i *LibpodAPI) InspectContainer(call ioprojectatomicpodman.VarlinkCall, name string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
inspectInfo, err := ctr.Inspect(true)
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
b, err := json.Marshal(inspectInfo)
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(fmt.Sprintf("unable to serialize"))
|
||||
}
|
||||
return call.ReplyInspectContainer(string(b))
|
||||
}
|
||||
|
||||
// ListContainerProcesses ...
|
||||
func (i *LibpodAPI) ListContainerProcesses(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("ListContainerProcesses")
|
||||
func (i *LibpodAPI) ListContainerProcesses(call ioprojectatomicpodman.VarlinkCall, name string, opts []string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
containerState, err := ctr.State()
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
if containerState != libpod.ContainerStateRunning {
|
||||
return call.ReplyErrorOccurred(fmt.Sprintf("container %s is not running", name))
|
||||
}
|
||||
var psArgs []string
|
||||
psOpts := []string{"-o", "uid,pid,ppid,c,stime,tname,time,cmd"}
|
||||
if len(opts) > 1 {
|
||||
psOpts = opts
|
||||
}
|
||||
psArgs = append(psArgs, psOpts...)
|
||||
psOutput, err := ctr.GetContainerPidInformation(psArgs)
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyListContainerProcesses(psOutput)
|
||||
}
|
||||
|
||||
// GetContainerLogs ...
|
||||
func (i *LibpodAPI) GetContainerLogs(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("GetContainerLogs")
|
||||
func (i *LibpodAPI) GetContainerLogs(call ioprojectatomicpodman.VarlinkCall, name string) error {
|
||||
var logs []string
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
logPath := ctr.LogPath()
|
||||
|
||||
containerState, err := ctr.State()
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
if _, err := os.Stat(logPath); err != nil {
|
||||
if containerState == libpod.ContainerStateConfigured {
|
||||
return call.ReplyGetContainerLogs(logs)
|
||||
}
|
||||
}
|
||||
file, err := os.Open(logPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to read container log file")
|
||||
}
|
||||
defer file.Close()
|
||||
reader := bufio.NewReader(file)
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
logs = append(logs, line)
|
||||
}
|
||||
return call.ReplyGetContainerLogs(logs)
|
||||
}
|
||||
|
||||
// ListContainerChanges ...
|
||||
func (i *LibpodAPI) ListContainerChanges(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("ListContianerChanges")
|
||||
func (i *LibpodAPI) ListContainerChanges(call ioprojectatomicpodman.VarlinkCall, name string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
changes, err := runtime.GetDiff("", name)
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
|
||||
m := make(map[string]string)
|
||||
for _, change := range changes {
|
||||
m[change.Path] = change.Kind.String()
|
||||
}
|
||||
return call.ReplyListContainerChanges(m)
|
||||
}
|
||||
|
||||
// ExportContainer ...
|
||||
func (i *LibpodAPI) ExportContainer(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("ExportContainer")
|
||||
func (i *LibpodAPI) ExportContainer(call ioprojectatomicpodman.VarlinkCall, name, path string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
if err := ctr.Export(path); err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyExportContainer(path)
|
||||
}
|
||||
|
||||
// GetContainerStats ...
|
||||
func (i *LibpodAPI) GetContainerStats(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("GetContainerStates")
|
||||
func (i *LibpodAPI) GetContainerStats(call ioprojectatomicpodman.VarlinkCall, name string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
containerStats, err := ctr.GetContainerStats(&libpod.ContainerStats{})
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
cs := ioprojectatomicpodman.ContainerStats{
|
||||
Id: ctr.ID(),
|
||||
Name: ctr.Name(),
|
||||
Cpu: containerStats.CPU,
|
||||
Cpu_nano: int64(containerStats.CPUNano),
|
||||
System_nano: int64(containerStats.SystemNano),
|
||||
Mem_usage: int64(containerStats.MemUsage),
|
||||
Mem_limit: int64(containerStats.MemLimit),
|
||||
Mem_perc: containerStats.MemPerc,
|
||||
Net_input: int64(containerStats.NetInput),
|
||||
Net_output: int64(containerStats.NetOutput),
|
||||
Block_input: int64(containerStats.BlockInput),
|
||||
Block_output: int64(containerStats.BlockOutput),
|
||||
Pids: int64(containerStats.PIDs),
|
||||
}
|
||||
return call.ReplyGetContainerStats(cs)
|
||||
}
|
||||
|
||||
// ResizeContainerTty ...
|
||||
|
|
@ -55,18 +235,56 @@ func (i *LibpodAPI) StartContainer(call ioprojectatomicpodman.VarlinkCall) error
|
|||
}
|
||||
|
||||
// StopContainer ...
|
||||
func (i *LibpodAPI) StopContainer(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("StopContainer")
|
||||
func (i *LibpodAPI) StopContainer(call ioprojectatomicpodman.VarlinkCall, name string, timeout int64) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
if err := ctr.StopWithTimeout(uint(timeout)); err != nil && err != libpod.ErrCtrStopped {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyStopContainer(ctr.ID())
|
||||
}
|
||||
|
||||
// RestartContainer ...
|
||||
func (i *LibpodAPI) RestartContainer(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("RestartContainer")
|
||||
func (i *LibpodAPI) RestartContainer(call ioprojectatomicpodman.VarlinkCall, name string, timeout int64) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
if err := ctr.RestartWithTimeout(getContext(), uint(timeout)); err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyRestartContainer(ctr.ID())
|
||||
}
|
||||
|
||||
// KillContainer ...
|
||||
func (i *LibpodAPI) KillContainer(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("KillContainer")
|
||||
// KillContainer kills a running container. If you want to use the default SIGTERM signal, just send a -1
|
||||
// for the signal arg.
|
||||
func (i *LibpodAPI) KillContainer(call ioprojectatomicpodman.VarlinkCall, name string, signal int64) error {
|
||||
var killSignal uint = uint(syscall.SIGTERM)
|
||||
if signal != -1 {
|
||||
killSignal = uint(signal)
|
||||
}
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
if err := ctr.Kill(killSignal); err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyKillContainer(ctr.ID())
|
||||
}
|
||||
|
||||
// UpdateContainer ...
|
||||
|
|
@ -80,13 +298,35 @@ func (i *LibpodAPI) RenameContainer(call ioprojectatomicpodman.VarlinkCall) erro
|
|||
}
|
||||
|
||||
// PauseContainer ...
|
||||
func (i *LibpodAPI) PauseContainer(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("PauseContainer")
|
||||
func (i *LibpodAPI) PauseContainer(call ioprojectatomicpodman.VarlinkCall, name string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
if err := ctr.Pause(); err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyPauseContainer(ctr.ID())
|
||||
}
|
||||
|
||||
// UnpauseContainer ...
|
||||
func (i *LibpodAPI) UnpauseContainer(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("UnpauseContainer")
|
||||
func (i *LibpodAPI) UnpauseContainer(call ioprojectatomicpodman.VarlinkCall, name string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
if err := ctr.Unpause(); err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyUnpauseContainer(ctr.ID())
|
||||
}
|
||||
|
||||
// AttachToContainer ...
|
||||
|
|
@ -96,16 +336,62 @@ func (i *LibpodAPI) AttachToContainer(call ioprojectatomicpodman.VarlinkCall) er
|
|||
}
|
||||
|
||||
// WaitContainer ...
|
||||
func (i *LibpodAPI) WaitContainer(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("WaitContainer")
|
||||
func (i *LibpodAPI) WaitContainer(call ioprojectatomicpodman.VarlinkCall, name string) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
exitCode, err := ctr.Wait()
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyWaitContainer(int64(exitCode))
|
||||
|
||||
}
|
||||
|
||||
// RemoveContainer ...
|
||||
func (i *LibpodAPI) RemoveContainer(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("RemoveContainer")
|
||||
func (i *LibpodAPI) RemoveContainer(call ioprojectatomicpodman.VarlinkCall, name string, force bool) error {
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
ctr, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
return call.ReplyContainerNotFound(name)
|
||||
}
|
||||
if err := runtime.RemoveContainer(ctr, force); err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
return call.ReplyRemoveContainer(ctr.ID())
|
||||
|
||||
}
|
||||
|
||||
// DeleteStoppedContainers ...
|
||||
func (i *LibpodAPI) DeleteStoppedContainers(call ioprojectatomicpodman.VarlinkCall) error {
|
||||
return call.ReplyMethodNotImplemented("DeleteContainer")
|
||||
var deletedContainers []string
|
||||
runtime, err := libpodruntime.GetRuntime(i.Cli)
|
||||
if err != nil {
|
||||
return call.ReplyRuntimeError(err.Error())
|
||||
}
|
||||
containers, err := runtime.GetAllContainers()
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
for _, ctr := range containers {
|
||||
state, err := ctr.State()
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
if state != libpod.ContainerStateRunning {
|
||||
if err := runtime.RemoveContainer(ctr, false); err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
deletedContainers = append(deletedContainers, ctr.ID())
|
||||
}
|
||||
}
|
||||
return call.ReplyDeleteStoppedContainers(deletedContainers)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,76 @@ package varlinkapi
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/projectatomic/libpod/cmd/podman/batchcontainer"
|
||||
"github.com/projectatomic/libpod/cmd/podman/varlink"
|
||||
"github.com/projectatomic/libpod/libpod"
|
||||
)
|
||||
|
||||
// getContext returns a non-nil, empty context
|
||||
func getContext() context.Context {
|
||||
return context.TODO()
|
||||
}
|
||||
|
||||
func makeListContainer(containerID string, batchInfo batchcontainer.BatchContainerStruct) ioprojectatomicpodman.ListContainerData {
|
||||
var (
|
||||
mounts []ioprojectatomicpodman.ContainerMount
|
||||
ports []ioprojectatomicpodman.ContainerPortMappings
|
||||
)
|
||||
ns := batchcontainer.GetNamespaces(batchInfo.Pid)
|
||||
|
||||
for _, mount := range batchInfo.ConConfig.Spec.Mounts {
|
||||
m := ioprojectatomicpodman.ContainerMount{
|
||||
Destination: mount.Destination,
|
||||
Type: mount.Type,
|
||||
Source: mount.Source,
|
||||
Options: mount.Options,
|
||||
}
|
||||
mounts = append(mounts, m)
|
||||
}
|
||||
|
||||
for _, pm := range batchInfo.ConConfig.PortMappings {
|
||||
p := ioprojectatomicpodman.ContainerPortMappings{
|
||||
Host_port: strconv.Itoa(int(pm.HostPort)),
|
||||
Host_ip: pm.HostIP,
|
||||
Protocol: pm.Protocol,
|
||||
Container_port: strconv.Itoa(int(pm.ContainerPort)),
|
||||
}
|
||||
ports = append(ports, p)
|
||||
|
||||
}
|
||||
|
||||
// If we find this needs to be done for other container endpoints, we should
|
||||
// convert this to a separate function or a generic map from struct function.
|
||||
namespace := ioprojectatomicpodman.ContainerNameSpace{
|
||||
User: ns.User,
|
||||
Uts: ns.UTS,
|
||||
Pidns: ns.PIDNS,
|
||||
Pid: ns.PID,
|
||||
Cgroup: ns.Cgroup,
|
||||
Net: ns.NET,
|
||||
Mnt: ns.MNT,
|
||||
Ipc: ns.IPC,
|
||||
}
|
||||
|
||||
lc := ioprojectatomicpodman.ListContainerData{
|
||||
Id: containerID,
|
||||
Image: batchInfo.ConConfig.RootfsImageName,
|
||||
Imageid: batchInfo.ConConfig.RootfsImageID,
|
||||
Command: batchInfo.ConConfig.Spec.Process.Args,
|
||||
Createdat: batchInfo.ConConfig.CreatedTime.String(),
|
||||
Runningfor: time.Since(batchInfo.ConConfig.CreatedTime).String(),
|
||||
Status: batchInfo.ConState.String(),
|
||||
Ports: ports,
|
||||
Rootfssize: batchInfo.RootFsSize,
|
||||
Rwsize: batchInfo.RwSize,
|
||||
Names: batchInfo.ConConfig.Name,
|
||||
Labels: batchInfo.ConConfig.Labels,
|
||||
Mounts: mounts,
|
||||
Containerrunning: batchInfo.ConState == libpod.ContainerStateRunning,
|
||||
Namespaces: namespace,
|
||||
}
|
||||
return lc
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue