Skip to content

Commit

Permalink
Merge pull request sous-chefs#1044 from smcavallo/image_purge
Browse files Browse the repository at this point in the history
Image prune
  • Loading branch information
tas50 authored Dec 9, 2018
2 parents 5890404 + ba9cc67 commit 3b9d146
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 0 deletions.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,44 @@ docker_image 'alpine' do
end
```

## docker_image_prune

The `docker_image_prune` is responsible for pruning Docker images from the system. It speaks directly to the [Docker Engine API](https://docs.docker.com/engine/api/v1.35/#operation/ImagePrune).
Note - this is best implemented by subscribing to `docker_image` changes. There is no need to to clean up old images upon each converge. It is best done at the end of a chef run (delayed) only if a new image was pulled.

### Actions

- `:prune` - Delete unused images

### Properties

The `docker_image_prune` resource properties map to filters

- `dangling` - When set to true (or 1), prune only unused and untagged images. When set to false (or 0), all unused images are pruned
- `prune_until` - Prune images created before this timestamp. The <timestamp> can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. 10m, 1h30m) computed relative to the daemon machine’s time.
- `with_label/without_label` - (label=<key>, label=<key>=<value>, label!=<key>, or label!=<key>=<value>) Prune images with (or without, in case label!=... is used) the specified labels.
- `host` - A string containing the host the API should communicate with. Defaults to `ENV['DOCKER_HOST']` if set.

### Examples

- default action, default properties

```ruby
docker_image_prune 'prune-old-images'
```

- All filters

```ruby
docker_image_prune "prune-old-images" do
dangling true
prune_until '1h30m'
with_label 'com.example.vendor=ACME'
without_label 'no_prune'
action :prune
end
```

## docker_tag

Docker tags work very much like hard links in a Unix filesystem. They are just references to an existing image. Therefore, the docker_tag resource has taken inspiration from the Chef `link` resource.
Expand Down
39 changes: 39 additions & 0 deletions libraries/docker_image_prune.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module DockerCookbook
class DockerImagePrune < DockerBase
resource_name :docker_image_prune
# Requires docker API v1.25
# Modify the default of read_timeout from 60 to 120
property :read_timeout, default: 120, desired_state: false
property :host, [String, nil], default: lazy { ENV['DOCKER_HOST'] }, desired_state: false

# https://docs.docker.com/engine/api/v1.35/#operation/ImagePrune
property :dangling, [TrueClass, FalseClass], default: true
property :prune_until, String
# https://docs.docker.com/engine/reference/builder/#label
property :with_label, String
property :without_label, String

#########
# Actions
#########

default_action :prune

action :prune do
# Have to call this method ourselves due to
# https://github.com/swipely/docker-api/pull/507
json = generate_json(new_resource)
# Post
res = connection.post('/images/prune', json)
Chef::Log.info res
end

def generate_json(new_resource)
opts = { filters: ["dangling=#{new_resource.dangling}"] }
opts[:filters].push("until=#{new_resource.prune_until}") if new_resource.property_is_set?(:prune_until)
opts[:filters].push("label=#{new_resource.with_label}") if new_resource.property_is_set?(:with_label)
opts[:filters].push("label!=#{new_resource.without_label}") if new_resource.property_is_set?(:without_label)
opts.to_json
end
end
end
24 changes: 24 additions & 0 deletions spec/docker_test/image_prune_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'spec_helper'

describe 'docker_test::image_prune' do
context 'it steps over the provider' do
cached(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '18.04').converge(described_recipe) }

context 'testing default action, default properties' do
it 'prunes docker_image[hello-world]' do
expect(chef_run).to prune_docker_image_prune('hello-world').with(
dangling: true
)
end

it 'prunes docker_image[hello-world]' do
expect(chef_run).to prune_docker_image_prune('prune-old-images').with(
dangling: true,
prune_until: '1h30m',
with_label: 'com.example.vendor=ACME',
without_label: 'no_prune'
)
end
end
end
end
27 changes: 27 additions & 0 deletions spec/libraries/image_prune_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Load all the libraries
require 'chef'
Dir['libraries/*.rb'].each { |f| require File.expand_path(f) }

describe DockerCookbook::DockerImagePrune do
let(:resource) { DockerCookbook::DockerImagePrune.new('rspec') }

it 'has a default action of [:prune]' do
expect(resource.action).to eql([:prune])
end

it 'generates filter json' do
# Arrange
expected = '{"filters":["dangling=true","until=1h30m","label=com.example.vendor=ACME","label!=no_prune"]}'
resource.dangling = true
resource.prune_until = '1h30m'
resource.with_label = 'com.example.vendor=ACME'
resource.without_label = 'no_prune'
resource.action :prune

# Act
actual = resource.generate_json(resource)

# Assert
expect(actual).to eq(expected)
end
end
15 changes: 15 additions & 0 deletions test/cookbooks/docker_test/recipes/image_prune.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#########################
# :prune
#########################

docker_image_prune 'hello-world' do
dangling true
end

docker_image_prune "prune-old-images" do
dangling true
prune_until '1h30m'
with_label 'com.example.vendor=ACME'
without_label 'no_prune'
action :prune
end

0 comments on commit 3b9d146

Please sign in to comment.