github.com/brandon-bethke-neudesic/moby@v1.13.1/daemon/prune.go (about)

     1  package daemon
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  
     7  	"github.com/Sirupsen/logrus"
     8  	"github.com/docker/distribution/digest"
     9  	"github.com/docker/docker/api/types"
    10  	"github.com/docker/docker/api/types/filters"
    11  	"github.com/docker/docker/image"
    12  	"github.com/docker/docker/layer"
    13  	"github.com/docker/docker/pkg/directory"
    14  	"github.com/docker/docker/reference"
    15  	"github.com/docker/docker/runconfig"
    16  	"github.com/docker/docker/volume"
    17  	"github.com/docker/libnetwork"
    18  )
    19  
    20  // ContainersPrune removes unused containers
    21  func (daemon *Daemon) ContainersPrune(pruneFilters filters.Args) (*types.ContainersPruneReport, error) {
    22  	rep := &types.ContainersPruneReport{}
    23  
    24  	allContainers := daemon.List()
    25  	for _, c := range allContainers {
    26  		if !c.IsRunning() {
    27  			cSize, _ := daemon.getSize(c)
    28  			// TODO: sets RmLink to true?
    29  			err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
    30  			if err != nil {
    31  				logrus.Warnf("failed to prune container %s: %v", c.ID, err)
    32  				continue
    33  			}
    34  			if cSize > 0 {
    35  				rep.SpaceReclaimed += uint64(cSize)
    36  			}
    37  			rep.ContainersDeleted = append(rep.ContainersDeleted, c.ID)
    38  		}
    39  	}
    40  
    41  	return rep, nil
    42  }
    43  
    44  // VolumesPrune removes unused local volumes
    45  func (daemon *Daemon) VolumesPrune(pruneFilters filters.Args) (*types.VolumesPruneReport, error) {
    46  	rep := &types.VolumesPruneReport{}
    47  
    48  	pruneVols := func(v volume.Volume) error {
    49  		name := v.Name()
    50  		refs := daemon.volumes.Refs(v)
    51  
    52  		if len(refs) == 0 {
    53  			vSize, err := directory.Size(v.Path())
    54  			if err != nil {
    55  				logrus.Warnf("could not determine size of volume %s: %v", name, err)
    56  			}
    57  			err = daemon.volumes.Remove(v)
    58  			if err != nil {
    59  				logrus.Warnf("could not remove volume %s: %v", name, err)
    60  				return nil
    61  			}
    62  			rep.SpaceReclaimed += uint64(vSize)
    63  			rep.VolumesDeleted = append(rep.VolumesDeleted, name)
    64  		}
    65  
    66  		return nil
    67  	}
    68  
    69  	err := daemon.traverseLocalVolumes(pruneVols)
    70  
    71  	return rep, err
    72  }
    73  
    74  // ImagesPrune removes unused images
    75  func (daemon *Daemon) ImagesPrune(pruneFilters filters.Args) (*types.ImagesPruneReport, error) {
    76  	rep := &types.ImagesPruneReport{}
    77  
    78  	danglingOnly := true
    79  	if pruneFilters.Include("dangling") {
    80  		if pruneFilters.ExactMatch("dangling", "false") || pruneFilters.ExactMatch("dangling", "0") {
    81  			danglingOnly = false
    82  		} else if !pruneFilters.ExactMatch("dangling", "true") && !pruneFilters.ExactMatch("dangling", "1") {
    83  			return nil, fmt.Errorf("Invalid filter 'dangling=%s'", pruneFilters.Get("dangling"))
    84  		}
    85  	}
    86  
    87  	var allImages map[image.ID]*image.Image
    88  	if danglingOnly {
    89  		allImages = daemon.imageStore.Heads()
    90  	} else {
    91  		allImages = daemon.imageStore.Map()
    92  	}
    93  	allContainers := daemon.List()
    94  	imageRefs := map[string]bool{}
    95  	for _, c := range allContainers {
    96  		imageRefs[c.ID] = true
    97  	}
    98  
    99  	// Filter intermediary images and get their unique size
   100  	allLayers := daemon.layerStore.Map()
   101  	topImages := map[image.ID]*image.Image{}
   102  	for id, img := range allImages {
   103  		dgst := digest.Digest(id)
   104  		if len(daemon.referenceStore.References(dgst)) == 0 && len(daemon.imageStore.Children(id)) != 0 {
   105  			continue
   106  		}
   107  		topImages[id] = img
   108  	}
   109  
   110  	for id := range topImages {
   111  		dgst := digest.Digest(id)
   112  		hex := dgst.Hex()
   113  		if _, ok := imageRefs[hex]; ok {
   114  			continue
   115  		}
   116  
   117  		deletedImages := []types.ImageDelete{}
   118  		refs := daemon.referenceStore.References(dgst)
   119  		if len(refs) > 0 {
   120  			if danglingOnly {
   121  				// Not a dangling image
   122  				continue
   123  			}
   124  
   125  			nrRefs := len(refs)
   126  			for _, ref := range refs {
   127  				// If nrRefs == 1, we have an image marked as myreponame:<none>
   128  				// i.e. the tag content was changed
   129  				if _, ok := ref.(reference.Canonical); ok && nrRefs > 1 {
   130  					continue
   131  				}
   132  				imgDel, err := daemon.ImageDelete(ref.String(), false, true)
   133  				if err != nil {
   134  					logrus.Warnf("could not delete reference %s: %v", ref.String(), err)
   135  					continue
   136  				}
   137  				deletedImages = append(deletedImages, imgDel...)
   138  			}
   139  		} else {
   140  			imgDel, err := daemon.ImageDelete(hex, false, true)
   141  			if err != nil {
   142  				logrus.Warnf("could not delete image %s: %v", hex, err)
   143  				continue
   144  			}
   145  			deletedImages = append(deletedImages, imgDel...)
   146  		}
   147  
   148  		rep.ImagesDeleted = append(rep.ImagesDeleted, deletedImages...)
   149  	}
   150  
   151  	// Compute how much space was freed
   152  	for _, d := range rep.ImagesDeleted {
   153  		if d.Deleted != "" {
   154  			chid := layer.ChainID(d.Deleted)
   155  			if l, ok := allLayers[chid]; ok {
   156  				diffSize, err := l.DiffSize()
   157  				if err != nil {
   158  					logrus.Warnf("failed to get layer %s size: %v", chid, err)
   159  					continue
   160  				}
   161  				rep.SpaceReclaimed += uint64(diffSize)
   162  			}
   163  		}
   164  	}
   165  
   166  	return rep, nil
   167  }
   168  
   169  // localNetworksPrune removes unused local networks
   170  func (daemon *Daemon) localNetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
   171  	rep := &types.NetworksPruneReport{}
   172  	var err error
   173  	// When the function returns true, the walk will stop.
   174  	l := func(nw libnetwork.Network) bool {
   175  		nwName := nw.Name()
   176  		predefined := runconfig.IsPreDefinedNetwork(nwName)
   177  		if !predefined && len(nw.Endpoints()) == 0 {
   178  			if err = daemon.DeleteNetwork(nw.ID()); err != nil {
   179  				logrus.Warnf("could not remove network %s: %v", nwName, err)
   180  				return false
   181  			}
   182  			rep.NetworksDeleted = append(rep.NetworksDeleted, nwName)
   183  		}
   184  		return false
   185  	}
   186  	daemon.netController.WalkNetworks(l)
   187  	return rep, err
   188  }
   189  
   190  // clusterNetworksPrune removes unused cluster networks
   191  func (daemon *Daemon) clusterNetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
   192  	rep := &types.NetworksPruneReport{}
   193  	cluster := daemon.GetCluster()
   194  	networks, err := cluster.GetNetworks()
   195  	if err != nil {
   196  		return rep, err
   197  	}
   198  	networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`)
   199  	for _, nw := range networks {
   200  		if nw.Name == "ingress" {
   201  			continue
   202  		}
   203  		// https://github.com/docker/docker/issues/24186
   204  		// `docker network inspect` unfortunately displays ONLY those containers that are local to that node.
   205  		// So we try to remove it anyway and check the error
   206  		err = cluster.RemoveNetwork(nw.ID)
   207  		if err != nil {
   208  			// we can safely ignore the "network .. is in use" error
   209  			match := networkIsInUse.FindStringSubmatch(err.Error())
   210  			if len(match) != 2 || match[1] != nw.ID {
   211  				logrus.Warnf("could not remove network %s: %v", nw.Name, err)
   212  			}
   213  			continue
   214  		}
   215  		rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name)
   216  	}
   217  	return rep, nil
   218  }
   219  
   220  // NetworksPrune removes unused networks
   221  func (daemon *Daemon) NetworksPrune(pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
   222  	rep := &types.NetworksPruneReport{}
   223  	clusterRep, err := daemon.clusterNetworksPrune(pruneFilters)
   224  	if err != nil {
   225  		logrus.Warnf("could not remove cluster networks: %v", err)
   226  	} else {
   227  		rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...)
   228  	}
   229  	localRep, err := daemon.localNetworksPrune(pruneFilters)
   230  	if err != nil {
   231  		logrus.Warnf("could not remove local networks: %v", err)
   232  	} else {
   233  		rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...)
   234  	}
   235  	return rep, err
   236  }