aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE.md8
-rw-r--r--cmd/podman/common.go9
-rw-r--r--cmd/podman/exec.go3
-rw-r--r--cmd/podman/shared/container.go4
-rw-r--r--cmd/podman/start.go16
-rw-r--r--completions/bash/podman2
-rw-r--r--docs/libpod.conf.5.md4
-rw-r--r--docs/podman-create.1.md26
-rw-r--r--docs/podman-exec.1.md8
-rw-r--r--docs/podman-run.1.md26
-rw-r--r--docs/podman-start.1.md2
-rw-r--r--docs/podman.1.md4
-rw-r--r--libpod/boltdb_state.go8
-rw-r--r--libpod/common_test.go6
-rw-r--r--libpod/container.go15
-rw-r--r--libpod/container_api.go4
-rw-r--r--libpod/container_easyjson.go14
-rw-r--r--libpod/container_internal.go30
-rw-r--r--libpod/container_internal_linux.go20
-rw-r--r--libpod/container_internal_test.go2
-rw-r--r--libpod/networking_linux.go22
-rw-r--r--libpod/oci.go6
-rw-r--r--libpod/runtime_ctr.go2
-rw-r--r--libpod/state_test.go8
-rw-r--r--pkg/hooks/exec/exec.go7
-rw-r--r--pkg/hooks/exec/exec_test.go6
-rw-r--r--pkg/hooks/exec/runtimeconfigfilter.go68
-rw-r--r--pkg/hooks/exec/runtimeconfigfilter_test.go266
-rw-r--r--pkg/util/utils.go1
-rw-r--r--test/e2e/checkpoint_test.go45
-rw-r--r--test/e2e/exec_test.go32
-rw-r--r--test/e2e/start_test.go10
-rw-r--r--vendor.conf2
-rw-r--r--vendor/github.com/containers/buildah/imagebuildah/build.go1
-rw-r--r--vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go20
35 files changed, 597 insertions, 110 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index acb2b2bd3..a7663f3e3 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -18,13 +18,11 @@ executes Buildah to perform container builds, and as such the Buildah
maintainers are best equipped to handle these bugs.
-->
-**Is this a BUG REPORT or FEATURE REQUEST?**:
+**Is this a BUG REPORT or FEATURE REQUEST? (leave only one on its own line)**
-[//]: # Uncomment only one, leave it on its own line:
+/kind bug
-[//]: # **kind bug**
-
-[//]: # **kind feature**
+/kind feature
**Description**
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 0fc9a6acc..d934c8699 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -28,6 +28,10 @@ var (
Name: "latest, l",
Usage: "act on the latest pod podman is aware of",
}
+ WorkDirFlag = cli.StringFlag{
+ Name: "workdir, w",
+ Usage: "Working directory inside the container",
+ }
)
const (
@@ -522,10 +526,7 @@ var createFlags = []cli.Flag{
Name: "volumes-from",
Usage: "Mount volumes from the specified container(s) (default [])",
},
- cli.StringFlag{
- Name: "workdir, w",
- Usage: "Working `directory inside the container",
- },
+ WorkDirFlag,
}
func getFormat(c *cli.Context) (string, error) {
diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go
index c03834dea..073e72e64 100644
--- a/cmd/podman/exec.go
+++ b/cmd/podman/exec.go
@@ -34,6 +34,7 @@ var (
Usage: "Sets the username or UID used and optionally the groupname or GID for the specified command",
},
LatestFlag,
+ WorkDirFlag,
}
execDescription = `
podman exec
@@ -108,5 +109,5 @@ func execCmd(c *cli.Context) error {
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}
- return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"))
+ return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"), c.String("workdir"))
}
diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go
index 6c7d8eb52..a904ef75a 100644
--- a/cmd/podman/shared/container.go
+++ b/cmd/podman/shared/container.go
@@ -52,7 +52,7 @@ type PsOptions struct {
// BatchContainerStruct is the return obkect from BatchContainer and contains
// container related information
type BatchContainerStruct struct {
- ConConfig *libpod.Config
+ ConConfig *libpod.ContainerConfig
ConState libpod.ContainerStatus
ExitCode int32
Exited bool
@@ -329,7 +329,7 @@ func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsCon
// locks.
func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) {
var (
- conConfig *libpod.Config
+ conConfig *libpod.ContainerConfig
conState libpod.ContainerStatus
err error
exitCode int32
diff --git a/cmd/podman/start.go b/cmd/podman/start.go
index 8bb386c68..df34deec2 100644
--- a/cmd/podman/start.go
+++ b/cmd/podman/start.go
@@ -27,9 +27,9 @@ var (
Name: "interactive, i",
Usage: "Keep STDIN open even if not attached",
},
- cli.BoolFlag{
+ cli.BoolTFlag{
Name: "sig-proxy",
- Usage: "proxy received signals to the process",
+ Usage: "proxy received signals to the process (default true if attaching, false otherwise)",
},
LatestFlag,
}
@@ -67,8 +67,14 @@ func startCmd(c *cli.Context) error {
return err
}
- if c.Bool("sig-proxy") && !attach {
- return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach")
+ sigProxy := c.BoolT("sig-proxy")
+
+ if sigProxy && !attach {
+ if c.IsSet("sig-proxy") {
+ return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach")
+ } else {
+ sigProxy = false
+ }
}
runtime, err := libpodruntime.GetRuntime(c)
@@ -111,7 +117,7 @@ func startCmd(c *cli.Context) error {
}
// attach to the container and also start it not already running
- err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.String("detach-keys"), c.Bool("sig-proxy"), !ctrRunning)
+ err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.String("detach-keys"), sigProxy, !ctrRunning)
if ctrRunning {
return err
}
diff --git a/completions/bash/podman b/completions/bash/podman
index d65f54690..e23615d52 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1111,6 +1111,8 @@ _podman_exec() {
--env
--user
-u
+ --workdir
+ -w
"
local boolean_options="
--help
diff --git a/docs/libpod.conf.5.md b/docs/libpod.conf.5.md
index c02d247fb..98eb5bece 100644
--- a/docs/libpod.conf.5.md
+++ b/docs/libpod.conf.5.md
@@ -37,7 +37,9 @@ libpod to manage containers.
For the bind-mount conditions, only mounts explicitly requested by the caller via `--volume` are considered. Bind mounts that libpod inserts by default (e.g. `/dev/shm`) are not considered.
- If `hooks_dir` is unset for root callers, Podman and libpod will currently default to `/usr/share/containers/oci/hooks.d` and `/etc/containers/oci/hooks.d` in order of increasing precedence. Using these defaults is deprecated, and callers should migrate to explicitly setting `hooks_dir`.
+ Podman and libpod currently support an additional `precreate` state which is called before the runtime's `create` operation. Unlike the other stages, which receive the container state on their standard input, `precreate` hooks receive the proposed runtime configuration on their standard input. They may alter that configuration as they see fit, and write the altered form to their standard output.
+
+ **WARNING**: the `precreate` hook lets you do powerful things, such as adding additional mounts to the runtime configuration. That power also makes it easy to break things. Before reporting libpod errors, try running your container with `precreate` hooks disabled to see if the problem is due to one of your hooks.
**static_dir**=""
Directory for persistent libpod files (database, etc)
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index 3a75a4b00..178542f0d 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -29,7 +29,7 @@ option can be set multiple times.
Add an annotation to the container. The format is key=value.
The **--annotation** option can be set multiple times.
-**-a**, **--attach**=[]
+**--attach**, **-a**=[]
Attach to STDIN, STDOUT or STDERR.
@@ -158,7 +158,7 @@ If you have four memory nodes on your system (0-3), use `--cpuset-mems=0,1`
then processes in your container will only use memory from the first
two memory nodes.
-**-d**, **--detach**=*true*|*false*
+**--detach**, **-d**=*true*|*false*
Detached mode: run the container in the background and print the new container ID. The default is *false*.
@@ -230,7 +230,7 @@ ENTRYPOINT.
You need to specify multi option commands in the form of a json string.
-**-e**, **--env**=[]
+**--env**, **-e**=[]
Set environment variables
@@ -284,7 +284,7 @@ Run an init inside the container that forwards signals and reaps processes.
Path to the container-init binary.
-**-i**, **--interactive**=*true*|*false*
+**--interactive**, **-i**=*true*|*false*
Keep STDIN open even if not attached. The default is *false*.
@@ -315,7 +315,7 @@ is not limited. If you specify a limit, it may be rounded up to a multiple
of the operating system's page size and the value can be very large,
millions of trillions.
-**-l**, **--label**=[]
+**--label**, **-l**=[]
Add metadata to a container (e.g., --label com.example.key=value)
@@ -347,7 +347,7 @@ according to RFC4862.
Not currently supported
-**-m**, **--memory**=""
+**--memory**, **-m**=""
Memory limit (format: <number>[<unit>], where unit = b, k, m or g)
@@ -426,7 +426,7 @@ to the container with **--name** then it will generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
-**--net**, **--network**="*bridge*"
+**--network**, **--net**="*bridge*"
Set the Network mode for the container
'bridge': create a network stack on the default bridge
@@ -480,7 +480,7 @@ to all devices on the host, turns off graphdriver mount options, as well as
turning off most of the security measures protecting the host from the
container.
-**-p**, **--publish**=[]
+**--publish**, **-p**=[]
Publish a container's port, or range of ports, to the host
@@ -492,7 +492,7 @@ but not `podman run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanR
With ip: `podman run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage`
Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT`
-**-P**, **--publish-all**=*true*|*false*
+**--publish-all**, **-P**=*true*|*false*
Publish all exposed ports to random ports on the host interfaces. The default is *false*.
@@ -621,7 +621,7 @@ options are the same as the Linux default `mount` flags. If you do not specify
any options, the systems uses the following options:
`rw,noexec,nosuid,nodev,size=65536k`.
-**-t**, **--tty**=*true*|*false*
+**--tty**, **-t**=*true*|*false*
Allocate a pseudo-TTY. The default is *false*.
@@ -642,7 +642,7 @@ The following example maps uids 0-2000 in the container to the uids 30000-31999
Ulimit options
-**-u**, **--user**=""
+**--user**, **-u**=""
Sets the username or UID used and optionally the groupname or GID for the specified command.
@@ -665,7 +665,7 @@ Set the UTS mode for the container
**ns**: specify the usernamespace to use.
Note: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
-**-v**|**--volume**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]
+**--volume**, **-v**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman
bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the podman
@@ -764,7 +764,7 @@ If the location of the volume from the source container overlaps with
data residing on a target container, then the volume hides
that data on the target.
-**-w**, **--workdir**=""
+**--workdir**, **-w**=""
Working directory inside the container
diff --git a/docs/podman-exec.1.md b/docs/podman-exec.1.md
index 284fa5a4a..77317b0ca 100644
--- a/docs/podman-exec.1.md
+++ b/docs/podman-exec.1.md
@@ -38,6 +38,14 @@ Sets the username or UID used and optionally the groupname or GID for the specif
The following examples are all valid:
--user [user | user:group | uid | uid:gid | user:gid | uid:group ]
+**--workdir**, **-w**=""
+
+Working directory inside the container
+
+The default working directory for running binaries within a container is the root directory (/).
+The image developer can set a different default with the WORKDIR instruction, which can be overridden
+when creating the container.
+
## SEE ALSO
podman(1), podman-run(1)
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index 971b8829a..8b96ea6d9 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -41,7 +41,7 @@ option can be set multiple times.
Add an annotation to the container. The format is key=value.
The **--annotation** option can be set multiple times.
-**-a**, **--attach**=[]
+**--attach**, **-a**=[]
Attach to STDIN, STDOUT or STDERR.
@@ -162,7 +162,7 @@ If you have four memory nodes on your system (0-3), use `--cpuset-mems=0,1`
then processes in your container will only use memory from the first
two memory nodes.
-**-d**, **--detach**=*true*|*false*
+**--detach**, **-d**=*true*|*false*
Detached mode: run the container in the background and print the new container ID. The default is *false*.
@@ -235,7 +235,7 @@ ENTRYPOINT.
You need to specify multi option commands in the form of a json string.
-**-e**, **--env**=[]
+**--env**, **-e**=[]
Set environment variables
@@ -293,7 +293,7 @@ Run an init inside the container that forwards signals and reaps processes.
Path to the container-init binary.
-**-i**, **--interactive**=*true*|*false*
+**--interactive**, **-i**=*true*|*false*
Keep STDIN open even if not attached. The default is *false*.
@@ -327,7 +327,7 @@ is not limited. If you specify a limit, it may be rounded up to a multiple
of the operating system's page size and the value can be very large,
millions of trillions.
-**-l**, **--label**=[]
+**--label**, **-l**=[]
Add metadata to a container (e.g., --label com.example.key=value)
@@ -359,7 +359,7 @@ according to RFC4862.
Not currently supported
-**-m**, **--memory**=""
+**--memory**, **-m**=""
Memory limit (format: <number>[<unit>], where unit = b, k, m or g)
@@ -408,7 +408,7 @@ to the container with **--name** then it will generate a random
string name. The name is useful any place you need to identify a container.
This works for both background and foreground containers.
-**--net**, **--network**="*bridge*"
+**--network**, **--net**="*bridge*"
Set the Network mode for the container:
- `bridge`: create a network stack on the default bridge
@@ -464,7 +464,7 @@ to all devices on the host, turns off graphdriver mount options, as well as
turning off most of the security measures protecting the host from the
container.
-**-p**, **--publish**=[]
+**--publish**, **-p**=[]
Publish a container's port, or range of ports, to the host
@@ -480,7 +480,7 @@ With ip: `podman run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t s
Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT`
-**-P**, **--publish-all**=*true*|*false*
+**--publish-all**, **-P**=*true*|*false*
Publish all exposed ports to random ports on the host interfaces. The default is *false*.
@@ -623,7 +623,7 @@ options are the same as the Linux default `mount` flags. If you do not specify
any options, the systems uses the following options:
`rw,noexec,nosuid,nodev,size=65536k`.
-**-t**, **--tty**=*true*|*false*
+**--tty**, **-t**=*true*|*false*
Allocate a pseudo-TTY. The default is *false*.
@@ -645,7 +645,7 @@ The example maps uids 0-2000 in the container to the uids 30000-31999 on the hos
Ulimit options
-**-u**, **--user**=""
+**--user**, **-u**=""
Sets the username or UID used and optionally the groupname or GID for the specified command.
@@ -703,7 +703,7 @@ Current supported mount TYPES are bind, and tmpfs.
ยท tmpfs-mode: File mode of the tmpfs in octal. (e.g. 700 or 0700.) Defaults to 1777 in Linux.
-**-v**|**--volume**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]
+**--volume**, **-v**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, podman
bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the podman
@@ -802,7 +802,7 @@ If the location of the volume from the source container overlaps with
data residing on a target container, then the volume hides
that data on the target.
-**-w**, **--workdir**=""
+**--workdir**, **-w**=""
Working directory inside the container
diff --git a/docs/podman-start.1.md b/docs/podman-start.1.md
index cfd44ac3a..f16a20efa 100644
--- a/docs/podman-start.1.md
+++ b/docs/podman-start.1.md
@@ -35,7 +35,7 @@ to run containers such as CRI-O, the last started container could be from either
**--sig-proxy**=*true*|*false*
-Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied. The default is false.
+Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied. The default is *true* when attaching, *false* otherwise.
## EXAMPLE
diff --git a/docs/podman.1.md b/docs/podman.1.md
index bde349e6f..a73ebb55e 100644
--- a/docs/podman.1.md
+++ b/docs/podman.1.md
@@ -43,6 +43,10 @@ For the bind-mount conditions, only mounts explicitly requested by the caller vi
If `--hooks-dir` is unset for root callers, Podman and libpod will currently default to `/usr/share/containers/oci/hooks.d` and `/etc/containers/oci/hooks.d` in order of increasing precedence. Using these defaults is deprecated, and callers should migrate to explicitly setting `--hooks-dir`.
+Podman and libpod currently support an additional `precreate` state which is called before the runtime's `create` operation. Unlike the other stages, which receive the container state on their standard input, `precreate` hooks receive the proposed runtime configuration on their standard input. They may alter that configuration as they see fit, and write the altered form to their standard output.
+
+**WARNING**: the `precreate` hook lets you do powerful things, such as adding additional mounts to the runtime configuration. That power also makes it easy to break things. Before reporting libpod errors, try running your container with `precreate` hooks disabled to see if the problem is due to one of your hooks.
+
**--log-level**
Log messages above specified level: debug, info, warn, error (default), fatal or panic
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index ba8f7375a..b154d8bda 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -322,7 +322,7 @@ func (s *BoltState) Container(id string) (*Container, error) {
ctrID := []byte(id)
ctr := new(Container)
- ctr.config = new(Config)
+ ctr.config = new(ContainerConfig)
ctr.state = new(containerState)
db, err := s.getDBCon()
@@ -358,7 +358,7 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
}
ctr := new(Container)
- ctr.config = new(Config)
+ ctr.config = new(ContainerConfig)
ctr.state = new(containerState)
db, err := s.getDBCon()
@@ -751,7 +751,7 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
}
ctr := new(Container)
- ctr.config = new(Config)
+ ctr.config = new(ContainerConfig)
ctr.state = new(containerState)
if err := s.getContainerFromDB(id, ctr, ctrBucket); err != nil {
@@ -1137,7 +1137,7 @@ func (s *BoltState) PodContainers(pod *Pod) ([]*Container, error) {
// Iterate through all containers in the pod
err = podCtrs.ForEach(func(id, val []byte) error {
newCtr := new(Container)
- newCtr.config = new(Config)
+ newCtr.config = new(ContainerConfig)
newCtr.state = new(containerState)
ctrs = append(ctrs, newCtr)
diff --git a/libpod/common_test.go b/libpod/common_test.go
index 882468a3a..efbb5f404 100644
--- a/libpod/common_test.go
+++ b/libpod/common_test.go
@@ -17,7 +17,7 @@ import (
func getTestContainer(id, name string, manager lock.Manager) (*Container, error) {
ctr := &Container{
- config: &Config{
+ config: &ContainerConfig{
ID: id,
Name: name,
RootfsImageID: id,
@@ -165,8 +165,8 @@ func testContainersEqual(t *testing.T, a, b *Container, allowedEmpty bool) {
require.NotNil(t, a.state)
require.NotNil(t, b.state)
- aConfig := new(Config)
- bConfig := new(Config)
+ aConfig := new(ContainerConfig)
+ bConfig := new(ContainerConfig)
aState := new(containerState)
bState := new(containerState)
diff --git a/libpod/container.go b/libpod/container.go
index 4e5088b32..d0eb6a992 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -113,7 +113,7 @@ func (ns LinuxNS) String() string {
// syncContainer() immediately after locking.
// ffjson: skip
type Container struct {
- config *Config
+ config *ContainerConfig
state *containerState
@@ -128,6 +128,11 @@ type Container struct {
rootlessSlirpSyncR *os.File
rootlessSlirpSyncW *os.File
+
+ // A restored container should have the same IP address as before
+ // being checkpointed. If requestedIP is set it will be used instead
+ // of config.StaticIP.
+ requestedIP net.IP
}
// containerState contains the current state of the container
@@ -200,11 +205,11 @@ type ExecSession struct {
PID int `json:"pid"`
}
-// Config contains all information that was used to create the
+// ContainerConfig contains all information that was used to create the
// container. It may not be changed once created.
// It is stored, read-only, on disk
// easyjson:json
-type Config struct {
+type ContainerConfig struct {
Spec *spec.Spec `json:"spec"`
ID string `json:"id"`
Name string `json:"name"`
@@ -385,8 +390,8 @@ func (t ContainerStatus) String() string {
// Unlocked
// Config returns the configuration used to create the container
-func (c *Container) Config() *Config {
- returnConfig := new(Config)
+func (c *Container) Config() *ContainerConfig {
+ returnConfig := new(ContainerConfig)
deepcopier.Copy(c.config).To(returnConfig)
return returnConfig
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 09bc46905..4eaf737b0 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -262,7 +262,7 @@ func (c *Container) Kill(signal uint) error {
// Exec starts a new process inside the container
// TODO allow specifying streams to attach to
// TODO investigate allowing exec without attaching
-func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) error {
+func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir string) error {
var capList []string
locked := false
@@ -324,7 +324,7 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e
logrus.Debugf("Creating new exec session in container %s with session id %s", c.ID(), sessionID)
- execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, hostUser, sessionID)
+ execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, hostUser, sessionID)
if err != nil {
return errors.Wrapf(err, "error exec %s", c.ID())
}
diff --git a/libpod/container_easyjson.go b/libpod/container_easyjson.go
index ae0972cf6..f1cb09bcc 100644
--- a/libpod/container_easyjson.go
+++ b/libpod/container_easyjson.go
@@ -1,6 +1,6 @@
// +build seccomp ostree selinux varlink exclude_graphdriver_devicemapper
-// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
+// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT
package libpod
@@ -1238,7 +1238,7 @@ func (v *ExecSession) UnmarshalJSON(data []byte) error {
func (v *ExecSession) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson1dbef17bDecodeGithubComContainersLibpodLibpod1(l, v)
}
-func easyjson1dbef17bDecodeGithubComContainersLibpodLibpod2(in *jlexer.Lexer, out *Config) {
+func easyjson1dbef17bDecodeGithubComContainersLibpodLibpod2(in *jlexer.Lexer, out *ContainerConfig) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
@@ -1722,7 +1722,7 @@ func easyjson1dbef17bDecodeGithubComContainersLibpodLibpod2(in *jlexer.Lexer, ou
in.Consumed()
}
}
-func easyjson1dbef17bEncodeGithubComContainersLibpodLibpod2(out *jwriter.Writer, in Config) {
+func easyjson1dbef17bEncodeGithubComContainersLibpodLibpod2(out *jwriter.Writer, in ContainerConfig) {
out.RawByte('{')
first := true
_ = first
@@ -2427,26 +2427,26 @@ func easyjson1dbef17bEncodeGithubComContainersLibpodLibpod2(out *jwriter.Writer,
}
// MarshalJSON supports json.Marshaler interface
-func (v Config) MarshalJSON() ([]byte, error) {
+func (v ContainerConfig) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson1dbef17bEncodeGithubComContainersLibpodLibpod2(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
-func (v Config) MarshalEasyJSON(w *jwriter.Writer) {
+func (v ContainerConfig) MarshalEasyJSON(w *jwriter.Writer) {
easyjson1dbef17bEncodeGithubComContainersLibpodLibpod2(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
-func (v *Config) UnmarshalJSON(data []byte) error {
+func (v *ContainerConfig) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson1dbef17bDecodeGithubComContainersLibpodLibpod2(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
-func (v *Config) UnmarshalEasyJSON(l *jlexer.Lexer) {
+func (v *ContainerConfig) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson1dbef17bDecodeGithubComContainersLibpodLibpod2(l, v)
}
func easyjson1dbef17bDecodeGithubComContainersLibpodVendorGithubComCriOOcicniPkgOcicni(in *jlexer.Lexer, out *ocicni.PortMapping) {
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index cc4c36bc9..69df33bc9 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1181,6 +1181,7 @@ func (c *Container) saveSpec(spec *spec.Spec) error {
return nil
}
+// Warning: precreate hooks may alter 'config' in place.
func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (extensionStageHooks map[string][]spec.Hook, err error) {
var locale string
var ok bool
@@ -1209,13 +1210,13 @@ func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (exten
}
}
+ allHooks := make(map[string][]spec.Hook)
if c.runtime.config.HooksDir == nil {
if rootless.IsRootless() {
return nil, nil
}
- allHooks := make(map[string][]spec.Hook)
for _, hDir := range []string{hooks.DefaultDir, hooks.OverrideDir} {
- manager, err := hooks.New(ctx, []string{hDir}, []string{"poststop"}, lang)
+ manager, err := hooks.New(ctx, []string{hDir}, []string{"precreate", "poststop"}, lang)
if err != nil {
if os.IsNotExist(err) {
continue
@@ -1233,19 +1234,32 @@ func (c *Container) setupOCIHooks(ctx context.Context, config *spec.Spec) (exten
allHooks[i] = hook
}
}
- return allHooks, nil
+ } else {
+ manager, err := hooks.New(ctx, c.runtime.config.HooksDir, []string{"precreate", "poststop"}, lang)
+ if err != nil {
+ if os.IsNotExist(err) {
+ logrus.Warnf("Requested OCI hooks directory %q does not exist", c.runtime.config.HooksDir)
+ return nil, nil
+ }
+ return nil, err
+ }
+
+ allHooks, err = manager.Hooks(config, c.Spec().Annotations, len(c.config.UserVolumes) > 0)
+ if err != nil {
+ return nil, err
+ }
}
- manager, err := hooks.New(ctx, c.runtime.config.HooksDir, []string{"poststop"}, lang)
+ hookErr, err := exec.RuntimeConfigFilter(ctx, allHooks["precreate"], config, exec.DefaultPostKillTimeout)
if err != nil {
- if os.IsNotExist(err) {
- logrus.Warnf("Requested OCI hooks directory %q does not exist", c.runtime.config.HooksDir)
- return nil, nil
+ logrus.Warnf("container %s: precreate hook: %v", c.ID(), err)
+ if hookErr != nil && hookErr != err {
+ logrus.Debugf("container %s: precreate hook (hook error): %v", c.ID(), hookErr)
}
return nil, err
}
- return manager.Hooks(config, c.Spec().Annotations, len(c.config.UserVolumes) > 0)
+ return allHooks, nil
}
// mount mounts the container's root filesystem
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 0745b7732..582a4c3e7 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -228,10 +228,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
}
- if c.state.ExtensionStageHooks, err = c.setupOCIHooks(ctx, g.Config); err != nil {
- return nil, errors.Wrapf(err, "error setting up OCI Hooks")
- }
-
// Bind builtin image volumes
if c.config.Rootfs == "" && c.config.ImageVolumes {
if err := c.addLocalVolumes(ctx, &g, execUser); err != nil {
@@ -384,6 +380,12 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
logrus.Debugf("set root propagation to %q", rootPropagation)
g.SetLinuxRootPropagation(rootPropagation)
}
+
+ // Warning: precreate hooks may alter g.Config in place.
+ if c.state.ExtensionStageHooks, err = c.setupOCIHooks(ctx, g.Config); err != nil {
+ return nil, errors.Wrapf(err, "error setting up OCI Hooks")
+ }
+
return g.Config, nil
}
@@ -547,10 +549,8 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}
}
if IP != nil {
- env := fmt.Sprintf("IP=%s", IP)
// Tell CNI which IP address we want.
- os.Setenv("CNI_ARGS", env)
- logrus.Debugf("Restoring container with %s", env)
+ c.requestedIP = IP
}
}
@@ -566,12 +566,6 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return err
}
- // TODO: use existing way to request static IPs, once it is merged in ocicni
- // https://github.com/cri-o/ocicni/pull/23/
-
- // CNI_ARGS was used to request a certain IP address. Unconditionally remove it.
- os.Unsetenv("CNI_ARGS")
-
// Read config
jsonPath := filepath.Join(c.bundlePath(), "config.json")
logrus.Debugf("generate.NewFromFile at %v", jsonPath)
diff --git a/libpod/container_internal_test.go b/libpod/container_internal_test.go
index 51fb58713..124f1d20e 100644
--- a/libpod/container_internal_test.go
+++ b/libpod/container_internal_test.go
@@ -28,7 +28,7 @@ func TestPostDeleteHooks(t *testing.T) {
statePath := filepath.Join(dir, "state")
copyPath := filepath.Join(dir, "copy")
c := Container{
- config: &Config{
+ config: &ContainerConfig{
ID: "123abc",
Spec: &rspec.Spec{
Annotations: map[string]string{
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 43d0a61a4..a343bee6a 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -50,7 +50,16 @@ func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, port
// Create and configure a new network namespace for a container
func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) ([]*cnitypes.Result, error) {
- podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, ctr.config.StaticIP)
+ var requestedIP net.IP
+ if ctr.requestedIP != nil {
+ requestedIP = ctr.requestedIP
+ // cancel request for a specific IP in case the container is reused later
+ ctr.requestedIP = nil
+ } else {
+ requestedIP = ctr.config.StaticIP
+ }
+
+ podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
results, err := r.netPlugin.SetUpPod(podNetwork)
if err != nil {
@@ -258,7 +267,16 @@ func (r *Runtime) teardownNetNS(ctr *Container) error {
logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID())
- podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, ctr.config.StaticIP)
+ var requestedIP net.IP
+ if ctr.requestedIP != nil {
+ requestedIP = ctr.requestedIP
+ // cancel request for a specific IP in case the container is reused later
+ ctr.requestedIP = nil
+ } else {
+ requestedIP = ctr.config.StaticIP
+ }
+
+ podNetwork := r.getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.Networks, ctr.config.PortMappings, requestedIP)
// The network may have already been torn down, so don't fail here, just log
if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
diff --git a/libpod/oci.go b/libpod/oci.go
index 093bfdd35..31c1a7e85 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -728,7 +728,7 @@ func (r *OCIRuntime) unpauseContainer(ctr *Container) error {
// TODO: Add --detach support
// TODO: Convert to use conmon
// TODO: add --pid-file and use that to generate exec session tracking
-func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, user, sessionID string) (*exec.Cmd, error) {
+func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, cwd, user, sessionID string) (*exec.Cmd, error) {
if len(cmd) == 0 {
return nil, errors.Wrapf(ErrInvalidArg, "must provide a command to execute")
}
@@ -749,7 +749,9 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty
args = append(args, "exec")
- args = append(args, "--cwd", c.config.Spec.Process.Cwd)
+ if cwd != "" {
+ args = append(args, "--cwd", cwd)
+ }
args = append(args, "--pid-file", c.execPidPath(sessionID))
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index e6f2c962f..ab79fe5fb 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -47,7 +47,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
}
ctr := new(Container)
- ctr.config = new(Config)
+ ctr.config = new(ContainerConfig)
ctr.state = new(containerState)
ctr.config.ID = stringid.GenerateNonCryptoID()
diff --git a/libpod/state_test.go b/libpod/state_test.go
index ee4201b1c..4bd00ab55 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -156,7 +156,7 @@ func TestGetContainerPodSameIDFails(t *testing.T) {
func TestAddInvalidContainerFails(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- err := state.AddContainer(&Container{config: &Config{ID: "1234"}})
+ err := state.AddContainer(&Container{config: &ContainerConfig{ID: "1234"}})
assert.Error(t, err)
})
}
@@ -756,7 +756,7 @@ func TestUpdateContainerNotInDatabaseReturnsError(t *testing.T) {
func TestUpdateInvalidContainerReturnsError(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- err := state.UpdateContainer(&Container{config: &Config{ID: "1234"}})
+ err := state.UpdateContainer(&Container{config: &ContainerConfig{ID: "1234"}})
assert.Error(t, err)
})
}
@@ -780,7 +780,7 @@ func TestUpdateContainerNotInNamespaceReturnsError(t *testing.T) {
func TestSaveInvalidContainerReturnsError(t *testing.T) {
runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
- err := state.SaveContainer(&Container{config: &Config{ID: "1234"}})
+ err := state.SaveContainer(&Container{config: &ContainerConfig{ID: "1234"}})
assert.Error(t, err)
})
}
@@ -2604,7 +2604,7 @@ func TestAddContainerToPodInvalidCtr(t *testing.T) {
err = state.AddPod(testPod)
assert.NoError(t, err)
- err = state.AddContainerToPod(testPod, &Container{config: &Config{ID: "1234"}})
+ err = state.AddContainerToPod(testPod, &Container{config: &ContainerConfig{ID: "1234"}})
assert.Error(t, err)
ctrs, err := state.PodContainersByID(testPod)
diff --git a/pkg/hooks/exec/exec.go b/pkg/hooks/exec/exec.go
index 94469b1d2..0dd091561 100644
--- a/pkg/hooks/exec/exec.go
+++ b/pkg/hooks/exec/exec.go
@@ -10,6 +10,7 @@ import (
"time"
rspec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
)
// DefaultPostKillTimeout is the recommended default post-kill timeout.
@@ -42,7 +43,11 @@ func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer,
}
exit := make(chan error, 1)
go func() {
- exit <- cmd.Wait()
+ err := cmd.Wait()
+ if err != nil {
+ err = errors.Wrapf(err, "executing %v", cmd.Args)
+ }
+ exit <- err
}()
select {
diff --git a/pkg/hooks/exec/exec_test.go b/pkg/hooks/exec/exec_test.go
index 62e45ff3a..7aac315cb 100644
--- a/pkg/hooks/exec/exec_test.go
+++ b/pkg/hooks/exec/exec_test.go
@@ -163,14 +163,14 @@ func TestRunCancel(t *testing.T) {
name: "context timeout",
contextTimeout: time.Duration(1) * time.Second,
expectedStdout: "waiting\n",
- expectedHookError: "^signal: killed$",
+ expectedHookError: "^executing \\[sh -c echo waiting; sleep 2; echo done]: signal: killed$",
expectedRunError: context.DeadlineExceeded,
},
{
name: "hook timeout",
hookTimeout: &one,
expectedStdout: "waiting\n",
- expectedHookError: "^signal: killed$",
+ expectedHookError: "^executing \\[sh -c echo waiting; sleep 2; echo done]: signal: killed$",
expectedRunError: context.DeadlineExceeded,
},
} {
@@ -207,7 +207,7 @@ func TestRunKillTimeout(t *testing.T) {
}
hookErr, err := Run(ctx, hook, []byte("{}"), nil, nil, time.Duration(0))
assert.Equal(t, context.DeadlineExceeded, err)
- assert.Regexp(t, "^(failed to reap process within 0s of the kill signal|signal: killed)$", hookErr)
+ assert.Regexp(t, "^(failed to reap process within 0s of the kill signal|executing \\[sh -c sleep 1]: signal: killed)$", hookErr)
}
func init() {
diff --git a/pkg/hooks/exec/runtimeconfigfilter.go b/pkg/hooks/exec/runtimeconfigfilter.go
new file mode 100644
index 000000000..c6971f680
--- /dev/null
+++ b/pkg/hooks/exec/runtimeconfigfilter.go
@@ -0,0 +1,68 @@
+package exec
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "reflect"
+ "time"
+
+ "github.com/davecgh/go-spew/spew"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/pmezard/go-difflib/difflib"
+ "github.com/sirupsen/logrus"
+)
+
+var spewConfig = spew.ConfigState{
+ Indent: " ",
+ DisablePointerAddresses: true,
+ DisableCapacities: true,
+ SortKeys: true,
+}
+
+// RuntimeConfigFilter calls a series of hooks. But instead of
+// passing container state on their standard input,
+// RuntimeConfigFilter passes the proposed runtime configuration (and
+// reads back a possibly-altered form from their standard output).
+func RuntimeConfigFilter(ctx context.Context, hooks []spec.Hook, config *spec.Spec, postKillTimeout time.Duration) (hookErr, err error) {
+ data, err := json.Marshal(config)
+ for i, hook := range hooks {
+ var stdout bytes.Buffer
+ hookErr, err = Run(ctx, &hook, data, &stdout, nil, postKillTimeout)
+ if err != nil {
+ return hookErr, err
+ }
+
+ data = stdout.Bytes()
+ var newConfig spec.Spec
+ err = json.Unmarshal(data, &newConfig)
+ if err != nil {
+ logrus.Debugf("invalid JSON from config-filter hook %d:\n%s", i, string(data))
+ return nil, errors.Wrapf(err, "unmarshal output from config-filter hook %d", i)
+ }
+
+ if !reflect.DeepEqual(config, &newConfig) {
+ old := spewConfig.Sdump(config)
+ new := spewConfig.Sdump(&newConfig)
+ diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
+ A: difflib.SplitLines(old),
+ B: difflib.SplitLines(new),
+ FromFile: "Old",
+ FromDate: "",
+ ToFile: "New",
+ ToDate: "",
+ Context: 1,
+ })
+ if err == nil {
+ logrus.Debugf("precreate hook %d made configuration changes:\n%s", i, diff)
+ } else {
+ logrus.Warnf("precreate hook %d made configuration changes, but we could not compute a diff: %v", i, err)
+ }
+ }
+
+ *config = newConfig
+ }
+
+ return nil, nil
+}
diff --git a/pkg/hooks/exec/runtimeconfigfilter_test.go b/pkg/hooks/exec/runtimeconfigfilter_test.go
new file mode 100644
index 000000000..52d590d14
--- /dev/null
+++ b/pkg/hooks/exec/runtimeconfigfilter_test.go
@@ -0,0 +1,266 @@
+package exec
+
+import (
+ "context"
+ "encoding/json"
+ "os"
+ "testing"
+ "time"
+
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/pkg/errors"
+ "github.com/stretchr/testify/assert"
+)
+
+func pointerInt(value int) *int {
+ return &value
+}
+
+func pointerUInt32(value uint32) *uint32 {
+ return &value
+}
+
+func pointerFileMode(value os.FileMode) *os.FileMode {
+ return &value
+}
+
+func TestRuntimeConfigFilter(t *testing.T) {
+ unexpectedEndOfJSONInput := json.Unmarshal([]byte("{\n"), nil)
+
+ for _, test := range []struct {
+ name string
+ contextTimeout time.Duration
+ hooks []spec.Hook
+ input *spec.Spec
+ expected *spec.Spec
+ expectedHookError string
+ expectedRunError error
+ }{
+ {
+ name: "no-op",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", "cat"},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ },
+ {
+ name: "device injection",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ Linux: &spec.Linux{
+ Devices: []spec.LinuxDevice{
+ {
+ Path: "/dev/fuse",
+ Type: "c",
+ Major: 10,
+ Minor: 229,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ },
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ Linux: &spec.Linux{
+ Devices: []spec.LinuxDevice{
+ {
+ Path: "/dev/fuse",
+ Type: "c",
+ Major: 10,
+ Minor: 229,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ {
+ Path: "/dev/sda",
+ Type: "b",
+ Major: 8,
+ Minor: 0,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "chaining",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", `sed 's|\("gid":0}\)|\1,{"path": "/dev/sda","type":"b","major":8,"minor":0,"fileMode":384,"uid":0,"gid":0}|'`},
+ },
+ {
+ Path: path,
+ Args: []string{"sh", "-c", `sed 's|/dev/sda|/dev/sdb|'`},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ Linux: &spec.Linux{
+ Devices: []spec.LinuxDevice{
+ {
+ Path: "/dev/fuse",
+ Type: "c",
+ Major: 10,
+ Minor: 229,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ },
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ Linux: &spec.Linux{
+ Devices: []spec.LinuxDevice{
+ {
+ Path: "/dev/fuse",
+ Type: "c",
+ Major: 10,
+ Minor: 229,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ {
+ Path: "/dev/sdb",
+ Type: "b",
+ Major: 8,
+ Minor: 0,
+ FileMode: pointerFileMode(0600),
+ UID: pointerUInt32(0),
+ GID: pointerUInt32(0),
+ },
+ },
+ },
+ },
+ },
+ {
+ name: "context timeout",
+ contextTimeout: time.Duration(1) * time.Second,
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", "sleep 2"},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$",
+ expectedRunError: context.DeadlineExceeded,
+ },
+ {
+ name: "hook timeout",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", "sleep 2"},
+ Timeout: pointerInt(1),
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expectedHookError: "^executing \\[sh -c sleep 2]: signal: killed$",
+ expectedRunError: context.DeadlineExceeded,
+ },
+ {
+ name: "invalid JSON",
+ hooks: []spec.Hook{
+ {
+ Path: path,
+ Args: []string{"sh", "-c", "echo '{'"},
+ },
+ },
+ input: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expected: &spec.Spec{
+ Version: "1.0.0",
+ Root: &spec.Root{
+ Path: "rootfs",
+ },
+ },
+ expectedRunError: unexpectedEndOfJSONInput,
+ },
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ ctx := context.Background()
+ if test.contextTimeout > 0 {
+ var cancel context.CancelFunc
+ ctx, cancel = context.WithTimeout(ctx, test.contextTimeout)
+ defer cancel()
+ }
+ hookErr, err := RuntimeConfigFilter(ctx, test.hooks, test.input, DefaultPostKillTimeout)
+ assert.Equal(t, test.expectedRunError, errors.Cause(err))
+ if test.expectedHookError == "" {
+ if hookErr != nil {
+ t.Fatal(hookErr)
+ }
+ } else {
+ assert.Regexp(t, test.expectedHookError, hookErr.Error())
+ }
+ assert.Equal(t, test.expected, test.input)
+ })
+ }
+}
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index 20ba20a52..2b752afe1 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -316,6 +316,7 @@ func GetDefaultStoreOptions() (storage.StoreOptions, string, error) {
storageConf := StorageConfigFile()
if _, err := os.Stat(storageConf); err == nil {
+ storageOpts = storage.StoreOptions{}
storage.ReloadConfigurationFile(storageConf, &storageOpts)
} else if os.IsNotExist(err) {
os.MkdirAll(filepath.Dir(storageConf), 0755)
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 4e892d11c..ca2f35fc9 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -199,14 +199,17 @@ var _ = Describe("Podman checkpoint", func() {
})
It("podman checkpoint container with established tcp connections", func() {
- Skip("Seems to not work (yet) in CI")
podmanTest.RestoreArtifact(redis)
- session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "--network", "host", "-d", redis})
+ session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "-d", redis})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
+ IP := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"})
+ IP.WaitWithDefaultTimeout()
+ Expect(IP.ExitCode()).To(Equal(0))
+
// Open a network connection to the redis server
- conn, err := net.Dial("tcp", "127.0.0.1:6379")
+ conn, err := net.Dial("tcp", IP.OutputToString()+":6379")
if err != nil {
os.Exit(1)
}
@@ -287,4 +290,40 @@ var _ = Describe("Podman checkpoint", func() {
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
})
+ It("podman checkpoint and restore container with same IP", func() {
+ session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "--name", "test_name", "-d", ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ IPBefore := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"})
+ IPBefore.WaitWithDefaultTimeout()
+ Expect(IPBefore.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"container", "checkpoint", "test_name"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+ result = podmanTest.Podman([]string{"container", "restore", "test_name"})
+ result.WaitWithDefaultTimeout()
+
+ IPAfter := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"})
+ IPAfter.WaitWithDefaultTimeout()
+ Expect(IPAfter.ExitCode()).To(Equal(0))
+
+ // Check that IP address did not change between checkpointing and restoring
+ Expect(IPBefore.OutputToString()).To(Equal(IPAfter.OutputToString()))
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+ result = podmanTest.Podman([]string{"rm", "-fa"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+
})
diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go
index fec80717f..a181501a5 100644
--- a/test/e2e/exec_test.go
+++ b/test/e2e/exec_test.go
@@ -127,4 +127,36 @@ var _ = Describe("Podman exec", func() {
Expect(session2.ExitCode()).To(Equal(0))
Expect(session2.OutputToString()).To(Equal(testUser))
})
+
+ It("podman exec simple working directory test", func() {
+ setup := podmanTest.RunTopContainer("test1")
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ session := podmanTest.Podman([]string{"exec", "-l", "--workdir", "/tmp", "pwd"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("/tmp")
+ Expect(match).Should(BeTrue())
+
+ session = podmanTest.Podman([]string{"exec", "-l", "-w", "/tmp", "pwd"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ = session.GrepString("/tmp")
+ Expect(match).Should(BeTrue())
+ })
+
+ It("podman exec missing working directory test", func() {
+ setup := podmanTest.RunTopContainer("test1")
+ setup.WaitWithDefaultTimeout()
+ Expect(setup.ExitCode()).To(Equal(0))
+
+ session := podmanTest.Podman([]string{"exec", "-l", "--workdir", "/missing", "pwd"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(1))
+
+ session = podmanTest.Podman([]string{"exec", "-l", "-w", "/missing", "pwd"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(1))
+ })
})
diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go
index 64245c609..9d7ac145c 100644
--- a/test/e2e/start_test.go
+++ b/test/e2e/start_test.go
@@ -115,4 +115,14 @@ var _ = Describe("Podman start", func() {
numContainers := podmanTest.NumberOfContainers()
Expect(numContainers).To(Equal(1))
})
+
+ It("podman start --sig-proxy should not work without --attach", func() {
+ session := podmanTest.Podman([]string{"create", ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"start", "-l", "--sig-proxy"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(125))
+ })
})
diff --git a/vendor.conf b/vendor.conf
index 9af218d15..18283cae6 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -92,7 +92,7 @@ k8s.io/kube-openapi 275e2ce91dec4c05a4094a7b1daee5560b555ac9 https://github.com/
k8s.io/utils 258e2a2fa64568210fbd6267cf1d8fd87c3cb86e https://github.com/kubernetes/utils
github.com/mrunalp/fileutils master
github.com/varlink/go master
-github.com/containers/buildah a4200ae6b590c4b08b223e45513b1894636d1d02
+github.com/containers/buildah e7ca330f923701dba8859f5c014d0a9a3f7f0a49
github.com/Nvveen/Gotty master
github.com/fsouza/go-dockerclient master
github.com/openshift/imagebuilder master
diff --git a/vendor/github.com/containers/buildah/imagebuildah/build.go b/vendor/github.com/containers/buildah/imagebuildah/build.go
index d838260e7..217bcfc79 100644
--- a/vendor/github.com/containers/buildah/imagebuildah/build.go
+++ b/vendor/github.com/containers/buildah/imagebuildah/build.go
@@ -517,6 +517,7 @@ func (b *Executor) Run(run imagebuilder.Run, config docker.Config) error {
Hostname: config.Hostname,
Runtime: b.runtime,
Args: b.runtimeArgs,
+ NoPivot: os.Getenv("BUILDAH_NOPIVOT") != "",
Mounts: convertMounts(b.transientMounts),
Env: config.Env,
User: config.User,
diff --git a/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go b/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go
index ae55316b0..31e6a428c 100644
--- a/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go
+++ b/vendor/github.com/containers/buildah/pkg/blobcache/blobcache.go
@@ -52,14 +52,18 @@ type BlobCache interface {
type blobCacheReference struct {
reference types.ImageReference
+ // WARNING: The contents of this directory may be accessed concurrently,
+ // both within this process and by multiple different processes
directory string
compress types.LayerCompression
}
type blobCacheSource struct {
- reference *blobCacheReference
- source types.ImageSource
- sys types.SystemContext
+ reference *blobCacheReference
+ source types.ImageSource
+ sys types.SystemContext
+ // this mutex synchronizes the counters below
+ mu sync.Mutex
cacheHits int64
cacheMisses int64
cacheErrors int64
@@ -219,7 +223,7 @@ func (s *blobCacheSource) GetManifest(ctx context.Context, instanceDigest *diges
}
func (s *blobCacheSource) HasThreadSafeGetBlob() bool {
- return false
+ return s.source.HasThreadSafeGetBlob()
}
func (s *blobCacheSource) GetBlob(ctx context.Context, blobinfo types.BlobInfo, cache types.BlobInfoCache) (io.ReadCloser, int64, error) {
@@ -232,16 +236,22 @@ func (s *blobCacheSource) GetBlob(ctx context.Context, blobinfo types.BlobInfo,
filename := filepath.Join(s.reference.directory, makeFilename(blobinfo.Digest, isConfig))
f, err := os.Open(filename)
if err == nil {
+ s.mu.Lock()
s.cacheHits++
+ s.mu.Unlock()
return f, size, nil
}
if !os.IsNotExist(err) {
+ s.mu.Lock()
s.cacheErrors++
+ s.mu.Unlock()
return nil, -1, errors.Wrapf(err, "error checking for cache file %q", filepath.Join(s.reference.directory, filename))
}
}
}
+ s.mu.Lock()
s.cacheMisses++
+ s.mu.Unlock()
rc, size, err := s.source.GetBlob(ctx, blobinfo, cache)
if err != nil {
return rc, size, errors.Wrapf(err, "error reading blob from source image %q", transports.ImageName(s.reference))
@@ -403,7 +413,7 @@ func saveStream(wg *sync.WaitGroup, decompressReader io.ReadCloser, tempFile *os
}
func (s *blobCacheDestination) HasThreadSafePutBlob() bool {
- return false
+ return s.destination.HasThreadSafePutBlob()
}
func (d *blobCacheDestination) PutBlob(ctx context.Context, stream io.Reader, inputInfo types.BlobInfo, cache types.BlobInfoCache, isConfig bool) (types.BlobInfo, error) {