ecrm

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2024 License: MIT Imports: 35 Imported by: 0

README

ecrm

A command line tool for managing Amazon ECR repositories.

ecrm can delete "unused" images safety.

"unused" means,

  • Images are not used by running tasks in ECS clusters.
  • Images are not specified in available ECS service deployments.
  • Images are not specified in existing ECS task definitions (latest N revisions).
  • Images are not specified by Lambda functions (latest N versions).

Usage

Usage: ecrm <command> [flags]

Flags:
  -h, --help                  Show context-sensitive help.
  -c, --config="ecrm.yaml"    Load configuration from FILE ($ECRM_CONFIG)
      --log-level="info"      Set log level (debug, info, notice, warn, error)
                              ($ECRM_LOG_LEVEL)
      --[no-]color            Whether or not to color the output ($ECRM_COLOR)
      --version               Show version.

Commands:
  generate [flags]
    Generate a configuration file.

  scan [flags]
    Scan ECS/Lambda resources. Output image URIs in use.

  plan [flags]
    Scan ECS/Lambda resources and find unused ECR images that can be deleted
    safely.

  delete [flags]
    Scan ECS/Lambda resources and delete unused ECR images.

  version [flags]
    Show version.

Configurations

Configuration file is YAML format. ecrm generate can generate a configuration file.

clusters:
  - name: my-cluster
  - name_pattern: "prod*"
  - name_pattern: "dev*"
task_definitions:
  - name: "*"
    keep_count: 3
lambda_functions:
  - name: "*"
    keep_count: 3
repositories:
  - name_pattern: "prod/*"
    expires: 90days
    keep_tag_patterns:
      - latest
  - name_pattern: "dev/*"
    expires: 30days
generate command

ecrm generate scans ECS, Lambda and ECR resources in an AWS account and generates a configuration file.

Usage: ecrm generate [flags]

Generate ecrm.yaml
scan command

ecrm scan scans your AWS account's ECS, Lambda, and ECR resources. It outputs image URIs in use.

ecrm scan --output path/to/file writes the image URIs in use to the file as JSON format.

The scanned files can be used in the next ecrm delete command with --scanned-files option.

The format of the file is a simple JSON array of image URIs.

[
  "012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/foo/bar:latest",
  "012345678901.dkr.ecr.ap-northeast-1.amazonaws.com/foo/bar@sha256:abcdef1234567890..."
]

You can create scanned files manually as you need.

If your workload runs on platforms that ecrm does not support (for example, AWS AppRunner, Amazon EKS, etc.), you can use ecrm with the scanned file you created.

plan command

The plan command runs ecrm scan internally and then creates a plan to delete images.

ecrm plan shows summaries of images in ECR repositories that can be deleted safely.

Usage: ecrm plan [flags]

Scan ECS/Lambda resources and find unused ECR images to delete safety.

Flags:
  -o, --output="-"            File name of the output. The default is STDOUT ($ECRM_OUTPUT).
      --format="table"        Output format of plan(table, json) ($ECRM_FORMAT)
      --[no-]scan             Scan ECS/Lambda resources that in use ($ECRM_SCAN).
  -r, --repository=STRING     Manage images in the repository only ($ECRM_REPOSITORY).
$ ecrm plan
       REPOSITORY      |    TOTAL     |    EXPIRED    |    KEEP      
-----------------------+--------------+---------------+--------------
      dev/app          | 732 (594 GB) | -707 (574 GB) | 25 (21 GB)   
      dev/nginx        | 720 (28 GB)  | -697 (27 GB)  | 23 (875 MB)  
      prod/app         | 97 (80 GB)   | -87 (72 GB)   | 10 (8.4 GB)  
      prod/nginx       | 95 (3.7 GB)  | -85 (3.3 GB)  | 10 (381 MB)  
delete command

The delete command first runs ecrm scan, then creates a plan to delete images, and finally deletes them.

By default, ecrm delete shows a prompt before deleting images. You can use --force option to delete images without confirmation.

Usage: ecrm delete [flags]

Scan ECS/Lambda resources and delete unused ECR images.

Flags:
  -o, --output="-"                         File name of the output. The default is STDOUT ($ECRM_OUTPUT).
      --format="table"                     Output format of plan(table, json) ($ECRM_FORMAT)
      --[no-]scan                          Scan ECS/Lambda resources that in use ($ECRM_SCAN).
  -r, --repository=STRING                  Manage images in the repository only ($ECRM_REPOSITORY).
      --scanned-files=SCANNED-FILES,...    Files of the scan result. ecrm does not delete images in these
                                           files ($ECRM_SCANNED_FILES).
      --force                              force delete images without confirmation ($ECRM_FORCE)

