diff --git a/lib/puppet/parser/functions/docker_service_flags.rb b/lib/puppet/parser/functions/docker_service_flags.rb index 3ca2713..bd467ba 100644 --- a/lib/puppet/parser/functions/docker_service_flags.rb +++ b/lib/puppet/parser/functions/docker_service_flags.rb @@ -1,87 +1,93 @@ # frozen_string_literal: true require 'shellwords' # # docker_service_flags.rb # module Puppet::Parser::Functions # Transforms a hash into a string of docker swarm init flags newfunction(:docker_service_flags, type: :rvalue) do |args| opts = args[0] || {} flags = [] if opts['detach'].to_s != 'true' flags << '--detach' end if opts['service_name'] && opts['service_name'].to_s != 'undef' flags << "'#{opts['service_name']}'" end if opts['env'].is_a? Array opts['env'].each do |env| flags << "--env #{env}" end end if opts['label'].is_a? Array opts['label'].each do |label| flags << "--label #{label}" end end if opts['mounts'].is_a? Array opts['mounts'].each do |mount| flags << "--mount #{mount}" end end + if opts['networks'].is_a? Array + opts['networks'].each do |network| + flags << "--network #{network}" + end + end + if opts['publish'].is_a? Array opts['publish'].each do |port| flags << "--publish #{port}" end elsif opts['publish'].to_s != 'undef' flags << "--publish '#{opts['publish']}'" end if opts['replicas'] && opts['replicas'].to_s != 'undef' flags << "--replicas '#{opts['replicas']}'" end if opts['tty'].to_s != 'false' flags << '--tty' end if opts['user'] && opts['user'].to_s != 'undef' flags << "--user '#{opts['publish']}'" end if opts['workdir'] && opts['workdir'].to_s != 'undef' flags << "--workdir '#{opts['workdir']}'" end if opts['extra_params'].is_a? Array opts['extra_params'].each do |param| flags << param end end if opts['host_socket'] && opts['host_socket'].to_s != 'undef' flags << "-H '#{opts['host_socket']}'" end if opts['registry_mirror'] && opts['registry_mirror'].to_s != 'undef' flags << "--registry-mirror='#{opts['registry_mirror']}'" end if opts['image'] && opts['image'].to_s != 'undef' flags << "'#{opts['image']}'" end if opts['command'] && opts['command'].to_s != 'undef' flags << opts['command'].to_s end flags.flatten.join(' ') end end diff --git a/manifests/services.pp b/manifests/services.pp index 937a4a1..5ea908f 100644 --- a/manifests/services.pp +++ b/manifests/services.pp @@ -1,212 +1,218 @@ # == Define: docker::services # # A define that managers a Docker services # # == Paramaters # # [*ensure*] # This ensures that the service is present or not. # Defaults to present # # [*image*] # The Docker image to spwan the service from. # Defualts to undef # # [*detach*] # Exit immediately instead of waiting for the service to converge (default true) # Defaults to true # # [*env*] # Set environment variables # Defaults to [] # # [*label*] # Service labels. # This used as metdata to configure constraints etc. # Defaults to [] # # [*publish*] # Publish port(s) as node ports. # Defaults to undef # # [*replicas*] # Number of tasks (containers per service) # defaults to undef # # [*tty*] # Allocate a pseudo-TTY # Defaults to false # # [*user*] # Username or UID (format: [:]) # Defaults to undef # # [*workdir*] # Working directory inside the container # Defaults to false # # [*extra_params*] # Allows you to pass any other flag that the Docker service create supports. # This must be passed as an array. See docker service create --help for all options # defaults to [] # # [*update*] # This changes the docker command to # docker service update, you must pass a service name with this option # # [*scale*] # This changes the docker command to # docker service scale, this can only be used with service name and # replicas # # [*host_socket*] # This will allow the service to connect to the host linux socket. # defaults to undef # # [*registry_mirror*] # This will allow the service to set a registry mirror. # defaults to undef # # [*mounts*] -# Allows attacking filesystem mounts to the service (specified as an array) +# Allows attaching filesystem mounts to the service (specified as an array) +# defaults to [] +# +# [*networks*] +# Allows attaching the service to networks (specified as an array) # defaults to [] # # [*command*] # Command to run on the container # define docker::services( Optional[Pattern[/^present$|^absent$/]] $ensure = 'present', Optional[Boolean] $create = true, Optional[Boolean] $update = false, Optional[Boolean] $scale = false, Optional[Boolean] $detach = true, Optional[Boolean] $tty = false, Optional[Array] $env = [], Optional[Array] $label = [], Optional[Array] $extra_params = [], Variant[String,Array,Undef] $image = undef, Variant[String,Array,Undef] $service_name = undef, Variant[String,Array,Undef] $publish = undef, Variant[String,Array,Undef] $replicas = undef, Variant[String,Array,Undef] $user = undef, Variant[String,Array,Undef] $workdir = undef, Variant[String,Array,Undef] $host_socket = undef, Variant[String,Array,Undef] $registry_mirror = undef, Variant[String,Array,Undef] $mounts = undef, + Variant[Array,Undef] $networks = undef, Variant[String,Array,Undef] $command = undef, ){ include docker::params $docker_command = "${docker::params::docker_command} service" if $ensure == 'absent' { if $update { fail translate(('When removing a service you can not update it.')) } if $scale { fail translate(('When removing a service you can not update it.')) } } if $::osfamily == 'windows' { $exec_timeout = 3000 $exec_path = ["${::docker_program_files_path}/Docker/"] $exec_provider = 'powershell' } else { $exec_environment = 'HOME=/root' $exec_path = ['/bin', '/usr/bin'] $exec_timeout = 0 $exec_provider = undef } if $create { $docker_service_create_flags = docker_service_flags({ detach => $detach, env => any2array($env), service_name => $service_name, label => any2array($label), publish => $publish, replicas => $replicas, tty => $tty, user => $user, workdir => $workdir, extra_params => any2array($extra_params), image => $image, host_socket => $host_socket, registry_mirror => $registry_mirror, mounts => $mounts, + networks => $networks, command => $command, }) $exec_create = "${docker_command} create --name ${docker_service_create_flags}" $unless_create = "docker service ps ${service_name}" exec { "${title} docker service create": command => $exec_create, environment => $exec_environment, path => $exec_path, timeout => $exec_timeout, provider => $exec_provider, unless => $unless_create, } } if $update { $docker_service_flags = docker_service_flags({ detach => $detach, env => any2array($env), service_name => $service_name, label => any2array($label), publish => $publish, replicas => $replicas, tty => $tty, user => $user, workdir => $workdir, extra_params => any2array($extra_params), image => $image, host_socket => $host_socket, registry_mirror => $registry_mirror, }) $exec_update = "${docker_command} update ${docker_service_flags}" exec { "${title} docker service update": command => $exec_update, environment => $exec_environment, path => $exec_path, provider => $exec_provider, timeout => $exec_timeout, } } if $scale { $docker_service_flags = docker_service_flags({ service_name => $service_name, replicas => $replicas, extra_params => any2array($extra_params), }) $exec_scale = "${docker_command} scale ${service_name}=${replicas}" exec { "${title} docker service scale": command => $exec_scale, environment => $exec_environment, path => $exec_path, timeout => $exec_timeout, provider => $exec_provider, } } if $ensure == 'absent' { exec { "${title} docker service remove": command => "docker service rm ${service_name}", onlyif => "docker service ps ${service_name}", path => $exec_path, provider => $exec_provider, timeout => $exec_timeout, } } } diff --git a/spec/defines/services_spec.rb b/spec/defines/services_spec.rb index abbbb34..0e25748 100644 --- a/spec/defines/services_spec.rb +++ b/spec/defines/services_spec.rb @@ -1,98 +1,103 @@ require 'spec_helper' describe 'docker::services', :type => :define do let(:title) { 'test_service' } let(:facts) { { :osfamily => 'Debian', :operatingsystem => 'Debian', :lsbdistid => 'Debian', :lsbdistcodename => 'jessie', :kernelrelease => '3.2.0-4-amd64', :operatingsystemmajrelease => '8', } } context 'with ensure => present and service create' do let(:params) { { 'create' => true, 'service_name' => 'foo', 'image' => 'foo:bar', 'publish' => '80:80', 'replicas' => '5', 'extra_params' => ['--update-delay 1m', '--restart-window 30s'], 'env' => ['MY_ENV=1', 'MY_ENV2=2'], 'label' => ['com.example.foo="bar"', 'bar=baz'], 'mounts' => ['type=bind,src=/tmp/a,dst=/tmp/a', 'type=bind,src=/tmp/b,dst=/tmp/b,readonly'], + 'networks' => ['overlay'], } } it { is_expected.to compile.with_all_deps } it { should contain_exec('test_service docker service create').with_command(/docker service create/) } it { should contain_exec('test_service docker service create').with_command(/--env MY_ENV=1/) } it { should contain_exec('test_service docker service create').with_command(/--label bar=baz/) } it { should contain_exec('test_service docker service create').with_command(/--mount type=bind,src=\/tmp\/b,dst=\/tmp\/b,readonly/) } + it { should contain_exec('test_service docker service create').with_command(/--network overlay/) } context 'multiple services declaration' do let(:pre_condition) { " docker::services { 'test_service_2': service_name => 'foo_2', image => 'foo:bar', } " } it { should contain_exec('test_service docker service create').with_command(/docker service create/) } it { should contain_exec('test_service_2 docker service create').with_command(/docker service create/) } end - context 'multiple publish ports' do + context 'multiple publish ports and multiple networks' do let(:pre_condition) { " docker::services { 'test_service_3': service_name => 'foo_3', image => 'foo:bar', publish => ['80:8080', '9000:9000' ], + networks => ['foo_1', 'foo_2'], } " } it { should contain_exec('test_service_3 docker service create').with_command(/--publish 80:8080/) } it { should contain_exec('test_service_3 docker service create').with_command(/--publish 9000:9000/) } + it { should contain_exec('test_service_3 docker service create').with_command(/--network foo_1/) } + it { should contain_exec('test_service_3 docker service create').with_command(/--network foo_2/) } end end context 'with ensure => present and service update' do let(:params) { { 'create' => false, 'update' => true, 'service_name' => 'foo', 'image' => 'bar:latest', } } it { is_expected.to compile.with_all_deps } it { should contain_exec('test_service docker service update').with_command(/docker service update/) } end context 'with ensure => present and service scale' do let(:params) { { 'create' => false, 'scale' => true, 'service_name' => 'bar', 'replicas' => '5', } } it { is_expected.to compile.with_all_deps } it { should contain_exec('test_service docker service scale').with_command(/docker service scale/) } end context 'with ensure => absent' do let(:params) { { 'ensure' => 'absent', 'service_name' => 'foo', } } it { is_expected.to compile.with_all_deps } it { should contain_exec('test_service docker service remove').with_command(/docker service rm/) } end context 'when adding a system user' do let(:params) { { 'user' => ['user1'], } } it { is_expected.to compile.with_all_deps } it { should_not contain_exec('docker-systemd-reload-before-service') } end end