summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/cliconfig/config.go6
-rw-r--r--cmd/podman/container.go1
-rw-r--r--cmd/podman/init.go64
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/shared/workers.go5
-rw-r--r--cmd/podman/varlink/io.podman.varlink8
-rw-r--r--commands.md1
-rw-r--r--completions/bash/podman25
-rw-r--r--docs/podman-container.1.md1
-rw-r--r--docs/podman-init.1.md41
-rw-r--r--docs/podman.1.md1
-rw-r--r--libpod/container_api.go2
-rw-r--r--libpod/container_internal.go16
-rw-r--r--pkg/adapter/containers.go37
-rw-r--r--pkg/adapter/containers_remote.go25
-rw-r--r--pkg/varlinkapi/containers.go12
-rw-r--r--test/e2e/init_test.go129
17 files changed, 369 insertions, 6 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 77156f47a..43ba7ddc9 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -177,6 +177,12 @@ type InfoValues struct {
Format string
}
+type InitValues struct {
+ PodmanCommand
+ All bool
+ Latest bool
+}
+
type InspectValues struct {
PodmanCommand
TypeObject string
diff --git a/cmd/podman/container.go b/cmd/podman/container.go
index b3058bf12..4dd9cbed6 100644
--- a/cmd/podman/container.go
+++ b/cmd/podman/container.go
@@ -56,6 +56,7 @@ var (
_diffCommand,
_exportCommand,
_createCommand,
+ _initCommand,
_killCommand,
_listSubCommand,
_logsCommand,
diff --git a/cmd/podman/init.go b/cmd/podman/init.go
new file mode 100644
index 000000000..68c80631d
--- /dev/null
+++ b/cmd/podman/init.go
@@ -0,0 +1,64 @@
+package main
+
+import (
+ "github.com/containers/libpod/cmd/podman/cliconfig"
+ "github.com/containers/libpod/pkg/adapter"
+ "github.com/opentracing/opentracing-go"
+ "github.com/pkg/errors"
+ "github.com/spf13/cobra"
+)
+
+var (
+ initCommand cliconfig.InitValues
+ initDescription = `Initialize one or more containers, creating the OCI spec and mounts for inspection. Container names or IDs can be used.`
+
+ _initCommand = &cobra.Command{
+ Use: "init [flags] CONTAINER [CONTAINER...]",
+ Short: "Initialize one or more containers",
+ Long: initDescription,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ initCommand.InputArgs = args
+ initCommand.GlobalFlags = MainGlobalOpts
+ initCommand.Remote = remoteclient
+ return initCmd(&initCommand)
+ },
+ Args: func(cmd *cobra.Command, args []string) error {
+ return checkAllAndLatest(cmd, args, false)
+ },
+ Example: `podman init --latest
+ podman init 3c45ef19d893
+ podman init test1`,
+ }
+)
+
+func init() {
+ initCommand.Command = _initCommand
+ initCommand.SetHelpTemplate(HelpTemplate())
+ initCommand.SetUsageTemplate(UsageTemplate())
+ flags := initCommand.Flags()
+ flags.BoolVarP(&initCommand.All, "all", "a", false, "Initialize all containers")
+ flags.BoolVarP(&initCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+ markFlagHiddenForRemoteClient("latest", flags)
+}
+
+// initCmd initializes a container
+func initCmd(c *cliconfig.InitValues) error {
+ if c.Bool("trace") {
+ span, _ := opentracing.StartSpanFromContext(Ctx, "initCmd")
+ defer span.Finish()
+ }
+
+ ctx := getContext()
+
+ runtime, err := adapter.GetRuntime(ctx, &c.PodmanCommand)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ ok, failures, err := runtime.InitContainers(ctx, c)
+ if err != nil {
+ return err
+ }
+ return printCmdResults(ok, failures)
+}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index f501ee674..7d2138ba7 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -39,6 +39,7 @@ var mainCommands = []*cobra.Command{
&_imagesCommand,
_importCommand,
_infoCommand,
+ _initCommand,
&_inspectCommand,
_killCommand,
_loadCommand,
diff --git a/cmd/podman/shared/workers.go b/cmd/podman/shared/workers.go
index 112af89cc..b6e3f10e7 100644
--- a/cmd/podman/shared/workers.go
+++ b/cmd/podman/shared/workers.go
@@ -110,9 +110,14 @@ func (p *Pool) newWorker(slot int) {
func DefaultPoolSize(name string) int {
numCpus := runtime.NumCPU()
switch name {
+ case "init":
+ fallthrough
case "kill":
+ fallthrough
case "pause":
+ fallthrough
case "rm":
+ fallthrough
case "unpause":
if numCpus <= 3 {
return numCpus * 3
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 309f9765a..1b6113b7c 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -641,6 +641,14 @@ method StartContainer(name: string) -> (container: string)
# ~~~
method StopContainer(name: string, timeout: int) -> (container: string)
+# InitContainer initializes the given container. It accepts a container name or
+# ID, and will initialize the container matching that ID if possible, and error
+# if not. Containers can only be initialized when they are in the Created or
+# Exited states. Initialization prepares a container to be started, but does not
+# start the container. It is intended to be used to debug a container's state
+# prior to starting it.
+method InitContainer(name: string) -> (container: string)
+
# RestartContainer will restart a running container given a container name or ID and timeout value. The timeout
# value is the time before a forcible stop is used to stop the container. If the container cannot be found by
# name or ID, a [ContainerNotFound](#ContainerNotFound) error will be returned; otherwise, the ID of the
diff --git a/commands.md b/commands.md
index 1c05640f2..88290dc1d 100644
--- a/commands.md
+++ b/commands.md
@@ -34,6 +34,7 @@ Command | Descr
[podman-images(1)](/docs/podman-images.1.md) | List images in local storage | [![...](/docs/play.png)](https://podman.io/asciinema/podman/images/) | [Here](https://github.com/containers/Demos/blob/master/podman_cli/podman_images.sh)
[podman-import(1)](/docs/podman-import.1.md) | Import a tarball and save it as a filesystem image |
[podman-info(1)](/docs/podman-info.1.md) | Display system information |
+[podman-init(1)](/docs/podman-init.1.md) | Initialize a container |
[podman-inspect(1)](/docs/podman-inspect.1.md) | Display the configuration of a container or image | [![...](/docs/play.png)](https://asciinema.org/a/133418)
[podman-kill(1)](/docs/podman-kill.1.md) | Kill the main process in one or more running containers |
[podman-load(1)](/docs/podman-load.1.md) | Load an image from a container image archive |
diff --git a/completions/bash/podman b/completions/bash/podman
index b5963f8b9..a02a47190 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -780,6 +780,10 @@ _podman_container_export() {
_podman_export
}
+_podman_container_init() {
+ _podman_init
+}
+
_podman_container_inspect() {
_podman_inspect
}
@@ -2223,6 +2227,27 @@ _podman_ps() {
_complete_ "$options_with_args" "$boolean_options"
}
+_podman_init() {
+ local boolean_options="
+ --all
+ -a
+ --help
+ -h
+ --latest
+ -l
+ "
+ local options_with_args="
+ "
+ case "$cur" in
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_containers_unpauseable
+ ;;
+ esac
+}
+
_podman_start() {
local options_with_args="
--detach-keys
diff --git a/docs/podman-container.1.md b/docs/podman-container.1.md
index 1ba957480..564d791fa 100644
--- a/docs/podman-container.1.md
+++ b/docs/podman-container.1.md
@@ -22,6 +22,7 @@ The container command allows you to manage containers
| exec | [podman-exec(1)](podman-exec.1.md) | Execute a command in a running container. |
| exists | [podman-container-exists(1)](podman-container-exists.1.md) | Check if a container exists in local storage |
| export | [podman-export(1)](podman-export.1.md) | Export a container's filesystem contents as a tar archive. |
+| init | [podman-init(1)](podman-init.1.md) | Initialize a container |
| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. |
| kill | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. |
| list | [podman-ps(1)](podman-ps.1.md) | List the containers on the system.(alias ls) |
diff --git a/docs/podman-init.1.md b/docs/podman-init.1.md
new file mode 100644
index 000000000..aff7a833d
--- /dev/null
+++ b/docs/podman-init.1.md
@@ -0,0 +1,41 @@
+% podman-init(1)
+
+## NAME
+podman\-init - Initialize one or more containers
+
+## SYNOPSIS
+**podman init** [*options*] *container* ...
+
+## DESCRIPTION
+Initialize one or more containers.
+You may use container IDs or names as input.
+Initializing a container performs all tasks necessary for starting the container (mounting filesystems, creating an OCI spec, initializing the container network) but does not start the container.
+If a container is not initialized, the `podman start` and `podman run` commands will do so automatically prior to starting it.
+This command is intended to be used for inspecting or modifying the container's filesystem or OCI spec prior to starting it.
+This can be used to inspect the container before it runs, or debug why a container is failing to run.
+
+## OPTIONS
+
+**--all, a**
+
+Initialize all containers. Containers that have already initialized (including containers that have been started and are running) are ignored.
+
+**--latest, -l**
+Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
+to run containers such as CRI-O, the last started container could be from either of those methods.
+
+The latest option is not supported on the remote client.
+
+## EXAMPLE
+
+podman init 35480fc9d568
+
+podman init test1
+
+podman init --latest
+
+## SEE ALSO
+podman(1), podman-start(1)
+
+## HISTORY
+April 2019, Originally compiled by Matthew Heon <mheon@redhat.com>
diff --git a/docs/podman.1.md b/docs/podman.1.md
index 9c0ca8a7a..ef12cf1cc 100644
--- a/docs/podman.1.md
+++ b/docs/podman.1.md
@@ -147,6 +147,7 @@ the exit codes follow the `chroot` standard, see below:
| [podman-images(1)](podman-images.1.md) | List images in local storage. |
| [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. |
| [podman-info(1)](podman-info.1.md) | Displays Podman related system information. |
+| [podman-init(1)](podman-init.1.md) | Initialize a container |
| [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. |
| [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. |
| [podman-load(1)](podman-load.1.md) | Load an image from a container image archive into container storage. |
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 465b23831..5bfd869b3 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -40,7 +40,7 @@ func (c *Container) Init(ctx context.Context) (err error) {
if !(c.state.State == ContainerStateConfigured ||
c.state.State == ContainerStateStopped ||
c.state.State == ContainerStateExited) {
- return errors.Wrapf(ErrCtrExists, "container %s has already been created in runtime", c.ID())
+ return errors.Wrapf(ErrCtrStateInvalid, "container %s has already been created in runtime", c.ID())
}
// don't recursively start
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 927b71b2b..d0fbc10fe 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -811,8 +811,9 @@ func (c *Container) cleanupRuntime(ctx context.Context) error {
span.SetTag("struct", "container")
defer span.Finish()
- // If the container is not ContainerStateStopped, do nothing
- if c.state.State != ContainerStateStopped {
+ // If the container is not ContainerStateStopped or
+ // ContainerStateCreated, do nothing.
+ if c.state.State != ContainerStateStopped && c.state.State != ContainerStateCreated {
return nil
}
@@ -825,9 +826,14 @@ func (c *Container) cleanupRuntime(ctx context.Context) error {
return err
}
- // Our state is now Exited, as we've removed ourself from
- // the runtime.
- c.state.State = ContainerStateExited
+ // If we were Stopped, we are now Exited, as we've removed ourself
+ // from the runtime.
+ // If we were Created, we are now Configured.
+ if c.state.State == ContainerStateStopped {
+ c.state.State = ContainerStateExited
+ } else if c.state.State == ContainerStateCreated {
+ c.state.State = ContainerStateConfigured
+ }
if c.valid {
if err := c.save(); err != nil {
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 9ec897a60..8be4d9d31 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -133,6 +133,43 @@ func (r *LocalRuntime) KillContainers(ctx context.Context, cli *cliconfig.KillVa
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{
+ ctr.ID(),
+ 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) == libpod.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 (
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index a3a48a564..268ac09e8 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -234,6 +234,31 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa
return ok, failures, nil
}
+// InitContainers initializes container(s) based on Varlink.
+// It returns a list of successful ID(s), a map of failed container ID to error,
+// or an error if a more general error occurred.
+func (r *LocalRuntime) InitContainers(ctx context.Context, cli *cliconfig.InitValues) ([]string, map[string]error, error) {
+ var (
+ ok = []string{}
+ failures = map[string]error{}
+ )
+
+ ids, err := iopodman.GetContainersByContext().Call(r.Conn, cli.All, cli.Latest, cli.InputArgs)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ for _, id := range ids {
+ initialized, err := iopodman.InitContainer().Call(r.Conn, id)
+ if err != nil {
+ failures[id] = err
+ } else {
+ ok = append(ok, initialized)
+ }
+ }
+ return ok, failures, nil
+}
+
// KillContainers sends signal to container(s) based on varlink.
// 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) {
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 872c7bc26..861e3210b 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -365,6 +365,18 @@ func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error
return call.ReplyStartContainer(ctr.ID())
}
+// InitContainer initializes the container given by Varlink.
+func (i *LibpodAPI) InitContainer(call iopodman.VarlinkCall, name string) error {
+ ctr, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyContainerNotFound(name, err.Error())
+ }
+ if err := ctr.Init(getContext()); err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ return call.ReplyInitContainer(ctr.ID())
+}
+
// StopContainer ...
func (i *LibpodAPI) StopContainer(call iopodman.VarlinkCall, name string, timeout int64) error {
ctr, err := i.Runtime.LookupContainer(name)
diff --git a/test/e2e/init_test.go b/test/e2e/init_test.go
new file mode 100644
index 000000000..5865930a5
--- /dev/null
+++ b/test/e2e/init_test.go
@@ -0,0 +1,129 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/containers/libpod/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman init", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ podmanTest.Setup()
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ processTestResult(f)
+
+ })
+
+ It("podman init bogus container", func() {
+ session := podmanTest.Podman([]string{"start", "123456"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(125))
+ })
+
+ It("podman init with no arguments", func() {
+ session := podmanTest.Podman([]string{"start"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(125))
+ })
+
+ It("podman init single container by ID", func() {
+ session := podmanTest.Podman([]string{"create", "-d", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ cid := session.OutputToString()
+ init := podmanTest.Podman([]string{"init", cid})
+ init.WaitWithDefaultTimeout()
+ Expect(init.ExitCode()).To(Equal(0))
+ result := podmanTest.Podman([]string{"inspect", cid})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(conData[0].State.Status).To(Equal("created"))
+ })
+
+ It("podman init single container by name", func() {
+ name := "test1"
+ session := podmanTest.Podman([]string{"create", "--name", name, "-d", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ init := podmanTest.Podman([]string{"init", name})
+ init.WaitWithDefaultTimeout()
+ Expect(init.ExitCode()).To(Equal(0))
+ result := podmanTest.Podman([]string{"inspect", name})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(conData[0].State.Status).To(Equal("created"))
+ })
+
+ It("podman init latest container", func() {
+ session := podmanTest.Podman([]string{"create", "-d", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ init := podmanTest.Podman([]string{"init", "--latest"})
+ init.WaitWithDefaultTimeout()
+ Expect(init.ExitCode()).To(Equal(0))
+ result := podmanTest.Podman([]string{"inspect", "--latest"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(conData[0].State.Status).To(Equal("created"))
+ })
+
+ It("podman init all three containers, one running", func() {
+ session := podmanTest.Podman([]string{"create", "--name", "test1", "-d", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session2 := podmanTest.Podman([]string{"create", "--name", "test2", "-d", ALPINE, "ls"})
+ session2.WaitWithDefaultTimeout()
+ Expect(session2.ExitCode()).To(Equal(0))
+ session3 := podmanTest.Podman([]string{"run", "--name", "test3", "-d", ALPINE, "top"})
+ session3.WaitWithDefaultTimeout()
+ Expect(session3.ExitCode()).To(Equal(0))
+ init := podmanTest.Podman([]string{"init", "--all"})
+ init.WaitWithDefaultTimeout()
+ Expect(init.ExitCode()).To(Equal(0))
+ result := podmanTest.Podman([]string{"inspect", "test1"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ conData := result.InspectContainerToJSON()
+ Expect(conData[0].State.Status).To(Equal("created"))
+ result2 := podmanTest.Podman([]string{"inspect", "test2"})
+ result2.WaitWithDefaultTimeout()
+ Expect(result2.ExitCode()).To(Equal(0))
+ conData2 := result2.InspectContainerToJSON()
+ Expect(conData2[0].State.Status).To(Equal("created"))
+ result3 := podmanTest.Podman([]string{"inspect", "test3"})
+ result3.WaitWithDefaultTimeout()
+ Expect(result3.ExitCode()).To(Equal(0))
+ conData3 := result3.InspectContainerToJSON()
+ Expect(conData3[0].State.Status).To(Equal("running"))
+ })
+
+ It("podman init running container errors", func() {
+ session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ init := podmanTest.Podman([]string{"init", "--latest"})
+ init.WaitWithDefaultTimeout()
+ Expect(init.ExitCode()).To(Equal(125))
+ })
+})