# @summary # A define which manages a running docker container. # # @param restart # Sets a restart policy on the docker run. # Note: If set, puppet will NOT setup an init script to manage, instead # it will do a raw docker run command using a CID file to track the container # ID. # # If you want a normal named container with an init script and a restart policy # you must use the extra_parameters feature and pass it in like this: # # extra_parameters => ['--restart=always'] # # However, if your system is using sytemd this restart policy will be # ineffective because the ExecStop commands will run which will cause # docker to stop restarting it. In this case you should use the # systemd_restart option to specify the policy you want. # # This will allow the docker container to be restarted if it dies, without # puppet help. # # @param verify_digest # (optional) Make sure, that the image has not modified. Compares the digest # checksum before starting the docker image. # To get the digest of an image, run the following command: # docker image inspect <> --format='{{index .RepoDigests 0}} # # @param service_prefix # (optional) The name to prefix the startup script with and the Puppet # service resource title with. Default: 'docker-' # # @param restart_service # (optional) Whether or not to restart the service if the the generated init # script changes. Default: true # # @param restart_service_on_docker_refresh # Whether or not to restart the service if the docker service is restarted. # Only has effect if the docker_service parameter is set. # Default: true # # @param manage_service # (optional) Whether or not to create a puppet Service resource for the init # script. Disabling this may be useful if integrating with existing modules. # Default: true # # @param docker_service # (optional) If (and how) the Docker service itself is managed by Puppet # true -> Service['docker'] # false -> no Service dependency # anything else -> Service[docker_service] # Default: false # # @param health_check_cmd # (optional) Specifies the command to execute to check that the container is healthy using the docker health check functionality. # Default: undef # # @param health_check_interval # (optional) Specifies the interval that the health check command will execute in seconds. # Default: undef # # @param restart_on_unhealthy # (optional) Checks the health status of Docker container and if it is unhealthy the service will be restarted. # The health_check_cmd parameter must be set to true to use this functionality. # Default: undef # # @param net # # The docker network to attach to a container. # Can be a String or Array (if using multiple networks) # Default: bridge # # @param extra_parameters # An array of additional command line arguments to pass to the `docker run` # command. Useful for adding additional new or experimental options that the # module does not yet support. # # @param systemd_restart # (optional) If the container is to be managed by a systemd unit file set the # Restart option on the unit file. Can be any valid value for this systemd # configuration. Most commonly used are on-failure or always. # Default: on-failure # # @param custom_unless # (optional) Specify an additional unless for the Docker run command when using restart. # Default: undef # # @param after_create # (optional) Specifies the command to execute after container is created but before it is started. # Default: undef # # @param remain_after_exit # (optional) If the container is to be managed by a systemd unit file set the # RemainAfterExit option on the unit file. Can be any valid value for this systemd # configuration. # Default: Not included in unit file # # @param prepare_service_only # (optional) Prepare the service and enable it as usual but do not run it right away. # Useful when building VM images using masterless Puppet and then letting the Docker images # to be downloaded when a new VM is created. # Default: false # # @param image # # @param ensure # # @param command # # @param memory_limit # # @param cpuset # # @param ports # # @param labels # # @param expose # # @param volumes # # @param links # # @param use_name # # @param running # # @param volumes_from # # @param username # # @param hostname # # @param env # # @param env_file # # @param dns # # @param dns_search # # @param lxc_conf # # @param service_provider # # @param disable_network # # @param privileged # # @param detach # # @param extra_systemd_parameters # # @param pull_on_start # # @param after # # @param after_service # # @param depends # # @param depend_services # # @param tty # # @param socket_connect # # @param hostentries # # @param before_start # # @param before_stop # # @param after_start # # @param after_stop # # @param remove_container_on_start # # @param remove_container_on_stop # # @param remove_volume_on_start # # @param remove_volume_on_stop # # @param stop_wait_time # # @param syslog_identifier # # @param syslog_facility # # @param read_only # define docker::run ( Optional[Pattern[/^[\S]*$/]] $image = undef, Enum[present,absent] $ensure = 'present', Optional[String] $verify_digest = undef, Optional[String] $command = undef, Pattern[/^[\d]*(b|k|m|g)$/] $memory_limit = '0b', Variant[String,Array,Undef] $cpuset = [], Variant[String,Array,Undef] $ports = [], Variant[String,Array,Undef] $labels = [], Variant[String,Array,Undef] $expose = [], Variant[String,Array,Undef] $volumes = [], Variant[String,Array,Undef] $links = [], Boolean $use_name = false, Boolean $running = true, Variant[String,Array] $volumes_from = [], Variant[String,Array[String[1],1],Undef] $net = undef, Variant[String,Boolean] $username = false, Variant[String,Boolean] $hostname = false, Variant[String,Array] $env = [], Variant[String,Array] $env_file = [], Variant[String,Array] $dns = [], Variant[String,Array] $dns_search = [], Variant[String,Array] $lxc_conf = [], String $service_prefix = 'docker-', Optional[String] $service_provider = undef, Boolean $restart_service = true, Boolean $restart_service_on_docker_refresh = true, Boolean $manage_service = true, Variant[String,Boolean] $docker_service = false, Boolean $disable_network = false, Boolean $privileged = false, Optional[Boolean] $detach = undef, Optional[Variant[String,Array[String]]] $extra_parameters = undef, String $systemd_restart = 'on-failure', Variant[String,Hash] $extra_systemd_parameters = {}, Boolean $pull_on_start = false, Variant[String,Array] $after = [], Variant[String,Array] $after_service = [], Variant[String,Array] $depends = [], Variant[String,Array] $depend_services = ['docker.service'], Boolean $tty = false, Variant[String,Array] $socket_connect = [], Variant[String,Array] $hostentries = [], Optional[String] $restart = undef, Variant[String,Boolean] $before_start = false, Variant[String,Boolean] $before_stop = false, Variant[String,Boolean] $after_start = false, Variant[String,Boolean] $after_stop = false, Optional[String] $after_create = undef, Boolean $remove_container_on_start = true, Boolean $remove_container_on_stop = true, Boolean $remove_volume_on_start = false, Boolean $remove_volume_on_stop = false, Integer $stop_wait_time = 10, Optional[String] $syslog_identifier = undef, Optional[String] $syslog_facility = undef, Boolean $read_only = false, Optional[String] $health_check_cmd = undef, Boolean $restart_on_unhealthy = false, Optional[Integer] $health_check_interval = undef, Variant[String,Array] $custom_unless = [], Optional[String] $remain_after_exit = undef, Boolean $prepare_service_only = false, ) { include docker::params if ($socket_connect != []) { $sockopts = join(any2array($socket_connect), ',') $docker_command = "${docker::params::docker_command} -H ${sockopts}" } else { $docker_command = $docker::params::docker_command } $service_name = $docker::service_name $docker_group = $docker::docker_group if $restart { assert_type(Pattern[/^(no|always|unless-stopped|on-failure)|^on-failure:[\d]+$/], $restart) } if ($remove_volume_on_start and !$remove_container_on_start) { fail("In order to remove the volume on start for ${title} you need to also remove the container") } if ($remove_volume_on_stop and !$remove_container_on_stop) { fail("In order to remove the volume on stop for ${title} you need to also remove the container") } if $use_name { notify { "docker use_name warning: ${title}": message => 'The use_name parameter is no-longer required and will be removed in a future release', withpath => true, } } if $systemd_restart { assert_type(Pattern[/^(no|always|on-success|on-failure|on-abnormal|on-abort|on-watchdog)$/], $systemd_restart) } $service_provider_real = $service_provider ? { undef => $docker::params::service_provider, default => $service_provider, } if $detach == undef { $valid_detach = $service_provider_real ? { 'systemd' => false, default => $docker::params::detach_service_in_init, } } else { $valid_detach = $detach } $extra_parameters_array = any2array($extra_parameters) $after_array = any2array($after) $depends_array = any2array($depends) $depend_services_array = any2array($depend_services) $docker_run_flags = docker_run_flags({ cpuset => any2array($cpuset), disable_network => $disable_network, dns => any2array($dns), dns_search => any2array($dns_search), env => any2array($env), env_file => any2array($env_file), expose => any2array($expose), extra_params => any2array($extra_parameters), hostentries => any2array($hostentries), hostname => $hostname, links => any2array($links), lxc_conf => any2array($lxc_conf), memory_limit => $memory_limit, net => $net, ports => any2array($ports), labels => any2array($labels), privileged => $privileged, socket_connect => any2array($socket_connect), tty => $tty, username => $username, volumes => any2array($volumes), volumes_from => any2array($volumes_from), read_only => $read_only, health_check_cmd => $health_check_cmd, restart_on_unhealthy => $restart_on_unhealthy, health_check_interval => $health_check_interval, osfamily => $facts['os']['family'], } ) $sanitised_title = docker::sanitised_name($title) if empty($depends_array) { $sanitised_depends_array = [] } else { $sanitised_depends_array = docker::sanitised_name($depends_array) } if empty($after_array) { $sanitised_after_array = [] } else { $sanitised_after_array = docker::sanitised_name($after_array) } if $facts['os']['family'] == 'windows' { $exec_environment = "PATH=${facts['docker_program_files_path']}/Docker/;${facts['docker_systemroot']}/System32/" $exec_timeout = 3000 $exec_path = ["${facts['docker_program_files_path']}/Docker/"] $exec_provider = 'powershell' $cidfile = "${facts['docker_user_temp_path']}/${service_prefix}${sanitised_title}.cid" $restart_check = "${docker_command} inspect ${sanitised_title} -f '{{ if eq \\\"unhealthy\\\" .State.Health.Status }} {{ .Name }}{{ end }}' | findstr ${sanitised_title}" # lint:ignore:140chars $container_running_check = "\$state = ${docker_command} inspect ${sanitised_title} -f \"{{ .State.Running }}\"; if (\$state -ieq \"true\") { Exit 0 } else { Exit 1 }" # lint:ignore:140chars } else { $exec_environment = 'HOME=/root' $exec_path = ['/bin', '/usr/bin'] $exec_timeout = 0 $exec_provider = undef $cidfile = "/var/run/${service_prefix}${sanitised_title}.cid" $restart_check = "${docker_command} inspect ${sanitised_title} -f '{{ if eq \"unhealthy\" .State.Health.Status }} {{ .Name }}{{ end }}' | grep ${sanitised_title}" # lint:ignore:140chars $container_running_check = "${docker_command} inspect ${sanitised_title} -f \"{{ .State.Running }}\" | grep true" # lint:ignore:140chars } if $restart_on_unhealthy { exec { "Restart unhealthy container ${title} with docker": command => "${docker_command} restart ${sanitised_title}", onlyif => $restart_check, environment => $exec_environment, path => $exec_path, provider => $exec_provider, timeout => $exec_timeout, } } if $restart { if $ensure == 'absent' { exec { "stop ${title} with docker": command => "${docker_command} stop --time=${stop_wait_time} ${sanitised_title}", onlyif => "${docker_command} inspect ${sanitised_title}", environment => $exec_environment, path => $exec_path, provider => $exec_provider, timeout => $exec_timeout, } exec { "remove ${title} with docker": command => "${docker_command} rm -v ${sanitised_title}", onlyif => "${docker_command} inspect ${sanitised_title}", environment => $exec_environment, path => $exec_path, provider => $exec_provider, timeout => $exec_timeout, } file { $cidfile: ensure => absent, } } else { $run_with_docker_command = [ "${docker_command} run -d ${docker_run_flags}", "--name ${sanitised_title} --cidfile=${cidfile}", "--restart=\"${restart}\" ${image} ${command}", ] $inspect = ["${docker_command} inspect ${sanitised_title}",] if $custom_unless { $exec_unless = concat($custom_unless, $inspect) } else { $exec_unless = $inspect } if versioncmp($facts['puppetversion'], '6') < 0 { exec { "run ${title} with docker": command => join($run_with_docker_command, ' '), unless => $exec_unless, environment => $exec_environment, path => $exec_path, provider => $exec_provider, timeout => $exec_timeout, } if $running == false { exec { "stop ${title} with docker": command => "${docker_command} stop --time=${stop_wait_time} ${sanitised_title}", onlyif => $container_running_check, environment => $exec_environment, path => $exec_path, provider => $exec_provider, timeout => $exec_timeout, } } else { exec { "start ${title} with docker": command => "${docker_command} start ${sanitised_title}", unless => $container_running_check, environment => $exec_environment, path => $exec_path, provider => $exec_provider, timeout => $exec_timeout, } } } else { $docker_params_changed_args = { sanitised_title => $sanitised_title, osfamily => $facts['os']['family'], command => join($run_with_docker_command, ' '), cidfile => $cidfile, image => $image, volumes => $volumes, ports => $ports, stop_wait_time => $stop_wait_time, container_running => $running, # logfile_path => ($facts['os']['family'] == 'windows') ? { # true => ::docker_user_temp_path, # default => '/tmp', # }, } $detect_changes = Deferred('docker_params_changed', [$docker_params_changed_args]) notify { "${title}_docker_params_changed": message => $detect_changes, } } } } else { $run_start_parameters = { 'before_start' => $before_start, 'remove_container_on_start' => $remove_container_on_start, 'docker_command' => $docker_command, 'remove_volume_on_start' => $remove_volume_on_start, 'sanitised_title' => $sanitised_title, 'pull_on_start' => $pull_on_start, 'image' => $image, 'verify_digest' => $verify_digest, 'docker_run_flags' => $docker_run_flags, 'command' => $command, 'after_create' => $after_create, 'net' => $net, 'valid_detach' => $valid_detach, 'after_start' => $after_start, } $docker_run_inline_start = epp('docker/docker-run-start.epp', $run_start_parameters) $run_stop_parameters = { 'before_stop' => $before_stop, 'docker_command' => $docker_command, 'stop_wait_time' => $stop_wait_time, 'sanitised_title' => $sanitised_title, 'remove_container_on_stop' => $remove_container_on_stop, 'remove_volume_on_stop' => $remove_volume_on_stop, 'after_stop' => $after_stop, } $docker_run_inline_stop = epp('docker/docker-run-stop.epp', $run_stop_parameters) case $service_provider_real { 'systemd': { $initscript = "/etc/systemd/system/${service_prefix}${sanitised_title}.service" $startscript = "/usr/local/bin/docker-run-${sanitised_title}-start.sh" $stopscript = "/usr/local/bin/docker-run-${sanitised_title}-stop.sh" $startstop_template = 'docker/usr/local/bin/docker-run.sh.epp' $init_template = 'docker/etc/systemd/system/docker-run.epp' $mode = '0644' $hasstatus = true } 'upstart': { $initscript = "/etc/init.d/${service_prefix}${sanitised_title}" $init_template = 'docker/etc/init.d/docker-run.epp' $mode = '0750' $startscript = undef $stopscript = undef $startstop_template = undef $hasstatus = true } default: { if $facts['os']['family'] != 'windows' { fail('Docker needs a Debian or RedHat based system.') } elsif $ensure == 'present' { fail('Restart parameter is required for Windows') } $hasstatus = $docker::params::service_hasstatus } } if $syslog_identifier { $_syslog_identifier = $syslog_identifier } else { $_syslog_identifier = "${service_prefix}${sanitised_title}" } if $ensure == 'absent' { if $facts['os']['family'] == 'windows' { exec { "stop container ${service_prefix}${sanitised_title}": command => "${docker_command} stop --time=${stop_wait_time} ${sanitised_title}", onlyif => "${docker_command} inspect ${sanitised_title}", environment => $exec_environment, path => $exec_path, provider => $exec_provider, timeout => $exec_timeout, notify => Exec["remove container ${service_prefix}${sanitised_title}"], } } else { service { "${service_prefix}${sanitised_title}": ensure => false, enable => false, hasstatus => $hasstatus, provider => $service_provider_real, notify => Exec["remove container ${service_prefix}${sanitised_title}"], } } exec { "remove container ${service_prefix}${sanitised_title}": command => "${docker_command} rm -v ${sanitised_title}", onlyif => "${docker_command} inspect ${sanitised_title}", environment => $exec_environment, path => $exec_path, refreshonly => true, provider => $exec_provider, timeout => $exec_timeout, } if $facts['os']['family'] != 'windows' { file { "/etc/systemd/system/${service_prefix}${sanitised_title}.service": ensure => absent, } if ($startscript) { file { $startscript: ensure => absent, } } if ($stopscript) { file { $stopscript: ensure => absent, } } } else { file { $cidfile: ensure => absent, } } } else { if ($startscript) { file { $startscript: ensure => file, content => epp($startstop_template, { 'script' => $docker_run_inline_start }), seltype => 'container_runtime_exec_t', owner => 'root', group => $docker_group, mode => '0770', } } if ($stopscript) { file { $stopscript: ensure => file, content => epp($startstop_template, { 'script' => $docker_run_inline_stop }), seltype => 'container_runtime_exec_t', owner => 'root', group => $docker_group, mode => '0770', } } if $service_provider_real == 'systemd' { $init_template_parameters = { 'depend_services_array' => $depend_services_array, 'sanitised_after_array' => $sanitised_after_array, 'service_prefix' => $service_prefix, 'sanitised_depends_array' => $sanitised_depends_array, 'title' => $title, 'have_systemd_v230' => $docker::params::have_systemd_v230, 'extra_systemd_parameters' => $extra_systemd_parameters, 'systemd_restart' => $systemd_restart, '_syslog_identifier' => $_syslog_identifier, 'syslog_facility' => $syslog_facility, 'sanitised_title' => $sanitised_title, 'remain_after_exit' => $remain_after_exit, 'service_name' => $service_name, } } elsif $service_provider_real == 'upstart' { $init_template_parameters = { 'sanitised_after_array' => $sanitised_after_array, 'service_prefix' => $service_prefix, 'sanitised_depends_array' => $sanitised_depends_array, 'depend_services_array' => $depend_services_array, 'docker_command' => $docker_command, 'sanitised_title' => $sanitised_title, 'docker_run_inline_start' => $docker_run_inline_start, 'docker_run_inline_stop' => $docker_run_inline_stop, } } file { $initscript: ensure => file, content => epp($init_template, $init_template_parameters), seltype => 'container_unit_file_t', owner => 'root', group => $docker_group, mode => $mode, } if $manage_service { if $running == false { service { "${service_prefix}${sanitised_title}": ensure => $running, enable => false, hasstatus => $hasstatus, require => File[$initscript], } } else { # Transition help from moving from CID based container detection to # Name-based container detection. See #222 for context. # This code should be considered temporary until most people have # transitioned. - 2015-04-15 if $initscript == "/etc/init.d/${service_prefix}${sanitised_title}" { # This exec sequence will ensure the old-style CID container is stopped # before we replace the init script with the new-style. $transition_onlyif = [ "/usr/bin/test -f /var/run/docker-${sanitised_title}.cid &&", "/usr/bin/test -f /etc/init.d/${service_prefix}${sanitised_title}", ] exec { "/bin/sh /etc/init.d/${service_prefix}${sanitised_title} stop": onlyif => join($transition_onlyif, ' '), require => [], } -> file { "/var/run/${service_prefix}${sanitised_title}.cid": ensure => absent, } -> File[$initscript] } service { "${service_prefix}${sanitised_title}": ensure => $running and !$prepare_service_only, enable => true, provider => $service_provider_real, hasstatus => $hasstatus, require => File[$initscript], } } if $docker_service { if $docker_service == true { Service['docker'] -> Service["${service_prefix}${sanitised_title}"] if $restart_service_on_docker_refresh == true { Service['docker'] ~> Service["${service_prefix}${sanitised_title}"] } } else { Service[$docker_service] -> Service["${service_prefix}${sanitised_title}"] if $restart_service_on_docker_refresh == true { Service[$docker_service] ~> Service["${service_prefix}${sanitised_title}"] } } } } if $service_provider_real == 'systemd' and !$prepare_service_only { exec { "docker-${sanitised_title}-systemd-reload": path => ['/bin/', '/sbin/', '/usr/bin/', '/usr/sbin/'], command => 'systemctl daemon-reload', refreshonly => true, require => [ File[$initscript], File[$startscript], File[$stopscript], ], subscribe => [ File[$initscript], File[$startscript], File[$stopscript], ], } Exec["docker-${sanitised_title}-systemd-reload"] -> Service <| title == "${service_prefix}${sanitised_title}" |> } if $restart_service { if $startscript or $stopscript { [File[$initscript], File[$startscript], File[$stopscript],] ~> Service <| title == "${service_prefix}${sanitised_title}" |> } else { [File[$initscript],] ~> Service <| title == "${service_prefix}${sanitised_title}" |> } } else { if $startscript or $stopscript { [File[$initscript], File[$startscript], File[$stopscript],] -> Service <| title == "${service_prefix}${sanitised_title}" |> } else { [File[$initscript],] -> Service <| title == "${service_prefix}${sanitised_title}" |> } } } } }