Notes

Support to image indexes and soci indexes.

ecrm supports image indexes and soci (Seekable OCI) indexes. ecrm deletes these images that are related to expired images safely.

  1. Scans ECR repositories.
    • Detect image type (Image, Image index, Soci index).
  2. Find expired images.
  3. Find expired image indexes related to expired images by the image tag (sha256-{digest of image}).
  4. Find soci indexes related to expired image indexes using ECR BatchGetImage API for expired images.

An example output is here.

  REPOSITORY |    TYPE     |   TOTAL    |   EXPIRED   |    KEEP     
-------------+-------------+------------+-------------+-------------
  xxx/app    | Image       | 30 (40 GB) | -27 (36 GB) | 3 (3.8 GB)  
  xxx/app    | Image index | 5 (163 MB) | -3 (98 MB)  | 2 (65 MB)   
  xxx/app    | Soci index  | 5 (163 MB) | -3 (98 MB)  | 2 (65 MB)  

See also

Multi accounts / regions support.

ecrm supports a single AWS account and region for each run.

If your workloads are deployed in multiple regions or accounts, you should run ecrm scan for each region or account to collect all image URIs in use.

Then, you can run ecrm delete with the --scanned-files option to delete unused images in all regions or accounts.

For example, your ECR in the account-a, and your ECS clusters are deployed in account-a and account-b.

At first, you run ecrm scan for each account.

$ AWS_PROFILE=account-a ecrm scan --output scan-account-a.json
$ AWS_PROFILE=account-b ecrm scan --output scan-account-b.json

Now, you can run ecrm delete with the --scanned-files option to safely delete unused images in all accounts.

$ AWS_PROFILE=account-a ecrm delete --scanned-files scan-account-a.json,scan-account-b.json

Author

Copyright (c) 2021 FUJIWARA Shunichiro

LICENSE

MIT

Documentation

Index

Constants

View Source
const (
	SummaryTypeImage      = "Image"
	SummaryTypeImageIndex = "Image index"
	SummaryTypeSociIndex  = "Soci index"
)
View Source
const (
	MediaTypeSociIndex = "application/vnd.amazon.soci.index.v1+json"
)

Variables

View Source
var (
	DefaultKeepCount       = 5
	DefaultExpiresStr      = "30d"
	DefaultKeepTagPatterns = []string{"latest"}
)
View Source
var LogLevelFilter = &logutils.LevelFilter{
	Levels: []logutils.LogLevel{"debug", "info", "notice", "warn", "error"},
	ModifierFuncs: []logutils.ModifierFunc{
		nil,
		logutils.Color(color.FgWhite),
		logutils.Color(color.FgHiBlue),
		logutils.Color(color.FgYellow),
		logutils.Color(color.FgRed, color.Bold),
	},
	Writer: os.Stderr,
}

Functions

func SetLogLevel added in v0.2.0

func SetLogLevel(level string)

func ShowScanResult added in v0.5.0

func ShowScanResult(s *Scanner, opt *Option) error

func ShowSummary added in v0.5.0

func ShowSummary(s SummaryTable, opt *Option) error

Types

type App

type App struct {
	Version string
	// contains filtered or unexported fields
}

func New

func New(ctx context.Context) (*App, error)

func (*App) DeleteImages

func (app *App) DeleteImages(ctx context.Context, repo RepositoryName, ids []ecrTypes.ImageIdentifier, force bool) error

DeleteImages deletes images from the repository

func (*App) GenerateConfig added in v0.2.0

func (app *App) GenerateConfig(ctx context.Context, path string) error

func (*App) NewCLI added in v0.2.0

func (app *App) NewCLI() *CLI

func (*App) Run

func (app *App) Run(ctx context.Context, path string, opt *Option) error

type CLI added in v0.5.0

type CLI struct {
	Config      string `help:"Load configuration from FILE" short:"c" default:"ecrm.yaml" env:"ECRM_CONFIG"`
	LogLevel    string `help:"Set log level (debug, info, notice, warn, error)" default:"info" env:"ECRM_LOG_LEVEL"`
	Color       bool   `help:"Whether or not to color the output" default:"true" env:"ECRM_COLOR" negatable:""`
	ShowVersion bool   `help:"Show version." name:"version"`

	Generate *GenerateCLI `cmd:"" help:"Generate a configuration file."`
	Scan     *ScanCLI     `cmd:"" help:"Scan ECS/Lambda resources. Output image URIs in use."`
	Plan     *PlanCLI     `cmd:"" help:"Scan ECS/Lambda resources and find unused ECR images that can be deleted safely."`
	Delete   *DeleteCLI   `cmd:"" help:"Scan ECS/Lambda resources and delete unused ECR images."`
	Version  struct{}     `cmd:"" default:"1" help:"Show version."`
	// contains filtered or unexported fields
}

