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 tmpdir option to docker_compose #823

Merged
merged 14 commits into from
May 13, 2022
129 changes: 66 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,20 @@ Docker provides a enterprise addition of the [Docker Engine](https://www.docker.

```puppet
class { 'docker':
docker_ee => true,
docker_ee => true,
docker_ee_source_location => 'https://<docker_ee_repo_url>',
docker_ee_key_source => 'https://<docker_ee_key_source_url>',
docker_ee_key_id => '<key id>',
docker_ee_key_source => 'https://<docker_ee_key_source_url>',
docker_ee_key_id => '<key id>',
}
```

To install Docker EE on RHEL/CentOS:

```puppet
class { 'docker':
docker_ee => true,
docker_ee => true,
docker_ee_source_location => 'https://<docker_ee_repo_url>',
docker_ee_key_source => 'https://<docker_ee_key_source_url>',
docker_ee_key_source => 'https://<docker_ee_key_source_url>',
}
```

Expand All @@ -108,21 +108,21 @@ To use the CE packages, add the following code to the manifest file:
```puppet
class { 'docker':
use_upstream_package_source => false,
repo_opt => '',
repo_opt => '',
}
```

By default, the Docker daemon binds to a unix socket at `/var/run/docker.sock`. To change this parameter and update the binding parameter to a tcp socket, add the following code to the manifest file:

```puppet
class { 'docker':
tcp_bind => ['tcp://127.0.0.1:2375'],
socket_bind => 'unix:///var/run/docker.sock',
ip_forward => true,
iptables => true,
ip_masq => true,
bip => '192.168.1.1/24',
fixed_cidr => '192.168.1.144/28',
tcp_bind => ['tcp://127.0.0.1:2375'],
socket_bind => 'unix:///var/run/docker.sock',
ip_forward => true,
iptables => true,
ip_masq => true,
bip => '192.168.1.1/24',
fixed_cidr => '192.168.1.144/28',
}
```

Expand All @@ -132,7 +132,7 @@ The default group ownership of the Unix control socket differs based on OS. For

```puppet
class {'docker':
socket_group => 'root',
socket_group => 'root',
socket_override => true,
}
```
Expand All @@ -153,11 +153,11 @@ When setting up TLS, upload the related files (CA certificate, server certificat

```puppet
class { 'docker':
tcp_bind => ['tcp://0.0.0.0:2376'],
tls_enable => true,
tls_cacert => '/etc/docker/tls/ca.pem',
tls_cert => '/etc/docker/tls/cert.pem',
tls_key => '/etc/docker/tls/key.pem',
tcp_bind => ['tcp://0.0.0.0:2376'],
tls_enable => true,
tls_cacert => '/etc/docker/tls/ca.pem',
tls_cert => '/etc/docker/tls/cert.pem',
tls_key => '/etc/docker/tls/key.pem',
}
```

Expand All @@ -177,7 +177,7 @@ To track the latest version of Docker, add the following code to the manifest fi

```puppet
class { 'docker':
version => 'latest',
version => latest,
}
```

Expand Down Expand Up @@ -477,18 +477,18 @@ To enable the restart of an unhealthy container, add the following code to the m

```puppet
docker::run { 'helloworld':
image => 'base',
command => 'command',
health_check_cmd => '<command_to_execute_to_check_your_containers_health>',
restart_on_unhealthy => true,
image => 'base',
command => 'command',
health_check_cmd => '<command_to_execute_to_check_your_containers_health>',
restart_on_unhealthy => true,
health_check_interval => '<time between running docker healthcheck>',
```

To run command on Windows 2016 requires the `restart` parameter to be set:

```puppet
docker::run { 'helloworld':
image => 'microsoft/nanoserver',
image => 'microsoft/nanoserver',
command => 'ping 127.0.0.1 -t',
restart => 'always'
```
Expand Down Expand Up @@ -611,7 +611,7 @@ To install Docker Compose, add the following code to the manifest file:

```puppet
class {'docker::compose':
ensure => present,
ensure => present,
version => '1.9.0',
}
```
Expand All @@ -631,22 +631,25 @@ Specify the `file` resource to add a Compose file to the machine you have Puppet
```puppet
docker_compose { 'test':
compose_files => ['/tmp/docker-compose.yml'],
ensure => present,
ensure => present,
}
```

Puppet automatically runs Compose because the relevant Compose services aren't running. If required, include additional options such as enabling experimental features and scaling rules.

In the example below, Puppet runs Compose when the number of containers specified for a service doesn't match the scale values.
Additionally, the TMPDIR environment variable can optionally be set when docker_compose runs if you want Puppet to manage the environment variable within the scope of the resource. This is effective when noexec is set on the default /tmp dir, however you must ensure that the target directory exists as the resource will not create it.

In the example below, Puppet runs Compose when the number of containers specified for a service doesn't match the scale values. The optional tmpdir parameter is also specified.

```puppet
docker_compose { 'test':
compose_files => ['/tmp/docker-compose.yml'],
ensure => present,
scale => {
ensure => present,
scale => {
'compose_test' => 2,
},
options => ['--x-networking']
tmpdir => '/usr/local/share/tmp_docker',
options => ['--x-networking']
}
```

Expand All @@ -670,10 +673,10 @@ To deploy the stack, add the following code to the manifest file:

```puppet
docker::stack { 'yourapp':
ensure => present,
stack_name => 'yourapp',
ensure => present,
stack_name => 'yourapp',
compose_files => ['/tmp/docker-compose.yaml'],
require => [Class['docker'], File['/tmp/docker-compose.yaml']],
require => [Class['docker'], File['/tmp/docker-compose.yaml']],
}
```

Expand All @@ -685,11 +688,11 @@ To deploy the stack, add the following code to the manifest file.

```puppet
docker::stack { 'yourapp':
ensure => present,
stack_name => 'yourapp',
compose_files => ['/tmp/docker-compose.yaml'],
ensure => present,
stack_name => 'yourapp',
compose_files => ['/tmp/docker-compose.yaml'],
with_registry_auth => true,
require => [Class['docker'], File['/tmp/docker-compose.yaml']],
require => [Class['docker'], File['/tmp/docker-compose.yaml']],
}
```

Expand All @@ -698,8 +701,8 @@ To use the equivalent type and provider, use the following in your manifest file
```puppet
docker_stack { 'test':
compose_files => ['/tmp/docker-compose.yml'],
ensure => present,
up_args => '--with-registry-auth',
ensure => present,
up_args => '--with-registry-auth',
}
```

Expand All @@ -717,7 +720,7 @@ To install Docker Machine, add the following code to the manifest file:

```puppet
class {'docker::machine':
ensure => present,
ensure => present,
version => '1.16.1',
}
```
Expand Down Expand Up @@ -834,14 +837,14 @@ To create a Docker service, add the following code to the manifest file:

```puppet
docker::services {'redis':
create => true,
create => true,
service_name => 'redis',
image => 'redis:latest',
publish => '6379:639',
replicas => '5',
mounts => ['type=bind,source=/etc/my-redis.conf,target=/etc/redis/redis.conf,readonly'],
image => 'redis:latest',
publish => '6379:639',
replicas => '5',
mounts => ['type=bind,source=/etc/my-redis.conf,target=/etc/redis/redis.conf,readonly'],
extra_params => ['--update-delay 1m', '--restart-window 30s'],
command => ['redis-server', '--appendonly', 'yes'],
command => ['redis-server', '--appendonly', 'yes'],
}
```

Expand All @@ -851,10 +854,10 @@ To update the service, add the following code to the manifest file:

```puppet
docker::services {'redis_update':
create => false,
update => true,
create => false,
update => true,
service_name => 'redis',
replicas => '3',
replicas => '3',
}
```

Expand All @@ -864,10 +867,10 @@ To scale a service, add the following code to the manifest file:

```puppet
docker::services {'redis_scale':
create => false,
scale => true,
create => false,
scale => true,
service_name => 'redis',
replicas => '10',
replicas => '10',
}
```

Expand All @@ -877,8 +880,8 @@ To remove a service, add the following code to the manifest file:

```puppet
docker::services {'redis':
create => false,
ensure => 'absent',
create => false,
ensure => 'absent',
service_name => 'redis',
}
```
Expand Down Expand Up @@ -962,14 +965,14 @@ Within the context of a running container, the docker module supports arbitrary

```puppet
docker::exec { 'cron_allow_root':
detach => true,
container => 'mycontainer',
command => '/bin/echo root >> /usr/lib/cron/cron.allow',
onlyif => 'running',
tty => true,
env => ['FOO=BAR', 'FOO2=BAR2'],
unless => 'grep root /usr/lib/cron/cron.allow 2>/dev/null',
refreshonly => true,
detach => true,
container => 'mycontainer',
command => '/bin/echo root >> /usr/lib/cron/cron.allow',
onlyif => 'running',
tty => true,
env => ['FOO=BAR', 'FOO2=BAR2'],
unless => 'grep root /usr/lib/cron/cron.allow 2>/dev/null',
refreshonly => true,
}
```

Expand Down
10 changes: 10 additions & 0 deletions lib/puppet/provider/docker_compose/ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,21 @@
environment(HOME: '/root')
end

def set_tmpdir
return unless resource[:tmpdir]
# Check if the the tmpdir target exists
Puppet.warning("#{resource[:tmpdir]} (defined as docker_compose tmpdir) does not exist") unless Dir.exist?(resource[:tmpdir])
# Set TMPDIR environment variable only if defined among resources and exists
ENV['TMPDIR'] = resource[:tmpdir] if Dir.exist?(resource[:tmpdir])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How/when is this variable reset to it's original value?

I suspect that this would have an impact with other types / provider depending on resource ordering:

foo { 'before': # ENV['TMPDIR'] #=> nil
}

docker_compose { 'bar':
  path => '/here/or/there',
}

foo { 'after': # ENV['TMPDIR'] #=>  '/here/or/there'
}

Copy link
Contributor Author

@canihavethisone canihavethisone Apr 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smortex the variable only appears to have effect within the resource, and also the resource collector and ordering in init.pp ensures that docker_compose runs last, so my tests to put ENV['TMPDIR'] during run show it as unset or reflect the system variable within prior resources. It also does not affect system-wide env whether set or unset.

    Class['docker::repos']
    -> Class['docker::install']
    -> Class['docker::config']
    -> Class['docker::service']
    -> Docker::Registry <||>
    -> Docker::Image <||>
    -> Docker::Run <||>
    -> Docker_compose <||>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@canihavethisone Thanks for that explanation. Makes sense to me.

@smortex What do you think?

end

def exists?
Puppet.info("Checking for compose project #{name}")
compose_services = {}
compose_containers = []

set_tmpdir

# get merged config using docker-compose config
args = [compose_files, '-p', name, 'config'].insert(3, resource[:options]).compact
compose_output = YAML.safe_load(execute([command(:dockercompose)] + args, combine: false))
Expand Down
15 changes: 15 additions & 0 deletions lib/puppet/type/docker_compose.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,19 @@ def refresh
isnamevar
desc 'The name of the project'
end

newparam(:tmpdir) do
desc "Override the temporary directory used by docker-compose.

This property is useful when the /tmp directory has been mounted
with the noexec option. Or is otherwise being prevented It allows the module consumer to redirect
docker-composes temporary files to a known directory.

The directory passed to this property must exist and be accessible
by the user that is executing the puppet agent.
"
validate do |value|
raise _('tmpdir should be a String') unless value.is_a? String
end
end
end
4 changes: 4 additions & 0 deletions spec/unit/lib/puppet/type/docker_compose_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@
it 'requires scale to be a hash' do
expect(compose).to require_hash_for('scale')
end

it 'requires tmpdir to be a string' do
expect(compose).to require_string_for('tmpdir')
end
end