Merge pull request #3039 from mheon/podman_init

Add podman init command
This commit is contained in:
OpenShift Merge Robot
2019-05-02 20:45:44 +02:00
committed by GitHub
18 changed files with 388 additions and 8 deletions

View File

@ -177,6 +177,12 @@ type InfoValues struct {
Format string
}
type InitValues struct {
PodmanCommand
All bool
Latest bool
}
type InspectValues struct {
PodmanCommand
TypeObject string

View File

@ -56,6 +56,7 @@ var (
_diffCommand,
_exportCommand,
_createCommand,
_initCommand,
_killCommand,
_listSubCommand,
_logsCommand,

View File

@ -33,6 +33,8 @@ func outputError(err error) {
ne = errors.New(e.Reason)
case *iopodman.VolumeNotFound:
ne = errors.New(e.Reason)
case *iopodman.InvalidState:
ne = errors.New(e.Reason)
case *iopodman.ErrorOccurred:
ne = errors.New(e.Reason)
default:

64
cmd/podman/init.go Normal file
View File

@ -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)
}

View File

@ -39,6 +39,7 @@ var mainCommands = []*cobra.Command{
&_imagesCommand,
_importCommand,
_infoCommand,
_initCommand,
&_inspectCommand,
_killCommand,
_loadCommand,

View File

@ -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

View File

@ -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
@ -1225,7 +1233,7 @@ error PodNotFound (name: string, reason: string)
# VolumeNotFound means the volume could not be found by the name or ID in local storage.
error VolumeNotFound (id: string, reason: string)
# PodContainerError means a container associated with a pod failed to preform an operation. It contains
# PodContainerError means a container associated with a pod failed to perform an operation. It contains
# a container ID of the container that failed.
error PodContainerError (podname: string, errors: []PodContainerErrorData)
@ -1233,6 +1241,9 @@ error PodContainerError (podname: string, errors: []PodContainerErrorData)
# the pod ID.
error NoContainersInPod (name: string)
# InvalidState indicates that a container or pod was in an improper state for the requested operation
error InvalidState (id: string, reason: string)
# ErrorOccurred is a generic error for an error that occurs during the execution. The actual error message
# is includes as part of the error's text.
error ErrorOccurred (reason: string)

View File

@ -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 |

View File

@ -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

View File

@ -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) |

41
docs/podman-init.1.md Normal file
View File

@ -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>

View File

@ -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. |

View File

@ -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

View File

@ -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.
// 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 {

View File

@ -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 (

View File

@ -248,6 +248,40 @@ 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 {
if cli.All {
switch err.(type) {
case *iopodman.InvalidState:
ok = append(ok, initialized)
default:
failures[id] = err
}
} else {
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) {

View File

@ -365,6 +365,21 @@ 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 {
if errors.Cause(err) == libpod.ErrCtrStateInvalid {
return call.ReplyInvalidState(ctr.ID(), err.Error())
}
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)

129
test/e2e/init_test.go Normal file
View File

@ -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))
})
})