diff --git a/cmd/podman/containers/rm.go b/cmd/podman/containers/rm.go index 9fa688d23a..d7d2370684 100644 --- a/cmd/podman/containers/rm.go +++ b/cmd/podman/containers/rm.go @@ -127,7 +127,7 @@ func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit responses, err := registry.ContainerEngine().ContainerRm(context.Background(), namesOrIDs, rmOptions) if err != nil { if setExit { - setExitCode(err) + setExitCode(rmOptions.Force, []error{err}) } return err } @@ -136,25 +136,50 @@ func removeContainers(namesOrIDs []string, rmOptions entities.RmOptions, setExit if errors.Is(r.Err, define.ErrWillDeadlock) { logrus.Errorf("Potential deadlock detected - please run 'podman system renumber' to resolve") } - if setExit { - setExitCode(r.Err) - } errs = append(errs, r.Err) } else { fmt.Println(r.Id) } } + if setExit { + setExitCode(rmOptions.Force, errs) + } + return errs.PrintErrors() } -func setExitCode(err error) { - // If error is set to no such container, do not reset - if registry.GetExitCode() == 1 { - return +func setExitCode(force bool, errs []error) { + var ( + // noSuchContainerErrors indicates the requested container does not exist + noSuchContainerErrors bool + // stateInvalidErrors indicates a container is in an improper state + stateInvalidErrors bool + ) + + if len(errs) == 0 { + registry.SetExitCode(0) + } + + for _, err := range errs { + if errors.Is(err, define.ErrNoSuchCtr) || strings.Contains(err.Error(), define.ErrNoSuchCtr.Error()) { + noSuchContainerErrors = true + } else if errors.Is(err, define.ErrCtrStateInvalid) || strings.Contains(err.Error(), define.ErrCtrStateInvalid.Error()) { + stateInvalidErrors = true + } } - if errors.Is(err, define.ErrNoSuchCtr) || strings.Contains(err.Error(), define.ErrNoSuchCtr.Error()) { - registry.SetExitCode(1) - } else if errors.Is(err, define.ErrCtrStateInvalid) || strings.Contains(err.Error(), define.ErrCtrStateInvalid.Error()) { + + switch { + case stateInvalidErrors: registry.SetExitCode(2) + case noSuchContainerErrors: + // One of the specified container did not exist, and no other + // failures. + if force { + registry.SetExitCode(define.ExecErrorCodeIgnore) + } else { + registry.SetExitCode(1) + } + default: + registry.SetExitCode(125) } } diff --git a/cmd/podman/images/rm.go b/cmd/podman/images/rm.go index 18b22e51d0..75ec8bdc37 100644 --- a/cmd/podman/images/rm.go +++ b/cmd/podman/images/rm.go @@ -6,6 +6,7 @@ import ( "github.com/containers/podman/v4/cmd/podman/common" "github.com/containers/podman/v4/cmd/podman/registry" + "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/errorhandling" "github.com/spf13/cobra" @@ -82,6 +83,9 @@ func rm(cmd *cobra.Command, args []string) error { } } registry.SetExitCode(report.ExitCode) + if imageOpts.Force && registry.GetExitCode() == 1 { + registry.SetExitCode(define.ExecErrorCodeIgnore) + } } return errorhandling.JoinErrors(rmErrors) diff --git a/cmd/podman/networks/prune.go b/cmd/podman/networks/prune.go index ee5389aa75..23294282c0 100644 --- a/cmd/podman/networks/prune.go +++ b/cmd/podman/networks/prune.go @@ -71,7 +71,7 @@ func networkPrune(cmd *cobra.Command, _ []string) error { } responses, err := registry.ContainerEngine().NetworkPrune(registry.Context(), networkPruneOptions) if err != nil { - setExitCode(err) + setExitCode(false, []error{err}) return err } return utils.PrintNetworkPruneResults(responses, false) diff --git a/cmd/podman/networks/rm.go b/cmd/podman/networks/rm.go index c2d3f655fc..d044bbcf93 100644 --- a/cmd/podman/networks/rm.go +++ b/cmd/podman/networks/rm.go @@ -63,24 +63,54 @@ func networkRm(cmd *cobra.Command, args []string) error { } responses, err := registry.ContainerEngine().NetworkRm(registry.Context(), args, networkRmOptions) if err != nil { - setExitCode(err) + setExitCode(networkRmOptions.Force, []error{err}) return err } for _, r := range responses { if r.Err == nil { fmt.Println(r.Name) } else { - setExitCode(r.Err) errs = append(errs, r.Err) } } + setExitCode(networkRmOptions.Force, errs) + return errs.PrintErrors() } -func setExitCode(err error) { - if errors.Is(err, define.ErrNoSuchNetwork) || strings.Contains(err.Error(), define.ErrNoSuchNetwork.Error()) { - registry.SetExitCode(1) - } else if errors.Is(err, define.ErrNetworkInUse) || strings.Contains(err.Error(), define.ErrNetworkInUse.Error()) { +func setExitCode(force bool, errs []error) { + var ( + // noSuchNetworkErrors indicates the requested network does not exist. + noSuchNetworkErrors bool + // inUseErrors indicates the requested operation failed because the network was in use + inUseErrors bool + ) + + if len(errs) == 0 { + registry.SetExitCode(0) + } + + for _, err := range errs { + if errors.Is(err, define.ErrNoSuchNetwork) || strings.Contains(err.Error(), define.ErrNoSuchNetwork.Error()) { + noSuchNetworkErrors = true + } else if errors.Is(err, define.ErrNetworkInUse) || strings.Contains(err.Error(), define.ErrNetworkInUse.Error()) { + inUseErrors = true + } + } + + switch { + case inUseErrors: + // network is being used. registry.SetExitCode(2) + case noSuchNetworkErrors && !inUseErrors: + // One of the specified network did not exist, and no other + // failures. + if force { + registry.SetExitCode(define.ExecErrorCodeIgnore) + } else { + registry.SetExitCode(1) + } + default: + registry.SetExitCode(125) } } diff --git a/cmd/podman/pods/rm.go b/cmd/podman/pods/rm.go index 2ffd968f9d..053e5b5ae2 100644 --- a/cmd/podman/pods/rm.go +++ b/cmd/podman/pods/rm.go @@ -93,7 +93,7 @@ func removePods(namesOrIDs []string, rmOptions entities.PodRmOptions, printIDs b responses, err := registry.ContainerEngine().PodRm(context.Background(), namesOrIDs, rmOptions) if err != nil { - setExitCode(err) + setExitCode(rmOptions.Force, []error{err}) return err } @@ -104,15 +104,37 @@ func removePods(namesOrIDs []string, rmOptions entities.PodRmOptions, printIDs b fmt.Println(r.Id) } } else { - setExitCode(r.Err) errs = append(errs, r.Err) } } + setExitCode(rmOptions.Force, errs) return errs.PrintErrors() } -func setExitCode(err error) { - if errors.Is(err, define.ErrNoSuchPod) || strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) { - registry.SetExitCode(1) +func setExitCode(force bool, errs []error) { + var ( + // noSuchNetworkErrors indicates the requested pod does not exist. + noSuchPodErrors bool + ) + + if len(errs) == 0 { + registry.SetExitCode(0) + } + + for _, err := range errs { + if errors.Is(err, define.ErrNoSuchPod) || strings.Contains(err.Error(), define.ErrNoSuchPod.Error()) { + noSuchPodErrors = true + } + } + + switch { + case noSuchPodErrors: + if force { + registry.SetExitCode(define.ExecErrorCodeIgnore) + } else { + registry.SetExitCode(1) + } + default: + registry.SetExitCode(125) } } diff --git a/cmd/podman/root.go b/cmd/podman/root.go index f28d92e2f1..72f120216e 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -106,6 +106,9 @@ func Execute() { fmt.Fprintln(os.Stderr, "Cannot connect to Podman. Please verify your connection to the Linux system using `podman system connection list`, or try `podman machine init` and `podman machine start` to manage a new Linux VM") } } + if registry.GetExitCode() == define.ExecErrorCodeIgnore { + registry.SetExitCode(0) + } fmt.Fprintln(os.Stderr, formatError(err)) } os.Exit(registry.GetExitCode()) diff --git a/cmd/podman/volumes/rm.go b/cmd/podman/volumes/rm.go index c160b86231..1ba6801bb0 100644 --- a/cmd/podman/volumes/rm.go +++ b/cmd/podman/volumes/rm.go @@ -65,24 +65,52 @@ func rm(cmd *cobra.Command, args []string) error { } responses, err := registry.ContainerEngine().VolumeRm(context.Background(), args, rmOptions) if err != nil { - setExitCode(err) + setExitCode(rmOptions.Force, []error{err}) return err } for _, r := range responses { if r.Err == nil { fmt.Println(r.Id) } else { - setExitCode(r.Err) errs = append(errs, r.Err) } } + setExitCode(rmOptions.Force, errs) return errs.PrintErrors() } -func setExitCode(err error) { - if errors.Is(err, define.ErrNoSuchVolume) || strings.Contains(err.Error(), define.ErrNoSuchVolume.Error()) { - registry.SetExitCode(1) - } else if errors.Is(err, define.ErrVolumeBeingUsed) || strings.Contains(err.Error(), define.ErrVolumeBeingUsed.Error()) { +func setExitCode(force bool, errs []error) { + var ( + // noSuchVolumeErrors indicates the requested volume does not exist + noSuchVolumeErrors bool + // inUseErrors indicates that a volume is being used by at least one container + inUseErrors bool + ) + + if len(errs) == 0 { + registry.SetExitCode(0) + } + + for _, err := range errs { + if errors.Is(err, define.ErrNoSuchVolume) || strings.Contains(err.Error(), define.ErrNoSuchVolume.Error()) { + noSuchVolumeErrors = true + } else if errors.Is(err, define.ErrVolumeBeingUsed) || strings.Contains(err.Error(), define.ErrVolumeBeingUsed.Error()) { + inUseErrors = true + } + } + + switch { + case inUseErrors: + // being used by a container. registry.SetExitCode(2) + case noSuchVolumeErrors && !inUseErrors: + // One of the specified volumes did not exist, and no other + if force { + registry.SetExitCode(define.ExecErrorCodeIgnore) + } else { + registry.SetExitCode(1) + } + default: + registry.SetExitCode(125) } } diff --git a/libpod/define/exec_codes.go b/libpod/define/exec_codes.go index 3f2da4910d..aeedfbb7e5 100644 --- a/libpod/define/exec_codes.go +++ b/libpod/define/exec_codes.go @@ -17,6 +17,8 @@ const ( ExecErrorCodeCannotInvoke = 126 // ExecErrorCodeNotFound is the error code to return when a command cannot be found ExecErrorCodeNotFound = 127 + // ExecErrorCodeIgnore is the error code to can be simply ignored + ExecErrorCodeIgnore = -128 ) // TranslateExecErrorToExitCode takes an error and checks whether it diff --git a/test/e2e/network_test.go b/test/e2e/network_test.go index d4f60d3e48..e18d1adacc 100644 --- a/test/e2e/network_test.go +++ b/test/e2e/network_test.go @@ -427,6 +427,10 @@ var _ = Describe("Podman network", func() { session := podmanTest.Podman([]string{"network", "rm", "bogus"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) + + session = podmanTest.Podman([]string{"network", "rm", "-t", "0", "-f", "bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(1)) }) It("podman network remove --force with pod", func() { diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index a5eab7eedc..69fb552598 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -197,6 +197,10 @@ var _ = Describe("Podman pod rm", func() { session := podmanTest.Podman([]string{"pod", "rm", "bogus"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) + + session = podmanTest.Podman([]string{"pod", "rm", "-t", "0", "-f", "bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) }) It("podman rm bogus pod and a running pod", func() { diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go index 7dbe5fed81..09cb6bc0bd 100644 --- a/test/e2e/rm_test.go +++ b/test/e2e/rm_test.go @@ -253,6 +253,10 @@ var _ = Describe("Podman rm", func() { session := podmanTest.Podman([]string{"rm", "bogus"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) + + session = podmanTest.Podman([]string{"rm", "-t", "0", "-f", "bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) }) It("podman rm bogus container and a running container", func() { diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go index d1a0cd6f54..1a0ea174c1 100644 --- a/test/e2e/rmi_test.go +++ b/test/e2e/rmi_test.go @@ -39,6 +39,9 @@ var _ = Describe("Podman rmi", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) + session = podmanTest.Podman([]string{"rmi", "-f", "debian:6.0.10"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) }) It("podman rmi with fq name", func() { diff --git a/test/e2e/volume_rm_test.go b/test/e2e/volume_rm_test.go index 0180b7a462..d6960a1e6b 100644 --- a/test/e2e/volume_rm_test.go +++ b/test/e2e/volume_rm_test.go @@ -74,6 +74,10 @@ var _ = Describe("Podman volume rm", func() { session := podmanTest.Podman([]string{"volume", "rm", "bogus"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) + + session = podmanTest.Podman([]string{"volume", "rm", "-t", "0", "-f", "bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) }) It("podman rm with --all flag", func() {