Skip to content

Commit

Permalink
chore: super simple verbose logs when pruning (#71)
Browse files Browse the repository at this point in the history
* chore: super simple verbose logs when pruning

* fix: indent

* fix: indent after editing using the UI

* chore: remove verbose flag

We only accept env vars for configuration

* docs: document the new environment variable

* fix: remove outdated test

* fix: remove outdated test
  • Loading branch information
mdelapenya authored May 17, 2023
1 parent 82e1555 commit bfea399
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 3 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ This project helps you to remove containers/networks/volumes/images by given fil
- `RYUK_CONNECTION_TIMEOUT` - Environment variable that defines the timeout for Ryuk to receive the first connection (default: 60s). Value layout is described in [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation.
- `RYUK_PORT` - Environment variable that defines the port where Ryuk will be bound to (default: 8080).
- `RYUK_RECONNECTION_TIMEOUT` - Environment variable that defines the timeout for Ryuk to reconnect to Docker (default: 10s). Value layout is described in [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation.
- `RYUK_VERBOSE` - Environment variable that defines if Ryuk should print debug logs (default: false).
45 changes: 42 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,21 @@ const (
portEnv string = "RYUK_PORT"
reconnectionTimeoutEnv string = "RYUK_RECONNECTION_TIMEOUT"
ryukLabel string = "org.testcontainers.ryuk"
verboseEnv string = "RYUK_VERBOSE"
)

var (
port int
connectionTimeout time.Duration
reconnectionTimeout time.Duration
verbose bool
)

type config struct {
Port int
ConnectionTimeout time.Duration
ReconnectionTimeout time.Duration
Verbose bool
}

// newConfig parses command line flags and returns a parsed config. config.timeout
Expand All @@ -49,6 +52,7 @@ func newConfig(args []string) (*config, error) {
Port: 8080,
ConnectionTimeout: 60 * time.Second,
ReconnectionTimeout: 10 * time.Second,
Verbose: false,
}

fs := flag.NewFlagSet("ryuk", flag.ExitOnError)
Expand Down Expand Up @@ -88,6 +92,15 @@ func newConfig(args []string) (*config, error) {
cfg.ReconnectionTimeout = parsedTimeout
}

if verbose, ok := os.LookupEnv(verboseEnv); ok {
v, err := strconv.ParseBool(verbose)
if err != nil {
return nil, fmt.Errorf("failed to parse \"%s\": %s", verboseEnv, err)
}

cfg.Verbose = v
}

return &cfg, nil
}

Expand All @@ -100,6 +113,7 @@ func main() {
port = cfg.Port
connectionTimeout = cfg.ConnectionTimeout
reconnectionTimeout = cfg.ReconnectionTimeout
verbose = cfg.Verbose

cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
Expand Down Expand Up @@ -254,29 +268,45 @@ func prune(cli *client.Client, deathNote *sync.Map) (deletedContainers int, dele

deathNote.Range(func(note, _ interface{}) bool {
param := fmt.Sprint(note)
log.Printf("Deleting %s\n", param)
if verbose {
log.Printf("Deleting %s\n", param)
}

args, err := filters.FromJSON(param)
if err != nil {
log.Println(err)
return true
}

if containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true, Filters: args}); err != nil {
containerListOpts := types.ContainerListOptions{All: true, Filters: args}
if verbose {
log.Printf("Listing containers with filter: %#v\n", containerListOpts)
}

if containers, err := cli.ContainerList(context.Background(), containerListOpts); err != nil {
log.Println(err)
} else {
containerRemoveOpts := types.ContainerRemoveOptions{RemoveVolumes: true, Force: true}

for _, container := range containers {
value, isReaper := container.Labels[ryukLabel]
if isReaper && value == "true" {
continue
}

_ = cli.ContainerRemove(context.Background(), container.ID, types.ContainerRemoveOptions{RemoveVolumes: true, Force: true})
if verbose {
log.Printf("Deleting containers with filter: %#v\n", containerRemoveOpts)
}
_ = cli.ContainerRemove(context.Background(), container.ID, containerRemoveOpts)
deletedContainersMap[container.ID] = true
}
}

_ = try.Do(func(attempt int) (bool, error) {
if verbose {
log.Printf("Deleting networks with filter: %#v. (Attempt %d/%d)\n", args, attempt, 10)
}

networksPruneReport, err := cli.NetworksPrune(context.Background(), args)
for _, networkID := range networksPruneReport.NetworksDeleted {
deletedNetworksMap[networkID] = true
Expand All @@ -292,6 +322,10 @@ func prune(cli *client.Client, deathNote *sync.Map) (deletedContainers int, dele
_ = try.Do(func(attempt int) (bool, error) {
argsClone := args.Clone()

if verbose {
log.Printf("Deleting volumes with filter: %#v. (Attempt %d/%d)\n", argsClone, attempt, 10)
}

// API version >= v1.42 prunes only anonymous volumes: https://github.com/moby/moby/releases/tag/v23.0.0.
if serverVersion, err := cli.ServerVersion(context.Background()); err != nil && serverVersion.APIVersion >= "1.42" {
argsClone.Add("all", "true")
Expand All @@ -312,6 +346,11 @@ func prune(cli *client.Client, deathNote *sync.Map) (deletedContainers int, dele
_ = try.Do(func(attempt int) (bool, error) {
argsClone := args.Clone()
argsClone.Add("dangling", "false")

if verbose {
log.Printf("Deleting images with filter: %#v. (Attempt %d/%d)\n", argsClone, attempt, 10)
}

imagesPruneReport, err := cli.ImagesPrune(context.Background(), argsClone)
for _, image := range imagesPruneReport.ImagesDeleted {
if image.Untagged != "" {
Expand Down
22 changes: 22 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,28 @@ func Test_newConfig(t *testing.T) {
assert.Equal(t, 100*time.Second, config.ReconnectionTimeout)
})

t.Run("should return an error when failing to parse RYUK_VERBOSE environment variable", func(t *testing.T) {
t.Setenv(verboseEnv, "bad_value")

config, err := newConfig([]string{})
require.NotNil(t, err)
require.Nil(t, config)
})

t.Run("should set verbose with RYUK_VERBOSE environment variable", func(t *testing.T) {
t.Setenv(verboseEnv, "true")

config, err := newConfig([]string{})
require.Nil(t, err)
assert.True(t, config.Verbose)

t.Setenv(verboseEnv, "false")

config, err = newConfig([]string{})
require.Nil(t, err)
assert.False(t, config.Verbose)
})

t.Run("should set port with port flag", func(t *testing.T) {
config, err := newConfig([]string{"-p", "3000"})
require.Nil(t, err)
Expand Down

0 comments on commit bfea399

Please sign in to comment.