github.com/wozhu6104/docker@v20.10.10+incompatible/daemon/prune.go (about)

     1  package daemon // import "github.com/docker/docker/daemon"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"regexp"
     7  	"strconv"
     8  	"sync/atomic"
     9  	"time"
    10  
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/api/types/events"
    13  	"github.com/docker/docker/api/types/filters"
    14  	timetypes "github.com/docker/docker/api/types/time"
    15  	"github.com/docker/docker/errdefs"
    16  	"github.com/docker/docker/runconfig"
    17  	"github.com/docker/libnetwork"
    18  	"github.com/pkg/errors"
    19  	"github.com/sirupsen/logrus"
    20  )
    21  
    22  var (
    23  	// errPruneRunning is returned when a prune request is received while
    24  	// one is in progress
    25  	errPruneRunning = errdefs.Conflict(errors.New("a prune operation is already running"))
    26  
    27  	containersAcceptedFilters = map[string]bool{
    28  		"label":  true,
    29  		"label!": true,
    30  		"until":  true,
    31  	}
    32  
    33  	networksAcceptedFilters = map[string]bool{
    34  		"label":  true,
    35  		"label!": true,
    36  		"until":  true,
    37  	}
    38  )
    39  
    40  // ContainersPrune removes unused containers
    41  func (daemon *Daemon) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (*types.ContainersPruneReport, error) {
    42  	if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
    43  		return nil, errPruneRunning
    44  	}
    45  	defer atomic.StoreInt32(&daemon.pruneRunning, 0)
    46  
    47  	rep := &types.ContainersPruneReport{}
    48  
    49  	// make sure that only accepted filters have been received
    50  	err := pruneFilters.Validate(containersAcceptedFilters)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	until, err := getUntilFromPruneFilters(pruneFilters)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	allContainers := daemon.List()
    61  	for _, c := range allContainers {
    62  		select {
    63  		case <-ctx.Done():
    64  			logrus.Debugf("ContainersPrune operation cancelled: %#v", *rep)
    65  			return rep, nil
    66  		default:
    67  		}
    68  
    69  		if !c.IsRunning() {
    70  			if !until.IsZero() && c.Created.After(until) {
    71  				continue
    72  			}
    73  			if !matchLabels(pruneFilters, c.Config.Labels) {
    74  				continue
    75  			}
    76  			cSize, _ := daemon.imageService.GetContainerLayerSize(c.ID)
    77  			// TODO: sets RmLink to true?
    78  			err := daemon.ContainerRm(c.ID, &types.ContainerRmConfig{})
    79  			if err != nil {
    80  				logrus.Warnf("failed to prune container %s: %v", c.ID, err)
    81  				continue
    82  			}
    83  			if cSize > 0 {
    84  				rep.SpaceReclaimed += uint64(cSize)
    85  			}
    86  			rep.ContainersDeleted = append(rep.ContainersDeleted, c.ID)
    87  		}
    88  	}
    89  	daemon.EventsService.Log("prune", events.ContainerEventType, events.Actor{
    90  		Attributes: map[string]string{"reclaimed": strconv.FormatUint(rep.SpaceReclaimed, 10)},
    91  	})
    92  	return rep, nil
    93  }
    94  
    95  // localNetworksPrune removes unused local networks
    96  func (daemon *Daemon) localNetworksPrune(ctx context.Context, pruneFilters filters.Args) *types.NetworksPruneReport {
    97  	rep := &types.NetworksPruneReport{}
    98  
    99  	until, _ := getUntilFromPruneFilters(pruneFilters)
   100  
   101  	// When the function returns true, the walk will stop.
   102  	l := func(nw libnetwork.Network) bool {
   103  		select {
   104  		case <-ctx.Done():
   105  			// context cancelled
   106  			return true
   107  		default:
   108  		}
   109  		if nw.Info().ConfigOnly() {
   110  			return false
   111  		}
   112  		if !until.IsZero() && nw.Info().Created().After(until) {
   113  			return false
   114  		}
   115  		if !matchLabels(pruneFilters, nw.Info().Labels()) {
   116  			return false
   117  		}
   118  		nwName := nw.Name()
   119  		if runconfig.IsPreDefinedNetwork(nwName) {
   120  			return false
   121  		}
   122  		if len(nw.Endpoints()) > 0 {
   123  			return false
   124  		}
   125  		if err := daemon.DeleteNetwork(nw.ID()); err != nil {
   126  			logrus.Warnf("could not remove local network %s: %v", nwName, err)
   127  			return false
   128  		}
   129  		rep.NetworksDeleted = append(rep.NetworksDeleted, nwName)
   130  		return false
   131  	}
   132  	daemon.netController.WalkNetworks(l)
   133  	return rep
   134  }
   135  
   136  // clusterNetworksPrune removes unused cluster networks
   137  func (daemon *Daemon) clusterNetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
   138  	rep := &types.NetworksPruneReport{}
   139  
   140  	until, _ := getUntilFromPruneFilters(pruneFilters)
   141  
   142  	cluster := daemon.GetCluster()
   143  
   144  	if !cluster.IsManager() {
   145  		return rep, nil
   146  	}
   147  
   148  	networks, err := cluster.GetNetworks(pruneFilters)
   149  	if err != nil {
   150  		return rep, err
   151  	}
   152  	networkIsInUse := regexp.MustCompile(`network ([[:alnum:]]+) is in use`)
   153  	for _, nw := range networks {
   154  		select {
   155  		case <-ctx.Done():
   156  			return rep, nil
   157  		default:
   158  			if nw.Ingress {
   159  				// Routing-mesh network removal has to be explicitly invoked by user
   160  				continue
   161  			}
   162  			if !until.IsZero() && nw.Created.After(until) {
   163  				continue
   164  			}
   165  			if !matchLabels(pruneFilters, nw.Labels) {
   166  				continue
   167  			}
   168  			// https://github.com/docker/docker/issues/24186
   169  			// `docker network inspect` unfortunately displays ONLY those containers that are local to that node.
   170  			// So we try to remove it anyway and check the error
   171  			err = cluster.RemoveNetwork(nw.ID)
   172  			if err != nil {
   173  				// we can safely ignore the "network .. is in use" error
   174  				match := networkIsInUse.FindStringSubmatch(err.Error())
   175  				if len(match) != 2 || match[1] != nw.ID {
   176  					logrus.Warnf("could not remove cluster network %s: %v", nw.Name, err)
   177  				}
   178  				continue
   179  			}
   180  			rep.NetworksDeleted = append(rep.NetworksDeleted, nw.Name)
   181  		}
   182  	}
   183  	return rep, nil
   184  }
   185  
   186  // NetworksPrune removes unused networks
   187  func (daemon *Daemon) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) {
   188  	if !atomic.CompareAndSwapInt32(&daemon.pruneRunning, 0, 1) {
   189  		return nil, errPruneRunning
   190  	}
   191  	defer atomic.StoreInt32(&daemon.pruneRunning, 0)
   192  
   193  	// make sure that only accepted filters have been received
   194  	err := pruneFilters.Validate(networksAcceptedFilters)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	if _, err := getUntilFromPruneFilters(pruneFilters); err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	rep := &types.NetworksPruneReport{}
   204  	if clusterRep, err := daemon.clusterNetworksPrune(ctx, pruneFilters); err == nil {
   205  		rep.NetworksDeleted = append(rep.NetworksDeleted, clusterRep.NetworksDeleted...)
   206  	}
   207  
   208  	localRep := daemon.localNetworksPrune(ctx, pruneFilters)
   209  	rep.NetworksDeleted = append(rep.NetworksDeleted, localRep.NetworksDeleted...)
   210  
   211  	select {
   212  	case <-ctx.Done():
   213  		logrus.Debugf("NetworksPrune operation cancelled: %#v", *rep)
   214  		return rep, nil
   215  	default:
   216  	}
   217  	daemon.EventsService.Log("prune", events.NetworkEventType, events.Actor{
   218  		Attributes: map[string]string{"reclaimed": "0"},
   219  	})
   220  	return rep, nil
   221  }
   222  
   223  func getUntilFromPruneFilters(pruneFilters filters.Args) (time.Time, error) {
   224  	until := time.Time{}
   225  	if !pruneFilters.Contains("until") {
   226  		return until, nil
   227  	}
   228  	untilFilters := pruneFilters.Get("until")
   229  	if len(untilFilters) > 1 {
   230  		return until, fmt.Errorf("more than one until filter specified")
   231  	}
   232  	ts, err := timetypes.GetTimestamp(untilFilters[0], time.Now())
   233  	if err != nil {
   234  		return until, err
   235  	}
   236  	seconds, nanoseconds, err := timetypes.ParseTimestamps(ts, 0)
   237  	if err != nil {
   238  		return until, err
   239  	}
   240  	until = time.Unix(seconds, nanoseconds)
   241  	return until, nil
   242  }
   243  
   244  func matchLabels(pruneFilters filters.Args, labels map[string]string) bool {
   245  	if !pruneFilters.MatchKVList("label", labels) {
   246  		return false
   247  	}
   248  	// By default MatchKVList will return true if field (like 'label!') does not exist
   249  	// So we have to add additional Contains("label!") check
   250  	if pruneFilters.Contains("label!") {
   251  		if pruneFilters.MatchKVList("label!", labels) {
   252  			return false
   253  		}
   254  	}
   255  	return true
   256  }