spiegel_podman/cmd/podman/containers/run.go
Paul Holzinger dc724e5e80
run: fix detach passthrough and --rmi
When we use passthrough logging and the --rmi option should not try to
delete the image right away. Simply speak passthough only means do not
print the cotnainer id but we should never try to delete the image here
as this will be done in the cleanup process.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2024-08-27 12:11:32 +02:00

253 lines
7.7 KiB
Go

package containers
import (
"errors"
"fmt"
"os"
"strings"
"github.com/containers/common/pkg/auth"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v5/cmd/podman/common"
"github.com/containers/podman/v5/cmd/podman/registry"
"github.com/containers/podman/v5/cmd/podman/utils"
"github.com/containers/podman/v5/libpod/define"
"github.com/containers/podman/v5/pkg/domain/entities"
"github.com/containers/podman/v5/pkg/rootless"
"github.com/containers/podman/v5/pkg/specgen"
"github.com/containers/podman/v5/pkg/specgenutil"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/term"
)
var (
runDescription = "Runs a command in a new container from the given image"
runCommand = &cobra.Command{
Args: cobra.MinimumNArgs(1),
Use: "run [options] IMAGE [COMMAND [ARG...]]",
Short: "Run a command in a new container",
Long: runDescription,
RunE: run,
ValidArgsFunction: common.AutocompleteCreateRun,
Example: `podman run imageID ls -alF /etc
podman run --network=host imageID dnf -y install java
podman run --volume /var/hostdir:/var/ctrdir -i -t fedora /bin/bash`,
}
containerRunCommand = &cobra.Command{
Args: cobra.MinimumNArgs(1),
Use: runCommand.Use,
Short: runCommand.Short,
Long: runCommand.Long,
RunE: runCommand.RunE,
ValidArgsFunction: runCommand.ValidArgsFunction,
Example: `podman container run imageID ls -alF /etc
podman container run --network=host imageID dnf -y install java
podman container run --volume /var/hostdir:/var/ctrdir -i -t fedora /bin/bash`,
}
)
var (
runOpts entities.ContainerRunOptions
runRmi bool
)
func runFlags(cmd *cobra.Command) {
flags := cmd.Flags()
flags.SetInterspersed(false)
common.DefineCreateDefaults(&cliVals)
common.DefineCreateFlags(cmd, &cliVals, entities.CreateMode)
common.DefineNetFlags(cmd)
flags.SetNormalizeFunc(utils.AliasFlags)
flags.BoolVar(&runOpts.SigProxy, "sig-proxy", true, "Proxy received signals to the process")
flags.BoolVar(&runRmi, "rmi", false, "Remove image unless used by other containers, implies --rm")
preserveFdsFlagName := "preserve-fds"
flags.UintVar(&runOpts.PreserveFDs, preserveFdsFlagName, 0, "Pass a number of additional file descriptors into the container")
_ = cmd.RegisterFlagCompletionFunc(preserveFdsFlagName, completion.AutocompleteNone)
preserveFdFlagName := "preserve-fd"
flags.UintSliceVar(&runOpts.PreserveFD, preserveFdFlagName, nil, "Pass a file descriptor into the container")
_ = cmd.RegisterFlagCompletionFunc(preserveFdFlagName, completion.AutocompleteNone)
flags.BoolVarP(&runOpts.Detach, "detach", "d", false, "Run container in background and print container ID")
detachKeysFlagName := "detach-keys"
flags.StringVar(&runOpts.DetachKeys, detachKeysFlagName, containerConfig.DetachKeys(), "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`")
_ = cmd.RegisterFlagCompletionFunc(detachKeysFlagName, common.AutocompleteDetachKeys)
passwdFlagName := "passwd"
flags.BoolVar(&runOpts.Passwd, passwdFlagName, true, "add entries to /etc/passwd and /etc/group")
if registry.IsRemote() {
_ = flags.MarkHidden(preserveFdsFlagName)
_ = flags.MarkHidden(preserveFdFlagName)
_ = flags.MarkHidden("conmon-pidfile")
_ = flags.MarkHidden("pidfile")
}
}
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: runCommand,
})
runFlags(runCommand)
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: containerRunCommand,
Parent: containerCmd,
})
runFlags(containerRunCommand)
}
func run(cmd *cobra.Command, args []string) error {
if err := commonFlags(cmd); err != nil {
return err
}
if runRmi {
if cmd.Flags().Changed("rm") && !cliVals.Rm {
return errors.New("the --rmi option does not work without --rm")
}
cliVals.Rm = true
}
// TODO: Breaking change should be made fatal in next major Release
if cliVals.TTY && cliVals.Interactive && !term.IsTerminal(int(os.Stdin.Fd())) {
logrus.Warnf("The input device is not a TTY. The --tty and --interactive flags might not work properly")
}
if cmd.Flags().Changed("authfile") {
if err := auth.CheckAuthFile(cliVals.Authfile); err != nil {
return err
}
}
runOpts.CIDFile = cliVals.CIDFile
runOpts.Rm = cliVals.Rm
cliVals, err := CreateInit(cmd, cliVals, false)
if err != nil {
return err
}
for _, fd := range runOpts.PreserveFD {
if !rootless.IsFdInherited(int(fd)) {
return fmt.Errorf("file descriptor %d is not available - the preserve-fd option requires that file descriptors must be passed", fd)
}
}
for fd := 3; fd < int(3+runOpts.PreserveFDs); fd++ {
if !rootless.IsFdInherited(fd) {
return fmt.Errorf("file descriptor %d is not available - the preserve-fds option requires that file descriptors must be passed", fd)
}
}
imageName := args[0]
rawImageName := ""
if !cliVals.RootFS {
rawImageName = args[0]
name, err := pullImage(cmd, args[0], &cliVals)
if err != nil {
return err
}
imageName = name
}
if cliVals.Replace {
if err := replaceContainer(cliVals.Name); err != nil {
return err
}
}
// First set the default streams before they get modified by any flags.
runOpts.OutputStream = os.Stdout
runOpts.InputStream = os.Stdin
runOpts.ErrorStream = os.Stderr
// If -i is not set, clear stdin
if !cliVals.Interactive {
runOpts.InputStream = nil
}
passthrough := cliVals.LogDriver == define.PassthroughLogging || cliVals.LogDriver == define.PassthroughTTYLogging
// If attach is set, clear stdin/stdout/stderr and only attach requested
if cmd.Flag("attach").Changed {
if passthrough {
return fmt.Errorf("cannot specify --attach with --log-driver=passthrough: %w", define.ErrInvalidArg)
}
runOpts.OutputStream = nil
runOpts.ErrorStream = nil
if !cliVals.Interactive {
runOpts.InputStream = nil
}
for _, stream := range cliVals.Attach {
switch strings.ToLower(stream) {
case "stdout":
runOpts.OutputStream = os.Stdout
case "stderr":
runOpts.ErrorStream = os.Stderr
case "stdin":
runOpts.InputStream = os.Stdin
default:
return fmt.Errorf("invalid stream %q for --attach - must be one of stdin, stdout, or stderr: %w", stream, define.ErrInvalidArg)
}
}
}
cliVals.PreserveFDs = runOpts.PreserveFDs
cliVals.PreserveFD = runOpts.PreserveFD
s := specgen.NewSpecGenerator(imageName, cliVals.RootFS)
if err := specgenutil.FillOutSpecGen(s, &cliVals, args); err != nil {
return err
}
s.RawImageName = rawImageName
s.ImageOS = cliVals.OS
s.ImageArch = cliVals.Arch
s.ImageVariant = cliVals.Variant
s.Passwd = &runOpts.Passwd
if runRmi {
s.RemoveImage = &runRmi
}
runOpts.Spec = s
if err := createPodIfNecessary(cmd, s, cliVals.Net); err != nil {
return err
}
report, err := registry.ContainerEngine().ContainerRun(registry.GetContext(), runOpts)
// report.ExitCode is set by ContainerRun even it returns an error
if report != nil {
registry.SetExitCode(report.ExitCode)
}
if err != nil {
// if pod was created as part of run
// remove it in case ctr creation fails
if err := rmPodIfNecessary(cmd, s); err != nil {
if !errors.Is(err, define.ErrNoSuchPod) {
logrus.Error(err.Error())
}
}
return err
}
if runOpts.Detach {
if !passthrough {
fmt.Println(report.Id)
}
return nil
}
if runRmi {
_, rmErrors := registry.ImageEngine().Remove(registry.GetContext(), []string{imageName}, entities.ImageRemoveOptions{Ignore: true})
for _, err := range rmErrors {
logrus.Warnf("Failed to remove image: %v", err)
}
}
return nil
}