diff options
39 files changed, 210 insertions, 111 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 5a9dbcb54..80c954ca0 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -371,6 +371,8 @@ testing_crun_task: networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' install_crun_script: 'dnf install -y crun' + # FIXME: use the package once all the fixes are in a release + override_crun_script: 'setenforce 0; yum builddep -y crun && (git clone --depth=1 https://github.com/containers/crun && cd crun && ./autogen.sh && ./configure --prefix=/usr && make -j4 && make install) && rm -rf crun' unit_test_script: '$SCRIPT_BASE/unit_test.sh |& ${TIMESTAMP}' integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}' system_test_script: '$SCRIPT_BASE/system_test.sh |& ${TIMESTAMP}' diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 2946f0b91..621430670 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -58,4 +58,10 @@ Briefly describe the problem you are having in a few paragraphs. (paste your output here) ``` +**Package info (e.g. output of `rpm -q podman` or `apt list podman`):** + +``` +(paste your output here) +``` + **Additional environment details (AWS, VirtualBox, physical, etc.):** @@ -87,6 +87,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func GetVolumes(args: []string, all: bool) Volume](#GetVolumes) +[func HealthCheckRun(nameOrID: string) string](#HealthCheckRun) + [func HistoryImage(name: string) ImageHistory](#HistoryImage) [func ImageExists(name: string) int](#ImageExists) @@ -681,6 +683,12 @@ GetVersion returns version and build information of the podman service method GetVolumes(args: [[]string](#[]string), all: [bool](https://godoc.org/builtin#bool)) [Volume](#Volume)</div> GetVolumes gets slice of the volumes on a remote host +### <a name="HealthCheckRun"></a>func HealthCheckRun +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method HealthCheckRun(nameOrID: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div> +HealthCheckRun executes defined container's healthcheck command +and returns the container's health status. ### <a name="HistoryImage"></a>func HistoryImage <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> diff --git a/Dockerfile b/Dockerfile index 3185586b9..1f51cd874 100644 --- a/Dockerfile +++ b/Dockerfile @@ -68,13 +68,13 @@ RUN set -x \ && rm -rf "$GOPATH" # Install CNI plugins -ENV CNI_COMMIT 7480240de9749f9a0a5c8614b17f1f03e0c06ab9 +ENV CNI_COMMIT 485be65581341430f9106a194a98f0f2412245fb RUN set -x \ - && export GOPATH="$(mktemp -d)" \ + && export GOPATH="$(mktemp -d)" GOCACHE="$(mktemp -d)" \ && git clone https://github.com/containernetworking/plugins.git "$GOPATH/src/github.com/containernetworking/plugins" \ && cd "$GOPATH/src/github.com/containernetworking/plugins" \ && git checkout -q "$CNI_COMMIT" \ - && ./build.sh \ + && ./build_linux.sh \ && mkdir -p /usr/libexec/cni \ && cp bin/* /usr/libexec/cni \ && rm -rf "$GOPATH" diff --git a/Dockerfile.centos b/Dockerfile.centos index bbb582840..513c4bdfd 100644 --- a/Dockerfile.centos +++ b/Dockerfile.centos @@ -27,13 +27,13 @@ RUN yum -y install btrfs-progs-devel \ iptables && yum clean all # Install CNI plugins -ENV CNI_COMMIT 7480240de9749f9a0a5c8614b17f1f03e0c06ab9 +ENV CNI_COMMIT 485be65581341430f9106a194a98f0f2412245fb RUN set -x \ - && export GOPATH="$(mktemp -d)" \ + && export GOPATH="$(mktemp -d)" GOCACHE="$(mktemp -d)" \ && git clone https://github.com/containernetworking/plugins.git "$GOPATH/src/github.com/containernetworking/plugins" \ && cd "$GOPATH/src/github.com/containernetworking/plugins" \ && git checkout -q "$CNI_COMMIT" \ - && ./build.sh \ + && ./build_linux.sh \ && mkdir -p /usr/libexec/cni \ && cp bin/* /usr/libexec/cni \ && rm -rf "$GOPATH" diff --git a/Dockerfile.fedora b/Dockerfile.fedora index 54ab690e9..0f82fdc5c 100644 --- a/Dockerfile.fedora +++ b/Dockerfile.fedora @@ -30,13 +30,14 @@ RUN dnf -y install btrfs-progs-devel \ iptables && dnf clean all # Install CNI plugins -ENV CNI_COMMIT 412b6d31280682bb4fab4446f113c22ff1886554 +ENV CNI_COMMIT 485be65581341430f9106a194a98f0f2412245fb +ENV RUN set -x \ - && export GOPATH="$(mktemp -d)" \ + && export GOPATH="$(mktemp -d)" GOCACHE="$(mktemp -d)" \ && git clone https://github.com/containernetworking/plugins.git "$GOPATH/src/github.com/containernetworking/plugins" \ && cd "$GOPATH/src/github.com/containernetworking/plugins" \ && git checkout -q "$CNI_COMMIT" \ - && ./build.sh \ + && ./build_linux.sh \ && mkdir -p /usr/libexec/cni \ && cp bin/* /usr/libexec/cni \ && rm -rf "$GOPATH" @@ -137,7 +137,8 @@ into other tools. Podman specializes in all of the commands and functions that help you to maintain and modify OCI images, such as pulling and tagging. It also allows you to create, run, and maintain those containers -created from those images. +created from those images. For building container images via Dockerfiles, Podman uses Buildah's +golang API and can be installed independently from Buildah. A major difference between Podman and Buildah is their concept of a container. Podman allows users to create "traditional containers" where the intent of these containers is diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 27f3fc214..e23918a5b 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -71,10 +71,3 @@ func getSystemSubCommands() []*cobra.Command { _migrateCommand, } } - -// Commands that the local client implements -func getHealthcheckSubCommands() []*cobra.Command { - return []*cobra.Command{ - _healthcheckrunCommand, - } -} diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go index 278fe229c..a278761c1 100644 --- a/cmd/podman/commands_remoteclient.go +++ b/cmd/podman/commands_remoteclient.go @@ -47,8 +47,3 @@ func getTrustSubCommands() []*cobra.Command { func getSystemSubCommands() []*cobra.Command { return []*cobra.Command{} } - -// Commands that the remoteclient implements -func getHealthcheckSubCommands() []*cobra.Command { - return []*cobra.Command{} -} diff --git a/cmd/podman/healthcheck.go b/cmd/podman/healthcheck.go index 9fb099ffa..140206dbe 100644 --- a/cmd/podman/healthcheck.go +++ b/cmd/podman/healthcheck.go @@ -16,11 +16,12 @@ var healthcheckCommand = cliconfig.PodmanCommand{ } // Commands that are universally implemented -var healthcheckCommands []*cobra.Command +var healthcheckCommands = []*cobra.Command{ + _healthcheckrunCommand, +} func init() { healthcheckCommand.AddCommand(healthcheckCommands...) - healthcheckCommand.AddCommand(getHealthcheckSubCommands()...) healthcheckCommand.SetUsageTemplate(UsageTemplate()) rootCmd.AddCommand(healthcheckCommand.Command) } diff --git a/cmd/podman/shared/funcs.go b/cmd/podman/shared/funcs.go index 2ceb9cdcb..bb4eed1e3 100644 --- a/cmd/podman/shared/funcs.go +++ b/cmd/podman/shared/funcs.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "github.com/containers/libpod/pkg/util" "github.com/google/shlex" ) @@ -13,13 +14,14 @@ func GetAuthFile(authfile string) string { if authfile != "" { return authfile } + authfile = os.Getenv("REGISTRY_AUTH_FILE") if authfile != "" { return authfile } - runtimeDir := os.Getenv("XDG_RUNTIME_DIR") - if runtimeDir != "" { - return filepath.Join(runtimeDir, "containers/auth.json") + + if runtimeDir, err := util.GetRuntimeDir(); err == nil { + return filepath.Join(runtimeDir, "auth.json") } return "" } diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go index 2f696445e..25514ec75 100644 --- a/cmd/podman/stats.go +++ b/cmd/podman/stats.go @@ -134,9 +134,13 @@ func statsCmd(c *cliconfig.StatsValues) error { initialStats, err := ctr.GetContainerStats(&libpod.ContainerStats{}) if err != nil { // when doing "all", dont worry about containers that are not running - if c.All && errors.Cause(err) == define.ErrCtrRemoved || errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrStateInvalid { + cause := errors.Cause(err) + if c.All && (cause == define.ErrCtrRemoved || cause == define.ErrNoSuchCtr || cause == define.ErrCtrStateInvalid) { continue } + if cause == cgroups.ErrCgroupV1Rootless { + err = cause + } return err } containerStats[ctr.ID()] = initialStats diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go index 5f89534be..cd21e3574 100644 --- a/cmd/podman/varlink.go +++ b/cmd/podman/varlink.go @@ -53,7 +53,7 @@ func init() { func varlinkCmd(c *cliconfig.VarlinkValues) error { varlinkURI := adapter.DefaultAddress if rootless.IsRootless() { - xdg, err := util.GetRootlessRuntimeDir() + xdg, err := util.GetRuntimeDir() if err != nil { return err } diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 08a69275e..1b10416a2 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -544,6 +544,10 @@ method GetContainersByStatus(status: []string) -> (containerS: []Container) method Top (nameOrID: string, descriptors: []string) -> (top: []string) +# HealthCheckRun executes defined container's healthcheck command +# and returns the container's health status. +method HealthCheckRun (nameOrID: string) -> (healthCheckStatus: string) + # GetContainer returns information about a single container. If a container # with the given id doesn't exist, a [ContainerNotFound](#ContainerNotFound) # error will be returned. See also [ListContainers](ListContainers) and diff --git a/docs/podman-stats.1.md b/docs/podman-stats.1.md index c1a87f210..2f604644f 100644 --- a/docs/podman-stats.1.md +++ b/docs/podman-stats.1.md @@ -9,6 +9,10 @@ podman\-stats - Display a live stream of 1 or more containers' resource usage st ## DESCRIPTION Display a live stream of one or more containers' resource usage statistics +Note: Podman stats will not work in rootless environments that use CGroups V1. +Podman stats relies on CGroup information for statistics, and CGroup v1 is not +supported for rootless use cases. + ## OPTIONS **--all**, **-a** @@ -69,14 +73,14 @@ a9f807ffaacd frosty_hodgkin -- 3.092MB / 16.7GB 0.02% -- / -- -- # podman stats --no-stream --format=json a9f80 [ { - "id": "a9f807ffaacd", - "name": "frosty_hodgkin", - "cpu_percent": "--", - "mem_usage": "3.092MB / 16.7GB", - "mem_percent": "0.02%", - "netio": "-- / --", - "blocki": "-- / --", - "pids": "2" + "id": "a9f807ffaacd", + "name": "frosty_hodgkin", + "cpu_percent": "--", + "mem_usage": "3.092MB / 16.7GB", + "mem_percent": "0.02%", + "netio": "-- / --", + "blocki": "-- / --", + "pids": "2" } ] ``` diff --git a/libpod.conf b/libpod.conf index 3bd3758b8..81fece5d2 100644 --- a/libpod.conf +++ b/libpod.conf @@ -120,7 +120,7 @@ runtime = "runc" # List of the OCI runtimes that support --format=json. When json is supported # libpod will use it for reporting nicer errors. -runtime_supports_json = ["runc"] +runtime_supports_json = ["crun", "runc"] # Paths to look for a valid OCI runtime (runc, runv, etc) # If the paths are empty or no valid path was found, then the `$PATH` diff --git a/libpod/container_api.go b/libpod/container_api.go index abcfcb271..9e59104cc 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -274,6 +274,11 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir } }() + // if the user is empty, we should inherit the user that the container is currently running with + if user == "" { + user = c.config.User + } + pid, attachChan, err := c.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, user, sessionID, streams, preserveFDs, resize, detachKeys) if err != nil { ec := define.ExecErrorCodeGeneric diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 78cfe3626..dbf3a4ef5 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -13,6 +13,7 @@ import ( dockerarchive "github.com/containers/image/docker/archive" "github.com/containers/image/docker/tarfile" ociarchive "github.com/containers/image/oci/archive" + oci "github.com/containers/image/oci/layout" is "github.com/containers/image/storage" "github.com/containers/image/transports" "github.com/containers/image/transports/alltransports" @@ -37,6 +38,9 @@ var ( DirTransport = directory.Transport.Name() // DockerTransport is the transport for docker registries DockerTransport = docker.Transport.Name() + // OCIDirTransport is the transport for pushing and pulling + // images to and from a directory containing an OCI image + OCIDirTransport = oci.Transport.Name() // AtomicTransport is the transport for atomic registries AtomicTransport = "atomic" // DefaultTransport is a prefix that we apply to an image name @@ -189,12 +193,12 @@ func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types. return ir.getSinglePullRefPairGoal(srcRef, dest) case DirTransport: - path := srcRef.StringWithinTransport() - image := path - if image[:1] == "/" { - // Set localhost as the registry so docker.io isn't prepended, and the path becomes the repository - image = DefaultLocalRegistry + image - } + image := toLocalImageName(srcRef.StringWithinTransport()) + return ir.getSinglePullRefPairGoal(srcRef, image) + + case OCIDirTransport: + split := strings.SplitN(srcRef.StringWithinTransport(), ":", 2) + image := toLocalImageName(split[0]) return ir.getSinglePullRefPairGoal(srcRef, image) default: @@ -202,6 +206,15 @@ func (ir *Runtime) pullGoalFromImageReference(ctx context.Context, srcRef types. } } +// toLocalImageName converts an image name into a 'localhost/' prefixed one +func toLocalImageName(imageName string) string { + return fmt.Sprintf( + "%s/%s", + DefaultLocalRegistry, + strings.TrimLeft(imageName, "/"), + ) +} + // pullImageFromHeuristicSource pulls an image based on inputName, which is heuristically parsed and may involve configured registries. // Use pullImageFromReference if the source is known precisely. func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName string, writer io.Writer, authfile, signaturePolicyPath string, signingOptions SigningOptions, dockerOptions *DockerRegistryOptions, label *string) ([]string, error) { diff --git a/libpod/oci.go b/libpod/oci.go index 4ba3114e3..8a873ca5b 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -213,7 +213,7 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) error { exitFile := ctr.exitFilePath() - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -336,7 +336,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) erro // Sets time the container was started, but does not save it. func (r *OCIRuntime) startContainer(ctr *Container) error { // TODO: streams should probably *not* be our STDIN/OUT/ERR - redirect to buffers? - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -356,7 +356,7 @@ func (r *OCIRuntime) startContainer(ctr *Container) error { // killContainer sends the given signal to the given container func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error { logrus.Debugf("Sending signal %d to container %s", signal, ctr.ID()) - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -370,7 +370,7 @@ func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error { // deleteContainer deletes a container from the OCI runtime func (r *OCIRuntime) deleteContainer(ctr *Container) error { - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -380,7 +380,7 @@ func (r *OCIRuntime) deleteContainer(ctr *Container) error { // pauseContainer pauses the given container func (r *OCIRuntime) pauseContainer(ctr *Container) error { - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -390,7 +390,7 @@ func (r *OCIRuntime) pauseContainer(ctr *Container) error { // unpauseContainer unpauses the given container func (r *OCIRuntime) unpauseContainer(ctr *Container) error { - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } diff --git a/libpod/oci_internal_linux.go b/libpod/oci_internal_linux.go index 6e4ee2cf2..48b7370e0 100644 --- a/libpod/oci_internal_linux.go +++ b/libpod/oci_internal_linux.go @@ -36,7 +36,7 @@ import ( func (r *OCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (err error) { var stderrBuf bytes.Buffer - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -449,6 +449,15 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { select { case ss := <-ch: if ss.err != nil { + if ociLog != "" { + ociLogData, err := ioutil.ReadFile(ociLog) + if err == nil { + var ociErr ociError + if err := json.Unmarshal(ociLogData, &ociErr); err == nil { + return -1, getOCIRuntimeError(ociErr.Msg) + } + } + } return -1, errors.Wrapf(ss.err, "error reading container (probably exited) json message") } logrus.Debugf("Received: %d", ss.si.Data) @@ -476,10 +485,11 @@ func readConmonPipeData(pipe *os.File, ociLog string) (int, error) { } func getOCIRuntimeError(runtimeMsg string) error { - if match, _ := regexp.MatchString(".*permission denied.*", runtimeMsg); match { + r := strings.ToLower(runtimeMsg) + if match, _ := regexp.MatchString(".*permission denied.*|.*operation not permitted.*", r); match { return errors.Wrapf(define.ErrOCIRuntimePermissionDenied, "%s", strings.Trim(runtimeMsg, "\n")) } - if match, _ := regexp.MatchString(".*executable file not found in.*", runtimeMsg); match { + if match, _ := regexp.MatchString(".*executable file not found in.*|.*no such file or directory.*", r); match { return errors.Wrapf(define.ErrOCIRuntimeNotFound, "%s", strings.Trim(runtimeMsg, "\n")) } return errors.Wrapf(define.ErrOCIRuntime, "%s", strings.Trim(runtimeMsg, "\n")) diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go index 45365203e..1613c3e68 100644 --- a/libpod/oci_linux.go +++ b/libpod/oci_linux.go @@ -208,7 +208,7 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty } }() - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return -1, nil, err } @@ -437,7 +437,7 @@ func (r *OCIRuntime) stopContainer(ctr *Container, timeout uint) error { args = []string{"kill", "--all", ctr.ID(), "KILL"} } - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } @@ -487,7 +487,7 @@ func (r *OCIRuntime) execStopContainer(ctr *Container, timeout uint) error { if len(execSessions) == 0 { return nil } - runtimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return err } diff --git a/libpod/runtime.go b/libpod/runtime.go index ffc4c0cbc..4d6a80d0b 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -367,7 +367,7 @@ func SetXdgDirs() error { if runtimeDir == "" { var err error - runtimeDir, err = util.GetRootlessRuntimeDir() + runtimeDir, err = util.GetRuntimeDir() if err != nil { return err } @@ -393,11 +393,11 @@ func getDefaultTmpDir() (string, error) { return "/var/run/libpod", nil } - rootlessRuntimeDir, err := util.GetRootlessRuntimeDir() + runtimeDir, err := util.GetRuntimeDir() if err != nil { return "", err } - libpodRuntimeDir := filepath.Join(rootlessRuntimeDir, "libpod") + libpodRuntimeDir := filepath.Join(runtimeDir, "libpod") if err := os.Mkdir(libpodRuntimeDir, 0700|os.ModeSticky); err != nil { if !os.IsExist(err) { diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 073c5054d..05866d05a 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -204,7 +204,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) // Get the conmon CGroup conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon") conmonCgroup, err := cgroups.Load(conmonCgroupPath) - if err != nil && err != cgroups.ErrCgroupDeleted { + if err != nil && err != cgroups.ErrCgroupDeleted && err != cgroups.ErrCgroupV1Rootless { removalErr = errors.Wrapf(err, "error retrieving pod %s conmon cgroup %s", p.ID(), conmonCgroupPath) } @@ -266,7 +266,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) // hard - instead, just log errors. conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon") conmonCgroup, err := cgroups.Load(conmonCgroupPath) - if err != nil && err != cgroups.ErrCgroupDeleted { + if err != nil && err != cgroups.ErrCgroupDeleted && err != cgroups.ErrCgroupV1Rootless { if removalErr == nil { removalErr = errors.Wrapf(err, "error retrieving pod %s conmon cgroup", p.ID()) } else { @@ -283,7 +283,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) } } cgroup, err := cgroups.Load(p.state.CgroupPath) - if err != nil && err != cgroups.ErrCgroupDeleted { + if err != nil && err != cgroups.ErrCgroupDeleted && err != cgroups.ErrCgroupV1Rootless { if removalErr == nil { removalErr = errors.Wrapf(err, "error retrieving pod %s cgroup", p.ID()) } else { diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 45a9a54a3..863640f97 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -342,7 +342,8 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode if err := ctr.Start(ctx, c.IsSet("pod")); err != nil { // This means the command did not exist exitCode = 127 - if strings.Contains(err.Error(), "permission denied") || strings.Contains(err.Error(), "file not found") { + e := strings.ToLower(err.Error()) + if strings.Contains(e, "permission denied") || strings.Contains(e, "operation not permitted") || strings.Contains(e, "file not found") || strings.Contains(e, "no such file or directory") { exitCode = 126 } return exitCode, err @@ -405,12 +406,13 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode } // This means the command did not exist exitCode = 127 - if strings.Contains(err.Error(), "permission denied") { + e := strings.ToLower(err.Error()) + if strings.Contains(e, "permission denied") || strings.Contains(e, "operation not permitted") { exitCode = 126 } if c.IsSet("rm") { if deleteError := r.Runtime.RemoveContainer(ctx, ctr, true, false); deleteError != nil { - logrus.Errorf("unable to remove container %s after failing to start and attach to it", ctr.ID()) + logrus.Debugf("unable to remove container %s after failing to start and attach to it", ctr.ID()) } } return exitCode, err diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index f4eb926c9..683bf1d35 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -21,7 +21,7 @@ import ( "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/remoteclientconfig" - "github.com/containers/libpod/cmd/podman/varlink" + iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" @@ -812,7 +812,7 @@ func IsImageNotFound(err error) bool { // HealthCheck executes a container's healthcheck over a varlink connection func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (string, error) { - return "", define.ErrNotImplemented + return iopodman.HealthCheckRun().Call(r.Conn, c.InputArgs[0]) } // Events monitors libpod/podman events over a varlink connection diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 085718855..9711e8120 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -10,6 +10,7 @@ import ( "strconv" "strings" + "github.com/containers/libpod/pkg/rootless" systemdDbus "github.com/coreos/go-systemd/dbus" "github.com/godbus/dbus" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -19,7 +20,9 @@ import ( var ( // ErrCgroupDeleted means the cgroup was deleted - ErrCgroupDeleted = errors.New("cgroups: cgroup deleted") + ErrCgroupDeleted = errors.New("cgroup deleted") + // ErrCgroupV1Rootless means the cgroup v1 were attempted to be used in rootless environmen + ErrCgroupV1Rootless = errors.New("no support for CGroups V1 in rootless environments") ) // CgroupControl controls a cgroup hierarchy @@ -339,6 +342,9 @@ func Load(path string) (*CgroupControl, error) { p := control.getCgroupv1Path(name) if _, err := os.Stat(p); err != nil { if os.IsNotExist(err) { + if rootless.IsRootless() { + return nil, ErrCgroupV1Rootless + } // compatible with the error code // used by containerd/cgroups return nil, ErrCgroupDeleted diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index c7c8787a0..707e193e9 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -16,8 +16,8 @@ import ( "github.com/sirupsen/logrus" ) -// GetRootlessRuntimeDir returns the runtime directory when running as non root -func GetRootlessRuntimeDir() (string, error) { +// GetRuntimeDir returns the runtime directory +func GetRuntimeDir() (string, error) { var rootlessRuntimeDirError error rootlessRuntimeDirOnce.Do(func() { @@ -100,7 +100,7 @@ func GetRootlessConfigHomeDir() (string, error) { // GetRootlessPauseProcessPidPath returns the path to the file that holds the pid for // the pause process func GetRootlessPauseProcessPidPath() (string, error) { - runtimeDir, err := GetRootlessRuntimeDir() + runtimeDir, err := GetRuntimeDir() if err != nil { return "", err } diff --git a/pkg/util/utils_windows.go b/pkg/util/utils_windows.go index e781e6717..9bba2d1ee 100644 --- a/pkg/util/utils_windows.go +++ b/pkg/util/utils_windows.go @@ -25,12 +25,12 @@ func GetRootlessPauseProcessPidPath() (string, error) { return "", errors.Wrap(errNotImplemented, "GetRootlessPauseProcessPidPath") } -// GetRootlessRuntimeDir returns the runtime directory when running as non root -func GetRootlessRuntimeDir() (string, error) { - return "", errors.Wrap(errNotImplemented, "GetRootlessRuntimeDir") +// GetRuntimeDir returns the runtime directory +func GetRuntimeDir() (string, error) { + return "", errors.New("this function is not implemented for windows") } // GetRootlessConfigHomeDir returns the config home directory when running as non root func GetRootlessConfigHomeDir() (string, error) { - return "", errors.Wrap(errNotImplemented, "GetRootlessConfigHomeDir") + return "", errors.New("this function is not implemented for windows") } diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index c7aa5233f..2dcdbc089 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -14,7 +14,7 @@ import ( "time" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/cmd/podman/varlink" + iopodman "github.com/containers/libpod/cmd/podman/varlink" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/logs" @@ -864,3 +864,16 @@ func (i *LibpodAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecO return ecErr.Error } + +//HealthCheckRun executes defined container's healthcheck command and returns the container's health status. +func (i *LibpodAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error { + hcStatus, err := i.Runtime.HealthCheck(nameOrID) + if err != nil && hcStatus != libpod.HealthCheckFailure { + return call.ReplyErrorOccurred(err.Error()) + } + status := libpod.HealthCheckUnhealthy + if hcStatus == libpod.HealthCheckSuccess { + status = libpod.HealthCheckHealthy + } + return call.ReplyHealthCheckRun(status) +} diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go index 3f9639fda..f3190978c 100644 --- a/test/e2e/exec_test.go +++ b/test/e2e/exec_test.go @@ -146,6 +146,25 @@ var _ = Describe("Podman exec", func() { Expect(session2.OutputToString()).To(Equal(testUser)) }) + It("podman exec with user from run", func() { + testUser := "guest" + setup := podmanTest.Podman([]string{"run", "--user", testUser, "-d", ALPINE, "top"}) + setup.WaitWithDefaultTimeout() + Expect(setup.ExitCode()).To(Equal(0)) + ctrID := setup.OutputToString() + + session := podmanTest.Podman([]string{"exec", ctrID, "whoami"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(testUser)) + + overrideUser := "root" + session = podmanTest.Podman([]string{"exec", "--user", overrideUser, ctrID, "whoami"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.OutputToString()).To(ContainSubstring(overrideUser)) + }) + It("podman exec simple working directory test", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() @@ -171,16 +190,14 @@ var _ = Describe("Podman exec", func() { session := podmanTest.Podman([]string{"exec", "--workdir", "/missing", "test1", "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session.ExitCode()).To(Not(Equal(0))) session = podmanTest.Podman([]string{"exec", "-w", "/missing", "test1", "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session.ExitCode()).To(Not(Equal(0))) }) It("podman exec cannot be invoked", func() { - SkipIfNotRunc() - setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() Expect(setup.ExitCode()).To(Equal(0)) @@ -191,8 +208,6 @@ var _ = Describe("Podman exec", func() { }) It("podman exec command not found", func() { - SkipIfNotRunc() - setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() Expect(setup.ExitCode()).To(Equal(0)) diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index dafc8a837..e10aef427 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( diff --git a/test/e2e/libpod_suite_remoteclient_test.go b/test/e2e/libpod_suite_remoteclient_test.go index a6cedfc58..7f33fec87 100644 --- a/test/e2e/libpod_suite_remoteclient_test.go +++ b/test/e2e/libpod_suite_remoteclient_test.go @@ -28,13 +28,6 @@ func SkipIfRootless() { } } -func SkipIfNotRunc() { - runtime := os.Getenv("OCI_RUNTIME") - if runtime != "" && filepath.Base(runtime) != "runc" { - ginkgo.Skip("Not using runc as runtime") - } -} - // Podman is the exec call to podman on the filesystem func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { podmanSession := p.PodmanBase(args, false, false) diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go index 22cc14d6b..1df59dbe3 100644 --- a/test/e2e/libpod_suite_test.go +++ b/test/e2e/libpod_suite_test.go @@ -21,13 +21,6 @@ func SkipIfRootless() { } } -func SkipIfNotRunc() { - runtime := os.Getenv("OCI_RUNTIME") - if runtime != "" && filepath.Base(runtime) != "runc" { - ginkgo.Skip("Not using runc as runtime") - } -} - // Podman is the exec call to podman on the filesystem func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { podmanSession := p.PodmanBase(args, false, false) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index d6e7b44d1..68fcaf133 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -150,6 +150,34 @@ var _ = Describe("Podman pull", func() { session = podmanTest.PodmanNoCache([]string{"pull", imgPath}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.PodmanNoCache([]string{"images"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContainsTag(filepath.Join("localhost", dirpath), "latest")).To(BeTrue()) + session = podmanTest.PodmanNoCache([]string{"rmi", "alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + }) + + It("podman pull from local OCI directory", func() { + podmanTest.RestoreArtifact(ALPINE) + dirpath := filepath.Join(podmanTest.TempDir, "alpine") + os.MkdirAll(dirpath, os.ModePerm) + imgPath := fmt.Sprintf("oci:%s", dirpath) + + session := podmanTest.PodmanNoCache([]string{"push", "alpine", imgPath}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.PodmanNoCache([]string{"rmi", "alpine"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.PodmanNoCache([]string{"pull", imgPath}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.PodmanNoCache([]string{"images"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContainsTag(filepath.Join("localhost", dirpath), "latest")).To(BeTrue()) session = podmanTest.PodmanNoCache([]string{"rmi", "alpine"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/run_exit_test.go b/test/e2e/run_exit_test.go index b05849ddb..861d6b3b7 100644 --- a/test/e2e/run_exit_test.go +++ b/test/e2e/run_exit_test.go @@ -41,16 +41,12 @@ var _ = Describe("Podman run exit", func() { }) It("podman run exit 126", func() { - SkipIfNotRunc() - result := podmanTest.Podman([]string{"run", ALPINE, "/etc"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(126)) }) It("podman run exit 127", func() { - SkipIfNotRunc() - result := podmanTest.Podman([]string{"run", ALPINE, "foobar"}) result.WaitWithDefaultTimeout() Expect(result.ExitCode()).To(Equal(127)) diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index ce2044a72..6e102cfa5 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -417,7 +417,6 @@ var _ = Describe("Podman run", func() { It("podman run notify_socket", func() { SkipIfRemote() - SkipIfNotRunc() host := GetHostDistributionInfo() if host.Distribution != "rhel" && host.Distribution != "centos" && host.Distribution != "fedora" { @@ -629,7 +628,6 @@ var _ = Describe("Podman run", func() { }) It("podman run exit code on failure to exec", func() { - SkipIfNotRunc() session := podmanTest.Podman([]string{"run", ALPINE, "/etc"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(126)) diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go index 2dbb9545b..fc1203ed1 100644 --- a/test/e2e/start_test.go +++ b/test/e2e/start_test.go @@ -101,8 +101,6 @@ var _ = Describe("Podman start", func() { }) It("podman failed to start with --rm should delete the container", func() { - SkipIfNotRunc() - session := podmanTest.Podman([]string{"create", "-it", "--rm", ALPINE, "foo"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -116,8 +114,6 @@ var _ = Describe("Podman start", func() { }) It("podman failed to start without --rm should NOT delete the container", func() { - SkipIfNotRunc() - session := podmanTest.Podman([]string{"create", "-it", ALPINE, "foo"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 5ef84e9b8..a9d2ed1b7 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -22,12 +22,24 @@ RUN apk add nginx RUN echo $rand_content > /$rand_filename EOF - run_podman build -t build_test --format=docker $tmpdir + # The 'apk' command can take a long time to fetch files; bump timeout + PODMAN_TIMEOUT=240 run_podman build -t build_test --format=docker $tmpdir is "$output" ".*STEP 4: COMMIT" "COMMIT seen in log" run_podman run --rm build_test cat /$rand_filename is "$output" "$rand_content" "reading generated file in image" - run_podman rmi build_test + run_podman rmi -f build_test } + +function teardown() { + # A timeout or other error in 'build' can leave behind stale images + # that podman can't even see and which will cascade into subsequent + # test failures. Try a last-ditch force-rm in cleanup, ignoring errors. + run_podman '?' rm -a -f + run_podman '?' rmi -f build_test + + basic_teardown +} + # vim: filetype=sh diff --git a/vendor/modules.txt b/vendor/modules.txt index 9697bc122..3acff38c9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -75,10 +75,10 @@ github.com/containers/image/storage github.com/containers/image/copy github.com/containers/image/docker/reference github.com/containers/image/docker/tarfile +github.com/containers/image/oci/layout github.com/containers/image/tarball github.com/containers/image/pkg/sysregistriesv2 github.com/containers/image/image -github.com/containers/image/oci/layout github.com/containers/image/directory/explicitfilepath github.com/containers/image/docker/policyconfiguration github.com/containers/image/pkg/blobinfocache/none |