|
| 1 | +#!/opt/puppetlabs/puppet/bin/ruby |
| 2 | + |
| 3 | +require 'open3' |
| 4 | +require 'etc' |
| 5 | +require 'json' |
| 6 | +require 'yaml' |
| 7 | +require_relative '../../ruby_task_helper/files/task_helper.rb' |
| 8 | + |
| 9 | +class RunAgentTask < TaskHelper |
| 10 | + def task(puppet_bin: '/opt/puppetlabs/bin/puppet', wait_time: 300, _noop: false, **_kwargs) |
| 11 | + @puppet_bin = puppet_bin |
| 12 | + @wait_time = wait_time |
| 13 | + @noop = _noop |
| 14 | + |
| 15 | + check_running_as_root |
| 16 | + check_agent_not_disabled |
| 17 | + wait_for_puppet_lockfile |
| 18 | + run_puppet |
| 19 | + end |
| 20 | + |
| 21 | + def wait_for_puppet_lockfile |
| 22 | + waited = 0 |
| 23 | + while agent_locked? && waited < @wait_time |
| 24 | + sleep 1 |
| 25 | + waited += 1 |
| 26 | + end |
| 27 | + |
| 28 | + if agent_locked? |
| 29 | + raise TaskHelper::Error.new( |
| 30 | + 'Lockfile still exists after waiting', |
| 31 | + 'theforeman-puppet/lockfile_timeout_expired' |
| 32 | + ) |
| 33 | + end |
| 34 | + |
| 35 | + waited |
| 36 | + end |
| 37 | + |
| 38 | + def check_agent_not_disabled |
| 39 | + if agent_disabled? |
| 40 | + raise TaskHelper::Error.new( |
| 41 | + 'Agent is disabled on this node', |
| 42 | + 'theforeman-puppet/agent_disabled', |
| 43 | + disabled_reason |
| 44 | + ) |
| 45 | + end |
| 46 | + end |
| 47 | + |
| 48 | + def agent_locked? |
| 49 | + File.exist?(agent_catalog_run_lockfile) |
| 50 | + end |
| 51 | + |
| 52 | + def agent_disabled? |
| 53 | + File.exist?(agent_disabled_lockfile) |
| 54 | + end |
| 55 | + |
| 56 | + def disabled_reason |
| 57 | + file = File.open agent_disabled_lockfile |
| 58 | + data = JSON.load file |
| 59 | + file.close |
| 60 | + data |
| 61 | + end |
| 62 | + |
| 63 | + def agent_disabled_lockfile |
| 64 | + return @agent_disabled_lockfile unless @agent_disabled_lockfile.nil? |
| 65 | + |
| 66 | + @agent_disabled_lockfile = agent_config('agent_disabled_lockfile') |
| 67 | + @agent_disabled_lockfile |
| 68 | + end |
| 69 | + |
| 70 | + def agent_catalog_run_lockfile |
| 71 | + return @lockfile unless @lockfile.nil? |
| 72 | + |
| 73 | + @lockfile = agent_config('agent_catalog_run_lockfile') |
| 74 | + @lockfile |
| 75 | + end |
| 76 | + |
| 77 | + def lastrunfile |
| 78 | + return @lastrunfile unless @lastrunfile.nil? |
| 79 | + |
| 80 | + @lastrunfile = agent_config('lastrunfile') |
| 81 | + @lastrunfile |
| 82 | + end |
| 83 | + |
| 84 | + def agent_config(setting) |
| 85 | + cmd = [@puppet_bin, 'config', 'print', setting] |
| 86 | + stdout, stderr, status = Open3.capture3(*cmd) |
| 87 | + |
| 88 | + unless status.exitstatus.zero? |
| 89 | + raise TaskHelper::Error.new( |
| 90 | + "Couldn't determine #{setting} configuration", |
| 91 | + 'theforeman-puppet/config_unknown', |
| 92 | + stderr: stderr |
| 93 | + ) |
| 94 | + end |
| 95 | + stdout.strip |
| 96 | + end |
| 97 | + |
| 98 | + def check_running_as_root |
| 99 | + unless Process.euid.zero? |
| 100 | + raise TaskHelper::Error.new( |
| 101 | + 'Puppet agent needs to run as root', |
| 102 | + 'theforeman-puppet/bad_euid', |
| 103 | + euid: Process.euid |
| 104 | + ) |
| 105 | + end |
| 106 | + end |
| 107 | + |
| 108 | + def run_puppet |
| 109 | + cmd = [@puppet_bin, 'agent', '-t', '--detailed-exitcodes'] |
| 110 | + cmd << if @noop |
| 111 | + '--noop' |
| 112 | + else |
| 113 | + '--no-noop' |
| 114 | + end |
| 115 | + |
| 116 | + stdout, stderr, status = Open3.capture3(*cmd) |
| 117 | + |
| 118 | + case status.exitstatus |
| 119 | + when 0 |
| 120 | + { |
| 121 | + result: 'The run succeeded with no changes or failures; the system was already in the desired state (or --noop was used).', |
| 122 | + stdout: stdout, |
| 123 | + stderr: stderr, |
| 124 | + detailed_exitcode: 0, |
| 125 | + last_run_summary: last_run_summary |
| 126 | + } |
| 127 | + when 1 |
| 128 | + raise TaskHelper::Error.new( |
| 129 | + 'Puppet run failed', |
| 130 | + 'theforeman-puppet/run_failed', |
| 131 | + stderr: stderr, |
| 132 | + stdout: stdout, |
| 133 | + detailed_exitcode: status.exitstatus |
| 134 | + ) |
| 135 | + when 2 |
| 136 | + { |
| 137 | + result: 'The run succeeded, and some resources were changed.', |
| 138 | + stdout: stdout, |
| 139 | + stderr: stderr, |
| 140 | + detailed_exitcode: 2, |
| 141 | + last_run_summary: last_run_summary |
| 142 | + } |
| 143 | + when 4, 6 |
| 144 | + raise TaskHelper::Error.new( |
| 145 | + 'Puppet run succeeded, but some resources failed', |
| 146 | + 'theforeman-puppet/failed_resources', |
| 147 | + stderr: stderr, |
| 148 | + stdout: stdout, |
| 149 | + detailed_exitcode: status.exitstatus, |
| 150 | + last_run_summary: last_run_summary |
| 151 | + ) |
| 152 | + end |
| 153 | + end |
| 154 | + |
| 155 | + def last_run_summary |
| 156 | + YAML.load_file(lastrunfile) |
| 157 | + end |
| 158 | +end |
| 159 | + |
| 160 | +RunAgentTask.run if $PROGRAM_NAME == __FILE__ |
0 commit comments