infra/abi: refactor ContainerRm

The function grew into a big hairy ball over time and I personally
refrained from touching it as it seemed fragile.  Hence, refactor
the function into something more comprehensible and maintainable.
There is still potential for improvement but I want to tackle one
thing at a time.

[NO NEW TESTS NEEDED] as it shouldn't change behavior.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2022-12-21 09:47:56 +01:00
parent aecb5d3853
commit 1e84e1a8db
2 changed files with 38 additions and 80 deletions

View File

@ -383,60 +383,37 @@ func (ic *ContainerEngine) removeContainer(ctx context.Context, ctr *libpod.Cont
func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*reports.RmReport, error) { func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*reports.RmReport, error) {
rmReports := []*reports.RmReport{} rmReports := []*reports.RmReport{}
names := namesOrIds containers, err := getContainers(ic.Libpod, getContainersOptions{
// Attempt to remove named containers directly from storage, if container is defined in libpod all: options.All,
// this will fail and code will fall through to removing the container from libpod.` latest: options.Latest,
tmpNames := []string{} filters: options.Filters,
for _, ctr := range names { names: namesOrIds,
report := reports.RmReport{Id: ctr} ignore: true, // Force ignore as `podman rm` also handles external containers
report.Err = ic.Libpod.RemoveStorageContainer(ctr, options.Force) })
//nolint:gocritic if err != nil {
if report.Err == nil {
// remove container names that we successfully deleted
rmReports = append(rmReports, &report)
} else if errors.Is(report.Err, define.ErrNoSuchCtr) || errors.Is(report.Err, define.ErrCtrExists) {
// There is still a potential this is a libpod container
tmpNames = append(tmpNames, ctr)
} else {
if _, err := ic.Libpod.LookupContainer(ctr); errors.Is(err, define.ErrNoSuchCtr) {
// remove container failed, but not a libpod container
rmReports = append(rmReports, &report)
continue
}
// attempt to remove as a libpod container
tmpNames = append(tmpNames, ctr)
}
}
names = tmpNames
containers, err := getContainers(ic.Libpod, getContainersOptions{all: options.All, latest: options.Latest, filters: options.Filters, names: names, ignore: options.Ignore})
if err != nil && !(options.Ignore && errors.Is(err, define.ErrNoSuchCtr)) {
// Failed to get containers. If force is specified, get the containers ID
// and evict them
if !options.Force {
return nil, err return nil, err
} }
for _, ctr := range names { libpodContainers := make([]*libpod.Container, 0, len(containers))
logrus.Debugf("Evicting container %q", ctr) idToRawInput := make(map[string]string, len(containers))
report := reports.RmReport{ for i, ctr := range containers {
Id: ctr, if ctr.doesNotExist {
RawInput: ctr, // If the container does not exist in Podman's database, it may
} // be an external one. Hence, try removing the external
_, err := ic.Libpod.EvictContainer(ctx, ctr, options.Volumes) // "storage" container.
if err != nil { if err := ic.Libpod.RemoveStorageContainer(ctr.rawInput, options.Force); err != nil {
if options.Ignore && errors.Is(err, define.ErrNoSuchCtr) { if options.Ignore && (errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrExists)) {
logrus.Debugf("Ignoring error (--allow-missing): %v", err)
rmReports = append(rmReports, &report)
continue continue
} }
report.Err = err return nil, err
rmReports = append(rmReports, &report)
continue
} }
rmReports = append(rmReports, &report) rmReports = append(rmReports, &reports.RmReport{RawInput: ctr.rawInput})
} else {
// If the container exists in the Podman database, we
// can remove it correctly below.
libpodContainers = append(libpodContainers, containers[i].Container)
idToRawInput[ctr.ID()] = ctr.rawInput
} }
return rmReports, nil
} }
alreadyRemoved := make(map[string]bool) alreadyRemoved := make(map[string]bool)
@ -446,44 +423,23 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
rmReports = append(rmReports, newReports[i]) rmReports = append(rmReports, newReports[i])
} }
} }
if !options.All && options.Depend {
// Add additional containers based on dependencies to container map // First, remove dependend containers.
for _, ctr := range containers { if options.All || options.Depend {
if ctr.doesNotExist || alreadyRemoved[ctr.ID()] { for _, ctr := range libpodContainers {
// When `All` is set, remove the infra containers and
// their dependencies first. Otherwise, we'd error out.
//
// TODO: All this logic should probably live in libpod.
if alreadyRemoved[ctr.ID()] || (options.All && !ctr.IsInfra()) {
continue continue
} }
reports, err := ic.Libpod.RemoveDepend(ctx, ctr.Container, options.Force, options.Volumes, options.Timeout) reports, err := ic.Libpod.RemoveDepend(ctx, ctr, options.Force, options.Volumes, options.Timeout)
if err != nil { if err != nil {
return rmReports, err return rmReports, err
} }
addReports(reports) addReports(reports)
} }
return rmReports, nil
}
// If --all is set, make sure to remove the infra containers'
// dependencies before doing the parallel removal below.
if options.All {
for _, ctr := range containers {
if ctr.doesNotExist || alreadyRemoved[ctr.ID()] || !ctr.IsInfra() {
continue
}
reports, err := ic.Libpod.RemoveDepend(ctx, ctr.Container, options.Force, options.Volumes, options.Timeout)
if err != nil {
return rmReports, err
}
addReports(reports)
}
}
idToRawInput := make(map[string]string, len(containers))
libpodContainers := make([]*libpod.Container, 0, len(containers))
for i, c := range containers {
if c.doesNotExist {
continue
}
idToRawInput[c.ID()] = c.rawInput
libpodContainers = append(libpodContainers, containers[i].Container)
} }
errMap, err := parallelctr.ContainerOp(ctx, libpodContainers, func(c *libpod.Container) error { errMap, err := parallelctr.ContainerOp(ctx, libpodContainers, func(c *libpod.Container) error {
@ -495,6 +451,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
if err != nil { if err != nil {
return nil, err return nil, err
} }
for ctr, err := range errMap { for ctr, err := range errMap {
if alreadyRemoved[ctr.ID()] { if alreadyRemoved[ctr.ID()] {
continue continue
@ -505,6 +462,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string,
report.RawInput = idToRawInput[ctr.ID()] report.RawInput = idToRawInput[ctr.ID()]
rmReports = append(rmReports, report) rmReports = append(rmReports, report)
} }
return rmReports, nil return rmReports, nil
} }

View File

@ -108,7 +108,7 @@ load helpers
@test "podman container rm --force bogus" { @test "podman container rm --force bogus" {
run_podman 1 container rm bogus run_podman 1 container rm bogus
is "$output" "Error: no container with name or ID \"bogus\" found: no such container" "Should print error" is "$output" "Error: no container with ID or name \"bogus\" found: no such container" "Should print error"
run_podman container rm --force bogus run_podman container rm --force bogus
is "$output" "" "Should print no output" is "$output" "" "Should print no output"
} }