Remove removes a list of `images`.
(ctx context.Context, client *containerd.Client, args []string, options types.ImageRemoveOptions)
| 34 | |
| 35 | // Remove removes a list of `images`. |
| 36 | func Remove(ctx context.Context, client *containerd.Client, args []string, options types.ImageRemoveOptions) error { |
| 37 | var delOpts []images.DeleteOpt |
| 38 | if !options.Async { |
| 39 | delOpts = append(delOpts, images.SynchronousDelete()) |
| 40 | } |
| 41 | |
| 42 | cs := client.ContentStore() |
| 43 | is := client.ImageService() |
| 44 | containerList, err := client.Containers(ctx) |
| 45 | if err != nil { |
| 46 | return err |
| 47 | } |
| 48 | usedImages := make(map[string]string) |
| 49 | runningImages := make(map[string]string) |
| 50 | for _, container := range containerList { |
| 51 | image, err := container.Image(ctx) |
| 52 | if err != nil { |
| 53 | continue |
| 54 | } |
| 55 | |
| 56 | // if err != nil, simply go to `default` |
| 57 | switch cStatus, _ := containerutil.ContainerStatus(ctx, container); cStatus.Status { |
| 58 | case containerd.Running, containerd.Pausing, containerd.Paused: |
| 59 | runningImages[image.Name()] = container.ID() |
| 60 | default: |
| 61 | usedImages[image.Name()] = container.ID() |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | walker := &imagewalker.ImageWalker{ |
| 66 | Client: client, |
| 67 | OnFound: func(ctx context.Context, found imagewalker.Found) error { |
| 68 | if found.NameMatchIndex == -1 { |
| 69 | // if found multiple images, return error unless in force-mode and |
| 70 | // there is only 1 unique image. |
| 71 | if found.MatchCount > 1 && !(options.Force && found.UniqueImages == 1) { |
| 72 | return fmt.Errorf("multiple IDs found with provided prefix: %s", found.Req) |
| 73 | } |
| 74 | } else if found.NameMatchIndex != found.MatchIndex { |
| 75 | // when there is an image with a name matching the argument but the argument is a digest short id, |
| 76 | // the deletion process is not performed. |
| 77 | return nil |
| 78 | } |
| 79 | |
| 80 | if cid, ok := runningImages[found.Image.Name]; ok { |
| 81 | if options.Force { |
| 82 | // This is a running image, so, we need to keep a ref on it so that containerd does not GC the layers |
| 83 | // First create the new image with an empty name |
| 84 | originalName := found.Image.Name |
| 85 | found.Image.Name = ":" |
| 86 | if _, err = is.Create(ctx, found.Image); err != nil { |
| 87 | return err |
| 88 | } |
| 89 | |
| 90 | // Now, delete the original |
| 91 | if err = is.Delete(ctx, originalName, delOpts...); err != nil { |
| 92 | return err |
| 93 | } |
no test coverage detected
searching dependent graphs…