github.com/endophage/docker@v1.4.2-0.20161027011718-242853499895/daemon/prune.go (about)

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