diff options
-rw-r--r-- | .cirrus.yml | 2 | ||||
-rw-r--r-- | .github/ISSUE_TEMPLATE.md | 6 | ||||
-rwxr-xr-x | API.md | 8 | ||||
-rw-r--r-- | cmd/podman/commands.go | 7 | ||||
-rw-r--r-- | cmd/podman/commands_remoteclient.go | 5 | ||||
-rw-r--r-- | cmd/podman/healthcheck.go | 5 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 4 | ||||
-rw-r--r-- | libpod.conf | 2 | ||||
-rw-r--r-- | libpod/image/pull.go | 25 | ||||
-rw-r--r-- | libpod/oci_internal_linux.go | 14 | ||||
-rw-r--r-- | pkg/adapter/containers.go | 8 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 4 | ||||
-rw-r--r-- | pkg/varlinkapi/containers.go | 15 | ||||
-rw-r--r-- | test/e2e/exec_test.go | 8 | ||||
-rw-r--r-- | test/e2e/healthcheck_run_test.go | 2 | ||||
-rw-r--r-- | test/e2e/libpod_suite_remoteclient_test.go | 7 | ||||
-rw-r--r-- | test/e2e/libpod_suite_test.go | 7 | ||||
-rw-r--r-- | test/e2e/pull_test.go | 28 | ||||
-rw-r--r-- | test/e2e/run_exit_test.go | 4 | ||||
-rw-r--r-- | test/e2e/run_test.go | 2 | ||||
-rw-r--r-- | test/e2e/start_test.go | 4 | ||||
-rw-r--r-- | vendor/modules.txt | 2 |
22 files changed, 107 insertions, 62 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/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/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/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/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_internal_linux.go b/libpod/oci_internal_linux.go index 6e4ee2cf2..607d5c14f 100644 --- a/libpod/oci_internal_linux.go +++ b/libpod/oci_internal_linux.go @@ -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/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/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..ac727f9bc 100644 --- a/test/e2e/exec_test.go +++ b/test/e2e/exec_test.go @@ -171,16 +171,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 +189,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/vendor/modules.txt b/vendor/modules.txt index c4a410be6..4b992352c 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 |