func (*CLI) NewLambdaHandler added in v0.5.0

func (c *CLI) NewLambdaHandler() func(context.Context) error

func (*CLI) Run added in v0.5.0

func (c *CLI) Run(ctx context.Context) error

type ClusterConfig

type ClusterConfig struct {
	Name        string `yaml:"name,omitempty"`
	NamePattern string `yaml:"name_pattern,omitempty"`
}

func (*ClusterConfig) Match

func (c *ClusterConfig) Match(name string) bool

func (*ClusterConfig) Validate

func (c *ClusterConfig) Validate() error

type Config

type Config struct {
	Clusters        []*ClusterConfig    `yaml:"clusters"`
	TaskDefinitions []*TaskdefConfig    `yaml:"task_definitions"`
	LambdaFunctions []*LambdaConfig     `yaml:"lambda_functions"`
	Repositories    []*RepositoryConfig `yaml:"repositories"`
}

func LoadConfig

func LoadConfig(path string) (*Config, error)

func (*Config) Validate

func (c *Config) Validate() error

type DeletableImageIDs added in v0.5.0

type DeletableImageIDs map[RepositoryName][]ecrTypes.ImageIdentifier

func (DeletableImageIDs) RepositoryNames added in v0.5.0

func (d DeletableImageIDs) RepositoryNames() []RepositoryName

type DeleteCLI added in v0.5.0

type DeleteCLI struct {
	PlanOrDelete
	ScannedFiles []string `help:"Files of the scan result. ecrm does not delete images in these files." env:"ECRM_SCANNED_FILES"`
	Force        bool     `help:"force delete images without confirmation" env:"ECRM_FORCE"`
}

func (*DeleteCLI) Option added in v0.5.0

func (c *DeleteCLI) Option() *Option

type GenerateCLI added in v0.5.0

type GenerateCLI struct {
}

func (*GenerateCLI) Option added in v0.5.0

func (c *GenerateCLI) Option() *Option

type Generator added in v0.5.0

type Generator struct {
	// contains filtered or unexported fields
}

func NewGenerator added in v0.5.0

func NewGenerator(cfg aws.Config) *Generator

func (*Generator) GenerateConfig added in v0.5.0

func (g *Generator) GenerateConfig(ctx context.Context, configFile string) error

type ImageURI added in v0.5.0

type ImageURI string

ImageURI represents an image URI.

func (ImageURI) Base added in v0.5.0

func (u ImageURI) Base() string

func (ImageURI) IsDigestURI added in v0.5.0

func (u ImageURI) IsDigestURI() bool

func (ImageURI) IsECRImage added in v0.5.0

func (u ImageURI) IsECRImage() bool

func (ImageURI) Short added in v0.5.0

func (u ImageURI) Short() string

func (ImageURI) String added in v0.5.0

func (u ImageURI) String() string

func (ImageURI) Tag added in v0.5.0

func (u ImageURI) Tag() string

type Images added in v0.5.0

type Images map[ImageURI]set

func (Images) Add added in v0.5.0

func (i Images) Add(u ImageURI, usedBy string) bool

func (Images) Contains added in v0.5.0

func (i Images) Contains(u ImageURI) bool

func (Images) LoadFile added in v0.5.0

func (i Images) LoadFile(filename string) error

func (Images) Merge added in v0.5.0

func (i Images) Merge(j Images)

func (Images) Print added in v0.5.0

func (i Images) Print(w io.Writer) error

type LambdaConfig added in v0.1.0

type LambdaConfig struct {
	Name        string `yaml:"name,omitempty"`
	NamePattern string `yaml:"name_pattern,omitempty"`
	KeepCount   int64  `yaml:"keep_count,omitempty"`
	KeepAliase  *bool  `yaml:"keep_aliase,omitempty"` // for backward compatibility
}

func (*LambdaConfig) Match added in v0.1.0

func (c *LambdaConfig) Match(name string) bool

func (*LambdaConfig) Validate added in v0.1.0

func (c *LambdaConfig) Validate() error

type NopCloserWriter added in v0.5.0

type NopCloserWriter struct {
	io.Writer
}

NopCloserWriter is a writer that does nothing on Close

func (NopCloserWriter) Close added in v0.5.0

func (NopCloserWriter) Close() error

type Option

type Option struct {
	ScanOnly     bool
	Scan         bool
	Delete       bool
	Force        bool
	Repository   RepositoryName
	OutputFile   string
	Format       outputFormat
	ScannedFiles []string
}

func (*Option) OutputWriter added in v0.5.0

