<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.jkwmoore.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.jkwmoore.dev/" rel="alternate" type="text/html" /><updated>2026-06-26T23:38:19+00:00</updated><id>https://blog.jkwmoore.dev/feed.xml</id><title type="html">blog.jkwmoore.dev</title><subtitle>A minimal blog I might write some stuff on.
</subtitle><author><name>James Moore</name></author><entry><title type="html">Scripting automatic power down for idle TrueNAS instances</title><link href="https://blog.jkwmoore.dev/truenas-auto-power-down.html" rel="alternate" type="text/html" title="Scripting automatic power down for idle TrueNAS instances" /><published>2025-09-06T00:00:00+00:00</published><updated>2025-09-06T00:00:00+00:00</updated><id>https://blog.jkwmoore.dev/TrueNAS-auto-power-down</id><content type="html" xml:base="https://blog.jkwmoore.dev/truenas-auto-power-down.html"><![CDATA[<p>Chances are that if you run one TrueNAS instance, you run a backup TrueNAS instance that you don’t actually need powered on all the time.</p>

<p>I’ll leave any automatic power up scheduling and replication job configuration to the reader. You can fill your boots with Wake-on-LAN, iDRAC, iLO API prodding etc…</p>

<p>I approached the automatic power down problem as follows:</p>

<ol>
  <li>Write a bash script which will power the system down only when no-one is logged in, no replication tasks are running and not enough online time has passed for scheduled replications to start.</li>
  <li>Add a matching CRON job.</li>
</ol>

<h3 id="script">Script</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>

<span class="nb">echo</span> <span class="s2">"Checking if Admin session exists"</span>
<span class="k">if </span><span class="nb">who</span> | <span class="nb">grep</span> <span class="s1">'^truenas_admin'</span> | <span class="nb">grep</span> <span class="nt">-vq</span> <span class="s1">'192\.168\.0\.22[1-3]$'</span><span class="p">;</span> <span class="k">then</span>
    <span class="c"># truenas_admin is logged in, but not from 192.168.0.221–223 (aka my TrueCommand instance)</span>
    <span class="nb">echo</span> <span class="s2">"truenas_admin currently has a web or SSH session (not from 192.168.0.221–223 aka the TrueCommand instance). Skipping power off."</span>
    <span class="nb">exit </span>0
<span class="k">fi

</span><span class="nb">echo</span> <span class="s2">"Checking if replications are active."</span>

<span class="c"># Check if any replication task is running</span>
<span class="k">if </span>midclt call replication.query | jq <span class="nt">-r</span> <span class="s1">'.[].state.state'</span> | <span class="nb">grep</span> <span class="nt">-q</span> <span class="s1">'^RUNNING$'</span><span class="p">;</span> <span class="k">then</span>
  <span class="c"># Replication running; don't power down</span>
  <span class="nb">echo</span> <span class="s2">"Replications currently active, not shutting down."</span>
  <span class="nb">exit </span>0
<span class="k">fi</span>

<span class="c"># Check uptime in seconds</span>
<span class="nb">echo</span> <span class="s2">"No replications active, checking uptime."</span>
<span class="nv">uptime_seconds</span><span class="o">=</span><span class="si">$(</span><span class="nb">cut</span> <span class="nt">-d</span><span class="s1">' '</span> <span class="nt">-f1</span> /proc/uptime | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'.'</span> <span class="nt">-f1</span><span class="si">)</span>

<span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$uptime_seconds</span><span class="s2">"</span> <span class="nt">-gt</span> 600 <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span>logger <span class="s2">"No replication running and uptime &gt; 600s, powering off system."</span>
  <span class="nb">echo</span>  <span class="s2">"No replication running and uptime &gt; 600s, powering off system."</span>
  systemctl poweroff
<span class="k">else
  </span><span class="nb">echo</span> <span class="s2">"Uptime lower than 600 seconds, skipping power off."</span>
<span class="k">fi</span>
</code></pre></div></div>

<p>Note: If you don’t have a TrueCommand instance you need to ignore, you should edit the logic in the first check block.</p>

