Skip to content

Commit

Permalink
Fix flag parsing for history mode
Browse files Browse the repository at this point in the history
The flags were parsed for orphan, but not history
  • Loading branch information
ccremer committed Mar 18, 2020
1 parent 9d8d9de commit 868f3d4
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 45 deletions.
33 changes: 23 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,29 @@

## General

The image cleanup client is used to clean up container images in an image registry (currently only OpenShift image registries are supported).
The image cleanup client is used to clean up container images in an image registry (currently only OpenShift image registries
are supported).

The tool is intended to be used closely within an application lifecycle management (Review apps). It analyzes a git repository and compares the history with the target image registry, removing old and unused image tags according to customizable rules.
The tool is intended to be used closely within an application lifecycle management (Review apps). It analyzes a git repository
and compares the history with the target image registry, removing old and unused image tags according to customizable rules.

The tool can also be used more aggressively by deleting unkown image tags too ("orphans"). See more usage examples below.

This tool is opinionated in the naming of image tags, as the image tags have to follow certain naming rules for this to work. E.g. stick to tagging with Git SHA value: `namespace/app:a3d0df2c5060b87650df6a94a0a9600510303003` or Git Tag: `namespace/app:v1.2.3`.
This tool is opinionated in the naming of image tags, as the image tags have to follow certain naming rules for this to work.
E.g. stick to tagging with Git SHA value: `namespace/app:a3d0df2c5060b87650df6a94a0a9600510303003` or Git Tag: `namespace/app:v1.2.3`.

This helps to save space (e.g. your private registry with billed S3 storage) or quota threshold because obsolete images are being removed from the registry.
This helps to save space (e.g. your private registry with billed S3 storage) or quota threshold because obsolete images are
being removed from the registry.

The cleanup **runs in dry-mode by default**, only when the `--force` flag is specified, it will delete the actual image tags. This prevents accidental deletions when testing.
The cleanup **runs in dry-mode by default**, only when the `--force` flag is specified, it will delete the actual image tags.
This prevents accidental deletions when testing.

The primary execution environments are CI/CD pipelines after image pushes, rollouts or any other sort of automated cleanup jobs.

## Usage

The following examples assume the namespace `namespace`, and and image stream named `app`. For the image cleanup to work, you need to be logged in to the target cluster, as the tool will indirectly read your kubeconfig file.
The following examples assume the namespace `namespace`, and and image stream named `app`. For the image cleanup to work,
you need to be logged in to the target cluster, as the tool will indirectly read your kubeconfig file.

For the following examples, we will assume the following Git history, with `c6` being the latest commit in branch `c`:

Expand Down Expand Up @@ -62,11 +70,14 @@ This would delete `a1` and `a2`, but *not* `a5`, as this image is being actively
```console
image-cleanup orphans namespace/app --older-than 7d
```
This will delete `a1`, `a2`, `b3` and `b4`, if we assume that `a5` is being actively used, and `c6` is younger than 7d (image tag push date, not commit date).
This will delete `a1`, `a2`, `b3` and `b4`, if we assume that `a5` is being actively used, and `c6` is younger than 7d
(image tag push date, not commit date).

That means it will also look in other branches too. It also deletes amended/force-pushed commits, which do not show up in the history anymore, but would still be available in the registry.
That means it will also look in other branches too. It also deletes amended/force-pushed commits, which do not show up
in the history anymore, but would still be available in the registry.

This is very useful in cases where the images from feature branches are being pushed to a `dev` namespace, but need to be cleaned up after some time. In the `production` namespace, we can apply different cleanup rules.
This is very useful in cases where the images from feature branches are being pushed to a `dev` namespace, but need to
be cleaned up after some time. In the `production` namespace, we can apply different cleanup rules.

### Example: Delete versioned image tags

Expand All @@ -80,7 +91,9 @@ v1.10.1
```console
image-cleanup history namespace/app --keep 2 --tags
```
This would delete `v1.9.3` as expected, since the `--sort` flag is `version` by default (including support for v prefix). If `alphabetic`, the tool might find that `v1.9.3` is newer than `v1.10.0`. For date-based tags, `alphabetic` sorting flag might be better suitable, e.g. `2020-03-17`.
This would delete `v1.9.3` as expected, since the `--sort` flag is `version` by default (including support for v prefix).
If `alphabetic`, the order for semver tags is reversed (probably undesired). For date-based tags, `alphabetic` sorting
flag might be better suitable, e.g. `2020-03-17`.

## Migrate from legacy cleanup plugin

