mirror of
https://github.com/containers/skopeo.git
synced 2025-08-18 14:37:16 +00:00
feat(prune): filter by date, skip summary and noninteractive opts
Signed-off-by: Ethan Balcik <ebalcik71@gmail.com>
This commit is contained in:
parent
3585b8de8f
commit
10b95819ba
@ -11,6 +11,7 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
commonFlag "github.com/containers/common/pkg/flag"
|
commonFlag "github.com/containers/common/pkg/flag"
|
||||||
"github.com/containers/common/pkg/retry"
|
"github.com/containers/common/pkg/retry"
|
||||||
@ -47,6 +48,7 @@ func newFilteredTags() *filteredTags {
|
|||||||
|
|
||||||
type tagFilterOptions struct {
|
type tagFilterOptions struct {
|
||||||
BeforeVersion commonFlag.OptionalString
|
BeforeVersion commonFlag.OptionalString
|
||||||
|
BeforeTime commonFlag.OptionalString
|
||||||
VersionLabel commonFlag.OptionalString
|
VersionLabel commonFlag.OptionalString
|
||||||
Valid commonFlag.OptionalBool
|
Valid commonFlag.OptionalBool
|
||||||
Invalid commonFlag.OptionalBool
|
Invalid commonFlag.OptionalBool
|
||||||
@ -54,13 +56,18 @@ type tagFilterOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (opts *tagFilterOptions) FilterPresent() bool {
|
func (opts *tagFilterOptions) FilterPresent() bool {
|
||||||
return opts.BeforeVersion.Present() || opts.Valid.Present() || opts.Invalid.Present()
|
return opts.BeforeVersion.Present() || opts.Valid.Present() || opts.Invalid.Present() || opts.BeforeTime.Present()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *tagFilterOptions) InspectFilterPresent() bool {
|
||||||
|
return (opts.BeforeVersion.Present() && opts.VersionLabel.Present()) || opts.BeforeTime.Present()
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterFlags() (pflag.FlagSet, *tagFilterOptions) {
|
func filterFlags() (pflag.FlagSet, *tagFilterOptions) {
|
||||||
opts := tagFilterOptions{}
|
opts := tagFilterOptions{}
|
||||||
fs := pflag.FlagSet{}
|
fs := pflag.FlagSet{}
|
||||||
fs.Var(commonFlag.NewOptionalStringValue(&opts.BeforeVersion), "before-version", "A version threshold prior to which to list tags")
|
fs.Var(commonFlag.NewOptionalStringValue(&opts.BeforeVersion), "before-version", "A version threshold prior to which to list tags")
|
||||||
|
fs.Var(commonFlag.NewOptionalStringValue(&opts.BeforeTime), "before-time", "A date threshold prior to which to list tags")
|
||||||
fs.Var(commonFlag.NewOptionalStringValue(&opts.VersionLabel), "version-label", "A label from which to derive the version for each tag")
|
fs.Var(commonFlag.NewOptionalStringValue(&opts.VersionLabel), "version-label", "A label from which to derive the version for each tag")
|
||||||
commonFlag.OptionalBoolFlag(&fs, &opts.Valid, "valid", "Whether to list only tags with valid semver")
|
commonFlag.OptionalBoolFlag(&fs, &opts.Valid, "valid", "Whether to list only tags with valid semver")
|
||||||
commonFlag.OptionalBoolFlag(&fs, &opts.Invalid, "invalid", "Whether to list only tags with invalid semver")
|
commonFlag.OptionalBoolFlag(&fs, &opts.Invalid, "invalid", "Whether to list only tags with invalid semver")
|
||||||
@ -190,6 +197,16 @@ func filterDockerTagBySemver(filtered *filteredTags, opts *tagsOptions, threshol
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterDockerTagByDate(filtered *filteredTags, opts *tagsOptions, timeThreshold time.Time, tag string, created time.Time) (error) {
|
||||||
|
// Compare the created date against the threshold date
|
||||||
|
if created.Before(timeThreshold) {
|
||||||
|
filtered.ToPrune = append(filtered.ToPrune, tag)
|
||||||
|
} else {
|
||||||
|
filtered.ToKeep = append(filtered.ToKeep, tag)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func filterDockerTagsByTagSemver(opts *tagsOptions, tags *tagListOutput) (*filteredTags, error) {
|
func filterDockerTagsByTagSemver(opts *tagsOptions, tags *tagListOutput) (*filteredTags, error) {
|
||||||
// Get the user-provided threshold version
|
// Get the user-provided threshold version
|
||||||
// This will be validated later when the comparison takes place
|
// This will be validated later when the comparison takes place
|
||||||
@ -213,15 +230,15 @@ func filterDockerTagsByTagSemver(opts *tagsOptions, tags *tagListOutput) (*filte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use goroutines to parallelize & display progress continuously
|
// Use goroutines to parallelize & display progress continuously
|
||||||
func filterDockerTagsByLabelSemver(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, tags *tagListOutput) (*filteredTags, error) {
|
func filterDockerTagsByImageMetadata(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, tags *tagListOutput) (*filteredTags, error) {
|
||||||
// Get the user-provided threshold version
|
// Get the user-provided threshold version
|
||||||
// This will be validated later when the comparison takes place
|
// This will be validated later when the comparison takes place
|
||||||
var threshold string
|
var versionThreshold string
|
||||||
if opts.filterOpts.BeforeVersion.Present() {
|
if opts.filterOpts.BeforeVersion.Present() {
|
||||||
threshold = opts.filterOpts.BeforeVersion.Value()
|
versionThreshold = opts.filterOpts.BeforeVersion.Value()
|
||||||
} else {
|
} else {
|
||||||
// Set as an arbitrary valid version since this isn't going to affect output
|
// Set as an arbitrary valid version since this isn't going to affect output
|
||||||
threshold = "v0.1.0"
|
versionThreshold = "v0.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize a zeroed filteredTags struct to return
|
// Initialize a zeroed filteredTags struct to return
|
||||||
@ -255,13 +272,13 @@ func filterDockerTagsByLabelSemver(ctx context.Context, sys *types.SystemContext
|
|||||||
if len(msg) > 0 {
|
if len(msg) > 0 {
|
||||||
// Log progress in-place
|
// Log progress in-place
|
||||||
tagsFiltered += 1
|
tagsFiltered += 1
|
||||||
fmt.Printf("\rfetching image labels\t%d / %d", tagsFiltered, totalTags)
|
fmt.Fprintf(os.Stderr, "\rinspecting image tags\t%d / %d", tagsFiltered, totalTags)
|
||||||
}
|
}
|
||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
// Print the error and append to the errors slice
|
// Print the error and append to the errors slice
|
||||||
if err != nil {
|
if err != nil {
|
||||||
numErrors += 1
|
numErrors += 1
|
||||||
fmt.Fprintf(os.Stderr, "\nerror while fetching image labels %s\n", err)
|
fmt.Fprintf(os.Stderr, "\nerror while inspecting image tags %s\n", err)
|
||||||
}
|
}
|
||||||
case <-doneChan:
|
case <-doneChan:
|
||||||
break filterLoop
|
break filterLoop
|
||||||
@ -269,11 +286,11 @@ func filterDockerTagsByLabelSemver(ctx context.Context, sys *types.SystemContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log completion in-place and close the goroutine
|
// Log completion in-place and close the goroutine
|
||||||
fmt.Printf("\rfetching image labels\t%d / %d (done)\n", tagsFiltered, totalTags)
|
fmt.Fprintf(os.Stderr, "\rinspecting image tags\t%d / %d (done)\n", tagsFiltered, totalTags)
|
||||||
readWaitGroup.Done()
|
readWaitGroup.Done()
|
||||||
}(filteredChan, errChan, doneChan)
|
}(filteredChan, errChan, doneChan)
|
||||||
|
|
||||||
// Goroutines for fetching image labels
|
// Goroutines for fetching image metadata
|
||||||
var fetchWaitGroup = sync.WaitGroup{}
|
var fetchWaitGroup = sync.WaitGroup{}
|
||||||
for i := 0; i < len(tags.Tags); i++ {
|
for i := 0; i < len(tags.Tags); i++ {
|
||||||
fetchWaitGroup.Add(1)
|
fetchWaitGroup.Add(1)
|
||||||
@ -319,17 +336,31 @@ func filterDockerTagsByLabelSemver(ctx context.Context, sys *types.SystemContext
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the version label and filter it into the filteredTags struct
|
if opts.filterOpts.VersionLabel.Present() {
|
||||||
versionLabel := opts.filterOpts.VersionLabel.Value()
|
// If there is a version label threshold, then filter by the version label
|
||||||
tagVersion, ok := imgInspect.Labels[versionLabel]
|
versionLabel := opts.filterOpts.VersionLabel.Value()
|
||||||
if !ok {
|
tagVersion, ok := imgInspect.Labels[versionLabel]
|
||||||
errChan <- fmt.Errorf("For tag %s: version label not found: %s", tag, versionLabel)
|
if !ok {
|
||||||
return
|
errChan <- fmt.Errorf("For tag %s: version label not found: %s", tag, versionLabel)
|
||||||
}
|
return
|
||||||
err = filterDockerTagBySemver(filtered, opts, threshold, tag, tagVersion)
|
}
|
||||||
if err != nil {
|
err = filterDockerTagBySemver(filtered, opts, versionThreshold, tag, tagVersion)
|
||||||
errChan <- fmt.Errorf("Error filtering tags: %w", err)
|
if err != nil {
|
||||||
return
|
errChan <- fmt.Errorf("For tag %s: error filtering: %w", tag, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If there is a time threshold, then filter by the created date
|
||||||
|
timeThreshold, err := time.Parse(time.RFC3339, opts.filterOpts.BeforeTime.Value())
|
||||||
|
if err != nil {
|
||||||
|
errChan <- fmt.Errorf("Error parsing time threshold for filtering: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = filterDockerTagByDate(filtered, opts, timeThreshold, tag, *imgInspect.Created)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- fmt.Errorf("For tag %s: error filtering: %w", tag, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If successful, then signal completion
|
// If successful, then signal completion
|
||||||
@ -357,7 +388,7 @@ func filterDockerTagsByLabelSemver(ctx context.Context, sys *types.SystemContext
|
|||||||
|
|
||||||
// Return the filtered tags, and an error summary if any errors occurred
|
// Return the filtered tags, and an error summary if any errors occurred
|
||||||
if numErrors > 0 {
|
if numErrors > 0 {
|
||||||
return filtered, fmt.Errorf("Encountered %d errors while filtering tags by label semver", numErrors)
|
return filtered, fmt.Errorf("Encountered %d errors while filtering tags by inspect metadata", numErrors)
|
||||||
}
|
}
|
||||||
return filtered, nil
|
return filtered, nil
|
||||||
}
|
}
|
||||||
@ -367,8 +398,8 @@ func filterDockerTags(ctx context.Context, sys *types.SystemContext, opts *tagsO
|
|||||||
Repository: repositoryName,
|
Repository: repositoryName,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
}
|
}
|
||||||
if opts.filterOpts.VersionLabel.Present() {
|
if opts.filterOpts.InspectFilterPresent() {
|
||||||
return filterDockerTagsByLabelSemver(ctx, sys, opts, tagList)
|
return filterDockerTagsByImageMetadata(ctx, sys, opts, tagList)
|
||||||
}
|
}
|
||||||
return filterDockerTagsByTagSemver(opts, tagList)
|
return filterDockerTagsByTagSemver(opts, tagList)
|
||||||
}
|
}
|
||||||
@ -378,8 +409,8 @@ func listFilteredDockerTags(ctx context.Context, sys *types.SystemContext, opts
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ``, nil, fmt.Errorf("Error filtering tags by semver: %w", err)
|
return ``, nil, fmt.Errorf("Error filtering tags by semver: %w", err)
|
||||||
}
|
}
|
||||||
if opts.filterOpts.BeforeVersion.Present() {
|
if opts.filterOpts.BeforeVersion.Present() || opts.filterOpts.BeforeTime.Present() {
|
||||||
// Optionally only list tags prior to given semver threshold
|
// Optionally only list tags prior to given semver or date threshold
|
||||||
return repositoryName, filtered.ToPrune, nil
|
return repositoryName, filtered.ToPrune, nil
|
||||||
} else if opts.filterOpts.Invalid.Present() {
|
} else if opts.filterOpts.Invalid.Present() {
|
||||||
// Optionally only list tags with invalid semver
|
// Optionally only list tags with invalid semver
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/containers/image/v5/transports/alltransports"
|
"github.com/containers/image/v5/transports/alltransports"
|
||||||
"github.com/containers/image/v5/types"
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enumerated constant for byte units
|
// Enumerated constant for byte units
|
||||||
@ -76,7 +77,7 @@ func bytesToByteUnit(bytes int64) string {
|
|||||||
return fmt.Sprintf("%.2f %s", unitDec, unitSuf)
|
return fmt.Sprintf("%.2f %s", unitDec, unitSuf)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pruneTransportHandlers = map[string]func(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, userInput string) error {
|
var pruneTransportHandlers = map[string]func(ctx context.Context, sys *types.SystemContext, opts *pruneOptions, userInput string) error {
|
||||||
docker.Transport.Name(): pruneDockerTags,
|
docker.Transport.Name(): pruneDockerTags,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,11 +87,25 @@ func supportedPruneTransports(joinStr string) string {
|
|||||||
return strings.Join(res, joinStr)
|
return strings.Join(res, joinStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type pruneUserOptions struct {
|
||||||
|
SkipSummary bool
|
||||||
|
NonInteractive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func pruneFlags() (pflag.FlagSet, *pruneUserOptions) {
|
||||||
|
opts := pruneUserOptions{}
|
||||||
|
fs := pflag.FlagSet{}
|
||||||
|
fs.BoolVarP(&opts.SkipSummary, "skip-summary", "s", false, "Skip computing the prune summary of freed storage space")
|
||||||
|
fs.BoolVarP(&opts.NonInteractive, "non-interactive", "y", false, "Do not display an interactive prompt for the user to confirm before beginning puning")
|
||||||
|
return fs, &opts
|
||||||
|
}
|
||||||
|
|
||||||
type pruneOptions struct {
|
type pruneOptions struct {
|
||||||
global *globalOptions
|
global *globalOptions
|
||||||
image *imageOptions
|
image *imageOptions
|
||||||
retryOpts *retry.Options
|
retryOpts *retry.Options
|
||||||
filterOpts *tagFilterOptions
|
filterOpts *tagFilterOptions
|
||||||
|
pruneOpts *pruneUserOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pruneOptions) intoTagsOptions() *tagsOptions {
|
func (p *pruneOptions) intoTagsOptions() *tagsOptions {
|
||||||
@ -108,12 +123,14 @@ func pruneCmd(global *globalOptions) *cobra.Command {
|
|||||||
imageFlags, imageOpts := dockerImageFlags(global, sharedOpts, nil, "", "")
|
imageFlags, imageOpts := dockerImageFlags(global, sharedOpts, nil, "", "")
|
||||||
retryFlags, retryOpts := retryFlags()
|
retryFlags, retryOpts := retryFlags()
|
||||||
filterFlags, filterOpts := filterFlags()
|
filterFlags, filterOpts := filterFlags()
|
||||||
|
pruneFlags, pruneOpts := pruneFlags()
|
||||||
|
|
||||||
opts := pruneOptions{
|
opts := pruneOptions{
|
||||||
global: global,
|
global: global,
|
||||||
image: imageOpts,
|
image: imageOpts,
|
||||||
retryOpts: retryOpts,
|
retryOpts: retryOpts,
|
||||||
filterOpts: filterOpts,
|
filterOpts: filterOpts,
|
||||||
|
pruneOpts: pruneOpts,
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@ -135,6 +152,7 @@ See skopeo-prune(1) section "REPOSITORY NAMES" for the expected format
|
|||||||
flags.AddFlagSet(&imageFlags)
|
flags.AddFlagSet(&imageFlags)
|
||||||
flags.AddFlagSet(&retryFlags)
|
flags.AddFlagSet(&retryFlags)
|
||||||
flags.AddFlagSet(&filterFlags)
|
flags.AddFlagSet(&filterFlags)
|
||||||
|
flags.AddFlagSet(&pruneFlags)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +161,7 @@ func displayPrunePrompt() bool {
|
|||||||
// Prompt the user whether they would like to proceed to prune
|
// Prompt the user whether they would like to proceed to prune
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
for {
|
for {
|
||||||
fmt.Println("\nwarning: continuing to prune will lead to irreversible data loss")
|
fmt.Println("warning: continuing to prune will lead to irreversible data loss")
|
||||||
fmt.Print("continue to prune? (y/n): ")
|
fmt.Print("continue to prune? (y/n): ")
|
||||||
input, _ := reader.ReadString('\n')
|
input, _ := reader.ReadString('\n')
|
||||||
input = strings.TrimSpace(input)
|
input = strings.TrimSpace(input)
|
||||||
@ -312,7 +330,7 @@ func getSizeMaps(ctx context.Context, sys *types.SystemContext, url string) (map
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function that computes the deduplicated size of a slice of image tags
|
// Function that computes the deduplicated size of a slice of image tags
|
||||||
func getDeduplicatedSizeParallel(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, repositoryName string, tags []string) int64 {
|
func getDeduplicatedSizeParallel(ctx context.Context, sys *types.SystemContext, repositoryName string, tags []string) int64 {
|
||||||
// Get the config & layer size maps
|
// Get the config & layer size maps
|
||||||
dedupConfigSizeMap := make(map[string]int64)
|
dedupConfigSizeMap := make(map[string]int64)
|
||||||
dedupLayerSizeMap := make(map[string]int64)
|
dedupLayerSizeMap := make(map[string]int64)
|
||||||
@ -428,7 +446,7 @@ func getDeduplicatedSizeParallel(ctx context.Context, sys *types.SystemContext,
|
|||||||
|
|
||||||
// Function that displays the prune summary of size freed in pruning
|
// Function that displays the prune summary of size freed in pruning
|
||||||
// TODO: Determine if errors occurred during size calculation
|
// TODO: Determine if errors occurred during size calculation
|
||||||
func displayPruneSummary(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, userInput string, toPrune []string, toKeep []string) error {
|
func displayPruneSummary(ctx context.Context, sys *types.SystemContext, userInput string, toPrune []string, toKeep []string) error {
|
||||||
imgRef, err := parseDockerRepositoryReference(userInput)
|
imgRef, err := parseDockerRepositoryReference(userInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error parsing image reference: %w", err)
|
return fmt.Errorf("Error parsing image reference: %w", err)
|
||||||
@ -436,8 +454,8 @@ func displayPruneSummary(ctx context.Context, sys *types.SystemContext, opts *ta
|
|||||||
repositoryName := imgRef.DockerReference().Name()
|
repositoryName := imgRef.DockerReference().Name()
|
||||||
|
|
||||||
// Compute the deduplicated size of the images that will be pruned vs kept
|
// Compute the deduplicated size of the images that will be pruned vs kept
|
||||||
pruneSize := getDeduplicatedSizeParallel(ctx, sys, opts, repositoryName, toPrune)
|
pruneSize := getDeduplicatedSizeParallel(ctx, sys, repositoryName, toPrune)
|
||||||
keepSize := getDeduplicatedSizeParallel(ctx, sys, opts, repositoryName, toKeep)
|
keepSize := getDeduplicatedSizeParallel(ctx, sys, repositoryName, toKeep)
|
||||||
|
|
||||||
// Summarize the list
|
// Summarize the list
|
||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
@ -447,12 +465,13 @@ func displayPruneSummary(ctx context.Context, sys *types.SystemContext, opts *ta
|
|||||||
keepDisp := fmt.Sprintf("Keep\t%d\t%s", len(toKeep), bytesToByteUnit(keepSize))
|
keepDisp := fmt.Sprintf("Keep\t%d\t%s", len(toKeep), bytesToByteUnit(keepSize))
|
||||||
fmt.Fprintln(w, pruneDisp)
|
fmt.Fprintln(w, pruneDisp)
|
||||||
fmt.Fprintln(w, keepDisp)
|
fmt.Fprintln(w, keepDisp)
|
||||||
|
fmt.Fprintln(w, "")
|
||||||
w.Flush()
|
w.Flush()
|
||||||
return nil // Success
|
return nil // Success
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function that prunes the tags identified for pruning and displays progress
|
// Function that prunes the tags identified for pruning and displays progress
|
||||||
func pruneDockerTagsParallel(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, repositoryName string, tags []string) error {
|
func pruneDockerTagsParallel(ctx context.Context, sys *types.SystemContext, repositoryName string, tags []string) error {
|
||||||
// Variable tracking how many tags have been pruned
|
// Variable tracking how many tags have been pruned
|
||||||
totalTags := len(tags)
|
totalTags := len(tags)
|
||||||
tagsPruned := 0
|
tagsPruned := 0
|
||||||
@ -577,9 +596,9 @@ func getFilteredDockerTags(ctx context.Context, sys *types.SystemContext, opts *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function that prunes docker tags
|
// Function that prunes docker tags
|
||||||
func pruneDockerTags(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, userInput string) error {
|
func pruneDockerTags(ctx context.Context, sys *types.SystemContext, opts *pruneOptions, userInput string) error {
|
||||||
// Get the filtered docker tags for the given repository
|
// Get the filtered docker tags for the given repository
|
||||||
filteredTags, err := getFilteredDockerTags(ctx, sys, opts, userInput)
|
filteredTags, err := getFilteredDockerTags(ctx, sys, opts.intoTagsOptions(), userInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error getting filtered docker tags: %w", err)
|
return fmt.Errorf("Error getting filtered docker tags: %w", err)
|
||||||
}
|
}
|
||||||
@ -597,19 +616,23 @@ func pruneDockerTags(ctx context.Context, sys *types.SystemContext, opts *tagsOp
|
|||||||
|
|
||||||
// Display the prune summary
|
// Display the prune summary
|
||||||
// TODO: Error check - determine if errors occurred during size calculation
|
// TODO: Error check - determine if errors occurred during size calculation
|
||||||
err = displayPruneSummary(ctx, sys, opts, userInput, toPrune, toKeep)
|
if !opts.pruneOpts.SkipSummary {
|
||||||
if err != nil {
|
err = displayPruneSummary(ctx, sys, userInput, toPrune, toKeep)
|
||||||
return fmt.Errorf("Error displaying prune summary: %w", err)
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error displaying prune summary: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display the prune prompt
|
// Display the prune prompt
|
||||||
accepted := displayPrunePrompt()
|
if !opts.pruneOpts.NonInteractive {
|
||||||
if !accepted{
|
accepted := displayPrunePrompt()
|
||||||
return nil // User decided not to prune
|
if !accepted{
|
||||||
|
return nil // User decided not to prune
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune the docker tags
|
// Prune the docker tags
|
||||||
err = pruneDockerTagsParallel(ctx, sys, opts, userInput, toPrune)
|
err = pruneDockerTagsParallel(ctx, sys, userInput, toPrune)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error pruning tags: %w", err)
|
return fmt.Errorf("Error pruning tags: %w", err)
|
||||||
}
|
}
|
||||||
@ -635,7 +658,7 @@ func (opts *pruneOptions) run(args []string, stdout io.Writer) (retErr error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := pruneTransportHandlers[transport.Name()]; ok {
|
if val, ok := pruneTransportHandlers[transport.Name()]; ok {
|
||||||
err = val(ctx, sys, opts.intoTagsOptions(), args[0])
|
err = val(ctx, sys, opts, args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -646,5 +669,3 @@ func (opts *pruneOptions) run(args []string, stdout io.Writer) (retErr error) {
|
|||||||
|
|
||||||
return nil // Success
|
return nil // Success
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Prune-many command
|
|
||||||
|
Loading…
Reference in New Issue
Block a user