From 5f0575cc8f9ea339e4206dc68431f24d6b71442c Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Tue, 22 Mar 2022 18:13:11 +0100 Subject: [PATCH] tkn-local: implement prune command Signed-off-by: Vincent Demeester --- cmd/tkn-local/main.go | 2 +- cmd/tkn-local/prune.go | 132 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 128 insertions(+), 6 deletions(-) diff --git a/cmd/tkn-local/main.go b/cmd/tkn-local/main.go index 0681a26d..0fbda16b 100644 --- a/cmd/tkn-local/main.go +++ b/cmd/tkn-local/main.go @@ -10,7 +10,7 @@ func Command() *cobra.Command { cmd := &cobra.Command{ Use: "local", Aliases: []string{}, - Short: "Local commands", + Short: "Tekton \"local\" commands", Annotations: map[string]string{ "commandType": "main", }, diff --git a/cmd/tkn-local/prune.go b/cmd/tkn-local/prune.go index 652afa78..f220f49a 100644 --- a/cmd/tkn-local/prune.go +++ b/cmd/tkn-local/prune.go @@ -1,22 +1,144 @@ package main import ( + "fmt" + "io" + "os" + "strings" + "text/tabwriter" + "time" + + "github.com/moby/buildkit/client" + "github.com/moby/buildkit/util/appcontext" "github.com/spf13/cobra" + "github.com/tonistiigi/units" + "github.com/vdemeester/buildkit-tekton/pkg/buildkit" ) -type pruneOption struct{} +type pruneOption struct { + filters []string + host string + duration time.Duration + storage float64 + all bool + verbose bool +} func pruneCommand() *cobra.Command { - // opts := &pruneOption{} + opts := &pruneOption{} cmd := &cobra.Command{ Use: "prune", Aliases: []string{}, - Short: "Run a tekton resource", + Short: "clean up buildkit build cache", RunE: func(cmd *cobra.Command, args []string) error { - return nil + return prune(opts) }, } - // c.Flags().BoolVarP(&opts.ForceDelete, "force", "f", false, "Whether to force deletion (default: false)") + cmd.Flags().StringVar(&opts.host, "host", "", "Host to use") + cmd.Flags().StringArrayVar(&opts.filters, "filter", []string{}, "Filter records") + cmd.Flags().DurationVar(&opts.duration, "keep-duration", 0, "keep data newer than this limit") + cmd.Flags().Float64Var(&opts.storage, "keep-storage", 0, "keep data below this limit (in MB)") + cmd.Flags().BoolVar(&opts.all, "all", false, "include internal/frontend references") + cmd.Flags().BoolVarP(&opts.verbose, "verbose", "v", false, "verbose output") return cmd } + +func prune(opts *pruneOption) error { + ctx := appcontext.Context() + + // Connect or start buildkit + c, err := buildkit.NewClient(ctx, opts.host) + if err != nil { + return err + } + + ch := make(chan client.UsageInfo) + printed := make(chan struct{}) + + tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0) + first := true + total := int64(0) + + go func() { + defer close(printed) + for du := range ch { + total += du.Size + if opts.verbose { + printVerbose(tw, []*client.UsageInfo{&du}) + } else { + if first { + printTableHeader(tw) + first = false + } + printTableRow(tw, &du) + tw.Flush() + } + } + }() + + clientopts := []client.PruneOption{ + client.WithFilter(opts.filters), + client.WithKeepOpt(opts.duration, int64(opts.storage*1e6)), + } + + if opts.all { + clientopts = append(clientopts, client.PruneAll) + } + + err = c.Prune(ctx, ch, clientopts...) + close(ch) + if err != nil { + return err + } + + return nil +} + +func printVerbose(tw *tabwriter.Writer, du []*client.UsageInfo) { + for _, di := range du { + printKV(tw, "ID", di.ID) + if len(di.Parents) > 0 { + printKV(tw, "Parents", strings.Join(di.Parents, ";")) + } + printKV(tw, "Created at", di.CreatedAt) + printKV(tw, "Mutable", di.Mutable) + printKV(tw, "Reclaimable", !di.InUse) + printKV(tw, "Shared", di.Shared) + printKV(tw, "Size", fmt.Sprintf("%.2f", units.Bytes(di.Size))) + if di.Description != "" { + printKV(tw, "Description", di.Description) + } + printKV(tw, "Usage count", di.UsageCount) + if di.LastUsedAt != nil { + printKV(tw, "Last used", di.LastUsedAt) + } + if di.RecordType != "" { + printKV(tw, "Type", di.RecordType) + } + + fmt.Fprintf(tw, "\n") + } + + tw.Flush() +} + +func printTableHeader(tw *tabwriter.Writer) { + fmt.Fprintln(tw, "ID\tRECLAIMABLE\tSIZE\tLAST ACCESSED") +} + +func printTableRow(tw *tabwriter.Writer, di *client.UsageInfo) { + id := di.ID + if di.Mutable { + id += "*" + } + size := fmt.Sprintf("%.2f", units.Bytes(di.Size)) + if di.Shared { + size += "*" + } + fmt.Fprintf(tw, "%-71s\t%-11v\t%s\t\n", id, !di.InUse, size) +} + +func printKV(w io.Writer, k string, v interface{}) { + fmt.Fprintf(w, "%s:\t%v\n", k, v) +}