Skip to content

Commit

Permalink
Add minimum image age option
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomás Tormo committed May 7, 2020
1 parent 0809758 commit 5a9d8d0
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 14 deletions.
9 changes: 5 additions & 4 deletions gc/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ type Collector interface {
type collector struct {
client docker.APIClient

whitelist []string // reserved containers
reserved []string // reserved images
threshold int64 // target threshold in bytes
filter FilterFunc
whitelist []string // reserved containers
reserved []string // reserved images
threshold int64 // target threshold in bytes
minImageAge time.Duration
filter FilterFunc
}

// New returns a garbage collector.
Expand Down
2 changes: 1 addition & 1 deletion gc/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (c *collector) collectImages(ctx context.Context) error {
if isImageUsed(image, df.Containers) {
continue
}
if time.Unix(image.Created, 0).Add(time.Hour).After(now) {
if time.Unix(image.Created, 0).Add(c.minImageAge).After(now) {
continue
}

Expand Down
43 changes: 42 additions & 1 deletion gc/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,47 @@ func TestCollectImages_BelowThreshold(t *testing.T) {
}
}

// this test verifies that we do not purge images that are not old enough
func TestCollectImages_SkipNotOldEnough(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()

mockdf := types.DiskUsage{
LayersSize: 1,
Images: []*types.ImageSummary{
// this image was created 20 mins ago
{
ID: "a180b24e38ed",
Created: time.Now().Add(-20*time.Minute).Unix(),
},
// this image was created 40 mins ago
{
ID: "481995377a04",
Created: time.Now().Add(-40*time.Minute).Unix(),
},
},
}

mockImages := []types.ImageInspect{
{ID: "a180b24e38ed"},
{ID: "481995377a04"},
}

client := mocks.NewMockAPIClient(controller)
client.EXPECT().DiskUsage(gomock.Any()).Return(mockdf, nil)

// We DO NOT expect image 0 to be removed since is not old enough
client.EXPECT().ImageInspectWithRaw(gomock.Any(), mockImages[1].ID).Return(mockImages[1], nil, nil)
client.EXPECT().ImageRemove(gomock.Any(), mockImages[1].ID, imageRemoveOpts).Return(nil, nil)

// Minimum image age set to 30 mins
c := New(client, WithMinImageAge(time.Hour/2)).(*collector)
err := c.collectImages(context.Background())
if err != nil {
t.Error(err)
}
}

// this test verifies that we do not purge images that are
// in-use by the system or are newly created.
func TestCollectImages_Skip(t *testing.T) {
Expand Down Expand Up @@ -156,7 +197,7 @@ func TestCollectImages_Skip(t *testing.T) {
client := mocks.NewMockAPIClient(controller)
client.EXPECT().DiskUsage(gomock.Any()).Return(mockdf, nil)

c := New(client).(*collector)
c := New(client, WithMinImageAge(time.Hour)).(*collector)
err := c.collectImages(context.Background())
if err != nil {
t.Error(err)
Expand Down
11 changes: 11 additions & 0 deletions gc/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package gc

import "time"

// Option configures a garbage collector option.
type Option func(*collector)

Expand Down Expand Up @@ -34,6 +36,15 @@ func WithThreshold(threshold int64) Option {
}
}

// WithMinImageAge returns an option to set the minimum
// age a image should be to become a candidate for removal.
// Images younger than this value won't be removed
func WithMinImageAge(minImageAge time.Duration) Option {
return func(c *collector) {
c.minImageAge = minImageAge
}
}

// ReservedImages provides a list of reserved images names
// that should not be removed.
var ReservedImages = []string{
Expand Down
7 changes: 7 additions & 0 deletions gc/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ package gc
import (
"reflect"
"testing"
"time"
)

func TestOptions(t *testing.T) {
expectedMinImageAge,_ := time.ParseDuration("5h")
c := New(nil,
WithImageWhitelist([]string{"foo"}),
WithThreshold(42),
WithWhitelist([]string{"bar"}),
WithMinImageAge(expectedMinImageAge),
).(*collector)

if got, want := c.threshold, int64(42); got != want {
Expand All @@ -25,4 +28,8 @@ func TestOptions(t *testing.T) {
if got, want := c.reserved, []string{"foo"}; !reflect.DeepEqual(want, got) {
t.Errorf("Want image whitelist %v, got %v", want, got)
}

if got, want := c.minImageAge, expectedMinImageAge; !reflect.DeepEqual(want, got) {
t.Errorf("Want minImageAge %v, got %v", want, got)
}
}
19 changes: 11 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ import (
)

type config struct {
Once bool `envconfig:"GC_ONCE"`
Debug bool `envconfig:"GC_DEBUG"`
Color bool `envconfig:"GC_DEBUG_COLOR"`
Pretty bool `envconfig:"GC_DEBUG_PRETTY"`
Images []string `envconfig:"GC_IGNORE_IMAGES"`
Containers []string `envconfig:"GC_IGNORE_CONTAINERS"`
Interval time.Duration `envconfig:"GC_INTERVAL" default:"5m"`
Cache string `envconfig:"GC_CACHE" default:"5gb"`
Once bool `envconfig:"GC_ONCE"`
Debug bool `envconfig:"GC_DEBUG"`
Color bool `envconfig:"GC_DEBUG_COLOR"`
Pretty bool `envconfig:"GC_DEBUG_PRETTY"`
Images []string `envconfig:"GC_IGNORE_IMAGES"`
Containers []string `envconfig:"GC_IGNORE_CONTAINERS"`
Interval time.Duration `envconfig:"GC_INTERVAL" default:"5m"`
MinImageAge time.Duration `envconfig:"GC_MIN_IMAGE_AGE" default:"1h"`
Cache string `envconfig:"GC_CACHE" default:"5gb"`
}

func main() {
Expand Down Expand Up @@ -61,6 +62,7 @@ func main() {
gc.WithImageWhitelist(cfg.Images),
gc.WithThreshold(size),
gc.WithWhitelist(gc.ReservedNames),
gc.WithMinImageAge(cfg.MinImageAge),
gc.WithWhitelist(cfg.Containers),
)
if cfg.Once {
Expand All @@ -71,6 +73,7 @@ func main() {
Strs("ignore-images", cfg.Images).
Str("cache", cfg.Cache).
Str("interval", units.HumanDuration(cfg.Interval)).
Str("minimal image age", units.HumanDuration(cfg.MinImageAge)).
Msg("starting the garbage collector")

gc.Schedule(ctx, collector, cfg.Interval)
Expand Down

0 comments on commit 5a9d8d0

Please sign in to comment.