Skip to content

Commit 442424c

Browse files
committed
WIP/RFC: Add run_agent task
1 parent 10f437e commit 442424c

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-0
lines changed

tasks/run_agent.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"description": "Runs the puppet agent",
3+
"supports_noop": true,
4+
"files": ["ruby_task_helper/files/task_helper.rb"],
5+
"input_method": "stdin",
6+
"parameters": {
7+
"puppet_bin": {
8+
"description": "The full path to the puppet binary. Defaults to `'/opt/puppetlabs/bin/puppet'`.",
9+
"type": "Optional[Stdlib::Absolutepath]"
10+
},
11+
"wait_time": {
12+
"description": "How many seconds should the task wait for an existing puppet run to finish. Defaults to `300` (5 minutes).",
13+
"type": "Optional[Integer[0]]"
14+
}
15+
}
16+
}

tasks/run_agent.rb

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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

Comments
 (0)