mirror of
https://github.com/containers/podman.git
synced 2025-05-22 01:27:07 +08:00
Merge pull request #9754 from mheon/add_dep
Add --requires flag to podman run/create
This commit is contained in:
@ -576,6 +576,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
|
||||
`If a container with the same name exists, replace it`,
|
||||
)
|
||||
|
||||
requiresFlagName := "requires"
|
||||
createFlags.StringSliceVar(
|
||||
&cf.Requires,
|
||||
requiresFlagName, []string{},
|
||||
"Add one or more requirement containers that must be started before this container will start",
|
||||
)
|
||||
_ = cmd.RegisterFlagCompletionFunc(requiresFlagName, AutocompleteContainers)
|
||||
|
||||
restartFlagName := "restart"
|
||||
createFlags.StringVar(
|
||||
&cf.Restart,
|
||||
|
@ -93,6 +93,7 @@ type ContainerCLIOpts struct {
|
||||
ReadOnlyTmpFS bool
|
||||
Restart string
|
||||
Replace bool
|
||||
Requires []string
|
||||
Rm bool
|
||||
RootFS bool
|
||||
Secrets []string
|
||||
|
@ -486,6 +486,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
|
||||
s.ReadOnlyFilesystem = c.ReadOnly
|
||||
s.ConmonPidFile = c.ConmonPIDFile
|
||||
|
||||
s.DependencyContainers = c.Requires
|
||||
|
||||
// TODO
|
||||
// outside of specgen and oci though
|
||||
// defaults to true, check spec/storage
|
||||
|
@ -781,6 +781,12 @@ If container is running in --read-only mode, then mount a read-write tmpfs on /r
|
||||
|
||||
If another container with the same name already exists, replace and remove it. The default is **false**.
|
||||
|
||||
#### **\-\-requires**=**container**
|
||||
|
||||
Specify one or more requirements.
|
||||
A requirement is a dependency container that will be started before this container.
|
||||
Containers can be specified by name or ID, with multiple containers being separated by commas.
|
||||
|
||||
#### **\-\-restart**=*policy*
|
||||
|
||||
Restart policy to follow when containers exit.
|
||||
@ -1250,6 +1256,25 @@ $ podman create --tz=Asia/Shanghai alpine date
|
||||
$ podman create --tz=US/Eastern alpine date
|
||||
```
|
||||
|
||||
### Adding dependency containers
|
||||
|
||||
Podman will make sure the first container, container1, is running before the second container (container2) is started.
|
||||
|
||||
```
|
||||
$ podman create --name container1 -t -i fedora bash
|
||||
$ podman create --name container2 --requires container1 -t -i fedora bash
|
||||
$ podman start --attach container2
|
||||
```
|
||||
|
||||
Multiple containers can be required.
|
||||
|
||||
```
|
||||
$ podman create --name container1 -t -i fedora bash
|
||||
$ podman create --name container2 -t -i fedora bash
|
||||
$ podman create --name container3 --requires container1,container2 -t -i fedora bash
|
||||
$ podman start --attach container3
|
||||
```
|
||||
|
||||
### Rootless Containers
|
||||
|
||||
Podman runs as a non root user on most systems. This feature requires that a new enough version of shadow-utils
|
||||
@ -1297,7 +1322,7 @@ b
|
||||
NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`.
|
||||
|
||||
## SEE ALSO
|
||||
**podman**(1), **podman-secret**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-kill**(1), **podman-stop**(1),
|
||||
**podman**(1), **podman-secret**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-start*(1), **podman-kill**(1), **podman-stop**(1),
|
||||
**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5)**.
|
||||
|
||||
## HISTORY
|
||||
|
@ -825,6 +825,12 @@ If container is running in **\-\-read-only** mode, then mount a read-write tmpfs
|
||||
|
||||
If another container with the same name already exists, replace and remove it. The default is **false**.
|
||||
|
||||
#### **\-\-requires**=**container**
|
||||
|
||||
Specify one or more requirements.
|
||||
A requirement is a dependency container that will be started before this container.
|
||||
Containers can be specified by name or ID, with multiple containers being separated by commas.
|
||||
|
||||
#### **\-\-restart**=*policy*
|
||||
|
||||
Restart policy to follow when containers exit.
|
||||
@ -1612,6 +1618,24 @@ $ podman run --tz=Asia/Shanghai alpine date
|
||||
$ podman run --tz=US/Eastern alpine date
|
||||
```
|
||||
|
||||
### Adding dependency containers
|
||||
|
||||
The first container, container1, is not started initially, but must be running before container2 will start.
|
||||
The `podman run` command will start the container automatically before starting container2.
|
||||
|
||||
```
|
||||
$ podman create --name container1 -t -i fedora bash
|
||||
$ podman run --name container2 --requires container1 -t -i fedora bash
|
||||
```
|
||||
|
||||
Multiple containers can be required.
|
||||
|
||||
```
|
||||
$ podman create --name container1 -t -i fedora bash
|
||||
$ podman create --name container2 -t -i fedora bash
|
||||
$ podman run --name container3 --requires container1,container2 -t -i fedora bash
|
||||
```
|
||||
|
||||
### Rootless Containers
|
||||
|
||||
Podman runs as a non root user on most systems. This feature requires that a new enough version of **shadow-utils**
|
||||
@ -1657,7 +1681,7 @@ b
|
||||
NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`.
|
||||
|
||||
## SEE ALSO
|
||||
**podman**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-kill**(1), **podman-stop**(1),
|
||||
**podman**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-start**(1), **podman-kill**(1), **podman-stop**(1),
|
||||
**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5)**.
|
||||
|
||||
## HISTORY
|
||||
|
@ -919,7 +919,7 @@ func (s *BoltState) removeContainer(ctr *Container, pod *Pod, tx *bolt.Tx) error
|
||||
return err
|
||||
}
|
||||
if len(deps) != 0 {
|
||||
return errors.Wrapf(define.ErrCtrExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", "))
|
||||
return errors.Wrapf(define.ErrDepExists, "container %s is a dependency of the following containers: %s", ctr.ID(), strings.Join(deps, ", "))
|
||||
}
|
||||
|
||||
if err := ctrBucket.DeleteBucket(ctrID); err != nil {
|
||||
|
@ -31,6 +31,10 @@ var (
|
||||
// not exist.
|
||||
ErrNoSuchExecSession = errors.New("no such exec session")
|
||||
|
||||
// ErrDepExists indicates that the current object has dependencies and
|
||||
// cannot be removed before them.
|
||||
ErrDepExists = errors.New("dependency exists")
|
||||
|
||||
// ErrNoAliases indicates that the container does not have any network
|
||||
// aliases.
|
||||
ErrNoAliases = errors.New("no aliases for container")
|
||||
|
@ -391,7 +391,7 @@ func (s *InMemoryState) RemoveContainer(ctr *Container) error {
|
||||
deps, ok := s.ctrDepends[ctr.ID()]
|
||||
if ok && len(deps) != 0 {
|
||||
depsStr := strings.Join(deps, ", ")
|
||||
return errors.Wrapf(define.ErrCtrExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
|
||||
return errors.Wrapf(define.ErrDepExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
|
||||
}
|
||||
|
||||
// Ensure we don't have active exec sessions
|
||||
@ -1497,7 +1497,7 @@ func (s *InMemoryState) RemoveContainerFromPod(pod *Pod, ctr *Container) error {
|
||||
deps, ok := s.ctrDepends[ctr.ID()]
|
||||
if ok && len(deps) != 0 {
|
||||
depsStr := strings.Join(deps, ", ")
|
||||
return errors.Wrapf(define.ErrCtrExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
|
||||
return errors.Wrapf(define.ErrDepExists, "the following containers depend on container %s: %s", ctr.ID(), depsStr)
|
||||
}
|
||||
|
||||
// Ensure we don't have active exec sessions
|
||||
|
@ -42,7 +42,7 @@ func StartContainer(w http.ResponseWriter, r *http.Request) {
|
||||
utils.WriteResponse(w, http.StatusNotModified, nil)
|
||||
return
|
||||
}
|
||||
if err := con.Start(r.Context(), len(con.PodID()) > 0); err != nil {
|
||||
if err := con.Start(r.Context(), true); err != nil {
|
||||
utils.InternalServerError(w, err)
|
||||
return
|
||||
}
|
||||
|
@ -154,6 +154,7 @@ type RestartOptions struct {
|
||||
// StartOptions are optional options for starting containers
|
||||
type StartOptions struct {
|
||||
DetachKeys *string
|
||||
Recursive *bool
|
||||
}
|
||||
|
||||
//go:generate go run ../generator/generator.go StatsOptions
|
||||
|
@ -35,3 +35,19 @@ func (o *StartOptions) GetDetachKeys() string {
|
||||
}
|
||||
return *o.DetachKeys
|
||||
}
|
||||
|
||||
// WithRecursive
|
||||
func (o *StartOptions) WithRecursive(value bool) *StartOptions {
|
||||
v := &value
|
||||
o.Recursive = v
|
||||
return o
|
||||
}
|
||||
|
||||
// GetRecursive
|
||||
func (o *StartOptions) GetRecursive() bool {
|
||||
var recursive bool
|
||||
if o.Recursive == nil {
|
||||
return recursive
|
||||
}
|
||||
return *o.Recursive
|
||||
}
|
||||
|
@ -585,7 +585,7 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string,
|
||||
}
|
||||
|
||||
// If the container is in a pod, also set to recursively start dependencies
|
||||
err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false, ctr.PodID() != "")
|
||||
err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, false)
|
||||
if err != nil && errors.Cause(err) != define.ErrDetach {
|
||||
return errors.Wrapf(err, "error attaching to container %s", ctr.ID())
|
||||
}
|
||||
@ -708,7 +708,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
|
||||
ctrRunning := ctrState == define.ContainerStateRunning
|
||||
|
||||
if options.Attach {
|
||||
err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning, ctr.PodID() != "")
|
||||
err = terminal.StartAttachCtr(ctx, ctr, options.Stdout, options.Stderr, options.Stdin, options.DetachKeys, options.SigProxy, !ctrRunning)
|
||||
if errors.Cause(err) == define.ErrDetach {
|
||||
// User manually detached
|
||||
// Exit cleanly immediately
|
||||
@ -784,7 +784,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
|
||||
RawInput: rawInput,
|
||||
ExitCode: 125,
|
||||
}
|
||||
if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil {
|
||||
if err := ctr.Start(ctx, true); err != nil {
|
||||
// if lastError != nil {
|
||||
// fmt.Fprintln(os.Stderr, lastError)
|
||||
// }
|
||||
@ -845,10 +845,6 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
|
||||
}
|
||||
}
|
||||
|
||||
var joinPod bool
|
||||
if len(ctr.PodID()) > 0 {
|
||||
joinPod = true
|
||||
}
|
||||
report := entities.ContainerRunReport{Id: ctr.ID()}
|
||||
|
||||
if logrus.GetLevel() == logrus.DebugLevel {
|
||||
@ -859,7 +855,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
|
||||
}
|
||||
if opts.Detach {
|
||||
// if the container was created as part of a pod, also start its dependencies, if any.
|
||||
if err := ctr.Start(ctx, joinPod); err != nil {
|
||||
if err := ctr.Start(ctx, true); err != nil {
|
||||
// This means the command did not exist
|
||||
report.ExitCode = define.ExitCode(err)
|
||||
return &report, err
|
||||
@ -869,7 +865,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
|
||||
}
|
||||
|
||||
// if the container was created as part of a pod, also start its dependencies, if any.
|
||||
if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true, joinPod); err != nil {
|
||||
if err := terminal.StartAttachCtr(ctx, ctr, opts.OutputStream, opts.ErrorStream, opts.InputStream, opts.DetachKeys, opts.SigProxy, true); err != nil {
|
||||
// We've manually detached from the container
|
||||
// Do not perform cleanup, or wait for container exit code
|
||||
// Just exit immediately
|
||||
|
@ -39,7 +39,7 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpo
|
||||
// StartAttachCtr starts and (if required) attaches to a container
|
||||
// if you change the signature of this function from os.File to io.Writer, it will trigger a downstream
|
||||
// error. we may need to just lint disable this one.
|
||||
func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool, recursive bool) error { //nolint-interfacer
|
||||
func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr, stdin *os.File, detachKeys string, sigProxy bool, startContainer bool) error { //nolint-interfacer
|
||||
resize := make(chan define.TerminalSize)
|
||||
|
||||
haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd()))
|
||||
@ -88,7 +88,7 @@ func StartAttachCtr(ctx context.Context, ctr *libpod.Container, stdout, stderr,
|
||||
return ctr.Attach(streams, detachKeys, resize)
|
||||
}
|
||||
|
||||
attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, recursive)
|
||||
attachChan, err := ctr.StartAndAttach(ctx, streams, detachKeys, resize, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -629,7 +629,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
|
||||
|
||||
if opts.Detach {
|
||||
// Detach and return early
|
||||
err := containers.Start(ic.ClientCtx, con.ID, nil)
|
||||
err := containers.Start(ic.ClientCtx, con.ID, new(containers.StartOptions).WithRecursive(true))
|
||||
if err != nil {
|
||||
report.ExitCode = define.ExitCode(err)
|
||||
}
|
||||
|
@ -364,6 +364,17 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
|
||||
if len(s.Secrets) != 0 {
|
||||
options = append(options, libpod.WithSecrets(s.Secrets))
|
||||
}
|
||||
if len(s.DependencyContainers) > 0 {
|
||||
deps := make([]*libpod.Container, 0, len(s.DependencyContainers))
|
||||
for _, ctr := range s.DependencyContainers {
|
||||
depCtr, err := rt.LookupContainer(ctr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "%q is not a valid container, cannot be used as a dependency", ctr)
|
||||
}
|
||||
deps = append(deps, depCtr)
|
||||
}
|
||||
options = append(options, libpod.WithDependencyCtrs(deps))
|
||||
}
|
||||
return options, nil
|
||||
}
|
||||
|
||||
|
@ -160,10 +160,17 @@ type ContainerBasicConfig struct {
|
||||
// to 0, 1, 2) that will be passed to the executed process. The total FDs
|
||||
// passed will be 3 + PreserveFDs.
|
||||
// set tags as `json:"-"` for not supported remote
|
||||
// Optional.
|
||||
PreserveFDs uint `json:"-"`
|
||||
// Timezone is the timezone inside the container.
|
||||
// Local means it has the same timezone as the host machine
|
||||
// Optional.
|
||||
Timezone string `json:"timezone,omitempty"`
|
||||
// DependencyContainers is an array of containers this container
|
||||
// depends on. Dependency containers must be started before this
|
||||
// container. Dependencies can be specified by name or full/partial ID.
|
||||
// Optional.
|
||||
DependencyContainers []string `json:"dependencyContainers,omitempty"`
|
||||
}
|
||||
|
||||
// ContainerStorageConfig contains information on the storage configuration of a
|
||||
|
@ -1588,4 +1588,29 @@ WORKDIR /madethis`, BB)
|
||||
Expect(session.OutputToString()).To(ContainSubstring("mysecret"))
|
||||
|
||||
})
|
||||
|
||||
It("podman run --requires", func() {
|
||||
depName := "ctr1"
|
||||
depContainer := podmanTest.Podman([]string{"create", "--name", depName, ALPINE, "top"})
|
||||
depContainer.WaitWithDefaultTimeout()
|
||||
Expect(depContainer.ExitCode()).To(Equal(0))
|
||||
|
||||
mainName := "ctr2"
|
||||
mainContainer := podmanTest.Podman([]string{"run", "--name", mainName, "--requires", depName, "-d", ALPINE, "top"})
|
||||
mainContainer.WaitWithDefaultTimeout()
|
||||
Expect(mainContainer.ExitCode()).To(Equal(0))
|
||||
|
||||
stop := podmanTest.Podman([]string{"stop", "--all"})
|
||||
stop.WaitWithDefaultTimeout()
|
||||
Expect(stop.ExitCode()).To(Equal(0))
|
||||
|
||||
start := podmanTest.Podman([]string{"start", mainName})
|
||||
start.WaitWithDefaultTimeout()
|
||||
Expect(start.ExitCode()).To(Equal(0))
|
||||
|
||||
running := podmanTest.Podman([]string{"ps", "-q"})
|
||||
running.WaitWithDefaultTimeout()
|
||||
Expect(running.ExitCode()).To(Equal(0))
|
||||
Expect(len(running.OutputToStringArray())).To(Equal(2))
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user