feat: add --ignore flag to network rm

Add `--ignore` to `podman network rm` so removing a missing
network returns success instead of exit code 1.

Keep existing error behavior for networks in use and other failures.

This commit message was translated from Korean to English using an LLM.

Fixes: #28363
Signed-off-by: KyounghoonJang <matkimchi_@naver.com>
This commit is contained in:
KyounghoonJang 2026-03-27 16:06:06 +09:00
parent 1fecb62b34
commit 9e38f86993
12 changed files with 70 additions and 7 deletions

View file

@ -34,6 +34,7 @@ var networkRmOptions entities.NetworkRmOptions
func networkRmFlags(flags *pflag.FlagSet) {
flags.BoolVarP(&networkRmOptions.Force, "force", "f", false, "remove any containers using network")
flags.BoolVarP(&networkRmOptions.Ignore, "ignore", "i", false, "ignore if a specified network does not exist")
timeFlagName := "time"
flags.IntVarP(&stopTimeout, timeFlagName, "t", int(containerConfig.Engine.StopTimeout), "Seconds to wait for running containers to stop before killing the container")
_ = networkrmCommand.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
@ -60,7 +61,7 @@ func networkRm(cmd *cobra.Command, args []string) error {
}
responses, err := registry.ContainerEngine().NetworkRm(registry.Context(), args, networkRmOptions)
if err != nil {
if networkRmOptions.Force && strings.Contains(err.Error(), define.ErrNoSuchNetwork.Error()) {
if (networkRmOptions.Force || networkRmOptions.Ignore) && strings.Contains(err.Error(), define.ErrNoSuchNetwork.Error()) {
return nil
}
setExitCode(err)
@ -70,7 +71,7 @@ func networkRm(cmd *cobra.Command, args []string) error {
if r.Err == nil {
fmt.Println(r.Name)
} else {
if networkRmOptions.Force && strings.Contains(r.Err.Error(), define.ErrNoSuchNetwork.Error()) {
if (networkRmOptions.Force || networkRmOptions.Ignore) && strings.Contains(r.Err.Error(), define.ErrNoSuchNetwork.Error()) {
continue
}
setExitCode(r.Err)

View file

@ -15,6 +15,11 @@ Delete one or more Podman networks.
The `force` option removes all containers that use the named network. If the container is
running, the container is stopped and removed.
#### **--ignore**, **-i**
Ignore the error when a specified network does not exist.
Other failures, including a network being in use, are still returned.
#### **--time**, **-t**=*seconds*
Seconds to wait before forcibly stopping the running containers that are using the specified network. The --force option must be specified to use the --time option. Use -1 for infinite wait.

View file

@ -109,7 +109,8 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
Force bool `schema:"force"`
Force bool `schema:"force"`
Ignore bool `schema:"ignore"`
}{
// override any golang type defaults
}
@ -121,7 +122,8 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
name := utils.GetName(r)
options := entities.NetworkRmOptions{
Force: query.Force,
Force: query.Force,
Ignore: query.Ignore,
}
ic := abi.ContainerEngine{Libpod: runtime}
reports, err := ic.NetworkRm(r.Context(), []string{name}, options)
@ -129,6 +131,10 @@ func RemoveNetwork(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
if len(reports) == 0 {
utils.WriteResponse(w, http.StatusOK, []*entities.NetworkRmReport{})
return
}
if reports[0].Err != nil {
// If the network cannot be found, we return a 404.
if errors.Is(reports[0].Err, define.ErrNoSuchNetwork) {

View file

@ -237,6 +237,10 @@ func (s *APIServer) registerNetworkHandlers(r *mux.Router) error {
// name: force
// type: boolean
// description: remove containers associated with network
// - in: query
// name: ignore
// type: boolean
// description: ignore if a specified network does not exist
// produces:
// - application/json
// responses:

View file

@ -45,6 +45,7 @@ type InspectOptions struct{}
type RemoveOptions struct {
// Force removes the network even if it is being used
Force *bool
Ignore *bool
Timeout *uint
}

View file

@ -32,6 +32,21 @@ func (o *RemoveOptions) GetForce() bool {
return *o.Force
}
// WithIgnore set field Ignore to given value
func (o *RemoveOptions) WithIgnore(value bool) *RemoveOptions {
o.Ignore = &value
return o
}
// GetIgnore returns value of field Ignore
func (o *RemoveOptions) GetIgnore() bool {
if o.Ignore == nil {
var z bool
return z
}
return *o.Ignore
}
// WithTimeout set field Timeout to given value
func (o *RemoveOptions) WithTimeout(value uint) *RemoveOptions {
o.Timeout = &value

View file

@ -178,6 +178,12 @@ var _ = Describe("Podman networks", func() {
Expect(err).ToNot(HaveOccurred())
Expect(code).To(BeNumerically("==", http.StatusNotFound))
// removing a noName network with ignore should succeed
options := new(network.RemoveOptions).WithIgnore(true)
report, err := network.Remove(connText, "noName", options)
Expect(err).ToNot(HaveOccurred())
Expect(report).To(BeEmpty())
// Removing an unused network should work
name := "unused"
net := types.Network{
@ -185,7 +191,7 @@ var _ = Describe("Podman networks", func() {
}
_, err = network.Create(connText, &net)
Expect(err).ToNot(HaveOccurred())
report, err := network.Remove(connText, name, nil)
report, err = network.Remove(connText, name, nil)
Expect(err).ToNot(HaveOccurred())
Expect(report[0].Name).To(Equal(name))
@ -211,7 +217,7 @@ var _ = Describe("Podman networks", func() {
// Removing with a network in use with force should work with a stopped container
err = containers.Stop(connText, container, new(containers.StopOptions).WithTimeout(0))
Expect(err).ToNot(HaveOccurred())
options := new(network.RemoveOptions).WithForce(true)
options = new(network.RemoveOptions).WithForce(true)
report, err = network.Remove(connText, name, options)
Expect(err).ToNot(HaveOccurred())
Expect(report[0].Name).To(Equal(name))

View file

@ -26,6 +26,7 @@ type NetworkReloadReport = entitiesTypes.NetworkReloadReport
// NetworkRmOptions describes options for removing networks
type NetworkRmOptions struct {
Force bool
Ignore bool
Timeout *uint
}

View file

@ -168,6 +168,9 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
return reports, err
}
if err := ic.Libpod.Network().NetworkRemove(name); err != nil {
if options.Ignore && errors.Is(err, define.ErrNoSuchNetwork) {
continue
}
report.Err = err
}
if len(net.Name) != 0 {

View file

@ -52,7 +52,7 @@ func (ic *ContainerEngine) NetworkReload(_ context.Context, _ []string, _ entiti
func (ic *ContainerEngine) NetworkRm(_ context.Context, namesOrIds []string, opts entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) {
reports := make([]*entities.NetworkRmReport, 0, len(namesOrIds))
options := new(network.RemoveOptions).WithForce(opts.Force)
options := new(network.RemoveOptions).WithForce(opts.Force).WithIgnore(opts.Ignore)
if opts.Timeout != nil {
options = options.WithTimeout(*opts.Timeout)
}

View file

@ -426,6 +426,11 @@ var _ = Describe("Podman network", func() {
Expect(session).Should(ExitWithError(1, "unable to find network with name or ID bogus: network not found"))
})
It("podman network remove --ignore bogus", func() {
session := podmanTest.PodmanExitCleanly("network", "rm", "--ignore", "bogus")
Expect(session.OutputToString()).To(Equal(""))
})
It("podman network remove --force with pod", func() {
netName := "net-" + stringid.GenerateRandomID()
session := podmanTest.Podman([]string{"network", "create", netName})

View file

@ -978,6 +978,22 @@ EOF
assert "$output" !~ "$(safename)" "all networks from this test should be gone"
}
# bats test_tags=ci:parallel
@test "podman network rm --ignore bogus" {
bogusnet=bogusnet-$(safename)
run_podman 1 network rm $bogusnet
is "$output" "Error: unable to find network with name or ID $bogusnet: network not found" "Should print error"
run_podman network rm --ignore $bogusnet
is "$output" "" "Should print no output"
netname=testnet-$(safename)
run_podman network create $netname
run_podman network rm --ignore $bogusnet $netname
assert "$output" = "$netname" "rm network"
run_podman network ls -q
assert "$output" !~ "$(safename)" "all networks from this test should be gone"
}
# bats test_tags=ci:parallel
@test "podman network rm --dns-option " {
dns_opt=dns$(random_string)