func (opt *Option) OutputWriter() (io.WriteCloser, error)

func (*Option) Validate added in v0.5.0

func (opt *Option) Validate() error

type OutputCLI added in v0.5.0

type OutputCLI struct {
	Output string `help:"File name of the output. The default is STDOUT." short:"o" default:"-" env:"ECRM_OUTPUT"`
}

type PlanCLI added in v0.5.0

type PlanCLI struct {
	PlanOrDelete
}

func (*PlanCLI) Option added in v0.5.0

func (c *PlanCLI) Option() *Option

type PlanOrDelete added in v0.5.0

type PlanOrDelete struct {
	OutputCLI
	Format     string `help:"Output format of plan(table, json)" default:"table" enum:"table,json" env:"ECRM_FORMAT"`
	Scan       bool   `help:"Scan ECS/Lambda resources that in use." default:"true" negatable:"" env:"ECRM_SCAN"`
	Repository string `help:"Manage images in the repository only." short:"r" env:"ECRM_REPOSITORY"`
}

type Planner added in v0.5.0

type Planner struct {
	// contains filtered or unexported fields
}

func NewPlanner added in v0.5.0

func NewPlanner(cfg aws.Config) *Planner

func (*Planner) Plan added in v0.5.0

Plan scans repositories and find expired images, and returns a summary table and a map of deletable image identifiers.

keepImages is a set of images in use by ECS tasks / task definitions / lambda functions so that they are not deleted

type RepoSummary added in v0.4.0

type RepoSummary []*Summary

func NewRepoSummary added in v0.4.0

func NewRepoSummary(repo RepositoryName) RepoSummary

func (RepoSummary) Add added in v0.4.0

func (s RepoSummary) Add(img ecrTypes.ImageDetail)

func (RepoSummary) Expire added in v0.4.0

func (s RepoSummary) Expire(img ecrTypes.ImageDetail)

type RepositoryConfig

type RepositoryConfig struct {
	Name            RepositoryName `yaml:"name,omitempty"`
	NamePattern     string         `yaml:"name_pattern,omitempty"`
	Expires         string         `yaml:"expires,omitempty"`
	KeepCount       int64          `yaml:"keep_count,omitempty"`
	KeepTagPatterns []string       `yaml:"keep_tag_patterns,omitempty"`
	// contains filtered or unexported fields
}

func (*RepositoryConfig) IsExpired

func (r *RepositoryConfig) IsExpired(at time.Time) bool

func (*RepositoryConfig) MatchName

func (r *RepositoryConfig) MatchName(name RepositoryName) bool

func (*RepositoryConfig) MatchTag

func (r *RepositoryConfig) MatchTag(tag string) bool

func (*RepositoryConfig) Validate

func (r *RepositoryConfig) Validate() error

type RepositoryName added in v0.5.0

type RepositoryName string

type ScanCLI added in v0.5.0

type ScanCLI struct {
	OutputCLI
}

func (*ScanCLI) Option added in v0.5.0

func (c *ScanCLI) Option() *Option

type Scanner added in v0.5.0

type Scanner struct {
	Images Images
	// contains filtered or unexported fields
}

func NewScanner added in v0.5.0

func NewScanner(cfg aws.Config) *Scanner

func (*Scanner) LoadFiles added in v0.5.0

func (s *Scanner) LoadFiles(files []string) error

func (*Scanner) Save added in v0.5.0

func (s *Scanner) Save(w io.Writer) error

func (*Scanner) Scan added in v0.5.0

func (s *Scanner) Scan(ctx context.Context, c *Config) error

type Summary added in v0.4.0

type Summary struct {
	Repo             RepositoryName `json:"repository"`
	Type             string         `json:"type"`
	ExpiredImages    int64          `json:"expired_images"`
	TotalImages      int64          `json:"total_images"`
	ExpiredImageSize int64          `json:"expired_image_size"`
	TotalImageSize   int64          `json:"total_image_size"`
}

type SummaryTable added in v0.4.0

type SummaryTable []*Summary

func (*SummaryTable) Print added in v0.5.0

func (s *SummaryTable) Print(w io.Writer, format outputFormat) error

func (SummaryTable) Sort added in v0.5.0

func (s SummaryTable) Sort()

type TaskdefConfig added in v0.1.0

type TaskdefConfig struct {
	Name        string `yaml:"name,omitempty"`
	NamePattern string `yaml:"name_pattern,omitempty"`
	KeepCount   int64  `yaml:"keep_count,omitempty"`
}

func (*TaskdefConfig) Match added in v0.1.0

func (c *TaskdefConfig) Match(name string) bool

func (*TaskdefConfig) Validate added in v0.1.0

func (c *TaskdefConfig) Validate() error

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL