github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/cli/docker_prune.go (about) 1 package cli 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "strings" 8 "time" 9 10 "github.com/spf13/cobra" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 13 "github.com/tilt-dev/tilt/internal/analytics" 14 "github.com/tilt-dev/tilt/internal/container" 15 ctrltiltfile "github.com/tilt-dev/tilt/internal/controllers/apis/tiltfile" 16 "github.com/tilt-dev/tilt/internal/docker" 17 "github.com/tilt-dev/tilt/internal/engine/dockerprune" 18 "github.com/tilt-dev/tilt/internal/k8s" 19 "github.com/tilt-dev/tilt/internal/tiltfile" 20 "github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1" 21 "github.com/tilt-dev/tilt/pkg/logger" 22 "github.com/tilt-dev/tilt/pkg/model" 23 ) 24 25 type dockerPruneCmd struct { 26 fileName string 27 } 28 29 type dpDeps struct { 30 dCli docker.Client 31 kCli k8s.Client 32 tfl tiltfile.TiltfileLoader 33 } 34 35 func newDPDeps(dCli docker.Client, kCli k8s.Client, tfl tiltfile.TiltfileLoader) dpDeps { 36 return dpDeps{ 37 dCli: dCli, 38 kCli: kCli, 39 tfl: tfl, 40 } 41 } 42 43 func (c *dockerPruneCmd) name() model.TiltSubcommand { return "docker-prune" } 44 45 func (c *dockerPruneCmd) register() *cobra.Command { 46 cmd := &cobra.Command{ 47 Use: "docker-prune", 48 Short: "Run docker prune as Tilt does", 49 } 50 51 addTiltfileFlag(cmd, &c.fileName) 52 53 return cmd 54 } 55 56 func (c *dockerPruneCmd) run(ctx context.Context, args []string) error { 57 a := analytics.Get(ctx) 58 a.Incr("cmd.dockerPrune", nil) 59 defer a.Flush(time.Second) 60 61 if !logger.Get(ctx).Level().ShouldDisplay(logger.VerboseLvl) { 62 // Docker Pruner filters output when nothing is pruned if not in verbose 63 // logging mode, which is suitable for when it runs in the background 64 // during `tilt up`, but we always want to include that for the CLI cmd 65 // N.B. we only override if we're not already showing verbose so that 66 // `--debug` flag isn't impacted 67 l := logger.NewLogger(logger.VerboseLvl, os.Stdout) 68 ctx = logger.WithLogger(ctx, l) 69 } 70 71 deps, err := wireDockerPrune(ctx, a, "docker-prune") 72 if err != nil { 73 return err 74 } 75 76 tlr := deps.tfl.Load(ctx, ctrltiltfile.MainTiltfile(c.fileName, args), nil) 77 if tlr.Error != nil { 78 return tlr.Error 79 } 80 81 imgSelectors, err := resolveImageSelectors(ctx, deps.kCli, &tlr) 82 if err != nil { 83 return err 84 } 85 86 dp := dockerprune.NewDockerPruner(deps.dCli) 87 88 // TODO: print the commands being run 89 dp.Prune(ctx, tlr.DockerPruneSettings.MaxAge, tlr.DockerPruneSettings.KeepRecent, imgSelectors) 90 91 return nil 92 } 93 94 // resolveImageSelectors finds image references from a tiltfile.TiltfileLoadResult object. 95 // 96 // The Kubernetes client is used to resolve the correct image names if a local registry is in use. 97 // 98 // This method is brittle and duplicates some logic from the actual reconcilers. 99 // In the future, we hope to have a mode where we can launch the full apiserver 100 // with all resources in a "disabled" state and rely on the API, but that's not 101 // possible currently. 102 func resolveImageSelectors(ctx context.Context, kCli k8s.Client, tlr *tiltfile.TiltfileLoadResult) ([]container.RefSelector, error) { 103 for _, m := range tlr.Manifests { 104 if err := m.InferImageProperties(); err != nil { 105 return nil, err 106 } 107 } 108 109 var reg *v1alpha1.RegistryHosting 110 if tlr.HasOrchestrator(model.OrchestratorK8s) { 111 // k8s.Client::LocalRegistry will return an empty registry on any error, 112 // so ensure the client is actually functional first 113 if _, err := kCli.CheckConnected(ctx); err != nil { 114 return nil, fmt.Errorf("determining local registry: %v", err) 115 } 116 reg = kCli.LocalRegistry(ctx) 117 } 118 119 clusters := map[string]*v1alpha1.Cluster{ 120 v1alpha1.ClusterNameDefault: { 121 ObjectMeta: metav1.ObjectMeta{Name: v1alpha1.ClusterNameDefault}, 122 Spec: v1alpha1.ClusterSpec{DefaultRegistry: reg}, 123 }, 124 } 125 126 imgSelectors := model.LocalRefSelectorsForManifests(tlr.Manifests, clusters) 127 if len(imgSelectors) != 0 && logger.Get(ctx).Level().ShouldDisplay(logger.DebugLvl) { 128 var sb strings.Builder 129 for _, is := range imgSelectors { 130 sb.WriteString(" - ") 131 sb.WriteString(is.RefFamiliarString()) 132 sb.WriteRune('\n') 133 } 134 135 logger.Get(ctx).Debugf("Running Docker Prune for images:\n%s", sb.String()) 136 } 137 138 return imgSelectors, nil 139 }