<h3 id="setup">Setup</h3>

<ol>
  <li>Add the script above to your local file system e.g. <code class="language-plaintext highlighter-rouge">/root/check_replication_and_poweroff.sh</code></li>
  <li>Add a CRON job.via the <code class="language-plaintext highlighter-rouge">System --&gt; Advanced Settings --&gt; Cron Jobs</code> which points at where you saved the script above. Choose to run as root, set your schedule (e.g. <code class="language-plaintext highlighter-rouge">*/1 * * * *</code>), make sure to hide standard output and enable the job.</li>
  <li>Do some testing to make sure this works how you need it to.</li>
</ol>]]></content><author><name>James Moore</name></author><category term="TrueNAS" /><summary type="html"><![CDATA[Chances are that if you run one TrueNAS instance, you run a backup TrueNAS instance that you don’t actually need powered on all the time.]]></summary></entry><entry><title type="html">Using the Bitwarden SSH Agent in WSL2 Ubuntu</title><link href="https://blog.jkwmoore.dev/bitwarden-desktop-client-as-ssh-agent-with-wsl.html" rel="alternate" type="text/html" title="Using the Bitwarden SSH Agent in WSL2 Ubuntu" /><published>2025-06-01T00:00:00+00:00</published><updated>2025-06-01T00:00:00+00:00</updated><id>https://blog.jkwmoore.dev/Bitwarden-Desktop-client-as-SSH-agent-with-WSL</id><content type="html" xml:base="https://blog.jkwmoore.dev/bitwarden-desktop-client-as-ssh-agent-with-wsl.html"><![CDATA[<p>Using SSH keys in <strong>WSL2</strong> (Ubuntu) when using <strong>Bitwarden</strong> as the SSH agent can be implemented with a few brief (scripted) steps. This post introduces the script designed to link the Bitwarden SSH agent through to your WSL2 environment (for Ubuntu only).</p>

<h3 id="script-functionality">Script Functionality</h3>

<p>The <code class="language-plaintext highlighter-rouge">setup-bw-ssh-agent.sh</code> script automates the necessary configurations:</p>

<ul>
  <li><strong>Installs <code class="language-plaintext highlighter-rouge">npiperelay</code></strong>: A utility that exposes Windows named pipes as standard input/output streams, enabling WSL processes to interact with services (like the OpenSSH agent) running on the Windows host via named pipes.</li>
  <li><strong>Installs <code class="language-plaintext highlighter-rouge">socat</code></strong>: A powerful command-line utility that establishes two bidirectional byte streams and transfers data between them, crucial for bridging the Unix socket to the npiperelay process for SSH agent forwarding.</li>
  <li><strong>Creates <code class="language-plaintext highlighter-rouge">agent-bridge.sh</code></strong>: This script establishes and forwards a Unix socket to your Bitwarden SSH agent on Windows.</li>
  <li><strong>Integrates with <code class="language-plaintext highlighter-rouge">.bashrc</code></strong>: The bridge script is automatically sourced in your <code class="language-plaintext highlighter-rouge">.bashrc</code>, ensuring the SSH agent is available upon shell startup.</li>
</ul>

<h3 id="usage">Usage</h3>

<p>To set up the Bitwarden SSH agent in your WSL2 Ubuntu environment, execute the following in your terminal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://gist.githubusercontent.com/jkwmoore/ce9eab106fe378709f447c843b0090e4/raw/setup-bw-ssh-agent.sh <span class="o">&amp;&amp;</span> bash setup-bw-ssh-agent.sh
</code></pre></div></div>

<script src="https://gist.github.com/jkwmoore/ce9eab106fe378709f447c843b0090e4.js"></script>

<p>After script execution, restart your shell or run <code class="language-plaintext highlighter-rouge">source ~/scripts/agent-bridge.sh</code> to activate the changes.</p>

<hr />

<p>Thanks go to Aaron and the original work discussed here: <a href="https://www.rebelpeon.com/bitwarden-ssh-agent-on-wsl2/">https://www.rebelpeon.com/bitwarden-ssh-agent-on-wsl2/</a></p>]]></content><author><name>James Moore</name></author><category term="Bitwarden SSH" /><summary type="html"><![CDATA[Using SSH keys in WSL2 (Ubuntu) when using Bitwarden as the SSH agent can be implemented with a few brief (scripted) steps. This post introduces the script designed to link the Bitwarden SSH agent through to your WSL2 environment (for Ubuntu only).]]></summary></entry><entry><title type="html">Puppet PDK testing: Show rendered templates while testing locally</title><link href="https://blog.jkwmoore.dev/puppet-pdk-testing-show-rendered-templates-while-testing-locally.html" rel="alternate" type="text/html" title="Puppet PDK testing: Show rendered templates while testing locally" /><published>2025-03-13T00:00:00+00:00</published><updated>2025-03-13T00:00:00+00:00</updated><id>https://blog.jkwmoore.dev/Puppet-PDK-testing-show-rendered-templates-while-testing-locally</id><content type="html" xml:base="https://blog.jkwmoore.dev/puppet-pdk-testing-show-rendered-templates-while-testing-locally.html"><![CDATA[<h3 id="what-was-the-problem">What was the problem?</h3>

<p>I want to see what my templates are rendering while doing local testing. This is pretty helpful especially if you’ve got some hiera data being used
that you want to ensure is being rendered correctly.</p>

<h3 id="how-can-we-show-the-rendered-templates-during-pdk-testing">How can we show the rendered templates during PDK testing?</h3>

<p>Turns out this is pretty easy (so long as you like cheap and kind of nasty) and needs only the most basic changes to your class’s spec file.</p>

<p>e.g. to show a specific template’s content via the CLI output, add the following in your class spec file, <code class="language-plaintext highlighter-rouge">spec/classes/myclass_spec.rb</code>:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'spec_helper'</span>

<span class="n">describe</span> <span class="s1">'mymodule::myclass'</span> <span class="k">do</span>
  <span class="n">on_supported_os</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">os</span><span class="p">,</span> <span class="n">os_facts</span><span class="o">|</span>
    <span class="n">context</span> <span class="s2">"on </span><span class="si">#{</span><span class="n">os</span><span class="si">}</span><span class="s2">"</span> <span class="k">do</span>
      <span class="n">let</span><span class="p">(</span><span class="ss">:facts</span><span class="p">)</span> <span class="p">{</span> <span class="n">os_facts</span> <span class="p">}</span>

      <span class="n">it</span> <span class="p">{</span> <span class="n">is_expected</span><span class="p">.</span><span class="nf">to</span> <span class="n">compile</span> <span class="p">}</span>

      <span class="c1"># To visualise the output of the /the/path/to/your/defined/file/resource.txt template during a PDK run, </span>
      <span class="c1"># set TEMPLATE_DEBUG=true in your shell environment before running ``pdk test unit``.</span>
      <span class="c1"># i.e. ``TEMPLATE_DEBUG=true pdk test unit``</span>

      <span class="n">it</span> <span class="s1">'echoes rendered template content with OS info and markers (if TEMPLATE_DEBUG is set true)'</span> <span class="k">do</span>
        <span class="k">if</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'TEMPLATE_DEBUG'</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'true'</span>
          <span class="n">content</span> <span class="o">=</span> <span class="n">catalogue</span><span class="p">.</span><span class="nf">resource</span><span class="p">(</span><span class="s1">'file'</span><span class="p">,</span> <span class="s1">'/the/path/to/your/defined/file/resource.txt'</span><span class="p">).</span><span class="nf">send</span><span class="p">(</span><span class="ss">:parameters</span><span class="p">)[</span><span class="ss">:content</span><span class="p">]</span>
          <span class="nb">puts</span> <span class="s2">"################################ Generated /the/path/to/your/defined/file/resource.txt content on: </span><span class="si">#{</span><span class="n">os</span><span class="si">}</span><span class="s2">"</span>
          <span class="nb">puts</span> <span class="s2">"</span><span class="si">#{</span><span class="n">content</span><span class="si">}</span><span class="s2">"</span>
          <span class="nb">puts</span> <span class="s2">"################################"</span>
        <span class="k">end</span>
      <span class="k">end</span>

    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This might be pretty noisy if you’re PDK testing against a lot of OSes with multiple templates though <code class="language-plaintext highlighter-rouge">¯\_(ツ)_/¯</code>, so I
figure why not be a bit more clever about it and simply render out all the template content to a subdirectory for inspection?</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'spec_helper'</span>

<span class="n">describe</span> <span class="s1">'mymodule::myclass'</span> <span class="k">do</span>
  <span class="n">on_supported_os</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">os</span><span class="p">,</span> <span class="n">os_facts</span><span class="o">|</span>
    <span class="n">context</span> <span class="s2">"on </span><span class="si">#{</span><span class="n">os</span><span class="si">}</span><span class="s2">"</span> <span class="k">do</span>
      <span class="n">let</span><span class="p">(</span><span class="ss">:facts</span><span class="p">)</span> <span class="p">{</span> <span class="n">os_facts</span> <span class="p">}</span>

      <span class="n">it</span> <span class="p">{</span> <span class="n">is_expected</span><span class="p">.</span><span class="nf">to</span> <span class="n">compile</span> <span class="p">}</span>

      <span class="c1"># To render out all templated content during a PDK run to the ``rendered_templates`` subdirectory,</span>
      <span class="c1"># set TEMPLATE_DEBUG=true in your shell environment before running ``pdk test unit``.</span>
      <span class="c1"># i.e. ``TEMPLATE_DEBUG=true pdk test unit``</span>

      <span class="n">it</span> <span class="s1">'writes generated file content to rendered_templates directory per OS version'</span> <span class="k">do</span>
        <span class="c1"># Check if TEMPLATE_DEBUG is set to 'true'</span>
        <span class="k">if</span> <span class="no">ENV</span><span class="p">[</span><span class="s1">'TEMPLATE_DEBUG'</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'true'</span>
          <span class="n">os_name</span> <span class="o">=</span> <span class="n">facts</span><span class="p">[</span><span class="ss">:os</span><span class="p">][</span><span class="s1">'name'</span><span class="p">].</span><span class="nf">downcase</span>
          <span class="n">os_version</span> <span class="o">=</span> <span class="n">facts</span><span class="p">[</span><span class="ss">:os</span><span class="p">][</span><span class="s1">'release'</span><span class="p">][</span><span class="s1">'full'</span><span class="p">]</span>
          <span class="c1"># Write out the content per OS version</span>
          <span class="n">base_path</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">'rendered_templates'</span><span class="p">,</span> <span class="s2">"</span><span class="si">#{</span><span class="n">os_name</span><span class="si">}</span><span class="s2">-</span><span class="si">#{</span><span class="n">os_version</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
      
          <span class="n">catalogue</span><span class="p">.</span><span class="nf">resources</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">resource</span><span class="o">|</span>
            <span class="c1"># Only process 'File' resources with a valid path.</span>
            <span class="c1"># This way we don't try to dump directories.</span>
            <span class="k">next</span> <span class="k">unless</span> <span class="n">resource</span><span class="p">.</span><span class="nf">type</span> <span class="o">==</span> <span class="s1">'File'</span>

            <span class="n">path</span> <span class="o">=</span> <span class="n">resource</span><span class="p">[</span><span class="ss">:path</span><span class="p">]</span> <span class="o">||</span> <span class="n">resource</span><span class="p">.</span><span class="nf">title</span>
            <span class="n">content</span> <span class="o">=</span> <span class="n">resource</span><span class="p">[</span><span class="ss">:content</span><span class="p">]</span>
            
            <span class="k">if</span> <span class="n">path</span> <span class="o">&amp;&amp;</span> <span class="n">content</span>
              <span class="n">output_path</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="n">base_path</span><span class="p">,</span> <span class="n">path</span><span class="p">)</span>
      
              <span class="c1"># Create the directory if it doesn't exist</span>
              <span class="no">FileUtils</span><span class="p">.</span><span class="nf">mkdir_p</span><span class="p">(</span><span class="no">File</span><span class="p">.</span><span class="nf">dirname</span><span class="p">(</span><span class="n">output_path</span><span class="p">))</span>

              <span class="c1"># Write the content to the file</span>
              <span class="no">File</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">output_path</span><span class="p">,</span> <span class="n">content</span><span class="p">)</span>
              <span class="nb">puts</span> <span class="s2">"✅ Wrote content to: </span><span class="si">#{</span><span class="n">output_path</span><span class="si">}</span><span class="s2">"</span>
            <span class="k">elsif</span> <span class="n">path</span>
              <span class="c1"># Note - if the content is sourced from a file, the resource catalog does not have a content</span>
              <span class="c1"># element for us to render out.</span>
              <span class="nb">puts</span> <span class="s2">"⚠️ No content for: </span><span class="si">#{</span><span class="n">path</span><span class="si">}</span><span class="s2"> (likely sourced)"</span>
            <span class="k">end</span>
          <span class="k">end</span>
        <span class="k">end</span>
      <span class="k">end</span>

    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>

<p>I’ve found this to be a useful method during PDK testing for confirming correct template rendering while working with hiera data (structures).</p>

<p>This has also been particularly useful for visualising the created directories, files and content if the classes you are creating are calling 
other classes, as their outputs will also be rendered!</p>

<p>This is all very helpful for determining and implementing new spec tests for your classes, e.g. for adding tests to automatically validate
template / file contents generated by the subordinate classes.</p>

<p>GLHF!</p>

<p>JKWM.</p>]]></content><author><name>James Moore</name></author><category term="Puppet" /><summary type="html"><![CDATA[What was the problem?]]></summary></entry><entry><title type="html">Puppet PDK testing when using hiera eyaml</title><link href="https://blog.jkwmoore.dev/puppet-eyaml-and-pdk-testing.html" rel="alternate" type="text/html" title="Puppet PDK testing when using hiera eyaml" /><published>2025-03-07T00:00:00+00:00</published><updated>2025-03-07T00:00:00+00:00</updated><id>https://blog.jkwmoore.dev/Puppet-eyaml-and-PDK-testing</id><content type="html" xml:base="https://blog.jkwmoore.dev/puppet-eyaml-and-pdk-testing.html"><![CDATA[<blockquote>
  <p>Warning: I cannot claim to be a Puppet backend expert but I managed to get past PDK testing issues as below.</p>
</blockquote>

<hr />

<h3 id="what-was-the-problem">What was the problem?</h3>

<p>PDK tests are failing because I don’t have the keys necessary to decrypt the secrets while testing locally. 
I also do not want to keep the encryption keys locally, nor anywhere except the PuppetServer.</p>

<p>So what do these errors look like?</p>

<p>Well, I am trying to use the following from my hiera <code class="language-plaintext highlighter-rouge">common.eyaml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">myclass::mysecretstring: ENC[PKCS7,MYENCRYPTEDSECRETSTRINGCONTENT]</span>
</code></pre></div></div>

<p>And PDK gives me:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  1<span class="o">)</span> myclass::mymanifest on redhat-8-x86_64 is expected to compile into a catalogue without dependency cycles
     Failure/Error: it <span class="o">{</span> is_expected.to compile <span class="o">}</span>
       error during compilation: Function lookup<span class="o">()</span> did not find a value <span class="k">for </span>the name <span class="s1">'myclass::mysecretstring'</span> on node my_dev_machine.local
     <span class="c"># ./spec/classes/mymanifest_spec.rb:10:in `block (4 levels) in &lt;top (required)&gt;'</span>
</code></pre></div></div>

<h3 id="how-did-i-sort-this">How did I sort this?</h3>

