265 lines
7.9 KiB
Go
265 lines
7.9 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
|
|
|
|
// Include the command used to create the container.
|
|
s.ContainerCreateCommand = os.Args
|
|
|
|
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
|
|
}
|
|
|
|
if s.HealthConfig == nil {
|
|
s.HealthConfig, err = common.GetHealthCheckOverrideConfig(cmd, &cliVals)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
report, err := registry.ContainerEngine().ContainerRun(registry.Context(), 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.Context(), []string{imageName}, entities.ImageRemoveOptions{Ignore: true})
|
|
for _, err := range rmErrors {
|
|
logrus.Warnf("Failed to remove image: %v", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|