summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/create.go8
-rw-r--r--cmd/podman/common/create_opts.go1
-rw-r--r--cmd/podman/common/specgen.go2
-rw-r--r--docs/source/markdown/podman-create.1.md27
-rw-r--r--docs/source/markdown/podman-run.1.md26
-rw-r--r--libpod/boltdb_state_internal.go2
-rw-r--r--libpod/define/errors.go4
-rw-r--r--libpod/in_memory_state.go4
-rw-r--r--pkg/api/handlers/compat/containers_start.go2
-rw-r--r--pkg/bindings/containers/types.go1
-rw-r--r--pkg/bindings/containers/types_start_options.go16
-rw-r--r--pkg/domain/infra/abi/containers.go14
-rw-r--r--pkg/domain/infra/abi/terminal/terminal_linux.go4
-rw-r--r--pkg/domain/infra/tunnel/containers.go2
-rw-r--r--pkg/specgen/generate/container_create.go11
-rw-r--r--pkg/specgen/specgen.go7
-rw-r--r--test/e2e/run_test.go25
17 files changed, 138 insertions, 18 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index dda709ecd..220a30a10 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -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,
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index a296ef4f1..e14918fe1 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -93,6 +93,7 @@ type ContainerCLIOpts struct {
ReadOnlyTmpFS bool
Restart string
Replace bool
+ Requires []string
Rm bool
RootFS bool
Secrets []string
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index d1b67d963..363a8f5f9 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -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
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 927b3df33..fcd5f3e3f 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -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
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 4c096ecfe..3fad9bf64 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -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
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index d4994334f..f63876c14 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -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 {
diff --git a/libpod/define/errors.go b/libpod/define/errors.go
index 2e85454b2..e19ac6a27 100644
--- a/libpod/define/errors.go
+++ b/libpod/define/errors.go
@@ -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")
diff --git a/libpod/in_memory_state.go b/libpod/in_memory_state.go
index 3875878ed..df45f8e73 100644
--- a/libpod/in_memory_state.go
+++ b/libpod/in_memory_state.go
@@ -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
diff --git a/pkg/api/handlers/compat/containers_start.go b/pkg/api/handlers/compat/containers_start.go
index 391aa752d..f1ed1b2b8 100644
--- a/pkg/api/handlers/compat/containers_start.go
+++ b/pkg/api/handlers/compat/containers_start.go
@@ -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
}
diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go
index f63e35bf1..0d22c32f8 100644
--- a/pkg/bindings/containers/types.go
+++ b/pkg/bindings/containers/types.go
@@ -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
diff --git a/pkg/bindings/containers/types_start_options.go b/pkg/bindings/containers/types_start_options.go
index f8ba29623..d419c755c 100644
--- a/pkg/bindings/containers/types_start_options.go
+++ b/pkg/bindings/containers/types_start_options.go
@@ -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
+}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 24261e5ed..6f8845f10 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -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
diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go
index 7a0c2907c..ab71f8f6f 100644
--- a/pkg/domain/infra/abi/terminal/terminal_linux.go
+++ b/pkg/domain/infra/abi/terminal/terminal_linux.go
@@ -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
}
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index a0f65f11f..4545d266b 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -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)
}
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 1d724ffb0..ef9975021 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -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
}
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index c10dc5ef5..28111f96d 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -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
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 23930b4f7..cefe00655 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -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))
+ })
})