github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/build/image_reaper.go (about)

     1  package build
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/pkg/errors"
     9  	"golang.org/x/sync/errgroup"
    10  
    11  	"github.com/distribution/reference"
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/api/types/filters"
    14  	"github.com/docker/docker/client"
    15  
    16  	"github.com/tilt-dev/tilt/internal/docker"
    17  	"github.com/tilt-dev/tilt/internal/dockerfile"
    18  )
    19  
    20  type ImageReaper struct {
    21  	docker docker.Client
    22  }
    23  
    24  func FilterByLabel(label dockerfile.Label) filters.KeyValuePair {
    25  	return filters.Arg("label", string(label))
    26  }
    27  
    28  func FilterByLabelValue(label dockerfile.Label, val dockerfile.LabelValue) filters.KeyValuePair {
    29  	return filters.Arg("label", fmt.Sprintf("%s=%s", label, val))
    30  }
    31  
    32  func FilterByRefName(ref reference.Named) filters.KeyValuePair {
    33  	return filters.Arg("reference", fmt.Sprintf("%s:*", ref.Name()))
    34  }
    35  
    36  func NewImageReaper(docker docker.Client) ImageReaper {
    37  	return ImageReaper{
    38  		docker: docker,
    39  	}
    40  }
    41  
    42  // Delete all Tilt builds
    43  //
    44  // For safety reasons, we only delete images with the tilt.buildMode label,
    45  // but we let the caller set additional filters.
    46  func (r ImageReaper) RemoveTiltImages(ctx context.Context, createdBefore time.Time, force bool, extraFilters ...filters.KeyValuePair) error {
    47  	defaultFilter := FilterByLabel(BuildMode)
    48  	filterList := append([]filters.KeyValuePair{defaultFilter}, extraFilters...)
    49  	listOptions := types.ImageListOptions{
    50  		Filters: filters.NewArgs(filterList...),
    51  	}
    52  
    53  	summaries, err := r.docker.ImageList(ctx, listOptions)
    54  	if err != nil {
    55  		return errors.Wrap(err, "RemoveTiltImages")
    56  	}
    57  
    58  	g, ctx := errgroup.WithContext(ctx)
    59  	rmOptions := types.ImageRemoveOptions{
    60  		PruneChildren: true,
    61  		Force:         force,
    62  	}
    63  	for _, summary := range summaries {
    64  		id := summary.ID
    65  		created := time.Unix(summary.Created, 0)
    66  		if !created.Before(createdBefore) {
    67  			continue
    68  		}
    69  
    70  		g.Go(func() error {
    71  			_, err := r.docker.ImageRemove(ctx, id, rmOptions)
    72  			if client.IsErrNotFound(err) {
    73  				return nil
    74  			}
    75  			return err
    76  		})
    77  	}
    78  
    79  	err = g.Wait()
    80  	if err != nil {
    81  		return errors.Wrap(err, "RemoveTiltImages")
    82  	}
    83  	return err
    84  }