<p>I added overrides to the spec test used for the class.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code>      <span class="n">before</span> <span class="k">do</span>
        <span class="n">allow</span><span class="p">(</span><span class="no">Puppet</span><span class="o">::</span><span class="no">Pops</span><span class="o">::</span><span class="no">Lookup</span><span class="p">).</span><span class="nf">to</span> <span class="n">receive</span><span class="p">(</span><span class="ss">:lookup</span><span class="p">).</span><span class="nf">and_call_original</span>  <span class="c1"># Allow normal lookups</span>

        <span class="n">allow</span><span class="p">(</span><span class="no">Puppet</span><span class="o">::</span><span class="no">Pops</span><span class="o">::</span><span class="no">Lookup</span><span class="p">).</span><span class="nf">to</span> <span class="n">receive</span><span class="p">(</span><span class="ss">:lookup</span><span class="p">)</span>
          <span class="p">.</span><span class="nf">with</span><span class="p">(</span><span class="s1">'myclass::mysecretstring'</span><span class="p">,</span> <span class="n">anything</span><span class="p">,</span> <span class="n">anything</span><span class="p">,</span> <span class="n">anything</span><span class="p">,</span> <span class="n">anything</span><span class="p">,</span> <span class="n">anything</span><span class="p">)</span>
          <span class="p">.</span><span class="nf">and_return</span><span class="p">(</span><span class="s1">'MOCKED_VALUE_STRING'</span><span class="p">)</span>
      <span class="k">end</span>
</code></pre></div></div>

<h3 id="why-do-i-think-this-works">Why do I think this works</h3>

<p>Aside of the fact I tested it and it appears to do what I wanted, see below:</p>

<h4 id="what-is-puppetpopslookup"><strong>What is <code class="language-plaintext highlighter-rouge">Puppet::Pops::Lookup</code>?</strong></h4>
<ul>
  <li>Puppet uses <strong>Hiera</strong> to retrieve values from a hierarchy of data sources.</li>
  <li>When a class requests a variable via <code class="language-plaintext highlighter-rouge">lookup('some_variable')</code>, Puppet internally calls <code class="language-plaintext highlighter-rouge">Puppet::Pops::Lookup.lookup('some_variable', ...)</code>.</li>
  <li>This function searches for the requested key across the defined Hiera hierarchy.</li>
  <li>https://www.rubydoc.info/gems/puppet/Puppet%2FPops%2FLookup.lookup</li>
</ul>

