mirror of
https://github.com/containers/podman.git
synced 2025-05-17 15:18:43 +08:00
refactor top code
Move the top logic from pkg/adapter into the (*libpod.Container).Top(). This way, we drop the dependency from pkg/api on pkg/adapters and have a clearer separation of concerns. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
@ -3,6 +3,8 @@
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -10,6 +12,7 @@ import (
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/containers/psgo"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Top gathers statistics about the running processes in a container. It returns a
|
||||
@ -36,7 +39,34 @@ func (c *Container) Top(descriptors []string) ([]string, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return c.GetContainerPidInformation(psgoDescriptors)
|
||||
|
||||
// If we encountered an ErrUnknownDescriptor error, fallback to executing
|
||||
// ps(1). This ensures backwards compatibility to users depending on ps(1)
|
||||
// and makes sure we're ~compatible with docker.
|
||||
output, psgoErr := c.GetContainerPidInformation(psgoDescriptors)
|
||||
if psgoErr == nil {
|
||||
return output, nil
|
||||
}
|
||||
if errors.Cause(psgoErr) != psgo.ErrUnknownDescriptor {
|
||||
return nil, psgoErr
|
||||
}
|
||||
|
||||
output, err = c.execPS(descriptors)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error executing ps(1) in the container")
|
||||
}
|
||||
|
||||
// Trick: filter the ps command from the output instead of
|
||||
// checking/requiring PIDs in the output.
|
||||
filtered := []string{}
|
||||
cmd := strings.Join(descriptors, " ")
|
||||
for _, line := range output {
|
||||
if !strings.Contains(line, cmd) {
|
||||
filtered = append(filtered, line)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
// GetContainerPidInformation returns process-related data of all processes in
|
||||
@ -65,3 +95,59 @@ func (c *Container) GetContainerPidInformation(descriptors []string) ([]string,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// execPS executes ps(1) with the specified args in the container.
|
||||
func (c *Container) execPS(args []string) ([]string, error) {
|
||||
rPipe, wPipe, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer wPipe.Close()
|
||||
defer rPipe.Close()
|
||||
|
||||
rErrPipe, wErrPipe, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer wErrPipe.Close()
|
||||
defer rErrPipe.Close()
|
||||
|
||||
streams := new(AttachStreams)
|
||||
streams.OutputStream = wPipe
|
||||
streams.ErrorStream = wErrPipe
|
||||
streams.AttachOutput = true
|
||||
streams.AttachError = true
|
||||
|
||||
stdout := []string{}
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(rPipe)
|
||||
for scanner.Scan() {
|
||||
stdout = append(stdout, scanner.Text())
|
||||
}
|
||||
}()
|
||||
stderr := []string{}
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(rErrPipe)
|
||||
for scanner.Scan() {
|
||||
stderr = append(stderr, scanner.Text())
|
||||
}
|
||||
}()
|
||||
|
||||
cmd := append([]string{"ps"}, args...)
|
||||
ec, err := c.Exec(false, false, map[string]string{}, cmd, "", "", streams, 0, nil, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if ec != 0 {
|
||||
return nil, errors.Errorf("Runtime failed with exit status: %d and output: %s", ec, strings.Join(stderr, " "))
|
||||
}
|
||||
|
||||
if logrus.GetLevel() >= logrus.DebugLevel {
|
||||
// If we're running in debug mode or higher, we might want to have a
|
||||
// look at stderr which includes debug logs from conmon.
|
||||
for _, log := range stderr {
|
||||
logrus.Debugf("%s", log)
|
||||
}
|
||||
}
|
||||
|
||||
return stdout, nil
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import (
|
||||
"github.com/containers/libpod/libpod/logs"
|
||||
"github.com/containers/libpod/pkg/adapter/shortcuts"
|
||||
"github.com/containers/libpod/pkg/systemdgen"
|
||||
"github.com/containers/psgo"
|
||||
"github.com/containers/storage"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -913,89 +912,7 @@ func (r *LocalRuntime) Top(cli *cliconfig.TopValues) ([]string, error) {
|
||||
return nil, errors.Wrapf(err, "unable to lookup requested container")
|
||||
}
|
||||
|
||||
output, psgoErr := container.Top(descriptors)
|
||||
if psgoErr == nil {
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// If we encountered an ErrUnknownDescriptor error, fallback to executing
|
||||
// ps(1). This ensures backwards compatibility to users depending on ps(1)
|
||||
// and makes sure we're ~compatible with docker.
|
||||
if errors.Cause(psgoErr) != psgo.ErrUnknownDescriptor {
|
||||
return nil, psgoErr
|
||||
}
|
||||
|
||||
output, err = r.execPS(container, descriptors)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error executing ps(1) in the container")
|
||||
}
|
||||
|
||||
// Trick: filter the ps command from the output instead of
|
||||
// checking/requiring PIDs in the output.
|
||||
filtered := []string{}
|
||||
cmd := strings.Join(descriptors, " ")
|
||||
for _, line := range output {
|
||||
if !strings.Contains(line, cmd) {
|
||||
filtered = append(filtered, line)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
func (r *LocalRuntime) execPS(c *libpod.Container, args []string) ([]string, error) {
|
||||
rPipe, wPipe, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer wPipe.Close()
|
||||
defer rPipe.Close()
|
||||
|
||||
rErrPipe, wErrPipe, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer wErrPipe.Close()
|
||||
defer rErrPipe.Close()
|
||||
|
||||
streams := new(libpod.AttachStreams)
|
||||
streams.OutputStream = wPipe
|
||||
streams.ErrorStream = wErrPipe
|
||||
streams.AttachOutput = true
|
||||
streams.AttachError = true
|
||||
|
||||
stdout := []string{}
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(rPipe)
|
||||
for scanner.Scan() {
|
||||
stdout = append(stdout, scanner.Text())
|
||||
}
|
||||
}()
|
||||
stderr := []string{}
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(rErrPipe)
|
||||
for scanner.Scan() {
|
||||
stderr = append(stderr, scanner.Text())
|
||||
}
|
||||
}()
|
||||
|
||||
cmd := append([]string{"ps"}, args...)
|
||||
ec, err := c.Exec(false, false, map[string]string{}, cmd, "", "", streams, 0, nil, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if ec != 0 {
|
||||
return nil, errors.Errorf("Runtime failed with exit status: %d and output: %s", ec, strings.Join(stderr, " "))
|
||||
}
|
||||
|
||||
if logrus.GetLevel() >= logrus.DebugLevel {
|
||||
// If we're running in debug mode or higher, we might want to have a
|
||||
// look at stderr which includes debug logs from conmon.
|
||||
for _, log := range stderr {
|
||||
logrus.Debugf("%s", log)
|
||||
}
|
||||
}
|
||||
|
||||
return stdout, nil
|
||||
return container.Top(descriptors)
|
||||
}
|
||||
|
||||
// ExecContainer executes a command in the container
|
||||
|
@ -4,10 +4,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/adapter"
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/schema"
|
||||
@ -30,19 +27,14 @@ func TopContainer(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
name := mux.Vars(r)["name"]
|
||||
|
||||
adapterRuntime := adapter.LocalRuntime{}
|
||||
adapterRuntime.Runtime = runtime
|
||||
|
||||
topValues := cliconfig.TopValues{}
|
||||
topValues.InputArgs = []string{name, query.PsArgs}
|
||||
|
||||
output, err := adapterRuntime.Top(&topValues)
|
||||
c, err := runtime.LookupContainer(name)
|
||||
if err != nil {
|
||||
utils.ContainerNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
|
||||
output, err := c.Top([]string{query.PsArgs})
|
||||
if err != nil {
|
||||
if errors.Cause(err) == define.ErrNoSuchCtr {
|
||||
utils.ContainerNotFound(w, name, err)
|
||||
return
|
||||
}
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user