Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add resource type and provider for docker_volumes #111

Merged
merged 1 commit into from
Jan 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,42 @@ docker::networks::networks:

A defined network can be used on a `docker::run` resource with the `net` parameter.

### Volumes

Docker 1.9.x added support for Volumes. These are *NOT* to be confused with the legacy volumes, now known as `bind mounts`. To expose the `docker_volume` type, which is used to manage volumes, add the following code to the manifest file:

```puppet
docker_volume { 'my-volume':
ensure => present,
}
```

The name value and the `ensure` parameter are required. If you do not include the `driver` value, the default `local` is used.

Some of the key advantages for using `volumes` over `bind mounts` are:
* Easier to back up or migrate than `bind mounts` (legacy volumes).
* Managed with Docker CLI or API (Puppet type uses the CLI commands).
* Work on both Windows and Linux.
* More easily shared between containers.
* Allows for the store volumes on remote hosts or cloud providers.
* Encrypt contents of volumes.
* Add other functionality
* New volume's contents can be pre-populated by a container.

When using the `volumes` array with `docker::run`, the command on the backend will know if it needs to use `bind mounts` or `volumes` based off the data passed to the `-v` option.

Running `docker::run` with native volumes:

```puppet
docker::run { 'helloworld':
image => 'ubuntu:precise',
command => '/bin/sh -c "while true; do echo hello world; sleep 1; done"',
volumes => ['my-volume:/var/log'],
}
```

For more information on volumes see the [Docker Volumes](https://docs.docker.com/engine/admin/volumes/volumes) documentation

### Compose

Docker Compose describes a set of containers in YAML format and runs a command to build and run those containers. Included in the docker module is the `docker_compose` type. This enables Puppet to run Compose and remediate any issues to ensure reality matches the model in your Compose file.
Expand Down
67 changes: 67 additions & 0 deletions lib/puppet/provider/docker_volume/ruby.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require 'json'

Puppet::Type.type(:docker_volume).provide(:ruby) do
desc 'Support for Docker Volumes'

mk_resource_methods
commands :docker => 'docker'

def volume_conf
flags = ['volume', 'create']
multi_flags = lambda { |values, format|
filtered = [values].flatten.compact
filtered.map { |val| sprintf(format, val) }
}

[
['--driver=%s', :driver],
['--opt=%s', :options]
].each do |(format, key)|
values = resource[key]
new_flags = multi_flags.call(values, format)
flags.concat(new_flags)
end
flags << resource[:name]
end

def self.instances
output = docker(['volume', 'ls'])
lines = output.split("\n")
lines.shift # remove header row
lines.collect do |line|
driver, name = line.split(' ')
inspect = docker(['volume', 'inspect', name])
obj = JSON.parse(inspect).first
new({
:name => name,
:mountpoint => obj['Mountpoint'],
:options => obj['Options'],
:ensure => :present,
:driver => driver
})
end
end

def self.prefetch(resources)
instances.each do |prov|
if resource = resources[prov.name] # rubocop:disable Lint/AssignmentInCondition
resource.provider = prov
end
end
end

def exists?
Puppet.info("Checking if docker volume #{name} exists")
@property_hash[:ensure] == :present
end

def create
Puppet.info("Creating docker volume #{name}")
docker(volume_conf)
end

def destroy
Puppet.info("Removing docker volume #{name}")
docker(['volume', 'rm', name])
end
end
24 changes: 24 additions & 0 deletions lib/puppet/type/docker_volume.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Puppet::Type.newtype(:docker_volume) do
@doc = 'A type representing a Docker volume'
ensurable

newparam(:name) do
isnamevar
desc 'The name of the volume'
end

newproperty(:driver) do
desc 'The volume driver used by the volume'
end

newproperty(:options) do
desc 'Additional options for the volume driver'
end

newproperty(:mountpoint) do
desc 'The location that the volume is mounted to'
validate do |value|
fail "#{self.name.to_s} is read-only and is only available via puppet resource."
end
end
end
4 changes: 4 additions & 0 deletions manifests/volumes.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# docker::volumes
class docker::volumes($volumes) {
create_resources(docker_volumes, $volumes)
}
38 changes: 38 additions & 0 deletions spec/acceptance/volume_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require 'spec_helper_acceptance'

describe 'docker network' do
command = 'docker'

before(:all) do
install_code = "class { 'docker': }"
apply_manifest(install_code, :catch_failures => true)
end

describe command("#{command} volume --help") do
its(:exit_status) { should eq 0 }
end

context 'with a local volume described in Puppet' do
before(:all) do
@name = 'test-volume'
@pp = <<-code
docker_volume { '#{@name}':
ensure => present,
}
code
apply_manifest(@pp, :catch_failures => true)
end

it 'should be idempotent' do
apply_manifest(@pp, :catch_changes => true)
end

it 'should have created a volume' do
shell("#{command} volume inspect #{@name}", :acceptable_exit_codes => [0])
end

after(:all) do
shell("#{command} volume rm #{@name}")
end
end
end
32 changes: 32 additions & 0 deletions spec/unit/docker_volume_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require 'spec_helper'

volume = Puppet::Type.type(:docker_volume)

describe volume do
let :params do
[
:name,
:provider,
]
end

let :properties do
[
:driver,
:options,
:mountpoint,
]
end

it 'should have expected properties' do
properties.each do |property|
expect(volume.properties.map(&:name)).to be_include(property)
end
end

it 'should have expected parameters' do
params.each do |param|
expect(volume.parameters).to be_include(param)
end
end
end