Expand Down
25 changes: 7 additions & 18 deletions cmd/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ var (
if err := validateHistoryCommandInput(args); err != nil {
return err
}
ExecuteHistoryCleanupCommand(args)
return nil
return ExecuteHistoryCleanupCommand(args)
},
}
)
Expand All @@ -32,9 +31,8 @@ func init() {
defaults := cfg.NewDefaultConfig()

addCommonFlagsForGit(historyCmd, defaults)
historyCmd.PersistentFlags().IntP("keep", "k", defaults.History.Keep, "Keep most current <k> images.")

bindFlags(historyCmd.PersistentFlags())
historyCmd.PersistentFlags().IntP("keep", "k", defaults.History.Keep,
"Keep most current <k> images. Does not include currently used image tags (if detected).")

}

Expand All @@ -48,19 +46,14 @@ func validateHistoryCommandInput(args []string) error {
return nil
}

func ExecuteHistoryCleanupCommand(args []string) {
func ExecuteHistoryCleanupCommand(args []string) error {

c := config.History
namespace, image, _ := splitNamespaceAndImagestream(args[0])

imageStreamObjectTags, err := openshift.GetImageStreamTags(namespace, image)
if err != nil {
log.WithError(err).
WithFields(log.Fields{
"ImageRepository": namespace,
"ImageName": image,
}).
Fatal("Could not retrieve image stream.")
return fmt.Errorf("could not retrive image stream '%s/%s': %w", namespace, image, err)
}

var imageStreamTags []string
Expand All @@ -78,12 +71,7 @@ func ExecuteHistoryCleanupCommand(args []string) {

activeImageStreamTags, err := openshift.GetActiveImageStreamTags(namespace, image, imageStreamTags)
if err != nil {
log.WithError(err).
WithFields(log.Fields{
"ImageRepository": namespace,
"ImageName": image,
"imageStreamTags": imageStreamTags}).
Fatal("Could not retrieve active image stream tags.")
return fmt.Errorf("could not retrieve active image stream tags for '%s/%s': %w", namespace, image, err)
}

inactiveTags := cleanup.GetInactiveImageTags(&activeImageStreamTags, &matchingTags)
Expand All @@ -96,4 +84,5 @@ func ExecuteHistoryCleanupCommand(args []string) {
} else {
log.Info("--force was not specified. Nothing has been deleted.")
}
return nil
}
2 changes: 0 additions & 2 deletions cmd/orphans.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ func init() {
"Delete images that are older than the duration. Ex.: [1y2mo3w4d5h6m7s]")
orphanCmd.PersistentFlags().StringP(orphanDeletionPatternCliFlag, "r", defaults.Orphan.OrphanDeletionRegex,
"Delete images that match the regex, defaults to matching Git SHA commits")

bindFlags(orphanCmd.PersistentFlags())
}

func validateOrphanCommandInput(args []string) error {
Expand Down
32 changes: 17 additions & 15 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ import (
var (
// rootCmd represents the base command when called without any subcommands
rootCmd = &cobra.Command{
Use: "image-cleanup",
Short: "Cleans up images tags on remote registries",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.WithField("config", config).Debug("Parsed configuration.")
},
Use: "image-cleanup",
Short: "Cleans up images tags on remote registries",
PersistentPreRun: parseConfig,
}
config = cfg.NewDefaultConfig()
)
Expand All @@ -40,10 +38,13 @@ func init() {

}

// initRootConfig reads in cfg file and ENV variables if set.
func initRootConfig() {

bindFlags(rootCmd.Flags())
}

// parseConfig reads the flags and ENV vars
func parseConfig(cmd *cobra.Command, args []string) {
bindFlags(cmd.PersistentFlags())
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
viper.AutomaticEnv()

Expand All @@ -61,16 +62,17 @@ func initRootConfig() {
log.SetOutput(os.Stderr)
}
if config.Log.Verbose {
log.SetLevel(log.DebugLevel)
config.Log.LogLevel = "debug"
}
level, err := log.ParseLevel(config.Log.LogLevel)
if err != nil {
log.WithField("error", err).Warn("Using info level.")
log.SetLevel(log.InfoLevel)
} else {
level, err := log.ParseLevel(config.Log.LogLevel)
if err != nil {
log.WithField("error", err).Warn("Using info level.")
log.SetLevel(log.InfoLevel)
} else {
log.SetLevel(level)
}
log.SetLevel(level)
}

log.WithField("config", config).Debug("Parsed configuration.")
}

func bindFlags(flagSet *pflag.FlagSet) {
Expand Down

0 comments on commit 868f3d4

Please sign in to comment.