Skip to content

Commit a67a2f7

Browse files
MarkWflorindragos
MarkW
authored andcommitted
CLOUD-2069 Adding support for multiple compose files. (#332)
Support for multiple compose files
1 parent 48fa672 commit a67a2f7

File tree

6 files changed

+147
-84
lines changed

6 files changed

+147
-84
lines changed

README.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,8 @@ compose_test:
554554
Specify the `file` resource to add a Compose file to the machine you have Puppet running on. To define a `docker_compose` resource pointing to the Compose file, add the following code to the manifest file:
555555

556556
```puppet
557-
docker_compose { '/tmp/docker-compose.yml':
557+
docker_compose { 'test':
558+
compose_files => ['/tmp/docker-compose.yml'],
558559
ensure => present,
559560
}
560561
```
@@ -564,7 +565,8 @@ Puppet automatically runs Compose, because the relevant Compose services aren't
564565
In the example below, Puppet runs Compose when the number of containers specified for a service don't match the scale values.
565566

566567
```puppet
567-
docker_compose { '/tmp/docker-compose.yml':
568+
docker_compose { 'test':
569+
compose_files => ['/tmp/docker-compose.yml'],
568570
ensure => present,
569571
scale => {
570572
'compose_test' => 2,
@@ -575,6 +577,16 @@ docker_compose { '/tmp/docker-compose.yml':
575577

576578
Give options to the ```docker-compose up``` command, such as ```--remove-orphans```, by using the ```up_args``` option.
577579

580+
To supply multiple overide compose files add the following to the manifest file:
581+
582+
```puppet
583+
docker_compose {'test':
584+
compose_files => ['master-docker-compose.yml', 'override-compose.yml],
585+
}
586+
```
587+
588+
Please note you should supply your master docker-compose file as the first element in the array. As per docker multi compose file support compose files will be merged in the order they are specified in the array.
589+
578590
If you are using a v3.2 compose file or above on a Docker Swarm cluster, use the `docker::stack` class. Include the file resource before you run the stack command.
579591

580592
To deploy the stack, add the following code to the manifest file:

lib/puppet/provider/docker_compose/ruby.rb

+50-48
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,47 @@
66
commands docker: 'docker'
77

88
def exists?
9-
Puppet.info("Checking for compose project #{project}")
10-
compose_file = YAML.safe_load(File.read(name))
9+
Puppet.info("Checking for compose project #{name}")
1110
compose_services = {}
12-
containers = docker([
13-
'ps',
14-
'--format',
15-
"{{.Label \"com.docker.compose.service\"}}-{{.Image}}",
16-
'--filter',
17-
"label=com.docker.compose.project=#{project}",
18-
]).split("\n")
19-
case compose_file['version']
20-
when %r{^2(\.[0-3])?$}, %r{^3(\.[0-6])?$}
21-
compose_services = compose_file['services']
22-
# in compose v1 "version" parameter is not specified
23-
when nil
24-
compose_services = compose_file
25-
else
26-
raise(Puppet::Error, "Unsupported docker compose file syntax version \"#{compose_file['version']}\"!")
27-
end
11+
resource[:compose_files].each do |file|
12+
compose_file = YAML.safe_load(File.read(file))
13+
containers = docker([
14+
'ps',
15+
'--format',
16+
"{{.Label \"com.docker.compose.service\"}}-{{.Image}}",
17+
'--filter',
18+
"label=com.docker.compose.project=#{name}",
19+
]).split("\n")
20+
case compose_file['version']
21+
when %r{^2(\.[0-3])?$}, %r{^3(\.[0-6])?$}
22+
compose_services = compose_services.merge(compose_file['services'])
23+
# in compose v1 "version" parameter is not specified
24+
when nil
25+
compose_services = compose_services.merge(compose_file)
26+
else
27+
raise(Puppet::Error, "Unsupported docker compose file syntax version \"#{compose_file['version']}\"!")
28+
end
2829

29-
if compose_services.count != containers.count
30-
return false
31-
end
30+
if compose_services.count != containers.count
31+
return false
32+
end
3233

33-
counts = Hash[*compose_services.each.map { |key, array|
34-
image = (array['image']) ? array['image'] : get_image(key, compose_services)
35-
Puppet.info("Checking for compose service #{key} #{image}")
36-
["#{key}-#{image}", containers.count("#{key}-#{image}")]
37-
}.flatten]
34+
counts = Hash[*compose_services.each.map { |key, array|
35+
image = (array['image']) ? array['image'] : get_image(key, compose_services)
36+
Puppet.info("Checking for compose service #{key} #{image}")
37+
["#{key}-#{image}", containers.count("#{key}-#{image}")]
38+
}.flatten]
3839

39-
# No containers found for the project
40-
if counts.empty? ||
41-
# Containers described in the compose file are not running
42-
counts.any? { |_k, v| v.zero? } ||
43-
# The scaling factors in the resource do not match the number of running containers
44-
resource[:scale] && counts.merge(resource[:scale]) != counts
45-
false
46-
else
47-
true
40+
# No containers found for the project
41+
if counts.empty? ||
42+
# Containers described in the compose file are not running
43+
counts.any? { |_k, v| v.zero? } ||
44+
# The scaling factors in the resource do not match the number of running containers
45+
resource[:scale] && counts.merge(resource[:scale]) != counts
46+
false
47+
else
48+
true
49+
end
4850
end
4951
end
5052

@@ -61,37 +63,37 @@ def get_image(service_name, compose_services)
6163
end
6264

6365
def create
64-
Puppet.info("Running compose project #{project}")
65-
args = ['-f', name, 'up', '-d', '--remove-orphans'].insert(2, resource[:options]).insert(5, resource[:up_args]).compact
66+
Puppet.info("Running compose project #{name}")
67+
args = [compose_files, '-p', name, 'up', '-d', '--remove-orphans'].insert(2, resource[:options]).insert(5, resource[:up_args]).compact
6668
dockercompose(args)
6769
return unless resource[:scale]
6870
instructions = resource[:scale].map { |k, v| "#{k}=#{v}" }
6971
Puppet.info("Scaling compose project #{project}: #{instructions.join(' ')}")
70-
args = ['-f', name, 'scale'].insert(2, resource[:options]).compact + instructions
72+
args = [compose_files, '-p', name, 'scale'].insert(2, resource[:options]).compact + instructions
7173
dockercompose(args)
7274
end
7375

7476
def destroy
75-
Puppet.info("Removing all containers for compose project #{project}")
76-
kill_args = ['-f', name, 'kill'].insert(2, resource[:options]).compact
77+
Puppet.info("Removing all containers for compose project #{name}")
78+
kill_args = [compose_files, '-p', name, 'kill'].insert(2, resource[:options]).compact
7779
dockercompose(kill_args)
78-
rm_args = ['-f', name, 'rm', '--force', '-v'].insert(2, resource[:options]).compact
80+
rm_args = [compose_files, '-p', name, 'rm', '--force', '-v'].insert(2, resource[:options]).compact
7981
dockercompose(rm_args)
8082
end
8183

8284
def restart
8385
return unless exists?
84-
Puppet.info("Rebuilding and Restarting all containers for compose project #{project}")
85-
kill_args = ['-f', name, 'kill'].insert(2, resource[:options]).compact
86+
Puppet.info("Rebuilding and Restarting all containers for compose project #{name}")
87+
kill_args = [compose_files, '-p', name, 'kill'].insert(2, resource[:options]).compact
8688
dockercompose(kill_args)
87-
build_args = ['-f', name, 'build'].insert(2, resource[:options]).compact
89+
build_args = [compose_files, '-p', name, 'build'].insert(2, resource[:options]).compact
8890
dockercompose(build_args)
8991
create
9092
end
9193

92-
private
93-
94-
def project
95-
File.basename(File.dirname(name)).downcase.gsub(%r{[^0-9a-z ]}i, '')
94+
def compose_files
95+
resource[:compose_files].map { |x| ['-f', x] }.flatten
9696
end
97+
98+
private
9799
end

lib/puppet/type/docker_compose.rb

+10-6
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ def refresh
77
provider.restart
88
end
99

10-
newparam(:name) do
11-
desc 'Docker compose file path.'
12-
end
13-
1410
newparam(:scale) do
1511
desc 'A hash of compose services and number of containers.'
1612
validate do |value|
@@ -38,7 +34,15 @@ def refresh
3834
end
3935
end
4036

41-
autorequire(:file) do
42-
self[:name]
37+
newparam(:compose_files, :array_matching => :all) do
38+
desc 'An array of Docker Compose Files paths.'
39+
validate do |value|
40+
raise _('compose files should be an array') unless value.is_a? Array
41+
end
42+
end
43+
44+
newparam(:name) do
45+
isnamevar
46+
desc 'The name of the project'
4347
end
4448
end

spec/acceptance/compose_spec.rb

+8-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ class { 'docker::compose': }
2525
context 'Creating compose projects' do
2626
before(:all) do
2727
@install = <<-code
28-
docker_compose { '/tmp/docker-compose.yml':
28+
docker_compose { 'test3':
29+
compose_files => ['#{tmp_path}/docker-compose.yml'],
2930
ensure => present,
3031
}
3132
code
@@ -36,21 +37,23 @@ class { 'docker::compose': }
3637
apply_manifest(@install, :catch_changes=>true)
3738
end
3839

39-
describe command("docker inspect tmp_compose_test_1"), :sudo => true do
40+
describe command("docker inspect test3_compose_test_1"), :sudo => true do
4041
its(:exit_status) { should eq 0 }
4142
end
4243
end
4344

4445
context 'Destroying compose projects' do
4546
before(:all) do
4647
install = <<-code
47-
docker_compose { '/tmp/docker-compose.yml':
48+
docker_compose { 'test4':
49+
compose_files => ['#{tmp_path}/docker-compose.yml'],
4850
ensure => present,
4951
}
5052
code
5153
apply_manifest(install, :catch_failures=>true)
5254
@uninstall = <<-code
53-
docker_compose { '/tmp/docker-compose.yml':
55+
docker_compose { 'test4':
56+
compose_files => ['#{tmp_path}/docker-compose.yml'],
5457
ensure => absent,
5558
}
5659
code
@@ -61,7 +64,7 @@ class { 'docker::compose': }
6164
apply_manifest(@uninstall, :catch_changes=>true)
6265
end
6366

64-
describe command("docker inspect tmp_compose_test_1"), :sudo => true do
67+
describe command("docker inspect test4_compose_test_1"), :sudo => true do
6568
its(:exit_status) { should eq 1 }
6669
end
6770
end

spec/acceptance/compose_v3_spec.rb

+44-22
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
file_extension = '.exe'
66
docker_args = 'docker_ee => true'
77
tmp_path = 'C:/cygwin64/tmp'
8+
test_container = 'nanoserver-sac2016'
89
else
910
install_dir = '/usr/local/bin'
1011
file_extension = ''
1112
docker_args = ''
1213
tmp_path = '/tmp'
14+
test_container = 'debian'
1315
end
1416

1517
describe 'docker compose' do
@@ -29,10 +31,10 @@ class { 'docker::compose':
2931
it 'should have docker compose installed' do
3032
shell('docker-compose --help', :acceptable_exit_codes => [0])
3133
end
32-
3334
before(:all) do
3435
@install = <<-code
35-
docker_compose { '#{tmp_path}/docker-compose-v3.yml':
36+
docker_compose { 'web':
37+
compose_files => ['#{tmp_path}/docker-compose-v3.yml'],
3638
ensure => present,
3739
}
3840
code
@@ -44,42 +46,62 @@ class { 'docker::compose':
4446
end
4547

4648
it 'should find a docker container' do
47-
shell('docker inspect tmp_compose_test_1', :acceptable_exit_codes => [0])
49+
shell('docker inspect web_compose_test_1', :acceptable_exit_codes => [0])
4850
end
4951
end
5052

51-
context 'Destroying compose v3 projects' do
53+
context 'creating compose projects with multi compose files' do
5254
before(:all) do
53-
install = <<-code
54-
docker_compose { '#{tmp_path}/docker-compose-v3.yml':
55+
@install = <<-pp1
56+
docker_compose { 'web1':
57+
compose_files => ['#{tmp_path}/docker-compose-v3.yml', '#{tmp_path}/docker-compose-override-v3.yml'],
5558
ensure => present,
5659
}
57-
code
58-
apply_manifest(install, :catch_failures=>true)
59-
@uninstall = <<-code
60-
docker_compose { '#{tmp_path}/docker-compose-v3.yml':
60+
pp1
61+
62+
apply_manifest(@install, :catch_failures=>true)
63+
end
64+
65+
it "should find container with #{test_container} tag" do
66+
shell("docker inspect web1_compose_test_1 | grep #{test_container}", :acceptable_exit_codes => [0])
67+
end
68+
end
69+
70+
context 'Destroying project with multiple compose files' do
71+
before(:all) do
72+
@install = <<-pp1
73+
docker_compose { 'web1':
74+
compose_files => ['#{tmp_path}/docker-compose-v3.yml', '#{tmp_path}/docker-compose-override-v3.yml'],
75+
ensure => present,
76+
}
77+
pp1
78+
79+
@destroy = <<-pp2
80+
docker_compose { 'web1':
81+
compose_files => ['#{tmp_path}/docker-compose-v3.yml', '#{tmp_path}/docker-compose-override-v3.yml'],
6182
ensure => absent,
6283
}
63-
code
64-
apply_manifest(@uninstall, :catch_failures=>true)
84+
pp2
85+
apply_manifest(@install, :catch_failures=>true)
86+
apply_manifest(@destroy, :catch_failures=>true)
6587
end
6688

6789
it 'should be idempotent' do
68-
apply_manifest(@uninstall, :catch_changes=>true)
90+
apply_manifest(@destroy, :catch_changes=>true)
6991
end
7092

7193
it 'should not find a docker container' do
72-
shell('docker inspect tmp_compose_test_1', :acceptable_exit_codes => [1])
94+
shell('docker inspect web1_compose_test_1', :acceptable_exit_codes => [1])
7395
end
7496
end
7597

7698
context 'Requesting a specific version of compose' do
7799
before(:all) do
78100
@version = '1.21.2'
79101
@pp = <<-code
80-
class { 'docker::compose':
81-
version => '#{@version}',
82-
}
102+
class { 'docker::compose':
103+
version => '#{@version}',
104+
}
83105
code
84106
apply_manifest(@pp, :catch_failures=>true)
85107
end
@@ -99,10 +121,10 @@ class { 'docker::compose':
99121
before(:all) do
100122
@version = '1.21.2'
101123
@pp = <<-code
102-
class { 'docker::compose':
103-
ensure => absent,
104-
version => '#{@version}',
105-
}
124+
class { 'docker::compose':
125+
ensure => absent,
126+
version => '#{@version}',
127+
}
106128
code
107129
apply_manifest(@pp, :catch_failures=>true)
108130
end
@@ -124,4 +146,4 @@ class { 'docker::compose': }
124146
apply_manifest(install_code, :catch_failures=>true)
125147
end
126148
end
127-
end
149+
end

0 commit comments

Comments
 (0)