<p>For example, if the Puppet manifest has:</p>
<div class="language-puppet highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$mysecret</span> <span class="o">=</span> <span class="nf">lookup</span><span class="p">(</span><span class="s1">'myclass::mysecretstring'</span><span class="p">)</span>
</code></pre></div></div>
<p>Puppet will call:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="no">Puppet</span><span class="o">::</span><span class="no">Pops</span><span class="o">::</span><span class="no">Lookup</span><span class="p">.</span><span class="nf">lookup</span><span class="p">(</span><span class="s1">'myclass::mysecretstring'</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>
</code></pre></div></div>
<p>and return the corresponding value from Hiera.</p>

<hr />

<h4 id="stub-only-the-problematic-lookup">Stub Only the Problematic Lookup</h4>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">allow</span><span class="p">(</span><span class="no">Puppet</span><span class="o">::</span><span class="no">Pops</span><span class="o">::</span><span class="no">Lookup</span><span class="p">).</span><span class="nf">to</span> <span class="n">receive</span><span class="p">(</span><span class="ss">:lookup</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">with</span><span class="p">(</span><span class="s1">'myclass::mysecretstring'</span><span class="p">,</span> <span class="n">anything</span><span class="p">,</span> <span class="n">anything</span><span class="p">,</span> <span class="n">anything</span><span class="p">,</span> <span class="n">anything</span><span class="p">,</span> <span class="n">anything</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">and_return</span><span class="p">(</span><span class="s1">'MOCKED_VALUE_STRING'</span><span class="p">)</span>
</code></pre></div></div>
<ul>
  <li>This overrides lookups <strong>only</strong> for <code class="language-plaintext highlighter-rouge">myclass::mysecretstring</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">.with(...)</code> ensures that this stub only activates when the exact expected arguments are passed.</li>
  <li><code class="language-plaintext highlighter-rouge">.and_return('MOCKED_VALUE_STRING')</code> means that <strong>whenever Puppet tries to look up this variable, it will get <code class="language-plaintext highlighter-rouge">'MOCKED_VALUE_STRING'</code> instead of failing.</strong></li>
  <li>Depending on the expected data type for encrypted values, different mocking values may be required.</li>
</ul>

<p><strong>Effect:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">lookup('myclass::mysecretstring')</code> <strong>returns <code class="language-plaintext highlighter-rouge">'MOCKED_VALUE_STRING'</code> instead of failing</strong>.</li>
  <li>All other lookups still use Hiera data lookups as normal/expected.</li>
</ul>

<hr />

<h4 id="allow-normal-lookups">Allow Normal Lookups</h4>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">allow</span><span class="p">(</span><span class="no">Puppet</span><span class="o">::</span><span class="no">Pops</span><span class="o">::</span><span class="no">Lookup</span><span class="p">).</span><span class="nf">to</span> <span class="n">receive</span><span class="p">(</span><span class="ss">:lookup</span><span class="p">).</span><span class="nf">and_call_original</span>
</code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">allow(...).to receive(:lookup)</code> tells RSpec to intercept calls to <code class="language-plaintext highlighter-rouge">Puppet::Pops::Lookup.lookup</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">.and_call_original</code> ensures that all other lookups (except the one we explicitly stub) continue to function normally.</li>
  <li>Without this, RSpec would try to stub all lookups, causing unexpected failures when other values like <code class="language-plaintext highlighter-rouge">myclass::someotherstring'</code> are requested from <code class="language-plaintext highlighter-rouge">common.yaml</code>.</li>
</ul>

<p><strong>Effect:</strong></p>
<ul>
  <li>Any <code class="language-plaintext highlighter-rouge">lookup('some_variable')</code> that is not explicitly stubbed will still perform its normal function.</li>
</ul>

<hr />

<h3 id="tldr-why-i-think-this-works-and-warnings"><strong>tl;dr Why I think this works and warnings</strong></h3>
<ol>
  <li><strong>Ensures normal lookups proceed without modification</strong> → The <code class="language-plaintext highlighter-rouge">.and_call_original</code> call ensures we do not break unrelated lookups.</li>
  <li><strong>Only stubs the problematic keys</strong> → This avoids unnecessary stubbing and allows normal Puppet behavior.</li>
  <li><strong>Prevents lookup errors in tests</strong> → PDK tests no longer fail due to “missing” Hiera data.</li>
  <li>Depending on the expected data type for encrypted values, different mocking values may be required.</li>
  <li>The <code class="language-plaintext highlighter-rouge">Method: Puppet::Pops::Lookup.lookup</code> method documentation explicitly says:
  <code class="language-plaintext highlighter-rouge">This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.</code></li>
  <li>I am not a Ruby expert nor Puppet/PDK backend expert - here be dragons!</li>
</ol>

<p>GLHF!</p>

<p>JKWM.</p>]]></content><author><name>James Moore</name></author><category term="Puppet" /><summary type="html"><![CDATA[Warning: I cannot claim to be a Puppet backend expert but I managed to get past PDK testing issues as below.]]></summary></entry><entry><title type="html">First Post</title><link href="https://blog.jkwmoore.dev/first-post.html" rel="alternate" type="text/html" title="First Post" /><published>2025-02-15T00:00:00+00:00</published><updated>2025-02-15T00:00:00+00:00</updated><id>https://blog.jkwmoore.dev/first-post</id><content type="html" xml:base="https://blog.jkwmoore.dev/first-post.html"><![CDATA[<p>This post is a placeholder for the first post on my blog. I will update it with the actual content soon.</p>

<p>GLHF!</p>

<p>JKWM.</p>]]></content><author><name>James Moore</name></author><summary type="html"><![CDATA[This post is a placeholder for the first post on my blog. I will update it with the actual content soon.]]></summary></entry></feed>