Skip to content

Commit 738a8b5

Browse files
committed
Site update
1 parent 56c0671 commit 738a8b5

15 files changed

Lines changed: 699 additions & 304 deletions

File tree

2025/03/12/perl-basics-for-rex/index.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ <h2 id="further-resources"><a href="#further-resources">Further resources</a></h
440440
</a>
441441
</li>
442442
<li class="next">
443+
<a href="/2025/03/21/minimum-viable-rex/index.html" rel="next">
444+
Newer →
445+
</a>
443446
</li>
444447
</ul>
445448

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta content="width=device-width, initial-scale=1" name="viewport">
6+
<meta content="Minimum Viable Rex" name="title" property="og:title">
7+
<meta content="article" name="type" property="og:type">
8+
<meta content="/theme/images/agile_sysadmin.webp" name="type" property="og:image">
9+
<meta content="https://blog.ferki.it/2025/03/21/minimum-viable-rex/index.html" name="url" property="og:url">
10+
<meta content="Ferenc Erki - agile sysadmin" name="description">
11+
<meta content="Ferenc Erki" name="author">
12+
<meta content="4KzbYclokErOfwrKiBpX8XCu8ckJ9A7zwueL9VAbAYE" name="google-site-verification">
13+
<link href="/theme/images/favicon.svg" rel="icon">
14+
<link href="/theme/css/selenized.css" rel="stylesheet">
15+
<script data-domain="blog.ferki.it" defer src="https://plausible.io/js/script.js"></script>
16+
<title>Minimum Viable Rex</title>
17+
</head>
18+
<body>
19+
<header>
20+
<h1><a href="/">🧑‍💻 agile sysadmin</a></h1>
21+
<p>by Ferenc Erki</p>
22+
</header>
23+
24+
<nav>
25+
<ul>
26+
<li><a href="/pages/about.html">About</a></li>
27+
<li><a href="/">Posts</a></li>
28+
<li><a href="https://ferki.it" target="_blank">Homepage</a></li>
29+
<li><a href="https://cal.com/ferki" target="_blank">Booking</a></li>
30+
</ul>
31+
</nav>
32+
33+
<main>
34+
<article>
35+
36+
<header>
37+
<h1><a href="/2025/03/21/minimum-viable-rex/">Minimum Viable Rex</a></h1>
38+
39+
<aside>
40+
<time datetime="2025-03-21" title="published">
41+
🗓 2025-03-21
42+
</time>
43+
<a href="/tag/rex/">#rex</a>
44+
</aside>
45+
46+
</header>
47+
48+
49+
<section id="section-1">
50+
<p>We consider enabling graceful bootstrapping as one of our main guiding
51+
principles around <a href="https://metacpan.org/pod/Rex">Rex, the friendly automation
52+
framework</a>.</p>
53+
54+
<p>While our <a href="https://www.rexify.org/docs/guides/start_using__r__ex.html">How to get started with
55+
Rex</a> page provides
56+
a good initial set of concepts, I wondered about the minimal set of features
57+
that already proves useful in practice. I find this especially interesting when
58+
using Rex from a cronjob or in a CI/CD pipeline.</p>
59+
60+
<p>Let’s see what I found through this exercise in minimalism.</p>
61+
62+
</section>
63+
<section id="section-2">
64+
<h2 id="basic-architecture"><a href="#basic-architecture">Basic architecture</a></h2>
65+
66+
<p>Rex itself can run where Perl can run, and also can manage hosts both locally
67+
and over SSH. To differentiate it from managed hosts, we call the host where
68+
Rex runs as a control host.</p>
69+
70+
<p>Rex code also stays entirely compatible with Perl code, making it extendable to
71+
support arbitrary use cases, including the use of other protocols when
72+
required.</p>
73+
74+
<p>Just like the GNU Make tool uses Makefile to describe its actions, Rex uses
75+
a Rexfile to describe its configuration, inventory, authentication, and tasks.</p>
76+
77+
<p>A task bundles related management steps together, optionally with task-specific
78+
options, like the default target host for these steps to run on.</p>
79+
80+
<p>It still proves useful to keep those details in mind, though when striving for
81+
minimalism, it also turns out we may either omit most of them to let the
82+
built-in logic try likely settings, or just provide enough hints via
83+
command-line options.</p>
84+
85+
<h2 id="requirements"><a href="#requirements">Requirements</a></h2>
86+
87+
<p>While developing Rex we strive for keeping the requirements low and light.</p>
88+
89+
<p>On the control hosts, Rex requires Perl itself and its dependencies to run. We
90+
generally aim to support Perl versions up to 10 years. We also consider many
91+
dependencies optional, especially external tools to streamline some specific
92+
features like rsync or Augeas.</p>
93+
94+
<p>On the managed hosts, Rex attempts to detect and use Perl and core modules,
95+
though it can fall back to other methods for the most common situations. In
96+
case of remotely managed hosts, Rex also expected SSH running there and a valid
97+
account to log in. For administrative tasks, Rex may use either root directly
98+
or gain extra privileges with sudo.</p>
99+
100+
<p>Since this post focuses on the minimal approach, in the examples I will use
101+
Perl-5.40.1 to run Rex-1.16.0 on Gentoo with Net::OpenSSH backend for SSH
102+
connections, and with SSH configured for key-based authentication for my user.</p>
103+
104+
<h2 id="documentation"><a href="#documentation">Documentation</a></h2>
105+
106+
<p>I find it always nice to keep related documentation at hand.</p>
107+
108+
<p>For Rex, that means starting with the main module documentation via <code>perldoc
109+
Rex</code> and the main binary documentation via <code>man rex</code> – and both of which also
110+
points to the available built-in commands via <code>perldoc Rex::Commands</code>.</p>
111+
112+
<p>On top of that, <code>rex -h</code> also shows the built-in help, which I reduced here for
113+
the minimal set I will cover in this post:</p>
114+
115+
<pre><code class="highlight">$ rex <span class="synSpecial">-h</span>
116+
usage:
117+
rex <span class="synOperator">[</span><span class="synConditional">&lt;</span>options<span class="synConditional">&gt;</span><span class="synOperator">]</span> <span class="synOperator">[</span><span class="synConditional">-H</span> <span class="synConditional">&lt;</span>host<span class="synConditional">&gt;</span><span class="synOperator">]</span> <span class="synOperator">[</span><span class="synConditional">-G</span> <span class="synConditional">&lt;</span>group<span class="synConditional">&gt;</span><span class="synOperator">]</span> <span class="synOperator">&lt;</span>task<span class="synOperator">&gt;</span> <span class="synOperator">[</span><span class="synConditional">&lt;</span>task-options<span class="synConditional">&gt;</span><span class="synOperator">]</span>
118+
rex <span class="synSpecial">-T</span><span class="synOperator">[</span>m<span class="synOperator">|</span>y<span class="synOperator">|</span>v<span class="synOperator">]</span> <span class="synOperator">[</span><span class="synConditional">&lt;</span>string<span class="synConditional">&gt;</span><span class="synOperator">]</span>
119+
120+
<span class="synSpecial">-e</span> Run the given code fragment
121+
<span class="synSpecial">-H</span> Execute a task on the given hosts <span class="synPreProc">(</span><span class="synSpecial">space delimited</span><span class="synPreProc">)</span>
122+
123+
<span class="synSpecial">-u</span> Username <span class="synStatement">for</span> the ssh connection
124+
125+
<span class="synConditional">-d</span> Show debug output
126+
<span class="synSpecial">-qw</span> Quiet mode: only output warnings and errors
127+
128+
<span class="synConditional">-h</span> Display this <span class="synStatement">help</span> message
129+
<span class="synConditional">-v</span> Display <span class="synPreProc">(</span>R<span class="synPreProc">)</span>?ex version
130+
</code></pre>
131+
132+
<p>Let’s check the current Rex version quickly to make sure we run what we think
133+
we run:</p>
134+
135+
<pre><code class="highlight">$ rex <span class="synSpecial">-v</span>
136+
<span class="synPreProc">(</span><span class="synSpecial">R</span><span class="synPreProc">)</span>?ex <span class="synNumber">1</span>.<span class="synNumber">16</span>.<span class="synNumber">0</span>
137+
</code></pre>
138+
139+
<h2 id="command-line-usage"><a href="#command-line-usage">Command line usage</a></h2>
140+
141+
<p>Similarly to Perl’s <code>-e</code> command line switch to run one-liner programs, Rex
142+
provides its own <code>-e</code> to run short code fragments.</p>
143+
144+
<p>Let’s print out the standard <code>Hello, World!</code> message with the help of Perl’s
145+
built-in <code>say()</code> function:</p>
146+
147+
<pre><code class="highlight">$ rex -e &#39;say &quot;Hello, World!&quot;&#39;
148+
[2025-03-20 12:46:52] INFO - Running task eval-line on &lt;local&gt;
149+
Hello, World!
150+
[2025-03-20 12:46:52] INFO - All tasks successful on all hosts
151+
</code></pre>
152+
153+
<p>On top of the output we asked for, Rex also shows the <code>INFO</code> log lines in
154+
green, letting us know that:</p>
155+
156+
<ul>
157+
<li>it runs an eval-line task on the local host (the default target for tasks)</li>
158+
<li>everything finished successfully</li>
159+
</ul>
160+
161+
<p>Some consider that an awful lot of single and double quotes next to each other,
162+
though. Since the argument of <code>rex -e</code> takes any Perl code, let’s use the <code>q()</code>
163+
quote-like operator instead:</p>
164+
165+
<pre><code class="highlight">$ rex -e &#39;say q(Hello, World!)&#39;
166+
[2025-03-20 12:53:41] INFO - Running task eval-line on &lt;local&gt;
167+
Hello, World!
168+
[2025-03-20 12:53:42] INFO - All tasks successful on all hosts
169+
</code></pre>
170+
171+
<p>Same result, though the code may feel easier to work with. For brevity, I will
172+
also exclude some of those logs in the further examples. The help shows a few
173+
options to hide the logs and I tend to use <code>-qw</code> to still keep any warnings and
174+
errors visible.</p>
175+
176+
<pre><code class="highlight">$ rex -qw -e &#39;say q(Hello, World!)&#39;
177+
Hello, World!
178+
</code></pre>
179+
180+
<h2 id="run-commands-and-manage-files"><a href="#run-commands-and-manage-files">Run commands and manage files</a></h2>
181+
182+
<p>While printing messages sounds interesting, we consider the core capabilities
183+
of Rex as running commands and managing files.</p>
184+
185+
<p>For example, the <code>hostname</code> command on Linux prints the hostname, and Rex
186+
provides the <code>run()</code> command to, well, run commands:</p>
187+
188+
<pre><code class="highlight">$ rex -qw -e &#39;say run q(hostname)&#39;
189+
mymachine
190+
</code></pre>
191+
192+
<p>The <code>file()</code> command of Rex helps manage files:</p>
193+
194+
<pre><code class="highlight">$ rex -qw -e &#39;file &quot;/tmp/hello&quot;, content =&gt; q(Hello, World!)&#39;
195+
</code></pre>
196+
197+
<p>Hmm, no output, though no signs of any warnings or errors. I’d like to check if
198+
the <code>/tmp/hello</code> file does indeed contain <code>Hello, World!</code> in it. On Linux,
199+
I would use the <code>cat</code> command for that, and Rex provides the same functionality
200+
under the same name:</p>
201+
202+
<pre><code class="highlight">$ rex -qw -e &#39;say cat q(/tmp/hello)&#39;
203+
Hello, World!
204+
</code></pre>
205+
206+
<p>Why does Rex duplicate the <code>cat</code> command, when we would also call <code>run q(cat
207+
/tmp/hello)</code>? Because we can only expect <code>cat</code>, the Linux command, to exist
208+
only on Linux – while Rex may have to manage other operating systems too, where
209+
the system does not have <code>cat</code>. On those systems Rex abstracts away the
210+
difference, and uses other methods to achieve the same results.</p>
211+
212+
<h2 id="manage-remote-hosts"><a href="#manage-remote-hosts">Manage remote hosts</a></h2>
213+
214+
<p>While executing ad-hoc tasks on the local machine sounds useful, many use cases
215+
involve running tasks remotely. Let’s pass a host to connect to via SSH with
216+
the <code>-H</code> option:</p>
217+
218+
<pre><code class="highlight">$ rex -H myserver -e &#39;say run q(hostname)&#39;
219+
[2025-03-20 19:49:16] INFO - Running task eval-line on myserver
220+
myserver
221+
[2025-03-20 19:49:19] INFO - All tasks successful on all hosts
222+
</code></pre>
223+
224+
<p>Note that the <code>INFO</code> log output now shows running the task on <code>myserver</code>, and
225+
the output of the <code>hostname</code> command changed accordingly – it did run on
226+
<code>myserver</code> after all.</p>
227+
228+
<p>Also note that Rex discovered the authentication details it may use to log in
229+
to <code>myserver</code>. It can parse (a subset of<a class="footnote" href="#fn:ssh_config" id="fnref:ssh_config">1</a>) the SSH configuration in
230+
<code>~/.ssh/config</code>, and can also fall back to use the current local username to
231+
log in. In case we need to, we can enforce a specific user, like <code>myuser</code>, with
232+
<code>-u</code>:</p>
233+
234+
<pre><code class="highlight">$ rex -qw -H myserver -u myuser -e &#39;say run q(hostname)&#39;
235+
myserver
236+
</code></pre>
237+
238+
<p>Since I use the Net::OpenSSH backend, it can pick up and use my SSH keys too.
239+
In case it needs to fall back to password-based authentication, or the key
240+
needs a passphrase, it will prompt for it.</p>
241+
242+
<h2 id="debug-output"><a href="#debug-output">Debug output</a></h2>
243+
244+
<p>These one-liner tasks may prove difficult to debug, and the built-in debug
245+
output via <code>-d</code> often helps a lot to understand what happens from the
246+
perspective of Rex.</p>
247+
248+
<pre><code class="highlight">$ rex -d -e &#39;say run q(hostname)&#39;
249+
</code></pre>
250+
251+
<p>I omit the full output here as it can get verbose, though it will contain
252+
information about the following:</p>
253+
254+
<ul>
255+
<li>the running Rex version</li>
256+
<li>list of command line parameters</li>
257+
<li>internal details of the whole process, including the picked up configuration,
258+
and dependencies</li>
259+
<li>authentication information used during execution</li>
260+
<li>task execution details</li>
261+
<li>list of shutdown steps</li>
262+
</ul>
263+
264+
<h2 id="summary"><a href="#summary">Summary</a></h2>
265+
266+
<p>For a frugal introduction to Rex, one may find the <code>-e</code> command line option
267+
already enough to execute ad-hoc tasks locally, and perhaps <code>-H</code> to execute the
268+
same remotely, along with how to find further information in the documentation.</p>
269+
270+
<p>Adding a few more options to the repertoire, like overriding authentication
271+
details, and controlling the verbosity of the output, makes the first
272+
experience with Rex viable for the most common practical uses.</p>
273+
274+
<div class="footnotes">
275+
<hr>
276+
<ol>
277+
278+
<li id="fn:ssh_config"><p>I work on fully transparent support of arbitrary SSH
279+
configuration<a class="reversefootnote" href="#fnref:ssh_config"> ↩</a></p></li>
280+
281+
</ol>
282+
</div>
283+
284+
</section>
285+
286+
</article>
287+
288+
<ul class="pager">
289+
<li class="prev">
290+
<a href="/2025/03/12/perl-basics-for-rex/index.html" rel="prev">
291+
← Older
292+
</a>
293+
</li>
294+
<li class="next">
295+
</li>
296+
</ul>
297+
298+
299+
</main>
300+
301+
<footer>
302+
303+
<ul>
304+
<li><a href="https://blog.ferki.it/index.rss" target="_blank">RSS</a></li>
305+
<li><a href="https://blog.ferki.it/index.atom" target="_blank">Atom</a></li>
306+
<li><a href="https://github.com/ferki" rel="me" target="_blank">GitHub</a></li>
307+
<li><a href="https://profile.codersrank.io/user/ferki" rel="me" target="_blank">CodersRank</a></li>
308+
<li><a href="https://www.linkedin.com/in/ferki" rel="me" target="_blank">LinkedIn</a></li>
309+
<li><a href="https://fosstodon.org/@ferki" rel="me" target="_blank">Mastodon</a></li>
310+
<li><a href="mailto:ferki@ferki.it">Email</a></li>
311+
<li><a href="/pages/impressum.html">Impressum</a></li>
312+
<li><a href="/pages/privacy_policy.html">Privacy policy</a></li>
313+
</ul>
314+
<ul>
315+
<li>© 2023–2025 Ferenc Erki</li>
316+
</ul>
317+
318+
</footer>
319+
</body>
320+
</html>

0 commit comments

Comments
 (0)