RemoveImages removes images specified by names. If no names are specified, remove images as specified via the options' filters. All images are expected to exist in the local containers storage. If an image has more names than one name, the image will be untagged with the specified name. RemoveIm
(ctx context.Context, names []string, options *RemoveImagesOptions)
| 616 | // Note that most errors are non-fatal and collected into `rmErrors` return |
| 617 | // value. |
| 618 | func (r *Runtime) RemoveImages(ctx context.Context, names []string, options *RemoveImagesOptions) (reports []*RemoveImageReport, rmErrors []error) { |
| 619 | if options == nil { |
| 620 | options = &RemoveImagesOptions{} |
| 621 | } |
| 622 | |
| 623 | if options.ExternalContainers && options.IsExternalContainerFunc == nil { |
| 624 | return nil, []error{fmt.Errorf("libimage error: cannot remove external containers without callback")} |
| 625 | } |
| 626 | |
| 627 | // The logic here may require some explanation. Image removal is |
| 628 | // surprisingly complex since it is recursive (intermediate parents are |
| 629 | // removed) and since multiple items in `names` may resolve to the |
| 630 | // *same* image. On top, the data in the containers storage is shared, |
| 631 | // so we need to be careful and the code must be robust. That is why |
| 632 | // users can only remove images via this function; the logic may be |
| 633 | // complex but the execution path is clear. |
| 634 | |
| 635 | // Bundle an image with a possible empty slice of names to untag. That |
| 636 | // allows for a decent untagging logic and to bundle multiple |
| 637 | // references to the same *Image (and circumvent consistency issues). |
| 638 | type deleteMe struct { |
| 639 | image *Image |
| 640 | referencedBy []string |
| 641 | } |
| 642 | |
| 643 | appendError := func(err error) { |
| 644 | rmErrors = append(rmErrors, err) |
| 645 | } |
| 646 | |
| 647 | deleteMap := make(map[string]*deleteMe) // ID -> deleteMe |
| 648 | toDelete := []string{} |
| 649 | // Look up images in the local containers storage and fill out |
| 650 | // toDelete and the deleteMap. |
| 651 | |
| 652 | switch { |
| 653 | case len(names) > 0: |
| 654 | // prepare lookupOptions |
| 655 | var lookupOptions *LookupImageOptions |
| 656 | if options.LookupManifest { |
| 657 | // LookupManifest configured as true make sure we only remove manifests and no referenced images. |
| 658 | lookupOptions = &LookupImageOptions{lookupManifest: true} |
| 659 | } else { |
| 660 | lookupOptions = &LookupImageOptions{returnManifestIfNoInstance: true} |
| 661 | } |
| 662 | // Look up the images one-by-one. That allows for removing |
| 663 | // images that have been looked up successfully while reporting |
| 664 | // lookup errors at the end. |
| 665 | for _, name := range names { |
| 666 | img, resolvedName, err := r.LookupImage(name, lookupOptions) |
| 667 | if err != nil { |
| 668 | appendError(err) |
| 669 | continue |
| 670 | } |
| 671 | dm, exists := deleteMap[img.ID()] |
| 672 | if !exists { |
| 673 | toDelete = append(toDelete, img.ID()) |
| 674 | dm = &deleteMe{image: img} |
| 675 | deleteMap[img.ID()] = dm |