mirror of
https://github.com/containers/podman.git
synced 2025-05-21 00:56:36 +08:00

filter flag helps to filter the containers based on labels, until(time), name, etc for prune command. Signed-off-by: Kunal Kushwaha <kunal.kushwaha@gmail.com>
1342 lines
37 KiB
Go
1342 lines
37 KiB
Go
// +build !remoteclient
|
|
|
|
package adapter
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/containers/buildah"
|
|
"github.com/containers/image/v5/manifest"
|
|
"github.com/containers/libpod/cmd/podman/cliconfig"
|
|
"github.com/containers/libpod/cmd/podman/shared"
|
|
"github.com/containers/libpod/cmd/podman/shared/parse"
|
|
"github.com/containers/libpod/libpod"
|
|
"github.com/containers/libpod/libpod/define"
|
|
"github.com/containers/libpod/libpod/events"
|
|
"github.com/containers/libpod/libpod/image"
|
|
"github.com/containers/libpod/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"
|
|
)
|
|
|
|
// GetLatestContainer gets the latest Container and wraps it in an adapter Container
|
|
func (r *LocalRuntime) GetLatestContainer() (*Container, error) {
|
|
Container := Container{}
|
|
c, err := r.Runtime.GetLatestContainer()
|
|
Container.Container = c
|
|
return &Container, err
|
|
}
|
|
|
|
// GetAllContainers gets all Containers and wraps each one in an adapter Container
|
|
func (r *LocalRuntime) GetAllContainers() ([]*Container, error) {
|
|
var containers []*Container
|
|
allContainers, err := r.Runtime.GetAllContainers()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, c := range allContainers {
|
|
containers = append(containers, &Container{c})
|
|
}
|
|
return containers, nil
|
|
}
|
|
|
|
// LookupContainer gets a Container by name or id and wraps it in an adapter Container
|
|
func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) {
|
|
ctr, err := r.Runtime.LookupContainer(idOrName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Container{ctr}, nil
|
|
}
|
|
|
|
// StopContainers stops container(s) based on CLI inputs.
|
|
// Returns list of successful id(s), map of failed id(s) + error, or error not from container
|
|
func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopValues) ([]string, map[string]error, error) {
|
|
var timeout *uint
|
|
if cli.Flags().Changed("timeout") || cli.Flags().Changed("time") {
|
|
t := cli.Timeout
|
|
timeout = &t
|
|
}
|
|
|
|
maxWorkers := shared.DefaultPoolSize("stop")
|
|
if cli.GlobalIsSet("max-workers") {
|
|
maxWorkers = cli.GlobalFlags.MaxWorks
|
|
}
|
|
logrus.Debugf("Setting maximum stop workers to %d", maxWorkers)
|
|
|
|
names := cli.InputArgs
|
|
for _, cidFile := range cli.CIDFiles {
|
|
content, err := ioutil.ReadFile(cidFile)
|
|
if err != nil {
|
|
return nil, nil, errors.Wrap(err, "error reading CIDFile")
|
|
}
|
|
id := strings.Split(string(content), "\n")[0]
|
|
names = append(names, id)
|
|
}
|
|
|
|
ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, names, r.Runtime)
|
|
if err != nil && !(cli.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
|
|
return nil, nil, err
|
|
}
|
|
|
|
pool := shared.NewPool("stop", maxWorkers, len(ctrs))
|
|
for _, c := range ctrs {
|
|
c := c
|
|
|
|
if timeout == nil {
|
|
t := c.StopTimeout()
|
|
timeout = &t
|
|
logrus.Debugf("Set timeout to container %s default (%d)", c.ID(), *timeout)
|
|
}
|
|
|
|
pool.Add(shared.Job{
|
|
ID: c.ID(),
|
|
Fn: func() error {
|
|
err := c.StopWithTimeout(*timeout)
|
|
if err != nil {
|
|
if errors.Cause(err) == define.ErrCtrStopped {
|
|
logrus.Debugf("Container %s is already stopped", c.ID())
|
|
return nil
|
|
} else if cli.All && errors.Cause(err) == define.ErrCtrStateInvalid {
|
|
logrus.Debugf("Container %s is not running, could not stop", c.ID())
|
|
return nil
|
|
}
|
|
logrus.Debugf("Failed to stop container %s: %s", c.ID(), err.Error())
|
|
}
|
|
return err
|
|
},
|
|
})
|
|
}
|
|
return pool.Run()
|
|
}
|
|
|
|
// KillContainers sends signal to container(s) based on CLI inputs.
|
|
// Returns list of successful id(s), map of failed id(s) + error, or error not from container
|
|
func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillValues, signal syscall.Signal) ([]string, map[string]error, error) {
|
|
maxWorkers := shared.DefaultPoolSize("kill")
|
|
if cli.GlobalIsSet("max-workers") {
|
|
maxWorkers = cli.GlobalFlags.MaxWorks
|
|
}
|
|
logrus.Debugf("Setting maximum kill workers to %d", maxWorkers)
|
|
|
|
ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
pool := shared.NewPool("kill", maxWorkers, len(ctrs))
|
|
for _, c := range ctrs {
|
|
c := c
|
|
|
|
pool.Add(shared.Job{
|
|
ID: c.ID(),
|
|
Fn: func() error {
|
|
return c.Kill(uint(signal))
|
|
},
|
|
})
|
|
}
|
|
return pool.Run()
|
|
}
|
|
|
|
// InitContainers initializes container(s) based on CLI inputs.
|
|
// Returns list of successful id(s), map of failed id(s) to errors, or a general
|
|
// error not from the container.
|
|
func (r *LocalRuntime) InitContainers(ctx context.Context, cli *cliconfig.InitValues) ([]string, map[string]error, error) {
|
|
maxWorkers := shared.DefaultPoolSize("init")
|
|
if cli.GlobalIsSet("max-workers") {
|
|
maxWorkers = cli.GlobalFlags.MaxWorks
|
|
}
|
|
logrus.Debugf("Setting maximum init workers to %d", maxWorkers)
|
|
|
|
ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
pool := shared.NewPool("init", maxWorkers, len(ctrs))
|
|
for _, c := range ctrs {
|
|
ctr := c
|
|
|
|
pool.Add(shared.Job{
|
|
ID: ctr.ID(),
|
|
Fn: func() error {
|
|
err := ctr.Init(ctx)
|
|
if err != nil {
|
|
// If we're initializing all containers, ignore invalid state errors
|
|
if cli.All && errors.Cause(err) == define.ErrCtrStateInvalid {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
})
|
|
}
|
|
return pool.Run()
|
|
}
|
|
|
|
// RemoveContainers removes container(s) based on CLI inputs.
|
|
func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmValues) ([]string, map[string]error, error) {
|
|
var (
|
|
ok = []string{}
|
|
failures = map[string]error{}
|
|
)
|
|
|
|
maxWorkers := shared.DefaultPoolSize("rm")
|
|
if cli.GlobalIsSet("max-workers") {
|
|
maxWorkers = cli.GlobalFlags.MaxWorks
|
|
}
|
|
logrus.Debugf("Setting maximum rm workers to %d", maxWorkers)
|
|
|
|
if cli.Storage {
|
|
for _, ctr := range cli.InputArgs {
|
|
if err := r.RemoveStorageContainer(ctr, cli.Force); err != nil {
|
|
failures[ctr] = err
|
|
}
|
|
ok = append(ok, ctr)
|
|
}
|
|
return ok, failures, nil
|
|
}
|
|
|
|
names := cli.InputArgs
|
|
for _, cidFile := range cli.CIDFiles {
|
|
content, err := ioutil.ReadFile(cidFile)
|
|
if err != nil {
|
|
return nil, nil, errors.Wrap(err, "error reading CIDFile")
|
|
}
|
|
id := strings.Split(string(content), "\n")[0]
|
|
names = append(names, id)
|
|
}
|
|
|
|
ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, names, r.Runtime)
|
|
if err != nil && !(cli.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) {
|
|
// Failed to get containers. If force is specified, get the containers ID
|
|
// and evict them
|
|
if !cli.Force {
|
|
return ok, failures, err
|
|
}
|
|
|
|
for _, ctr := range cli.InputArgs {
|
|
logrus.Debugf("Evicting container %q", ctr)
|
|
id, err := r.EvictContainer(ctx, ctr, cli.Volumes)
|
|
if err != nil {
|
|
if cli.Ignore && errors.Cause(err) == define.ErrNoSuchCtr {
|
|
logrus.Debugf("Ignoring error (--allow-missing): %v", err)
|
|
continue
|
|
}
|
|
failures[ctr] = errors.Wrapf(err, "Failed to evict container: %q", id)
|
|
continue
|
|
}
|
|
ok = append(ok, id)
|
|
}
|
|
return ok, failures, nil
|
|
}
|
|
|
|
pool := shared.NewPool("rm", maxWorkers, len(ctrs))
|
|
for _, c := range ctrs {
|
|
c := c
|
|
|
|
pool.Add(shared.Job{
|
|
ID: c.ID(),
|
|
Fn: func() error {
|
|
err := r.RemoveContainer(ctx, c, cli.Force, cli.Volumes)
|
|
if err != nil {
|
|
if cli.Ignore && errors.Cause(err) == define.ErrNoSuchCtr {
|
|
logrus.Debugf("Ignoring error (--allow-missing): %v", err)
|
|
return nil
|
|
}
|
|
logrus.Debugf("Failed to remove container %s: %s", c.ID(), err.Error())
|
|
}
|
|
return err
|
|
},
|
|
})
|
|
}
|
|
return pool.Run()
|
|
}
|
|
|
|
// UmountRootFilesystems removes container(s) based on CLI inputs.
|
|
func (r *LocalRuntime) UmountRootFilesystems(ctx context.Context, cli *cliconfig.UmountValues) ([]string, map[string]error, error) {
|
|
var (
|
|
ok = []string{}
|
|
failures = map[string]error{}
|
|
)
|
|
|
|
ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
|
|
if err != nil {
|
|
return ok, failures, err
|
|
}
|
|
|
|
for _, ctr := range ctrs {
|
|
state, err := ctr.State()
|
|
if err != nil {
|
|
logrus.Debugf("Error umounting container %s state: %s", ctr.ID(), err.Error())
|
|
continue
|
|
}
|
|
if state == define.ContainerStateRunning {
|
|
logrus.Debugf("Error umounting container %s, is running", ctr.ID())
|
|
continue
|
|
}
|
|
|
|
if err := ctr.Unmount(cli.Force); err != nil {
|
|
if cli.All && errors.Cause(err) == storage.ErrLayerNotMounted {
|
|
logrus.Debugf("Error umounting container %s, storage.ErrLayerNotMounted", ctr.ID())
|
|
continue
|
|
}
|
|
failures[ctr.ID()] = errors.Wrapf(err, "error unmounting container %s", ctr.ID())
|
|
} else {
|
|
ok = append(ok, ctr.ID())
|
|
}
|
|
}
|
|
return ok, failures, nil
|
|
}
|
|
|
|
// WaitOnContainers waits for all given container(s) to stop
|
|
func (r *LocalRuntime) WaitOnContainers(ctx context.Context, cli *cliconfig.WaitValues, interval time.Duration) ([]string, map[string]error, error) {
|
|
var (
|
|
ok = []string{}
|
|
failures = map[string]error{}
|
|
)
|
|
|
|
ctrs, err := shortcuts.GetContainersByContext(false, cli.Latest, cli.InputArgs, r.Runtime)
|
|
if err != nil {
|
|
return ok, failures, err
|
|
}
|
|
|
|
for _, c := range ctrs {
|
|
if returnCode, err := c.WaitWithInterval(interval); err == nil {
|
|
ok = append(ok, strconv.Itoa(int(returnCode)))
|
|
} else {
|
|
failures[c.ID()] = err
|
|
}
|
|
}
|
|
return ok, failures, err
|
|
}
|
|
|
|
// Log logs one or more containers
|
|
func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *logs.LogOptions) error {
|
|
|
|
var wg sync.WaitGroup
|
|
options.WaitGroup = &wg
|
|
if len(c.InputArgs) > 1 {
|
|
options.Multi = true
|
|
}
|
|
tailLen := int(c.Tail)
|
|
if tailLen < 0 {
|
|
tailLen = 0
|
|
}
|
|
logChannel := make(chan *logs.LogLine, tailLen*len(c.InputArgs)+1)
|
|
containers, err := shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := r.Runtime.Log(containers, options, logChannel); err != nil {
|
|
return err
|
|
}
|
|
go func() {
|
|
wg.Wait()
|
|
close(logChannel)
|
|
}()
|
|
for line := range logChannel {
|
|
fmt.Println(line.String(options))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CreateContainer creates a libpod container
|
|
func (r *LocalRuntime) CreateContainer(ctx context.Context, c *cliconfig.CreateValues) (string, error) {
|
|
results := shared.NewIntermediateLayer(&c.PodmanCommand, false)
|
|
ctr, _, err := shared.CreateContainer(ctx, &results, r.Runtime)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return ctr.ID(), nil
|
|
}
|
|
|
|
// Run a libpod container
|
|
func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode int) (int, error) {
|
|
results := shared.NewIntermediateLayer(&c.PodmanCommand, false)
|
|
|
|
ctr, createConfig, err := shared.CreateContainer(ctx, &results, r.Runtime)
|
|
if err != nil {
|
|
return exitCode, err
|
|
}
|
|
|
|
if logrus.GetLevel() == logrus.DebugLevel {
|
|
cgroupPath, err := ctr.CGroupPath()
|
|
if err == nil {
|
|
logrus.Debugf("container %q has CgroupParent %q", ctr.ID(), cgroupPath)
|
|
}
|
|
}
|
|
|
|
// Handle detached start
|
|
if createConfig.Detach {
|
|
// if the container was created as part of a pod, also start its dependencies, if any.
|
|
if err := ctr.Start(ctx, c.IsSet("pod")); err != nil {
|
|
// This means the command did not exist
|
|
return define.ExitCode(err), err
|
|
}
|
|
|
|
fmt.Printf("%s\n", ctr.ID())
|
|
exitCode = 0
|
|
return exitCode, nil
|
|
}
|
|
|
|
outputStream := os.Stdout
|
|
errorStream := os.Stderr
|
|
inputStream := os.Stdin
|
|
|
|
// If -i is not set, clear stdin
|
|
if !c.Bool("interactive") {
|
|
inputStream = nil
|
|
}
|
|
|
|
// If attach is set, clear stdin/stdout/stderr and only attach requested
|
|
if c.IsSet("attach") || c.IsSet("a") {
|
|
outputStream = nil
|
|
errorStream = nil
|
|
if !c.Bool("interactive") {
|
|
inputStream = nil
|
|
}
|
|
|
|
attachTo := c.StringSlice("attach")
|
|
for _, stream := range attachTo {
|
|
switch strings.ToLower(stream) {
|
|
case "stdout":
|
|
outputStream = os.Stdout
|
|
case "stderr":
|
|
errorStream = os.Stderr
|
|
case "stdin":
|
|
inputStream = os.Stdin
|
|
default:
|
|
return exitCode, errors.Wrapf(define.ErrInvalidArg, "invalid stream %q for --attach - must be one of stdin, stdout, or stderr", stream)
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the container was created as part of a pod, also start its dependencies, if any.
|
|
if err := StartAttachCtr(ctx, ctr, outputStream, errorStream, inputStream, c.String("detach-keys"), c.Bool("sig-proxy"), true, c.IsSet("pod")); err != nil {
|
|
// We've manually detached from the container
|
|
// Do not perform cleanup, or wait for container exit code
|
|
// Just exit immediately
|
|
if errors.Cause(err) == define.ErrDetach {
|
|
return 0, nil
|
|
}
|
|
if c.IsSet("rm") {
|
|
if deleteError := r.Runtime.RemoveContainer(ctx, ctr, true, false); deleteError != nil {
|
|
logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID())
|
|
}
|
|
}
|
|
return define.ExitCode(err), err
|
|
}
|
|
|
|
if ecode, err := ctr.Wait(); err != nil {
|
|
if errors.Cause(err) == define.ErrNoSuchCtr {
|
|
// Check events
|
|
event, err := r.Runtime.GetLastContainerEvent(ctr.ID(), events.Exited)
|
|
if err != nil {
|
|
logrus.Errorf("Cannot get exit code: %v", err)
|
|
exitCode = define.ExecErrorCodeNotFound
|
|
} else {
|
|
exitCode = event.ContainerExitCode
|
|
}
|
|
}
|
|
} else {
|
|
exitCode = int(ecode)
|
|
}
|
|
|
|
if c.IsSet("rm") {
|
|
if err := r.Runtime.RemoveContainer(ctx, ctr, false, true); err != nil {
|
|
if errors.Cause(err) == define.ErrNoSuchCtr {
|
|
logrus.Warnf("Container %s does not exist: %v", ctr.ID(), err)
|
|
} else {
|
|
logrus.Errorf("Error removing container %s: %v", ctr.ID(), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return exitCode, nil
|
|
}
|
|
|
|
// Ps ...
|
|
func (r *LocalRuntime) Ps(c *cliconfig.PsValues, opts shared.PsOptions) ([]shared.PsContainerOutput, error) {
|
|
maxWorkers := shared.Parallelize("ps")
|
|
if c.GlobalIsSet("max-workers") {
|
|
maxWorkers = c.GlobalFlags.MaxWorks
|
|
}
|
|
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
|
return shared.GetPsContainerOutput(r.Runtime, opts, c.Filter, maxWorkers)
|
|
}
|
|
|
|
// Attach ...
|
|
func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) error {
|
|
var (
|
|
ctr *libpod.Container
|
|
err error
|
|
)
|
|
|
|
if c.Latest {
|
|
ctr, err = r.Runtime.GetLatestContainer()
|
|
} else {
|
|
ctr, err = r.Runtime.LookupContainer(c.InputArgs[0])
|
|
}
|
|
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to exec into %s", c.InputArgs[0])
|
|
}
|
|
|
|
conState, err := ctr.State()
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to determine state of %s", ctr.ID())
|
|
}
|
|
if conState != define.ContainerStateRunning {
|
|
return errors.Errorf("you can only attach to running containers")
|
|
}
|
|
|
|
inputStream := os.Stdin
|
|
if c.NoStdin {
|
|
inputStream = nil
|
|
}
|
|
// If the container is in a pod, also set to recursively start dependencies
|
|
if err := StartAttachCtr(ctx, ctr, os.Stdout, os.Stderr, inputStream, c.DetachKeys, c.SigProxy, false, ctr.PodID() != ""); err != nil && errors.Cause(err) != define.ErrDetach {
|
|
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Checkpoint one or more containers
|
|
func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues) error {
|
|
var (
|
|
containers []*libpod.Container
|
|
err, lastError error
|
|
)
|
|
|
|
options := libpod.ContainerCheckpointOptions{
|
|
Keep: c.Keep,
|
|
KeepRunning: c.LeaveRunning,
|
|
TCPEstablished: c.TcpEstablished,
|
|
TargetFile: c.Export,
|
|
IgnoreRootfs: c.IgnoreRootfs,
|
|
}
|
|
if c.Export == "" && c.IgnoreRootfs {
|
|
return errors.Errorf("--ignore-rootfs can only be used with --export")
|
|
}
|
|
if c.All {
|
|
containers, err = r.Runtime.GetRunningContainers()
|
|
} else {
|
|
containers, err = shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, ctr := range containers {
|
|
if err = ctr.Checkpoint(context.TODO(), options); err != nil {
|
|
if lastError != nil {
|
|
fmt.Fprintln(os.Stderr, lastError)
|
|
}
|
|
lastError = errors.Wrapf(err, "failed to checkpoint container %v", ctr.ID())
|
|
} else {
|
|
fmt.Println(ctr.ID())
|
|
}
|
|
}
|
|
return lastError
|
|
}
|
|
|
|
// Restore one or more containers
|
|
func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues) error {
|
|
var (
|
|
containers []*libpod.Container
|
|
err, lastError error
|
|
filterFuncs []libpod.ContainerFilter
|
|
)
|
|
|
|
options := libpod.ContainerCheckpointOptions{
|
|
Keep: c.Keep,
|
|
TCPEstablished: c.TcpEstablished,
|
|
TargetFile: c.Import,
|
|
Name: c.Name,
|
|
IgnoreRootfs: c.IgnoreRootfs,
|
|
IgnoreStaticIP: c.IgnoreStaticIP,
|
|
IgnoreStaticMAC: c.IgnoreStaticMAC,
|
|
}
|
|
|
|
filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
|
|
state, _ := c.State()
|
|
return state == define.ContainerStateExited
|
|
})
|
|
|
|
if c.Import != "" {
|
|
containers, err = crImportCheckpoint(ctx, r.Runtime, c.Import, c.Name)
|
|
} else if c.All {
|
|
containers, err = r.GetContainers(filterFuncs...)
|
|
} else {
|
|
containers, err = shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, ctr := range containers {
|
|
if err = ctr.Restore(context.TODO(), options); err != nil {
|
|
if lastError != nil {
|
|
fmt.Fprintln(os.Stderr, lastError)
|
|
}
|
|
lastError = errors.Wrapf(err, "failed to restore container %v", ctr.ID())
|
|
} else {
|
|
fmt.Println(ctr.ID())
|
|
}
|
|
}
|
|
return lastError
|
|
}
|
|
|
|
// Start will start a container
|
|
func (r *LocalRuntime) Start(ctx context.Context, c *cliconfig.StartValues, sigProxy bool) (int, error) {
|
|
var (
|
|
exitCode = define.ExecErrorCodeGeneric
|
|
lastError error
|
|
)
|
|
|
|
args := c.InputArgs
|
|
if c.Latest {
|
|
lastCtr, err := r.GetLatestContainer()
|
|
if err != nil {
|
|
return 0, errors.Wrapf(err, "unable to get latest container")
|
|
}
|
|
args = append(args, lastCtr.ID())
|
|
}
|
|
|
|
for _, container := range args {
|
|
ctr, err := r.LookupContainer(container)
|
|
if err != nil {
|
|
if lastError != nil {
|
|
fmt.Fprintln(os.Stderr, lastError)
|
|
}
|
|
lastError = errors.Wrapf(err, "unable to find container %s", container)
|
|
continue
|
|
}
|
|
|
|
ctrState, err := ctr.State()
|
|
if err != nil {
|
|
return exitCode, errors.Wrapf(err, "unable to get container state")
|
|
}
|
|
|
|
ctrRunning := ctrState == define.ContainerStateRunning
|
|
|
|
if c.Attach {
|
|
inputStream := os.Stdin
|
|
if !c.Interactive {
|
|
if !ctr.Stdin() {
|
|
inputStream = nil
|
|
}
|
|
}
|
|
|
|
// attach to the container and also start it not already running
|
|
// If the container is in a pod, also set to recursively start dependencies
|
|
err = StartAttachCtr(ctx, ctr.Container, os.Stdout, os.Stderr, inputStream, c.DetachKeys, sigProxy, !ctrRunning, ctr.PodID() != "")
|
|
if errors.Cause(err) == define.ErrDetach {
|
|
// User manually detached
|
|
// Exit cleanly immediately
|
|
exitCode = 0
|
|
return exitCode, nil
|
|
}
|
|
|
|
if ctrRunning {
|
|
return 0, err
|
|
}
|
|
|
|
if err != nil {
|
|
return exitCode, errors.Wrapf(err, "unable to start container %s", ctr.ID())
|
|
}
|
|
|
|
if ecode, err := ctr.Wait(); err != nil {
|
|
if errors.Cause(err) == define.ErrNoSuchCtr {
|
|
// Check events
|
|
event, err := r.Runtime.GetLastContainerEvent(ctr.ID(), events.Exited)
|
|
if err != nil {
|
|
logrus.Errorf("Cannot get exit code: %v", err)
|
|
exitCode = define.ExecErrorCodeNotFound
|
|
} else {
|
|
exitCode = event.ContainerExitCode
|
|
}
|
|
}
|
|
} else {
|
|
exitCode = int(ecode)
|
|
}
|
|
|
|
return exitCode, nil
|
|
}
|
|
// Start the container if it's not running already.
|
|
if !ctrRunning {
|
|
// Handle non-attach start
|
|
// If the container is in a pod, also set to recursively start dependencies
|
|
if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil {
|
|
if lastError != nil {
|
|
fmt.Fprintln(os.Stderr, lastError)
|
|
}
|
|
lastError = errors.Wrapf(err, "unable to start container %q", container)
|
|
continue
|
|
}
|
|
}
|
|
// Check if the container is referenced by ID or by name and print
|
|
// it accordingly.
|
|
if strings.HasPrefix(ctr.ID(), container) {
|
|
fmt.Println(ctr.ID())
|
|
} else {
|
|
fmt.Println(container)
|
|
}
|
|
}
|
|
return exitCode, lastError
|
|
}
|
|
|
|
// PauseContainers removes container(s) based on CLI inputs.
|
|
func (r *LocalRuntime) PauseContainers(ctx context.Context, cli *cliconfig.PauseValues) ([]string, map[string]error, error) {
|
|
var (
|
|
ok = []string{}
|
|
failures = map[string]error{}
|
|
ctrs []*libpod.Container
|
|
err error
|
|
)
|
|
|
|
maxWorkers := shared.DefaultPoolSize("pause")
|
|
if cli.GlobalIsSet("max-workers") {
|
|
maxWorkers = cli.GlobalFlags.MaxWorks
|
|
}
|
|
logrus.Debugf("Setting maximum rm workers to %d", maxWorkers)
|
|
|
|
if cli.All {
|
|
ctrs, err = r.GetRunningContainers()
|
|
} else {
|
|
ctrs, err = shortcuts.GetContainersByContext(false, false, cli.InputArgs, r.Runtime)
|
|
}
|
|
if err != nil {
|
|
return ok, failures, err
|
|
}
|
|
|
|
pool := shared.NewPool("pause", maxWorkers, len(ctrs))
|
|
for _, c := range ctrs {
|
|
ctr := c
|
|
pool.Add(shared.Job{
|
|
ID: ctr.ID(),
|
|
Fn: func() error {
|
|
err := ctr.Pause()
|
|
if err != nil {
|
|
logrus.Debugf("Failed to pause container %s: %s", ctr.ID(), err.Error())
|
|
}
|
|
return err
|
|
},
|
|
})
|
|
}
|
|
return pool.Run()
|
|
}
|
|
|
|
// UnpauseContainers removes container(s) based on CLI inputs.
|
|
func (r *LocalRuntime) UnpauseContainers(ctx context.Context, cli *cliconfig.UnpauseValues) ([]string, map[string]error, error) {
|
|
var (
|
|
ok = []string{}
|
|
failures = map[string]error{}
|
|
ctrs []*libpod.Container
|
|
err error
|
|
)
|
|
|
|
maxWorkers := shared.DefaultPoolSize("pause")
|
|
if cli.GlobalIsSet("max-workers") {
|
|
maxWorkers = cli.GlobalFlags.MaxWorks
|
|
}
|
|
logrus.Debugf("Setting maximum rm workers to %d", maxWorkers)
|
|
|
|
if cli.All {
|
|
var filterFuncs []libpod.ContainerFilter
|
|
filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
|
|
state, _ := c.State()
|
|
return state == define.ContainerStatePaused
|
|
})
|
|
ctrs, err = r.GetContainers(filterFuncs...)
|
|
} else {
|
|
ctrs, err = shortcuts.GetContainersByContext(false, false, cli.InputArgs, r.Runtime)
|
|
}
|
|
if err != nil {
|
|
return ok, failures, err
|
|
}
|
|
|
|
pool := shared.NewPool("pause", maxWorkers, len(ctrs))
|
|
for _, c := range ctrs {
|
|
ctr := c
|
|
pool.Add(shared.Job{
|
|
ID: ctr.ID(),
|
|
Fn: func() error {
|
|
err := ctr.Unpause()
|
|
if err != nil {
|
|
logrus.Debugf("Failed to unpause container %s: %s", ctr.ID(), err.Error())
|
|
}
|
|
return err
|
|
},
|
|
})
|
|
}
|
|
return pool.Run()
|
|
}
|
|
|
|
// Restart containers without or without a timeout
|
|
func (r *LocalRuntime) Restart(ctx context.Context, c *cliconfig.RestartValues) ([]string, map[string]error, error) {
|
|
var (
|
|
containers []*libpod.Container
|
|
restartContainers []*libpod.Container
|
|
err error
|
|
)
|
|
useTimeout := c.Flag("timeout").Changed || c.Flag("time").Changed
|
|
inputTimeout := c.Timeout
|
|
|
|
// Handle --latest
|
|
if c.Latest {
|
|
lastCtr, err := r.Runtime.GetLatestContainer()
|
|
if err != nil {
|
|
return nil, nil, errors.Wrapf(err, "unable to get latest container")
|
|
}
|
|
restartContainers = append(restartContainers, lastCtr)
|
|
} else if c.Running {
|
|
containers, err = r.GetRunningContainers()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
restartContainers = append(restartContainers, containers...)
|
|
} else if c.All {
|
|
containers, err = r.Runtime.GetAllContainers()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
restartContainers = append(restartContainers, containers...)
|
|
} else {
|
|
for _, id := range c.InputArgs {
|
|
ctr, err := r.Runtime.LookupContainer(id)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
restartContainers = append(restartContainers, ctr)
|
|
}
|
|
}
|
|
|
|
maxWorkers := shared.DefaultPoolSize("restart")
|
|
if c.GlobalIsSet("max-workers") {
|
|
maxWorkers = c.GlobalFlags.MaxWorks
|
|
}
|
|
|
|
logrus.Debugf("Setting maximum workers to %d", maxWorkers)
|
|
|
|
// We now have a slice of all the containers to be restarted. Iterate them to
|
|
// create restart Funcs with a timeout as needed
|
|
pool := shared.NewPool("restart", maxWorkers, len(restartContainers))
|
|
for _, c := range restartContainers {
|
|
ctr := c
|
|
timeout := ctr.StopTimeout()
|
|
if useTimeout {
|
|
timeout = inputTimeout
|
|
}
|
|
pool.Add(shared.Job{
|
|
ID: ctr.ID(),
|
|
Fn: func() error {
|
|
err := ctr.RestartWithTimeout(ctx, timeout)
|
|
if err != nil {
|
|
logrus.Debugf("Failed to restart container %s: %s", ctr.ID(), err.Error())
|
|
}
|
|
return err
|
|
},
|
|
})
|
|
}
|
|
return pool.Run()
|
|
}
|
|
|
|
// Top display the running processes of a container
|
|
func (r *LocalRuntime) Top(cli *cliconfig.TopValues) ([]string, error) {
|
|
var (
|
|
descriptors []string
|
|
container *libpod.Container
|
|
err error
|
|
)
|
|
if cli.Latest {
|
|
descriptors = cli.InputArgs
|
|
container, err = r.Runtime.GetLatestContainer()
|
|
} else {
|
|
descriptors = cli.InputArgs[1:]
|
|
container, err = r.Runtime.LookupContainer(cli.InputArgs[0])
|
|
}
|
|
if err != nil {
|
|
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 {
|
|
// Note: return psgoErr to guide users into using the AIX descriptors
|
|
// instead of using ps(1).
|
|
return nil, psgoErr
|
|
}
|
|
|
|
// 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()
|
|
|
|
streams := new(libpod.AttachStreams)
|
|
streams.OutputStream = wPipe
|
|
streams.ErrorStream = wPipe
|
|
streams.InputStream = bufio.NewReader(os.Stdin)
|
|
streams.AttachOutput = true
|
|
streams.AttachError = true
|
|
streams.AttachInput = true
|
|
|
|
psOutput := []string{}
|
|
go func() {
|
|
scanner := bufio.NewScanner(rPipe)
|
|
for scanner.Scan() {
|
|
psOutput = append(psOutput, 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(psOutput, " "))
|
|
}
|
|
|
|
return psOutput, nil
|
|
}
|
|
|
|
// ExecContainer executes a command in the container
|
|
func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecValues) (int, error) {
|
|
var (
|
|
ctr *Container
|
|
err error
|
|
cmd []string
|
|
)
|
|
// default invalid command exit code
|
|
ec := define.ExecErrorCodeGeneric
|
|
|
|
if cli.Latest {
|
|
if ctr, err = r.GetLatestContainer(); err != nil {
|
|
return ec, err
|
|
}
|
|
cmd = cli.InputArgs[0:]
|
|
} else {
|
|
if ctr, err = r.LookupContainer(cli.InputArgs[0]); err != nil {
|
|
return ec, err
|
|
}
|
|
cmd = cli.InputArgs[1:]
|
|
}
|
|
|
|
if cli.PreserveFDs > 0 {
|
|
entries, err := ioutil.ReadDir("/proc/self/fd")
|
|
if err != nil {
|
|
return ec, errors.Wrapf(err, "unable to read /proc/self/fd")
|
|
}
|
|
|
|
m := make(map[int]bool)
|
|
for _, e := range entries {
|
|
i, err := strconv.Atoi(e.Name())
|
|
if err != nil {
|
|
return ec, errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name())
|
|
}
|
|
m[i] = true
|
|
}
|
|
|
|
for i := 3; i < 3+cli.PreserveFDs; i++ {
|
|
if _, found := m[i]; !found {
|
|
return ec, errors.New("invalid --preserve-fds=N specified. Not enough FDs available")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate given environment variables
|
|
env := map[string]string{}
|
|
if err := parse.ReadKVStrings(env, []string{}, cli.Env); err != nil {
|
|
return ec, errors.Wrapf(err, "unable to process environment variables")
|
|
}
|
|
|
|
streams := new(libpod.AttachStreams)
|
|
streams.OutputStream = os.Stdout
|
|
streams.ErrorStream = os.Stderr
|
|
if cli.Interactive {
|
|
streams.InputStream = bufio.NewReader(os.Stdin)
|
|
streams.AttachInput = true
|
|
}
|
|
streams.AttachOutput = true
|
|
streams.AttachError = true
|
|
|
|
ec, err = ExecAttachCtr(ctx, ctr.Container, cli.Tty, cli.Privileged, env, cmd, cli.User, cli.Workdir, streams, uint(cli.PreserveFDs), cli.DetachKeys)
|
|
return define.TranslateExecErrorToExitCode(ec, err), err
|
|
}
|
|
|
|
// Prune removes stopped containers
|
|
func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool, filters []string) ([]string, map[string]error, error) {
|
|
var (
|
|
ok = []string{}
|
|
failures = map[string]error{}
|
|
err error
|
|
filterFunc []libpod.ContainerFilter
|
|
)
|
|
|
|
logrus.Debugf("Setting maximum rm workers to %d", maxWorkers)
|
|
|
|
for _, filter := range filters {
|
|
filterSplit := strings.SplitN(filter, "=", 2)
|
|
if len(filterSplit) < 2 {
|
|
return ok, failures, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", filter)
|
|
}
|
|
|
|
f, err := shared.GenerateContainerFilterFuncs(filterSplit[0], filterSplit[1], r.Runtime)
|
|
if err != nil {
|
|
return ok, failures, err
|
|
}
|
|
filterFunc = append(filterFunc, f)
|
|
}
|
|
|
|
containerStateFilter := func(c *libpod.Container) bool {
|
|
state, err := c.State()
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
return false
|
|
}
|
|
if c.PodID() != "" {
|
|
return false
|
|
}
|
|
if state == define.ContainerStateStopped || state == define.ContainerStateExited {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
filterFunc = append(filterFunc, containerStateFilter)
|
|
|
|
delContainers, err := r.Runtime.GetContainers(filterFunc...)
|
|
if err != nil {
|
|
return ok, failures, err
|
|
}
|
|
if len(delContainers) < 1 {
|
|
return ok, failures, err
|
|
}
|
|
pool := shared.NewPool("prune", maxWorkers, len(delContainers))
|
|
for _, c := range delContainers {
|
|
ctr := c
|
|
pool.Add(shared.Job{
|
|
ID: ctr.ID(),
|
|
Fn: func() error {
|
|
err := r.Runtime.RemoveContainer(ctx, ctr, force, false)
|
|
if err != nil {
|
|
logrus.Debugf("Failed to prune container %s: %s", ctr.ID(), err.Error())
|
|
}
|
|
return err
|
|
},
|
|
})
|
|
}
|
|
return pool.Run()
|
|
}
|
|
|
|
// CleanupContainers any leftovers bits of stopped containers
|
|
func (r *LocalRuntime) CleanupContainers(ctx context.Context, cli *cliconfig.CleanupValues) ([]string, map[string]error, error) {
|
|
var (
|
|
ok = []string{}
|
|
failures = map[string]error{}
|
|
)
|
|
|
|
ctrs, err := shortcuts.GetContainersByContext(cli.All, cli.Latest, cli.InputArgs, r.Runtime)
|
|
if err != nil {
|
|
return ok, failures, err
|
|
}
|
|
|
|
for _, ctr := range ctrs {
|
|
if cli.Remove {
|
|
err = removeContainer(ctx, ctr, r)
|
|
} else {
|
|
err = cleanupContainer(ctx, ctr, r)
|
|
}
|
|
|
|
if err == nil {
|
|
ok = append(ok, ctr.ID())
|
|
} else {
|
|
failures[ctr.ID()] = err
|
|
}
|
|
}
|
|
return ok, failures, nil
|
|
}
|
|
|
|
// Only used when cleaning up containers
|
|
func removeContainer(ctx context.Context, ctr *libpod.Container, runtime *LocalRuntime) error {
|
|
if err := runtime.RemoveContainer(ctx, ctr, false, true); err != nil {
|
|
return errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cleanupContainer(ctx context.Context, ctr *libpod.Container, runtime *LocalRuntime) error {
|
|
if err := ctr.Cleanup(ctx); err != nil {
|
|
return errors.Wrapf(err, "failed to cleanup container %v", ctr.ID())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Port displays port information about existing containers
|
|
func (r *LocalRuntime) Port(c *cliconfig.PortValues) ([]*Container, error) {
|
|
var (
|
|
portContainers []*Container
|
|
containers []*libpod.Container
|
|
err error
|
|
)
|
|
|
|
if !c.All {
|
|
containers, err = shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime)
|
|
} else {
|
|
containers, err = r.Runtime.GetRunningContainers()
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
//Convert libpod containers to adapter Containers
|
|
for _, con := range containers {
|
|
if state, _ := con.State(); state != define.ContainerStateRunning {
|
|
continue
|
|
}
|
|
portContainers = append(portContainers, &Container{con})
|
|
}
|
|
return portContainers, nil
|
|
}
|
|
|
|
// generateServiceName generates the container name and the service name for systemd service.
|
|
func generateServiceName(c *cliconfig.GenerateSystemdValues, ctr *libpod.Container, pod *libpod.Pod) (string, string) {
|
|
var kind, name, ctrName string
|
|
if pod == nil {
|
|
kind = "container"
|
|
name = ctr.ID()
|
|
if c.Name {
|
|
name = ctr.Name()
|
|
}
|
|
ctrName = name
|
|
} else {
|
|
kind = "pod"
|
|
name = pod.ID()
|
|
ctrName = ctr.ID()
|
|
if c.Name {
|
|
name = pod.Name()
|
|
ctrName = ctr.Name()
|
|
}
|
|
}
|
|
return ctrName, fmt.Sprintf("%s-%s", kind, name)
|
|
}
|
|
|
|
// generateSystemdgenContainerInfo is a helper to generate a
|
|
// systemdgen.ContainerInfo for `GenerateSystemd`.
|
|
func (r *LocalRuntime) generateSystemdgenContainerInfo(c *cliconfig.GenerateSystemdValues, nameOrID string, pod *libpod.Pod) (*systemdgen.ContainerInfo, bool, error) {
|
|
ctr, err := r.Runtime.LookupContainer(nameOrID)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
|
|
timeout := int(ctr.StopTimeout())
|
|
if c.StopTimeout >= 0 {
|
|
timeout = c.StopTimeout
|
|
}
|
|
|
|
config := ctr.Config()
|
|
conmonPidFile := config.ConmonPidFile
|
|
if conmonPidFile == "" {
|
|
return nil, true, errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag")
|
|
}
|
|
|
|
name, serviceName := generateServiceName(c, ctr, pod)
|
|
info := &systemdgen.ContainerInfo{
|
|
ServiceName: serviceName,
|
|
ContainerName: name,
|
|
RestartPolicy: c.RestartPolicy,
|
|
PIDFile: conmonPidFile,
|
|
StopTimeout: timeout,
|
|
GenerateTimestamp: true,
|
|
}
|
|
|
|
return info, true, nil
|
|
}
|
|
|
|
// GenerateSystemd creates a unit file for a container or pod.
|
|
func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) {
|
|
// First assume it's a container.
|
|
if info, found, err := r.generateSystemdgenContainerInfo(c, c.InputArgs[0], nil); found && err != nil {
|
|
return "", err
|
|
} else if found && err == nil {
|
|
return systemdgen.CreateContainerSystemdUnit(info, c.Files)
|
|
}
|
|
|
|
// We're either having a pod or garbage.
|
|
pod, err := r.Runtime.LookupPod(c.InputArgs[0])
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Error out if the pod has no infra container, which we require to be the
|
|
// main service.
|
|
if !pod.HasInfraContainer() {
|
|
return "", fmt.Errorf("error generating systemd unit files: Pod %q has no infra container", pod.Name())
|
|
}
|
|
|
|
// Generate a systemdgen.ContainerInfo for the infra container. This
|
|
// ContainerInfo acts as the main service of the pod.
|
|
infraID, err := pod.InfraContainerID()
|
|
if err != nil {
|
|
return "", nil
|
|
}
|
|
podInfo, _, err := r.generateSystemdgenContainerInfo(c, infraID, pod)
|
|
if err != nil {
|
|
return "", nil
|
|
}
|
|
|
|
// Compute the container-dependency graph for the Pod.
|
|
containers, err := pod.AllContainers()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(containers) == 0 {
|
|
return "", fmt.Errorf("error generating systemd unit files: Pod %q has no containers", pod.Name())
|
|
}
|
|
graph, err := libpod.BuildContainerGraph(containers)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Traverse the dependency graph and create systemdgen.ContainerInfo's for
|
|
// each container.
|
|
containerInfos := []*systemdgen.ContainerInfo{podInfo}
|
|
for ctr, dependencies := range graph.DependencyMap() {
|
|
// Skip the infra container as we already generated it.
|
|
if ctr.ID() == infraID {
|
|
continue
|
|
}
|
|
ctrInfo, _, err := r.generateSystemdgenContainerInfo(c, ctr.ID(), nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
// Now add the container's dependencies and at the container as a
|
|
// required service of the infra container.
|
|
for _, dep := range dependencies {
|
|
if dep.ID() == infraID {
|
|
ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, podInfo.ServiceName)
|
|
} else {
|
|
_, serviceName := generateServiceName(c, dep, nil)
|
|
ctrInfo.BoundToServices = append(ctrInfo.BoundToServices, serviceName)
|
|
}
|
|
}
|
|
podInfo.RequiredServices = append(podInfo.RequiredServices, ctrInfo.ServiceName)
|
|
containerInfos = append(containerInfos, ctrInfo)
|
|
}
|
|
|
|
// Now generate the systemd service for all containers.
|
|
builder := strings.Builder{}
|
|
for i, info := range containerInfos {
|
|
if i > 0 {
|
|
builder.WriteByte('\n')
|
|
}
|
|
out, err := systemdgen.CreateContainerSystemdUnit(info, c.Files)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
builder.WriteString(out)
|
|
}
|
|
|
|
return builder.String(), nil
|
|
}
|
|
|
|
// GetNamespaces returns namespace information about a container for PS
|
|
func (r *LocalRuntime) GetNamespaces(container shared.PsContainerOutput) *shared.Namespace {
|
|
return shared.GetNamespaces(container.Pid)
|
|
}
|
|
|
|
// Commit creates a local image from a container
|
|
func (r *LocalRuntime) Commit(ctx context.Context, c *cliconfig.CommitValues, container, imageName string) (string, error) {
|
|
var (
|
|
writer io.Writer
|
|
mimeType string
|
|
)
|
|
switch c.Format {
|
|
case "oci":
|
|
mimeType = buildah.OCIv1ImageManifest
|
|
if c.Flag("message").Changed {
|
|
return "", errors.Errorf("messages are only compatible with the docker image format (-f docker)")
|
|
}
|
|
case "docker":
|
|
mimeType = manifest.DockerV2Schema2MediaType
|
|
default:
|
|
return "", errors.Errorf("unrecognized image format %q", c.Format)
|
|
}
|
|
if !c.Quiet {
|
|
writer = os.Stderr
|
|
}
|
|
ctr, err := r.Runtime.LookupContainer(container)
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "error looking up container %q", container)
|
|
}
|
|
|
|
rtc, err := r.Runtime.GetConfig()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
sc := image.GetSystemContext(rtc.SignaturePolicyPath, "", false)
|
|
coptions := buildah.CommitOptions{
|
|
SignaturePolicyPath: rtc.SignaturePolicyPath,
|
|
ReportWriter: writer,
|
|
SystemContext: sc,
|
|
PreferredManifestType: mimeType,
|
|
}
|
|
options := libpod.ContainerCommitOptions{
|
|
CommitOptions: coptions,
|
|
Pause: c.Pause,
|
|
IncludeVolumes: c.IncludeVolumes,
|
|
Message: c.Message,
|
|
Changes: c.Change,
|
|
Author: c.Author,
|
|
}
|
|
newImage, err := ctr.Commit(ctx, imageName, options)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return newImage.ID(), nil
|
|
}
|