<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Planet Puppet</title>
  <link rel="self" href=""/>
  <link href=""/>
  <id></id>
  <updated></updated>
  <generator uri="http://feedreader.github.io/">Pluto 1.6.3 on Ruby 2.7.4 (2021-07-07) [x86_64-linux-gnu]</generator>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Announcing Pabawi, a web frontend for classic infrastructures</title>
    <link href="https://example42.com/blog/2026/01/16/announcing-pabawi-a-web-frontend-to-classic-infrastructures/"/>
    <id>https://example42.com/blog/2026/01/16/announcing-pabawi-a-web-frontend-to-classic-infrastructures</id>
    <updated>2026-01-16T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    Is there still space in AI and Cloud native days for a new tool to manage classic infrastructures based on physical or virtual servers?

If you handle good old servers, and use Puppet, Bolt or Ansible to configure and manage them, then the answer might be yes, and we may have a nice tool for you.

Pabawi is a new, modern, sleek web frontend to inventory your systems, check their configuration management status, and run actions on them.

Needless to sat that it’s also Open Source.

Version 0.4.0, just released after a few weeks of AI based coding, has integrations with Bolt, Hiera, PuppetDB and PuppetServer.

Current version is expected to be executed by a Puppet developer or user on her/his localhost, featuring:


  Multi-Source Inventory: View and manage nodes from Bolt inventory and PuppetDB
  Command Execution: Run ad-hoc commands on remote nodes with whitelist security
  Task Execution: Execute Bolt tasks with automatic parameter support
  Puppet Integration: Trigger Puppet agent runs with full configuration control
  Package Management: Install and manage packages across your infrastructure
  Execution History: Track all operations with detailed results and re-execution capability
  Dynamic Inventory: Automatically discover nodes from PuppetDB
  Node Facts: View comprehensive system information from Puppet agents
  Puppet Reports: Browse detailed Puppet run reports with metrics and resource changes
  Catalog Inspection: Examine compiled Puppet catalogs and resource relationships
  Event Tracking: Monitor individual resource changes and failures over time
  PQL Queries: Filter nodes using PuppetDB Query Language
  Hiera Data Browser: Explore hierarchical configuration data and key usage analysis
  Puppet code analysis: Analyses Puppet code base, reporting class usage, lint issues, outdated modules in Puppetfile


More robust users management and RBAC, and Ansible and other tools integrations are planned for future versions.

Check the git repository for installation instructions and details.

Screenshots

Here are some screenshots, from a test setup with few nodes. The interface adapts well and is still fast on way larger setups (currently tested with infrastructures up to 350 nodes).

Nodes Inventory



Node inventory with multi-source support, blazing fast search and filtering options

Task Execution



Bolt task execution interface with automatic parameters discovery

Executions Tracking



Execution history with filtering and detailed execution results with re-run capabilities

Node Details



Node detail page with access to facts, reports, events, managed resources and other useful info

Puppet reports



Puppet reports view, with run times and number of affected resources

Free installation service (time limited)

Pabawi is easy to integrate in your existing Puppet / Bolt infrastructure and has contextual setup instructions for each integration, but if you want help in trying it in your infrastructure, example42 offers a launch special, time limited, free installation service, no strings attached.

In a shared screen call we can setup Pabawi together, either using its container image or via npm.

We want to test and validate it in different conditions, collecting users’ feedback and suggestions.

Just contact me on LinkedIN for planning a call: half an hour should be enough to set it up and see how it works.

Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">R.I.Pienaar - www.devco.net: Choria Hierarchical Data</title>
    <link href="https://www.devco.net/posts/2025/11/30/choria_hierarchical_data/"/>
    <id>https://www.devco.net/posts/2025/11/30/choria_hierarchical_data/</id>
    <updated>2025-11-24T23:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;As most are aware, I created the widely used Hiera system in Puppet. I &lt;a href=&quot;https://www.devco.net/archives/2011/06/05/hiera_a_pluggable_hierarchical_data_store.php&quot;&gt;introduced it in 2011&lt;/a&gt;, and it has since become essentially the only way to use Puppet in any meaningful fashion. Given its widespread adoption, I donated the code to Puppet, and it became integrated with Puppet core.&lt;/p&gt;
&lt;p&gt;Unfortunately, during this integration we lost some key values—the command line and the ability to use it in scripts and elsewhere.&lt;/p&gt;
&lt;p&gt;Meanwhile, our world is changing, and we are ever more focussing on small, single purpose compute. I intend to create a new kind of Configuration Management system that is focussed on the small, single, purpose needs. Filling the gap that has always existed - how to manage our applications rather than systems, an area Puppet has always been weak at.&lt;/p&gt;
&lt;p&gt;There is then still the need for hierarchical data and given that I have the flexibility to start completely fresh I am at best taking some inspiration from Hiera.&lt;/p&gt;
&lt;p&gt;So today I&amp;rsquo;ll introduce a new tool called Choria Hierarchical Data - current code name &lt;code&gt;tinyhiera&lt;/code&gt; but that might change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt; This post was first posted &lt;a href=&quot;https://choria.io/blog/post/2025/11/25/hierarchical_data/&quot;&gt;on the Choria blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Read on for the details&lt;/p&gt;
&lt;p&gt;The big difference here is that this new system supports just one data file, you express a data structure that is the complete outcome of the query and then configure in the same file how to override and extend that data file.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at it, here I&amp;rsquo;ll show the different sections, in reality it&amp;rsquo;s just one file.&lt;/p&gt;
&lt;p&gt;This is the data we wish to manage:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;nt&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;httpd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;listen_port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lets define the hierarchy for overriding this data; this should be familiar to Puppet users:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;hierarchy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;deep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# or first&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;env:{{ lookup(&amp;#39;env&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;role:{{ lookup(&amp;#39;role&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;host:{{ lookup(&amp;#39;hostname&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And finally, we show the overrides, here we just specify how the data will be extended:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;nt&quot;&gt;overrides&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;env:prod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;WARN&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;role:web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;nginx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;listen_port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;tls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;host:web01&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;TRACE&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;listen_port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can now call our CLI tool to resolve this entire structure.&lt;/p&gt;
&lt;p&gt;We query the data setting the &lt;code&gt;role&lt;/code&gt; fact to &lt;code&gt;web&lt;/code&gt;, the data from the &lt;code&gt;role:web&lt;/code&gt; section is merged into the data from the &lt;code&gt;data&lt;/code&gt; section:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ tinyhiera parse data.yaml role=web
{
  &amp;#34;log_level&amp;#34;: &amp;#34;INFO&amp;#34;,
  &amp;#34;packages&amp;#34;: [
    &amp;#34;ca-certificates&amp;#34;,
    &amp;#34;nginx&amp;#34;
  ],
  &amp;#34;web&amp;#34;: {
    &amp;#34;listen_port&amp;#34;: 443,
    &amp;#34;tls&amp;#34;: true
  }
}
$ tinyhiera parse data.yaml role=web --query web.listen_port
443
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;features-and-differences&quot;&gt;Features and Differences&lt;/h2&gt;
&lt;p&gt;This is the basic feature, similar to what you might have used in Puppet except differing in a few areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s all one file and one data structure, it&amp;rsquo;s not key-lookup orientated. The entire outcome is rendered in one query&lt;/li&gt;
&lt;li&gt;Expressions used in overrides and data lookups are a full query language called &lt;a href=&quot;https://expr-lang.org&quot;&gt;expr-lang&lt;/a&gt;, this means we can call many functions, generate data, derive data and easily extend this over time&lt;/li&gt;
&lt;li&gt;The data is typed, if your facts have rich data the results can also be rich data, if &lt;code&gt;value&lt;/code&gt; is a integer, array or map the result will be the same type &lt;code&gt;x=&amp;quot;{{ lookup(&amp;quot;value&amp;quot;) }}&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The CLI has built in system facts, can take facts on the CLI, read them from JSON and YAML files, parse your environment variables as facts or all at the same time&lt;/li&gt;
&lt;li&gt;The CLI can emit data as environment variables for use in scripts&lt;/li&gt;
&lt;li&gt;There is a Golang library to use it in your own code&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;environment-variable-output&quot;&gt;Environment Variable Output&lt;/h2&gt;
&lt;p&gt;We want this to be usable in scripts, the easiest might be dig into the data and just get a single value:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;PORT=$(tinyhiera parse data.yaml role=web --query web.listen_port)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But this involves many calls to the command, we can instead emit all the data as variables.&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ tinyhiera parse data.yaml role=web --env
HIERA_LOG_LEVEL=INFO
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can see the data is emitted as environment variables that you can just source into your shell script, obviously in this use case it benefits to have flat data.&lt;/p&gt;
&lt;h2 id=&quot;built-in-facts&quot;&gt;Built-in facts&lt;/h2&gt;
&lt;p&gt;We have many ways to get facts into it such as files, environment variables, JSON and YAML files. The CLI also includes it&amp;rsquo;s own mini fact source called &lt;code&gt;system&lt;/code&gt; which will return facts about the system it is running on.&lt;/p&gt;
&lt;p&gt;To view the facts fully resolved, we can run the following, I am removing much of the details here but you can see this offers enough to make most required decisions:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ tinyhiera facts --system-facts
{
  &amp;#34;host&amp;#34;: {
    &amp;#34;info&amp;#34;: {
      &amp;#34;hostname&amp;#34;: &amp;#34;grime.local&amp;#34;,
      &amp;#34;uptime&amp;#34;: 1843949,
      &amp;#34;bootTime&amp;#34;: 1762258234,
      &amp;#34;procs&amp;#34;: 711,
      &amp;#34;os&amp;#34;: &amp;#34;darwin&amp;#34;,
      &amp;#34;platform&amp;#34;: &amp;#34;darwin&amp;#34;,
      &amp;#34;platformFamily&amp;#34;: &amp;#34;Standalone Workstation&amp;#34;,
      &amp;#34;platformVersion&amp;#34;: &amp;#34;26.1&amp;#34;,
      &amp;#34;kernelVersion&amp;#34;: &amp;#34;25.1.0&amp;#34;,
      &amp;#34;kernelArch&amp;#34;: &amp;#34;arm64&amp;#34;
    }
  },
  &amp;#34;memory&amp;#34;: {
    &amp;#34;swap&amp;#34;: { },
    &amp;#34;virtual&amp;#34;: { }
  },
  &amp;#34;network&amp;#34;: {
    &amp;#34;interfaces&amp;#34;: [ ]
  },
  &amp;#34;partition&amp;#34;: {
    &amp;#34;partitions&amp;#34;: [ ],
    &amp;#34;usage&amp;#34;: [ ]
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;long-term-view&quot;&gt;Long-term view&lt;/h2&gt;
&lt;h3 id=&quot;autonomous-agents&quot;&gt;Autonomous Agents&lt;/h3&gt;
&lt;p&gt;The goal here is to incorporate this into various other places, first we pull it into a &lt;a href=&quot;https://choria.io/docs/autoagents/&quot;&gt;Choria Autonomous Agents&lt;/a&gt;, these agents own the full lifecycle of an application:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deploy dependencies&lt;/li&gt;
&lt;li&gt;Configure the application&lt;/li&gt;
&lt;li&gt;Run the application&lt;/li&gt;
&lt;li&gt;Monitor the application&lt;/li&gt;
&lt;li&gt;Restart the application on failure&lt;/li&gt;
&lt;li&gt;Orchestrate rolling upgrades&lt;/li&gt;
&lt;li&gt;Present APIs for interacting with the management layer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Agents can fetch data from a Key-Value store, but it&amp;rsquo;s kind of all the same data. With Hiera integrated into the KV feature, we get:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;- &lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;watch_tag&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;kv&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;10s&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;success_transition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;regional_update&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;state_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;RUN, RESTART]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;NATS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;hiera_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here the autonomous agent will check KV every 10 seconds for data, fully resolve it using Hiera and save the resulting data into the Autonomous Agent internal data store. If the KV data chanfes or the referenced facts change to the extent that the data change, the machine will update the stored data and fire the &lt;code&gt;regional_update&lt;/code&gt; event.&lt;/p&gt;
&lt;p&gt;This way we can create role-orientated specific data all in the same Key-Value key.&lt;/p&gt;
&lt;h3 id=&quot;configuration-management&quot;&gt;Configuration Management&lt;/h3&gt;
&lt;p&gt;I want to create a new CM system that takes the model we are used to in Puppet but brings it to scripts and reusable APIs.&lt;/p&gt;
&lt;p&gt;The script use case would essentially be standalone commands:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ marionette package ensure zsh --version 1.2.3
$ marionette service ensure ssh-server --enable --running
$ marionette service info httpd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This should be familiar to Puppet users, we&amp;rsquo;re basically pulling the resources and RAL into standalone commands.  The commands will be fully idempotent like Puppet and support multiple Operating Systems.&lt;/p&gt;
&lt;p&gt;For integration with other languages, you can also deal with JSON-in-JSON-out style:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;# Can be driven by JSON in and returns JSON out instead
$ echo &amp;#39;{&amp;#34;name&amp;#34;:&amp;#34;zsh&amp;#34;, &amp;#34;version&amp;#34;:&amp;#34;1.2.3&amp;#34;}&amp;#39;|marionette package apply
{
....
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I do not want to create something huge like Puppet, but just basically enough that can enable the package-config-service trio pattern, ultimately a manifest would look like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;l&quot;&gt;cat &amp;lt;&amp;lt;EOF&amp;gt;manifest.yaml&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;nt&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;myapp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;nt&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;myapp&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;hierarchy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;deep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;platform:{{ lookup(&amp;#39;host.info.platform&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;hostname:{{ lookup(&amp;#39;host.info.hostname&amp;#39;) }}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;overrides&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;platform:darwin&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;            &lt;/span&gt;- &lt;span class=&quot;nt&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;                &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;MyApp-1.2.3.dmg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;&lt;/span&gt;&lt;span class=&quot;l&quot;&gt;$ marionette apply manifest.json --system-facts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pull this into Autonomous Agents and you have self-contained, self-managing applications similar to Kubernetes Operators but anywhere.&lt;/p&gt;
&lt;p&gt;Use this in scripts, simple setups, during container creation or orchestration, or even in CI pipelines.&lt;/p&gt;
&lt;p&gt;We should even be able to compile this manifest, or a full autonomous agent, into a single, static, binary that you can just run as &lt;code&gt;./setup&lt;/code&gt; or &lt;code&gt;./manage&lt;/code&gt; and the entire lifecycle is managed with zero dependencies.&lt;/p&gt;
&lt;h2 id=&quot;availability-and-status&quot;&gt;Availability and Status&lt;/h2&gt;
&lt;p&gt;You can get the command on &lt;a href=&quot;https://github.com/choria-io/tinyhiera&quot;&gt;GitHub choria-io/tinyhiera&lt;/a&gt;, we are pretty far along but expect some more breaking changes including a potential name change.&lt;/p&gt;
  </content>
    <author>
      <name>R.I.Pienaar - www.devco.net</name>
      <uri>http://www.devco.net/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: The new gem server in town</title>
    <link href="https://overlookinfratech.com/2025/10/06/the-new-gem-server-in-town/"/>
    <id>https://overlookinfratech.com/2025/10/06/the-new-gem-server-in-town</id>
    <updated>2025-10-06T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;If you’re in the Ruby world you may have been following the RubyGems shenanigans
of the last few weeks. They suffered a poorly communicated corporate led rug-pull,
not all that dissimilar to &lt;a href=&quot;/2024/11/08/sequestered-source/&quot;&gt;the games Perforce has played&lt;/a&gt;
over the last year or so. Amusingly, they also claimed it was for security
purposes without providing any actual rationale supporting that claim.&lt;/p&gt;

&lt;p&gt;As you can guess, this caused a lot of consternation, especially in projects like
&lt;a href=&quot;https://voxpupuli.org/openvox&quot;&gt;OpenVox&lt;/a&gt; based on Ruby and in companies like
Overlook InfraTech whose livelihood depends on Ruby.&lt;/p&gt;

&lt;blockquote class=&quot;quote&quot;&gt;
  &lt;p&gt;You may want to read that last bit again. This heavily impacts companies whose
&lt;strong&gt;&lt;em&gt;livelihoods depend on Ruby.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ah. Now that’s the root of the problem, isn’t it? There aren’t enough companies
financially supporting the projects they’ve built their own businesses on top of.
For example, Puppetserver would not be possible without JRuby and yet
Puppet/Perforce didn’t bother helping the project through &lt;a href=&quot;https://blog.jruby.org/2024/07/independence-day&quot;&gt;its funding
crisis&lt;/a&gt;
until &lt;a href=&quot;/2025/02/28/paying-their-fair-share/&quot;&gt;they were publicly named and shamed&lt;/a&gt;
and it became a PR problem for them.&lt;/p&gt;

&lt;p&gt;We don’t believe in doing business that way and we now have an opportunity to
live up to our ideals. The original RubyGems team has &lt;a href=&quot;https://www.fullstackruby.dev/ruby-infrastructure/2025/10/06/theres-a-new-gem-server-in-town/&quot;&gt;announced a community led
and governed gem server&lt;/a&gt;.
This means that we now have the opportunity to help build a new ecosystem that’s
not supported and dominated by a single company out of touch with its community.&lt;/p&gt;

&lt;blockquote class=&quot;mastodon-embed&quot; data-embed-url=&quot;https://ruby.social/@getajobmike/115254758995805006/embed&quot; style=&quot;background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;&quot;&gt; &lt;a href=&quot;https://ruby.social/@getajobmike/115254758995805006&quot; target=&quot;_blank&quot; style=&quot;align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Oxygen, Ubuntu, Cantarell, &#39;Fira Sans&#39;, &#39;Droid Sans&#39;, &#39;Helvetica Neue&#39;, Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 79 75&quot;&gt;&lt;path d=&quot;M63 45.3v-20c0-4.1-1-7.3-3.2-9.7-2.1-2.4-5-3.7-8.5-3.7-4.1 0-7.2 1.6-9.3 4.7l-2 3.3-2-3.3c-2-3.1-5.1-4.7-9.2-4.7-3.5 0-6.4 1.3-8.6 3.7-2.1 2.4-3.1 5.6-3.1 9.7v20h8V25.9c0-4.1 1.7-6.2 5.2-6.2 3.8 0 5.8 2.5 5.8 7.4V37.7H44V27.1c0-4.9 1.9-7.4 5.8-7.4 3.5 0 5.2 2.1 5.2 6.2V45.3h8ZM74.7 16.6c.6 6 .1 15.7.1 17.3 0 .5-.1 4.8-.1 5.3-.7 11.5-8 16-15.6 17.5-.1 0-.2 0-.3 0-4.9 1-10 1.2-14.9 1.4-1.2 0-2.4 0-3.6 0-4.8 0-9.7-.6-14.4-1.7-.1 0-.1 0-.1 0s-.1 0-.1 0 0 .1 0 .1 0 0 0 0c.1 1.6.4 3.1 1 4.5.6 1.7 2.9 5.7 11.4 5.7 5 0 9.9-.6 14.8-1.7 0 0 0 0 0 0 .1 0 .1 0 .1 0 0 .1 0 .1 0 .1.1 0 .1 0 .1.1v5.6s0 .1-.1.1c0 0 0 0 0 .1-1.6 1.1-3.7 1.7-5.6 2.3-.8.3-1.6.5-2.4.7-7.5 1.7-15.4 1.3-22.7-1.2-6.8-2.4-13.8-8.2-15.5-15.2-.9-3.8-1.6-7.6-1.9-11.5-.6-5.8-.6-11.7-.8-17.5C3.9 24.5 4 20 4.9 16 6.7 7.9 14.1 2.2 22.3 1c1.4-.2 4.1-1 16.5-1h.1C51.4 0 56.7.8 58.1 1c8.4 1.2 15.5 7.5 16.6 15.6Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt; &lt;div style=&quot;color: #787588; margin-top: 16px;&quot;&gt;Post by @getajobmike@ruby.social&lt;/div&gt; &lt;div style=&quot;font-weight: 500;&quot;&gt;View on Mastodon&lt;/div&gt; &lt;/a&gt; &lt;/blockquote&gt;
&lt;script data-allowed-prefixes=&quot;https://ruby.social/&quot; async=&quot;&quot; src=&quot;https://ruby.social/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;They don’t currently have a way to sponsor the project, so we are using GitHub
Sponsors to throw down &lt;a href=&quot;https://github.com/orgs/overlookinfra/sponsoring&quot;&gt;$100USD monthly for each of the current developer team&lt;/a&gt;.
We encourage you and/or your employers to do the same by clicking the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{sponsor}&lt;/code&gt;
icon on each of &lt;a href=&quot;https://github.com/orgs/gem-coop/people&quot;&gt;these GitHub profiles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s to a better and more resilient Ruby ecosystem going forward!&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Camptocamp Blog: Platform Engineering at CNS Munich</title>
    <link href="https://dev.to/camptocamp-ops/platform-engineering-at-cns-munich-20f6"/>
    <id>https://dev.to/camptocamp-ops/platform-engineering-at-cns-munich-20f6</id>
    <updated>2025-08-26T14:33:58+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This year Cloud Native Summit (CNS) in Munich gathered adopters and technologists from open source and cloud native communities.&lt;/p&gt;

&lt;p&gt;Compared to last year&#39;s Kubernetes Communities Days (KCD), the agenda dedicated many slots to Platform Engineering. This discipline can be seen as a way to fill gaps between different stakeholders of an Internal Developer Platform (IDP). Last year I wrote an article on this topic at the KCD Munich. This year I would like to share fresh ideas or principles from some talks given during the conference.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying problems before solutions
&lt;/h2&gt;

&lt;p&gt;Day 1 opened with a presentation of &quot;Product thinking&quot; approach made by &lt;em&gt;Stéphane Di Cesare&lt;/em&gt; (Deutsche Kreditbank) and &lt;em&gt;Dominik Schmidle&lt;/em&gt; (Giant Swarm). They reminded some core principles and techniques of &quot;Platform Engineering&quot; to manage the link between problem space and solution space. &lt;/p&gt;

&lt;p&gt;Day 1 also closed with a panel discussion on &quot;Platform Engineering&quot; animated by &lt;em&gt;Max Körbächer&lt;/em&gt; (Liquid Reply),  focused on the adoption of IDPs by end users and within projects.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=g-sZwa52DNE&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=1&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 1 Platform Thinking&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=MpU-vo4K7BQ&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 2 Panel Discussion&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  AI workloads on K8s
&lt;/h2&gt;

&lt;p&gt;&quot;Use platform engineering for AI workloads on K8s&quot;, that was the motto of the presentation given by &lt;em&gt;Mario-Leander Reimer&lt;/em&gt; (QAware GmbH). He  tackled emerging challenges for deploying Agentic AI workloads and introduced an example of what a platform should cover including quality plane and compliance plane. His talk is part of an open source initiative launched at QAware this year to mature the concepts of a dedicated control plane: the agentic layer.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=pg2DKYc9n_o&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=24&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 3 Architecting and Building a K8s-based AI Platform&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Composable platforms on Kubernetes
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Hossein Salahi&lt;/em&gt; (Liquid Reply) presented a reference implementation to streamline the delivery of services for developers. Some of the challenges were to link dev and ops, to manage services in configuration, to orchestrate infrastructure deployment and to manage access. For that purpose the open source tools Kratix codify a contract between dev and ops. Combined with Backstage, this abstraction allows services to be packaged and delivered to developers.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aLdgVrnMxcs&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=10&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 4  Modular Platform Engineering with Kratix and Backstage&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce cognitive workloads
&lt;/h2&gt;

&lt;p&gt;Interestingly the problem of cognitive workloads due to some platform complexity was a recurring theme. But &lt;em&gt;Michel Murabito&lt;/em&gt; (Mia Platform) positioned platform engineering as a way to reduce cognitive workloads. He highlighted essential building blocks and features that can reduce mental burden. &lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=iSMk7a62wUc&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=39&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 5 Creating a smooth Developer Experience&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Involved user as contributor
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Lian Li&lt;/em&gt; (lianmakesthings) told us a story of what can go wrong when building a platform based on real examples and some facts. She advocated some receipts that can improve communication among teams in an organisation and  explain some benefits of having contributing users in the loop.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=4CcNPHT_-nA&amp;amp;t=1597s&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 6 Many Cooks, One Platform: Balancing Ownership and Contribution for the Perfect Broth&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Convergence
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Evelyn Osman&lt;/em&gt; (enmacc) discussed the importance of convergence when building a platform in order to avoid fragmentation in teams and solutions. She shared some important steps that foster innovations and better meet user expectations.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=EztpUoi0hgU&amp;amp;list=PL54A_DPe8WtDLSA_EA7ETfprpRWzd2yqV&amp;amp;index=27&quot; rel=&quot;noopener noreferrer&quot;&gt;Video 7 Convergence on Platforms&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is not an exhaustive list of what was discussed, but somehow it made it clear that Platform Engineering approach can help picking the right solution in the open source landscape. Some enablers already provide a strong integration with Kubernetes to develop and publish new services on top of it. And depending on the organization, some shift in practices also might be required to follow the evolution of the platform at build and runtime. The conference was a great place to exchange with the community on technical and organization challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kcdmunich.de/schedule/&quot; rel=&quot;noopener noreferrer&quot;&gt;Agenda of the conference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.to/xavier_rakotomamonjy/platform-engineering-at-kcd-munich-25i1&quot;&gt;Platform Engineering at KCD Munich&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://landscape.cncf.io/?group=cnai&quot; rel=&quot;noopener noreferrer&quot;&gt;CNCF landscape for AI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.agentic-layer.ai/&quot; rel=&quot;noopener noreferrer&quot;&gt;Agentic Layer by QAware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.kratix.io&quot; rel=&quot;noopener noreferrer&quot;&gt;kratix.io&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
  </content>
    <author>
      <name>Camptocamp Blog</name>
      <uri>https://dev.to/camptocamp-ops</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: 🧩 Puppet Module Update Process</title>
    <link href="https://dev.to/betadots/puppet-module-update-process-3f54"/>
    <id>https://dev.to/betadots/puppet-module-update-process-3f54</id>
    <updated>2025-07-11T07:59:30+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;h2&gt;
  
  
  🔍 1. Identify and Analyze the Module
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Extract a module name from the &lt;code&gt;Puppetfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Search for the module on &lt;a href=&quot;https://forge.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet Forge&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Compare available versions and identify the latest one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📝 2. Review Changes in the Module
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Follow the &lt;strong&gt;Project URL&lt;/strong&gt; on the Forge page to the GitHub repository.&lt;/li&gt;
&lt;li&gt;Check recent changes under &lt;strong&gt;Releases&lt;/strong&gt; or in the &lt;strong&gt;Changelog&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forge.puppet.com/modules/puppet/systemd/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;Forge: puppet/systemd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/voxpupuli/puppet-systemd/releases&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/voxpupuli/puppet-systemd/blob/master/CHANGELOG.md&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ 3. Watch for Breaking Changes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Possible breaking changes:

&lt;ul&gt;
&lt;li&gt;Removal of support for EOL software&lt;/li&gt;
&lt;li&gt;API changes&lt;/li&gt;
&lt;li&gt;Renamed parameters or variables&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Note: Not every breaking change will affect your setup.

&lt;ul&gt;
&lt;li&gt;Example: Dropping EL6 support likely doesn’t concern you.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 4. Evaluate Other Changes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Linked pull requests (PRs) may offer additional insights.&lt;/li&gt;
&lt;li&gt;Check whether changes are understandable and whether breaking changes apply to your environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌱 5. Integrate into Development Branches
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;If the module is considered ready for update:

&lt;ul&gt;
&lt;li&gt;Integrate into a &lt;strong&gt;feature&lt;/strong&gt; or &lt;strong&gt;development&lt;/strong&gt; branch.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌲 6. Control Repo Branch Structure
&lt;/h2&gt;

&lt;p&gt;Typically present:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;development&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;production&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Optionally: &lt;code&gt;staging&lt;/code&gt; before &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Additionally: 0–n &lt;strong&gt;feature branches&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧪 7. Testing in Development Environment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test the updated module in a suitable environment (VM, container).&lt;/li&gt;
&lt;li&gt;Observe how it interacts with other modules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Possible findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dependencies on specific module versions&lt;/li&gt;
&lt;li&gt;New facts writing additional data to PuppetDB&lt;/li&gt;
&lt;li&gt;New or modified parameters requiring Hiera data&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔄 8. Forward the Change
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Once all adjustments are complete:

&lt;ul&gt;
&lt;li&gt;Pass the change to the next branch for further testing or rollout preparation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🤖 9. Automation with Renovate Bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This process is quite detailed and time-consuming.&lt;/li&gt;
&lt;li&gt;With &lt;a href=&quot;https://www.mend.io/renovate/&quot; rel=&quot;noopener noreferrer&quot;&gt;Renovate Bot&lt;/a&gt;, collecting and reviewing relevant updates becomes much easier.&lt;/li&gt;
&lt;li&gt;Renovate can be integrated into GitLab or GitHub.&lt;/li&gt;
&lt;li&gt;It works similarly to GitHub’s built-in &lt;a href=&quot;https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically&quot; rel=&quot;noopener noreferrer&quot;&gt;Dependabot&lt;/a&gt;, but is more flexible and configurable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧰 10. Automation with VoxBox
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You can also try using the Vox Pupuli VoxBox to list all dependencies for the entire control repo.

&lt;ul&gt;
&lt;li&gt;This doesn&#39;t work with every setup.&lt;/li&gt;
&lt;li&gt;If there are private modules in the Puppetfile, the person running the command must have access to them.&lt;/li&gt;
&lt;li&gt;Example command:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;podman run -it --rm -v $PWD:/repo:Z ghcr.io/voxpupuli/voxbox:latest r10k:dependencies&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;More useful information can be found in the &lt;a href=&quot;https://github.com/voxpupuli/container-voxbox&quot; rel=&quot;noopener noreferrer&quot;&gt;Vox Pupuli VoxBox documentation&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: 🧩 Puppet Modul Update Prozess</title>
    <link href="https://dev.to/betadots/puppet-modul-update-prozess-3mek"/>
    <id>https://dev.to/betadots/puppet-modul-update-prozess-3mek</id>
    <updated>2025-07-11T07:58:40+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;h2&gt;
  
  
  🔍 1. Modul identifizieren und analysieren
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Einen Modulnamen aus dem &lt;code&gt;Puppetfile&lt;/code&gt; entnehmen.&lt;/li&gt;
&lt;li&gt;In der &lt;a href=&quot;https://forge.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet Forge&lt;/a&gt; nach dem Modul suchen.&lt;/li&gt;
&lt;li&gt;Verfügbare Versionen vergleichen und die neueste identifizieren.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📝 2. Änderungen im Modul prüfen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Über den Link zur &lt;strong&gt;Project URL&lt;/strong&gt; auf der Forge-Seite zum GitHub-Repository wechseln.&lt;/li&gt;
&lt;li&gt;Dort unter &lt;strong&gt;Releases&lt;/strong&gt; oder im &lt;strong&gt;Changelog&lt;/strong&gt; die letzten Änderungen prüfen:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beispiel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://forge.puppet.com/modules/puppet/systemd/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;Forge: puppet/systemd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/voxpupuli/puppet-systemd/releases&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/voxpupuli/puppet-systemd/blob/master/CHANGELOG.md&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub Changelog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚠️ 3. Auf Breaking Changes achten
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mögliche Breaking Changes:

&lt;ul&gt;
&lt;li&gt;Entfernung der Unterstützung für EOL-Software&lt;/li&gt;
&lt;li&gt;Änderungen an der API&lt;/li&gt;
&lt;li&gt;Umbenennung von Variablen&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Hinweis: Nicht jede Breaking Change ist für jedes Setup relevant.

&lt;ul&gt;
&lt;li&gt;Beispiel: Der Entfall von EL6-Support betrifft euch vermutlich nicht.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 4. Sonstige Änderungen bewerten
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Auch verlinkte Pull Requests (PRs) können Aufschluss geben.&lt;/li&gt;
&lt;li&gt;Prüfen, ob Änderungen nachvollziehbar sind und keine Breaking Changes enthalten, die euch betreffen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌱 5. Aufnahme in Entwicklungszweige
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wenn das Modul als updatefähig eingeschätzt wird:

&lt;ul&gt;
&lt;li&gt;Aufnahme in einen &lt;strong&gt;Feature-&lt;/strong&gt; oder &lt;strong&gt;Development-Branch&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🌲 6. Branch-Struktur im Control-Repo
&lt;/h2&gt;

&lt;p&gt;Typischerweise vorhanden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;development&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;staging&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;production&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Zusätzlich: 0–n &lt;strong&gt;Feature-Branches&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧪 7. Tests in Entwicklungsumgebung
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Test des neuen Moduls in geeigneter Umgebung (VM, Container)&lt;/li&gt;
&lt;li&gt;Beobachtung des Zusammenspiels mit anderen Modulen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mögliche Erkenntnisse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Abhängigkeit zu anderen Modul-Versionen&lt;/li&gt;
&lt;li&gt;Neue Facts, die zusätzliche Daten in die PuppetDB schreiben&lt;/li&gt;
&lt;li&gt;Neue oder geänderte Parameter, die Hiera-Daten erfordern&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔄 8. Change weiterreichen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wenn alle Anpassungen erledigt sind:

&lt;ul&gt;
&lt;li&gt;Übergabe des Changes in den nächsten Branch zur weiteren Prüfung oder Vorbereitung für den Rollout.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  🤖 9. Automatisierung mit Renovate Bot
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Dieser Prozess ist sehr kleinteilig und kann zeitaufwendig sein.&lt;/li&gt;
&lt;li&gt;Mit Hilfe von &lt;a href=&quot;https://www.mend.io/renovate/&quot; rel=&quot;noopener noreferrer&quot;&gt;Renovate Bot&lt;/a&gt; lässt sich das Sichten und Sammeln der relevanten Informationen deutlich erleichtern.&lt;/li&gt;
&lt;li&gt;Renovate ist ein Bot, der sich in GitLab oder GitHub integrieren lässt.&lt;/li&gt;
&lt;li&gt;Er funktioniert ähnlich wie der GitHub-eigene &lt;a href=&quot;https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically&quot; rel=&quot;noopener noreferrer&quot;&gt;Dependabot&lt;/a&gt;, ist aber deutlich flexibler und konfigurierbarer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧰 10. Automatisierung mit VoxBox
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Man kann auch versuchen mit der Vox Pupuli Voxbox die Abhängigkeiten für das gesamte Control-Repo anzeigen zu lassen.

&lt;ul&gt;
&lt;li&gt;Das klappt nicht bei jedem Setup.&lt;/li&gt;
&lt;li&gt;Wenn im Puppetfile private Module sind, muss der jenige der es ausführt, Zugriff auf diese haben.&lt;/li&gt;
&lt;li&gt;Beispielbefehl:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;podman run -it --rm -v $PWD:/repo:Z ghcr.io/voxpupuli/voxbox:latest r10k:dependencies&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Weitere nützliche Informationen gibt es in der &lt;a href=&quot;https://github.com/voxpupuli/container-voxbox&quot; rel=&quot;noopener noreferrer&quot;&gt;Vox Pupuli VoxBox Dokumentation&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: So much for being developer first</title>
    <link href="https://overlookinfratech.com/2025/06/12/so-much-for-being-developer-first/"/>
    <id>https://overlookinfratech.com/2025/06/12/so-much-for-being-developer-first</id>
    <updated>2025-06-12T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Well, it appears that Perforce has taken another step down the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Enshittification&quot;&gt;enshittification&lt;/a&gt; journey. For
some strange reason, it appears that they don’t want you writing Puppet modules
unless you’re a paying customer. That’s right, they killed the open source PDK
and sequestered the source away, just like they did with Puppet. They would like
you to pay for the privilege of creating content for them.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/pdk_enshittified.jpeg&quot; alt=&quot;PDK release announcement&quot; class=&quot;img right w30&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is a very strange strategic move on their part. Their ecosystem is almost
entirely supported by community authors building and maintaining Puppet modules.
A full 96% of their paying customers use modules supported by Vox Pupuli, many
of which are open source users. They barely even maintain their own modules in
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppetlabs&lt;/code&gt; namespace. Most of the contributions there have for years come
from community members using the &lt;a href=&quot;https://web.archive.org/web/20250321181447/https://www.puppet.com/ecosy
stem/contribute/trusted-contributors&quot;&gt;Trusted Contributors
program&lt;/a&gt; that I built when I was still there.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;On a related note, it also appears that they’ve shut down the Trusted
Contributors program as it’s no longer mentioned on their website.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;In reality, they don’t even have a dedicated modules team anymore!&lt;/strong&gt; One
with any sense at all would think that given this lack that they’d want to
encourage people to build and maintain quality content in the ecosystem rather
than putting barriers in place to make that harder to do. 🤔&lt;/p&gt;

&lt;p&gt;My only assumption can be that the Puppet Core sales aren’t so hot and they need
something, anything, to juice the numbers a bit.&lt;/p&gt;

&lt;h2 id=&quot;but-does-it-actually-matter&quot;&gt;But does it actually matter?&lt;/h2&gt;

&lt;p&gt;In all reality, many of us never really liked using the PDK. It’s really big
and inflexible. The customization options are confusing and tedious. It
generates way too many random config files for services that most people never
used, unless you customize it, which again is super awkward. And maybe worst of
all, it was designed primarily for the use of Puppet’s modules team (it still
existed back then) rather than being designed for community developers. For
example, until a few years ago, it configured PR testing and publishing
workflows with GitHub actions that assumed that you were using Honeycomb for
profiling and hardcoded the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppetlabs&lt;/code&gt; namespace.&lt;/p&gt;

&lt;h2 id=&quot;vox-pupuli-developer-tools&quot;&gt;Vox Pupuli developer tools&lt;/h2&gt;

&lt;p&gt;Luckily, Vox Pupuli has been working on replacements for a while. We believe
that developer tools should be small and purpose-built rather than trying to jam
pack all the things for all the people into a single massively overloaded tool.&lt;/p&gt;

&lt;p&gt;Rather than using the exact same command used to&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;create a module from a template&lt;/li&gt;
  &lt;li&gt;run unit testing in CI pipeline&lt;/li&gt;
  &lt;li&gt;manage installed Puppet versions&lt;/li&gt;
  &lt;li&gt;build and publish modules&lt;/li&gt;
  &lt;li&gt;drop into the debugger REPL that you probably didn’t even know existed,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vox Pupuli’s toolkit includes smaller tools that do one or two things
well instead of being mediocre at trying to do everything.&lt;/p&gt;

&lt;p&gt;For example, our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;modulesync&lt;/code&gt; tool makes it easy to create, manage, and update
a group of modules based on a template. No more broken dependencies because you
haven’t remembered to update one of your modules for the last four years. All
your modules, whether that’s one or one hundred, will be in a consistent state
and tested against your current infrastructure.&lt;/p&gt;

&lt;p&gt;Or you can get zero-install access to a &lt;a href=&quot;https://github.com/voxpupuli/container-voxbox?tab=readme-ov-file#availab
le-rake-tasks&quot;&gt;whole suite of module testing and
management tasks with
voxbox&lt;/a&gt;. Update your module’s metadata, validate all of its dependencies,
and publish a new version all with a few &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;podman&lt;/code&gt; commands.&lt;/p&gt;

&lt;h2 id=&quot;the-vox-pupuli-devkit-sdk&quot;&gt;The Vox Pupuli Devkit SDK&lt;/h2&gt;

&lt;p&gt;Now that easy access to the PDK is disappearing, we’ve put together a project
to collect all these tools and more into a coherent suite that’s usable,
discoverable, and maintainable. We’ll build the missing parts along the way. For
example, I’m currently working on an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;asdf&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mise&lt;/code&gt; plugin to easily manage
OpenVox versions using an industry standard tool rather than something bespoke
that’s constrained to very specific and limited workflows.&lt;/p&gt;

&lt;p&gt;We don’t have a timeline on this yet. But luckily, there’s not much development
on the PDK except to make it harder for you to use. You can very likely simply
continue to use the version you already have installed until we have more news
for you.&lt;/p&gt;

&lt;p&gt;Watch this space and we’ll soon have information for you about the new Devkit SDK!&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: OpenVox InfraTales - MacOS Signing and Notarization</title>
    <link href="https://overlookinfratech.com/2025/05/12/macos-signing/"/>
    <id>https://overlookinfratech.com/2025/05/12/macos-signing</id>
    <updated>2025-05-12T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Hi everyone! Nick here. I figured it was time I finally write a blog post. If you’ve seen me around on the &lt;a href=&quot;https://voxpupuli.slack.com&quot;&gt;Vox Pupuli Slack&lt;/a&gt;, you know that I’ve been deep in the trenches of OpenVox build code for some time now, getting the various bits to a secure and stable place where it’s easily accessible to community contributors.&lt;/p&gt;

&lt;p&gt;Today’s story is about wrestling with MacOS’s Gatekeeper so we can release a production-ready &lt;a href=&quot;https://downloads.voxpupuli.org/mac/openvox8/15/arm64/&quot;&gt;MacOS agent build&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;no-unsigned-code-ever&quot;&gt;No unsigned code, ever&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/posts/no_unsigned_code.png&quot; alt=&quot;No unsigned code, ever&quot; class=&quot;img left&quot; height=&quot;300px&quot; /&gt;
Perforce recently dropped a LinkedIn post about their latest Puppet Core release, bragging about how secure and enterprise-ready Puppet Core is. The one that stuck out to me: &lt;em&gt;Fully signed binaries and agents - no unsigned code, ever&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They’re right - unsigned binaries are like showing up to a formal dinner party in a &lt;a href=&quot;https://www.frenchiely.com/products/dog-crab-halloween-costume&quot;&gt;crab costume&lt;/a&gt;. In OpenVox-land, all of our Linux binaries are fully signed. However, just like how Perforce’s internal dev builds are not always signed, our &lt;em&gt;prerelease, non-final&lt;/em&gt; MacOS and Windows agents were unsigned (Windows remains so until we navigate the extortion racket that is paying for an Extended Validation code signing certificate from a vendor).&lt;/p&gt;

&lt;p&gt;I applaud Perforce on their journey towards creating the safest, most secure Puppet environment possible, so I thought I’d write this little guide to help them take the next step. In order to reach the utopia of a fully signed and notarized MacOS agent that Gatekeeper is happy with, we have to not only sign the right things, but notarize it as well.&lt;/p&gt;

&lt;div style=&quot;clear: both;&quot; /&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/macos_unsigned.png&quot; alt=&quot;Gatekeeper warning on an unsigned installer&quot; class=&quot;img right w30&quot; height=&quot;200px&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;gatekeeper-the-ever-vigilant-security-guard-of-macos&quot;&gt;Gatekeeper: The ever vigilant security guard of MacOS&lt;/h2&gt;

&lt;p&gt;Apple’s &lt;a href=&quot;https://support.apple.com/guide/security/gatekeeper-and-runtime-protection-sec5599b66df/web&quot;&gt;Gatekeeper&lt;/a&gt; ensures that the software you download and run is trusted and safe. If your software isn’t both signed and notarized in the correct way, it will flat out refuse to open it (without extra shenanigans), warning that you potentially downloaded a radioactive piece of malware and you should be ashamed of yourself.&lt;/p&gt;

&lt;p&gt;To make Gatekeeper happy, all you used to have to do in the halcyon days of pre-Sonoma was to sign the specific binary entrypoints into your app with an “application developer identity” (certificate + private key). Let’s dive into the GitHub mines and see how this is currently done.&lt;/p&gt;

&lt;h2 id=&quot;warning-there-be-dragons-in-these-mines&quot;&gt;Warning: There be dragons in these mines&lt;/h2&gt;

&lt;p&gt;The way that Perforce does this signing process for their current MacOS agents (or at least they did before taking everything private, but I don’t believe it has changed) is by sending the three executable files in the puppet-agent package to a signing server, which signs those files, and then sends them back to continue the build process. You can see how this is defined in the &lt;a href=&quot;https://github.com/puppetlabs/puppet-agent/blob/2ed175ddae769662ccb7872d7041aca2c5d30211/configs/projects/puppet-agent.rb#L24-L31&quot;&gt;puppet-agent repo&lt;/a&gt;. This utilizes Vanagon’s &lt;a href=&quot;https://github.com/puppetlabs/vanagon/blob/main/lib/vanagon/utilities/extra_files_signer.rb&quot;&gt;ExtraFilesSigner&lt;/a&gt; to &lt;a href=&quot;https://github.com/puppetlabs/vanagon/blob/701cccd065d8ebac128fe262873c94020be983db/lib/vanagon/utilities/extra_files_signer.rb#L28-L33&quot;&gt;lay down a bash file to run the command, rsync the file over, run the command, then rsync the file back&lt;/a&gt;. This process is embedded in Vanagon’s &lt;a href=&quot;https://github.com/puppetlabs/vanagon/blob/701cccd065d8ebac128fe262873c94020be983db/lib/vanagon/platform/osx.rb#L44-L87&quot;&gt;MacOS platform build process&lt;/a&gt;. This code ends up with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; that contains a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; installer, which contains all the files, including those three signed binaries. Finally, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; file must be signed with a different “installer developer identity”. Perforce &lt;a href=&quot;https://github.com/puppetlabs/packaging/blob/15da180e9574e4582c096b0537334627d3f0f814/lib/packaging/sign/dmg.rb#L43-L70&quot;&gt;does this&lt;/a&gt; by mounting the created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt;, signing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; with that identity, then recreating the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; file with that signed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;signing-and-notarization-everyones-favorite-migraines&quot;&gt;Signing and Notarization: Everyone’s favorite migraines&lt;/h2&gt;

&lt;p&gt;While this worked fine before, as of MacOS 15 Sonoma, the requirements are much more stringent. Not only must you sign the these binaries, but you must sign &lt;em&gt;every single binary, dylib, and bundle file&lt;/em&gt;, no exceptions. And on top of that, you have to sign all of them specifying it should use the &lt;a href=&quot;https://developer.apple.com/documentation/security/hardened-runtime&quot;&gt;hardened runtime&lt;/a&gt; via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--options runtime&lt;/code&gt; flag to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;codesign&lt;/code&gt; tool, as well a secure timestamp via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--timestamp&lt;/code&gt; flag. Doesn’t matter if the files are already signed in a different manner. MacOS 15 has a refined palate and will only consume the &lt;a href=&quot;https://www.youtube.com/watch?v=G__PVLB8Nm4&quot;&gt;finest artisinal binaries&lt;/a&gt;. Yes, it’s probably for the best, but I digress.&lt;/p&gt;

&lt;p&gt;Additionally, you must notarize either the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; file. This involves sending your file to Apple, where their system scans for any hidden nasties and verifies everything that should be signed, is signed in the correct way. Once approved, you then must staple the notarization to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; so Gatekeeper can stop judging us.&lt;/p&gt;

&lt;h2 id=&quot;bringing-a-first-pass-of-order-to-chaos&quot;&gt;Bringing a first pass of order to chaos&lt;/h2&gt;

&lt;p&gt;Right now in OpenVox, unlike Linux agents where we use containers to build them, we’re building the MacOS agent in a &lt;a href=&quot;https://github.com/OpenVoxProject/planning/wiki/How-OpenVox-builds-work#macos-builds&quot;&gt;VM that is configured in a particular way&lt;/a&gt; with the appropriate bits and bobs installed. This is similar to how Perforce does it, except that this particular VM template also includes the bits needed for signing and notarization. This is not highly portable and accessible to people yet, but we wanted to get this agent into your hands as soon as possible. Soon, we’ll be moving the process into GitHub Actions and hopefully finding a way to build it locally without a Mac as well. We’ll take this awkward Ford Pinto and turn it into a Ferrari. Or at least a Honda Accord.&lt;/p&gt;

&lt;p&gt;For this first pass at MacOS agent signing and notarizing, I moved the process &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03&quot;&gt;entirely into Vanagon&lt;/a&gt;. While this may not be where we want to leave it since it reduces potential future flexibility, it makes sense for now since the only MacOS thing we sign is the agent, and this keeps the signing code from being spread across three different repos. This also streamlines the process, since we can do the signing and notarizing as part of the build, rather than having to unpack the build, sign the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt;, then repackage it into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This new code &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R68-R75&quot;&gt;hard-codes where to look for binaries&lt;/a&gt; that need signing within the agent package. This is not at all ideal, and is ripe for future improvements like moving this definition back into the vanagon project itself. But because it isn’t obvious which files need signing (i.e. binaries don’t all live in the same place, don’t all have a particular suffix), this is an easy way for us to utilize &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find -exec&lt;/code&gt; to sign all the pieces. Another improvement made here is that at every step, we verify the signing/notarizing worked correctly. This gives us an extra layer of confidence that the delivered agent will not shout obscenities at you.&lt;/p&gt;

&lt;p&gt;As before, we &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R87&quot;&gt;sign the pkg file&lt;/a&gt;, but we do it before building the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt;. Then, we build the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; and &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R87&quot;&gt;sign that&lt;/a&gt;, after which we &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R101&quot;&gt;notarize&lt;/a&gt; using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notarytool&lt;/code&gt; in Xcode. Usually, the notary process takes less than 2 minutes. On our initial submissions, it took days as they were being manually reviewed. Now that the system recognizes our submission, it is much faster. Once notarization is approved, we &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R102&quot;&gt;staple&lt;/a&gt; that notarization to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dmg&lt;/code&gt; and then &lt;a href=&quot;https://github.com/OpenVoxProject/vanagon/commit/8f3d1b7112714eb7ed92f707d91fdba3cb895e03#diff-c501f4738f5008608803f365c53c9de1358abaf52a9d1da5e982f910f5dde0d7R103&quot;&gt;check with Gatekeeper&lt;/a&gt; that it isn’t going to complain.&lt;/p&gt;

&lt;h2 id=&quot;the-bureaucracy-d-u-n-s-numbers-and-overlook-infratech-inc&quot;&gt;The Bureaucracy: D-U-N-S numbers and Overlook InfraTech, Inc.&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/assets/posts/background_tasks.png&quot; alt=&quot;Gatekeeper warning on an unsigned installer&quot; class=&quot;img right w30&quot; /&gt;
Figuring out this whole process was quite a bear. Apple’s documentation mostly assumes you are developing with the Xcode GUI app, and not trying to do everything from the command line. It isn’t terribly clear exactly what needs to be signed and notarized and how without some more digging and trial and error.&lt;/p&gt;

&lt;p&gt;Another thing that isn’t terribly ideal is that in order to have a developer account tied to an organization, that organization has to be a full fledged legal entity with a &lt;a href=&quot;https://www.dnb.com/en-us/smb/duns/duns-lookup.html&quot;&gt;D-U-N-S number&lt;/a&gt;. If you ever start a business, you’ll find that you need a franky ridiculous amount of different identification numbers that tell people your business is, in fact, a real business, and this number is just one of many. Vox Pupuli doesn’t have this, so the agent is signed with the Overlook InfraTech, Inc. account. This has the side effect that our company name shows as the one that owns the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pxp-agent&lt;/code&gt; background tasks. This should absolutely say Vox Pupuli and hopefully one of these days we can change that.&lt;/p&gt;

&lt;h2 id=&quot;final-thoughts-lets-work-together&quot;&gt;Final thoughts: Let’s work together&lt;/h2&gt;

&lt;p&gt;To my knowledge, Perforce does not have their MacOS 15 agent notarized, nor signed with MacOS 15’s newer stringent requirements (repeating my disclaimer: I do not know for sure, as none of us has visibility into their current build process or access to their current agents without signing a EULA which prevents you from inspecting any of the code anyway). I’d love to submit some PRs to their repos, but unfortunately, they have seemingly completely abandoned them in favor of their internal forks. That’s a bummer, because &lt;em&gt;security is a team sport&lt;/em&gt;, and we all benefit when the ecosystem is safe. Nevertheless, I invite them to take a look at our work here to assist in getting their MacOS agents notarized, and I’d be more than happy to work with their engineers to assist. Thrilled, in fact. And I promise I’m not being sarcastic about that.&lt;/p&gt;

&lt;p&gt;If you’re curious for more details about how we did this or how we do any of the OpenVox things, drop by the Vox Pupuli Slack or IRC, both of which you can find in the &lt;em&gt;Connect&lt;/em&gt; menu of &lt;a href=&quot;https://voxpupuli.org&quot;&gt;voxpupuli.org&lt;/a&gt;. I’m always happy to chat, even while I’m debugging why a binary isn’t signing properly at 2AM. If you have any other topics you’d be interested in hearing more about in this blog, drop me a line any time.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: The OSU Open Source Lab needs your help today</title>
    <link href="https://overlookinfratech.com/2025/05/01/open-source-lab-needs-your-help-today/"/>
    <id>https://overlookinfratech.com/2025/05/01/open-source-lab-needs-your-help-today</id>
    <updated>2025-05-01T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Have you been looking at the OpenVox project and wondered what you could do to
help out? What if I told you about an opportunity to help us, and also hundreds
of other OSS projects that you probably rely on? The &lt;a href=&quot;https://osuosl.org&quot;&gt;OSU Open Source Lab&lt;/a&gt;
is OpenVox’s primary package host and helped us get the project off the
ground right from the start. They help hundreds of open source projects with
critical infrastructure needs that many projects (like us) would not have been
able to afford on their own.&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;🔔 tl;dr; &lt;a href=&quot;https://osuosl.org/blog/osl-future/&quot;&gt;the Open Source Lab needs your help&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not only that, but they have been training the next generation of FOSS DevOps
and system infrastructure engineers for YEARS. Many of the first Puppet community
practitioners came up through the OSL. They contributed many of the first Puppet
modules. The venerable &lt;a href=&quot;https://www.oreilly.com/library/view/pro-puppet-second/9781430260400/&quot;&gt;Pro Puppet&lt;/a&gt;
second edition was written by OSL students in collaboration with PuppetLabs
developers, including myself.&lt;/p&gt;

&lt;p&gt;OSL has played a HUGE part in Puppet’s story and helped Puppet, Vox Pupuli, and
OpenVox get to where they are today. I’d wager that your company uses OSL resources
on a regular basis and may not even realize it. Take a &lt;a href=&quot;https://osuosl.org/communities/&quot;&gt;look at the list&lt;/a&gt;
and you’re certain to find something business critical.&lt;/p&gt;

&lt;p&gt;However, with the current crisis in academic funding in the USA, they are in
critical need of funding and without your help, they may cease to exist. See
&lt;a href=&quot;https://osuosl.org/blog/osl-future/&quot;&gt;this blog post for more information&lt;/a&gt; and
go talk to your boss today! Don’t hesitate to
&lt;a href=&quot;https://osuosl.org/donate/&quot;&gt;drop them some cash yourself if you can spare it&lt;/a&gt;.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Camptocamp Blog: Talos Linux: a new standard for on-premises Kubernetes clusters?</title>
    <link href="https://dev.to/camptocamp-ops/talos-linux-a-new-standard-for-on-premises-kubernetes-clusters-283i"/>
    <id>https://dev.to/camptocamp-ops/talos-linux-a-new-standard-for-on-premises-kubernetes-clusters-283i</id>
    <updated>2025-04-22T17:44:21+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;A few weeks ago, I was at KubeCon Europe 2025 in London and I had the opportunity to attend a presentation that tackled the monumental challenge of migrating 35 Kubernetes clusters in an air-gapped environment from nodes deployed with a mix of kubeadm/Ansible/Puppet to &lt;a href=&quot;https://www.talos.dev&quot; rel=&quot;noopener noreferrer&quot;&gt;Talos Linux&lt;/a&gt; nodes deployed using &lt;a href=&quot;https://cluster-api.sigs.k8s.io&quot; rel=&quot;noopener noreferrer&quot;&gt;Cluster API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While the presentation was quite interesting (you can get the slides &lt;a href=&quot;https://kccnceu2025.sched.com/event/1tx78/day-2000-migration-from-kubeadmansible-to-clusterapitalos-a-swiss-banks-journey-clement-nussbaumer-postfinance&quot; rel=&quot;noopener noreferrer&quot;&gt;here&lt;/a&gt; and watch the session recording on &lt;a href=&quot;https://www.youtube.com/watch?v=uQ_WN1kuDo0&amp;amp;list=PLj6h78yzYM2MP0QhYFK8HOb8UqgbIkLMc&amp;amp;index=253&quot; rel=&quot;noopener noreferrer&quot;&gt;CNCF&#39;s YouTube Channel&lt;/a&gt;), I want to dive more into the Talos Linux project and its features.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Talos Linux?
&lt;/h2&gt;

&lt;p&gt;Talos Linux is a modern Linux distribution purpose-built for running Kubernetes clusters. Some noteworthy characteristics of Talos Linux are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Immutable&lt;/strong&gt;: Talos Linux is designed to be immutable and always runs from a SquashFS image. This means that the operating system is read-only and cannot be modified at runtime. This immutability provides a strong security posture and means that there is no need to worry about unintended changes to the operating system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal&lt;/strong&gt;: Talos Linux is a minimal operating system that only includes the components necessary to run Kubernetes. All the OS is built from the ground-up and no unnecessary components are included. This minimalism reduces the attack surface and improves performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ephemeral and Declarative&lt;/strong&gt;: Talos Linux nodes are ephemeral and everything written to disk is reconstructible. It is also declarative, meaning that the desired state of the system is defined in a configuration file and gRPC API, which is perfect for someone that loves automation and reproducibility, like myself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt;: As a consequence of its design, Talos Linux provides enhanced security features, ensuring that the system remains robust against various threats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agnostic&lt;/strong&gt;: Talos Linux is cloud-agnostic, allowing it to run on various cloud providers and on-premises environments without vendor lock-in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In summary, and I&#39;m quoting the official documentation, &lt;em&gt;&quot;Talos is meant to do one thing: maintain a Kubernetes cluster, and it does this very, very well.&quot;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My thoughts
&lt;/h2&gt;

&lt;p&gt;I have been following the Talos Linux project for a while, and I was gladly surprised to see a Swiss-bank like PostFinance being on the forefront of adopting such modern solutions like Talos Linux and Cluster API.&lt;/p&gt;

&lt;p&gt;I think Talos Linux will be a key player in the Kubernetes ecosystem, especially for organizations looking for an on-premises solution that&#39;s secure, efficient and easy to manage.&lt;/p&gt;

&lt;p&gt;The fact that Talos is declarative and immutable might seem like a drawback at first for someone used to the &lt;em&gt;old ways&lt;/em&gt; of managing infrastructure with Ansible or Puppet, but I believe that this is the future of managing Kubernetes clusters.&lt;/p&gt;

&lt;p&gt;I want my nodes to behave like pods that I can easily create, destroy, and replace. Besides, I don&#39;t want to deal with the overhead of managing the operating system. I already have enough to deal with the on-premises infrastructure for the network and storage and the Kubernetes cluster itself, so why not offload the management of the operating system to a purpose-built distribution like Talos?&lt;/p&gt;

&lt;p&gt;With &lt;a href=&quot;https://www.siderolabs.com/platform/saas-for-kubernetes/&quot; rel=&quot;noopener noreferrer&quot;&gt;Omni&lt;/a&gt;, Sidero Lab&#39;s SaaS platform for managing Talos Linux clusters, I believe Sidero Labs have a good revenue model to continue developing Talos Linux. As a fan of open-source, we are all aware of the challenges of maintaining a project like Talos Linux, and I believe that having a SaaS platform to manage Talos Linux clusters is a good way to ensure the project&#39;s sustainability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Talos Linux vs. other solutions
&lt;/h2&gt;

&lt;p&gt;Red Hat OpenShift is a well-known solution in large enterprises. However, more than a Kubernetes distribution, it is a complete platform that includes a lot of features and components, including CI/CD tools, monitoring, etc. It is also expensive.&lt;/p&gt;

&lt;p&gt;On the other hand, Talos Linux shines with its simplicity and minimalism, which brings more flexibility and allows teams to choose their solution to complete the platform as they see fit.&lt;/p&gt;

&lt;p&gt;RKE2 is another Kubernetes distribution that focuses on simplicity and security, making it a strong contender for organizations looking for a lightweight solution. However, it still requires an underlying operating system that you need to operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;While at KubeCon, I had the opportunity to visit the Sidero Labs&#39; booth and talk to the team behind Talos Linux. I thank the team for a warm welcome and great conversations about the project.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb8gy1k9411do5c5a3grc.jpg&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb8gy1k9411do5c5a3grc.jpg&quot; alt=&quot;Sidero Booth&quot; width=&quot;800&quot; height=&quot;1066&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Go further
&lt;/h2&gt;

&lt;p&gt;I wanted to keep this blog post short and not too technical, but if you want to learn more about Talos Linux, I recommend checking out the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.talos.dev/v1.9/introduction/what-is-talos/&quot; rel=&quot;noopener noreferrer&quot;&gt;What is Talos?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/siderolabs/talos&quot; rel=&quot;noopener noreferrer&quot;&gt;Talos Linux GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.talos.dev/v1.9/introduction/quickstart/&quot; rel=&quot;noopener noreferrer&quot;&gt;Quickstart a Talos Linux cluster with Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.talos.dev/v1.9/learn-more/philosophy/&quot; rel=&quot;noopener noreferrer&quot;&gt;Philosophy of Talos Linux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/kubernetes/comments/16v0j8x/talos_linux_a_modern_linux_distribution_purpose/&quot; rel=&quot;noopener noreferrer&quot;&gt;Interesting Reddit thread with some comments from Sidero Labs&#39; employees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
  </content>
    <author>
      <name>Camptocamp Blog</name>
      <uri>https://dev.to/camptocamp-ops</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: A whirlwind conversation on trademarks</title>
    <link href="https://overlookinfratech.com/2025/03/20/a-whirlwind-conversation-on-trademarks/"/>
    <id>https://overlookinfratech.com/2025/03/20/a-whirlwind-conversation-on-trademarks</id>
    <updated>2025-03-20T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Quite a few people over the last few months have helpfully pointed out the name
collisions in the &lt;a href=&quot;https://voxpupuli.org/openvox&quot;&gt;OpenVox&lt;/a&gt; Puppet fork. If you didn’t
recognize it, there’s also a VoIP/PBX provider called &lt;a href=&quot;https://www.openvoxtech.com&quot;&gt;OpenVox&lt;/a&gt;.
Surely that’s got to be a violation of some kind of law, right? Even Google gets
it very wrong! See how our information is interleaved in this autogenerated profile?
&lt;img src=&quot;/assets/posts/openvox_profile.png&quot; alt=&quot;The profile Google shows on the right side of the page when you search for an entity it thinks it knows how to describe.&quot; class=&quot;img right w30&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Hold up though. Not so fast because it’s not quite that simple. Lemme tell you a
quick story about a name that I’m pretty familiar with: Bolt.&lt;/p&gt;

&lt;h2 id=&quot;the-bolt-story&quot;&gt;The Bolt story&lt;/h2&gt;

&lt;p&gt;In August of 2017, Puppet began work on a new project intended to be an agent-less
orchestration tool for automating the manual work it takes to maintain your
infrastructure. Sure, it started life as an Ansible competitor, but it grew to
be pretty neat. For the first time, you could trivially and seamlessly interleave
shell scripts and Puppet code and just push that configuration out to nodes in
your infrastructure as needed.&lt;/p&gt;

&lt;p&gt;Imagine my surprise when I visited the Slack booth at KubeCon the following year
and saw their new application library called… none other than Bolt. It had
gone through several name iterations, including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slapp&lt;/code&gt; which is my personal
favorite, but Bolt is what they settled on.  I asked if they knew about our tool
and they nodded and brushed off my concerns. Obviously, I raised it to our Legal
team and was surprised when they also brushed it off.&lt;/p&gt;

&lt;p&gt;So then I started noticing all the things named Bolt.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;In the Pacific Northwest where I lived, we had the &lt;a href=&quot;https://en.wikipedia.org/wiki/BoltBus&quot;&gt;Bolt Bus&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;There were the &lt;a href=&quot;https://electrek.co/2022/08/16/bolt-mobility-abandoned-electric-bikes-element-lev/&quot;&gt;Bolt e-bike rentals&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;There’s the &lt;a href=&quot;https://bolt.eu/en/&quot;&gt;Bolt rideshare and rental company&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;There’s the silly &lt;a href=&quot;https://www.imdb.com/title/tt0397892/&quot;&gt;Bolt movie&lt;/a&gt; about “superhero” dog.&lt;/li&gt;
  &lt;li&gt;There’s an &lt;a href=&quot;https://www.bolt.com&quot;&gt;ecommerce platform&lt;/a&gt; named Bolt&lt;/li&gt;
  &lt;li&gt;There’s a &lt;a href=&quot;https://bolt.new&quot;&gt;mobile app tool&lt;/a&gt; named Bolt&lt;/li&gt;
  &lt;li&gt;There’s a &lt;a href=&quot;https://boltcms.io&quot;&gt;CMS&lt;/a&gt; named Boltcms&lt;/li&gt;
  &lt;li&gt;There’s an &lt;a href=&quot;https://bolt.io&quot;&gt;investment platform&lt;/a&gt; named Bolt.&lt;/li&gt;
  &lt;li&gt;There’s a &lt;a href=&quot;https://www.boltlock.com&quot;&gt;gear lock brand&lt;/a&gt; named BOLT&lt;/li&gt;
  &lt;li&gt;There’s a &lt;a href=&quot;https://boltthreads.com&quot;&gt;biotech company&lt;/a&gt; named Bolt&lt;/li&gt;
  &lt;li&gt;There’s an &lt;a href=&quot;https://bolttech.io&quot;&gt;insurance company&lt;/a&gt; named Bolt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aaaaand I’m tired of typing out that list, so I’m just gonna stop at that.&lt;/p&gt;

&lt;h2 id=&quot;what-purpose-does-trademark-serve&quot;&gt;What purpose does trademark serve?&lt;/h2&gt;

&lt;p&gt;Clearly there’s more to trademark enforcement than just the name itself. As it
turns out, that’s only a small part of it. At this point, I’ll take a real quick
aside to remind you that I’m an open source software nerd, not a lawyer. And I’m
&lt;em&gt;certainly not your lawyer&lt;/em&gt; so none of this is legal advice. In any case, the
concept of trademark law isn’t really to give you an asset to own, it’s to
protect consumers in the market from brand confusion. You’ll notice that none of
those companies I listed are named Bolt and also do infrastructure
orchestration.&lt;/p&gt;

&lt;p class=&quot;quote&quot;&gt;A trademark is a word, phrase, or logo that identifies the source of goods or
services. Trademark law protects a business’ commercial identity or brand by
discouraging other businesses from adopting a name or logo that is “confusingly
similar” to an existing trademark. &lt;strong&gt;The goal is to allow consumers to easily
identify the producers of goods and services and avoid confusion&lt;/strong&gt;.&lt;br /&gt;
– &lt;a href=&quot;https://en.wikipedia.org/wiki/United_States_trademark_law&quot;&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s a commonly used rubric called the &lt;a href=&quot;https://www.mekiplaw.com/the-dupont-factors-an-easy-guide/#:~:text=II.%20THE%20DUPONT%20FACTORS&quot;&gt;DuPont Factors&lt;/a&gt;
which can help identify whether there is a probable infringement. The rubric has
13 criteria, but they boil down roughly to&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Similarity of the marks.&lt;/li&gt;
  &lt;li&gt;Similarity of goods/services.&lt;/li&gt;
  &lt;li&gt;Similarity of trade channels.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So that explains why we aren’t concerned with the fact that there’s a product
out there named OpenVox but is in a totally unrelated industry.  In other words,
someone looking for an infrastructure management system isn’t likely to
accidentally purchase a PBX system, even if the name is the same.&lt;/p&gt;

&lt;h2 id=&quot;what-about-the-filenames&quot;&gt;What about the filenames?&lt;/h2&gt;

&lt;p&gt;But there’s another point that comes up frequently that’s not quite as clear.
That’s the name and paths of the executables and the configuration files and
such within the OpenVox packages. At this point, they’re exactly the same as the
original Puppet packages. In other words, to configure OpenVox, you edit the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet.conf&lt;/code&gt; file and to run the agent you invoke &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet agent -t&lt;/code&gt;. We negotiated
this point very heavily with Perforce in order to not cause community disruption.&lt;/p&gt;

&lt;p&gt;There are a few non-legally-defensible reasons why Perforce might agree to this
usage and a couple reasons that are legally defensible. First off, let’s look at
why Perforce might be motivated to allow this usage. These are not reasons that
would stand up in court, if it came to that, but are ways in which Perforce
benefits from it.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;They get a little bit of free marketing. Every time someone types
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet agent -t&lt;/code&gt; they’re reminded of where OpenVox came from.&lt;/li&gt;
  &lt;li&gt;If someone using the system &lt;em&gt;were&lt;/em&gt; to be confused by the name, it would likely
be to Perforce’s benefit. In other words, if someone trialled OpenVox and typed
‘puppet’ a whole bunch, there is a small chance that when they submit an order
to their purchasing department that they might accidentally request a Puppet
purchase. It would never go the other direction.&lt;/li&gt;
  &lt;li&gt;If we had to change all the commands and paths today, then the ecosystem would
be forced to write complex abstractions or explanations with every module, tool,
tutorial, README, guide, etc. It’s more likely that they’d take the easy way
out and choose one or the other, thereby fragmenting the ecosystem.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last point deserves more explanation. OpenVox and Puppet by themselves aren’t
terribly useful. Their value comes from the modules that enable the management of
Apache, MySQL, SELinux, etc. So the modules and the tools that make building modules
easier are of utmost importance. All these modules and tools and the documentation
around them are currently written with the expectation of the existing paths.&lt;/p&gt;

&lt;p&gt;If we changed these paths, then every single part of the ecosystem would have to
account for the path change. It’s a comparatively high lift to abstract the name
and paths out, whether it’s in code or docs, so it’s likely that most people will
just choose a side and then search and replace ‘openvox’ for ‘puppet’. This is why
Puppet modules and GitHub repos are all still in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppetlabs&lt;/code&gt; namespace, for example.&lt;/p&gt;

&lt;p&gt;There are two reasons why Perforce would be on the losing end of that schism.
First is that Perforce is now attempting to sell something infinitely more expensive
than its former price of free. Very few people creating and sharing &lt;em&gt;free open source
content&lt;/em&gt; would choose to support that ecosystem rather than the FOSS alternative.
The second reason is that 96% of Puppet’s enterprise customers use Vox Pupuli modules
and, for all practical purposes, 100% of them use Vox Pupuli’s dev tools since
they’re vendored into the PDK. Vox Pupuli is obviously choosing to support OpenVox
even if that has to be at the exclusion of Puppet itself.&lt;/p&gt;

&lt;p&gt;There are also legally defensible reasons. Even if Perforce weren’t convinced by
the reasons already stated, these would almost certainly lead to them losing a
court case if it came to it.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;By the time that someone has researched, and purchased (or not), and downloaded,
and installed, and configured the system, it’s highly unlikely that they would be
confused about whether they were running Puppet or OpenVox. Remember that the goal of
trademark laws are to protect against confusion in the market. Several of the
DuPont factors look at trade channels and the difference between downloading
an OSS tool and talking to Sales is quite stark. If this were the only point
we had to go on, it probably would not be enough. But there are better defenses.&lt;/li&gt;
  &lt;li&gt;Trademark is intended to protect a brand, not functionality. If something is
used for functional purposes, then it cannot under the &lt;em&gt;Functionality Doctrine&lt;/em&gt;
be considered a trademark infringement. The canonical case here is
&lt;a href=&quot;https://en.wikipedia.org/wiki/Sega_v._Accolade&quot;&gt;&lt;em&gt;Sega v. Accolade&lt;/em&gt;&lt;/a&gt; of 1992.
Sega included a “Trademark Security System” which relied on the string “SEGA”
to exist in a known memory address on a game cartridge. If the code were
present, the game system would display a trademark notification and play the
game. If it were not present, then the game wouldn’t play. Accolade added the
code to their games and won the court case when Sega sued them for trademark
infringement because the trademark was used for functionality.&lt;/li&gt;
  &lt;li&gt;Command names and file paths are a functional interface to a piece of software.
In other words, if a tool or system wants to integrate with Puppet, then it will
often do so by configuring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet.conf&lt;/code&gt; or running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppet&lt;/code&gt; commands. If we
are to build a Puppet-compatible product, then it has to use the same command
names and paths. Our industry is littered with examples of this going back for
many decades. Think about how many MTAs install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/sbin/sendmail&lt;/code&gt;, for example.
On my Mac, that’s provided by Postfix. The actual Sendmail company, now owned
by my former employer Proofpoint, has had three decades to dispute that use and
has not. Another example is MySQL, owned by what might be the most litigious
company in existence (Oracle.) When MariahDB forked away, it was ten years
before they bothered to replace the admin commands like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqladmin&lt;/code&gt; and even
today they ship symlinks so you can &lt;em&gt;still run that command&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;the-path-forward&quot;&gt;The path forward&lt;/h2&gt;

&lt;p&gt;We have no concerns about trademark infringement today. The OpenVox PBX system is
in an entirely different industry and won’t be confused with the OpenVox configuration
management system. Perforce and OpenVox are both incentivized for us to keep using
the existing command names and paths. And even if they weren’t, there is significant
legal precedence to defend our use.&lt;/p&gt;

&lt;p&gt;With that said, it would benefit our own brand to have our own command names and
paths. Over the next year or so, we have a roadmap to achieving this safely and
with as little community disruption as possible. We will first build an
integration acceptance test framework so that we can tell quickly when we screw
it up. Then we’ll gradually start replacing commands and paths with our own
versions while leaving symlinks in place so that integrations continue to work.
Then finally, we’ll extract those symlinks into an optional
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;openvox-puppet-compat&lt;/code&gt; package that will likely never go away.&lt;/p&gt;

&lt;p&gt;In order to make Puppet extensions universal, we aren’t planning to ever change
the internal file hierarchy that makes up the documented API. And if that ever
does change, we’ll do it in cooperation with Perforce to retain compatibility.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Paying their fair share</title>
    <link href="https://overlookinfratech.com/2025/02/28/paying-their-fair-share/"/>
    <id>https://overlookinfratech.com/2025/02/28/paying-their-fair-share</id>
    <updated>2025-02-28T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;The other day someone shared a Puppet Community Slack post in which a very
senior Perforce sales engineer stated plainly that the reason for their
&lt;a href=&quot;https://overlookinfratech.com/2024/11/08/sequestered-source/&quot;&gt;sequestered source shenanigans&lt;/a&gt;
is that they’re “asking open source users to pay their fair share.” Now, I don’t
want to sound like I’m poking fun at this particular corporate flunky, he’s only
repeating what he’s clearly been told over and over again, but What. The. Fuck.&lt;/p&gt;

&lt;p&gt;Besides making it exceedingly clear that the only thing that Perforce
understands about open source communities is a bunch of people who happen to own
wallets or purses, this also demonstrates a devastating lack of understanding of
their own product. Let’s break it down, shall we?&lt;/p&gt;

&lt;p&gt;What you think of as a big beastly monolithic application called Puppet
Enterprise is mostly a lot of open source tools jammed together with some
proprietary business logic duct tape and baling wire. No surprise there, that’s
how most enterprise apps are built these days.&lt;/p&gt;

&lt;p&gt;The puppetserver application is business logic implemented in the &lt;a href=&quot;https://github.com/sponsors/richhickey&quot;&gt;open source
Clojure runtime&lt;/a&gt;. It drives puppet via
the &lt;a href=&quot;https://github.com/sponsors/headius&quot;&gt;open source jRuby runtime&lt;/a&gt;.
These are packaged up into a jar with a lot of open source components like the
&lt;a href=&quot;https://www.bouncycastle.org/engage/donate/&quot;&gt;open source Bouncy Castle&lt;/a&gt; which
is of course built on top of the &lt;a href=&quot;https://openssl-foundation.org/donate/&quot;&gt;open source OpenSSL
library&lt;/a&gt;. This jar is then run with the
&lt;a href=&quot;https://openjdk.org/guide/#reviewing-and-sponsoring-a-change&quot;&gt;open source openjdk Java
runtime&lt;/a&gt;. To build
it, they use the &lt;a href=&quot;https://www.jenkins.io/donate/&quot;&gt;open source Jenkins&lt;/a&gt; CI/CD
platform. Packages are generated with the &lt;a href=&quot;https://github.com/sponsors/jordansissel&quot;&gt;open source
fpm&lt;/a&gt; tool.&lt;/p&gt;

&lt;p&gt;And that’s only puppetserver. The puppet-agent package uses open source nssm
(now defunct) to run on Windows because they couldn’t be arsed to write their
own service management. PuppetDB is just a business logic wrapper over the &lt;a href=&quot;https://www.postgresql.org/about/donate/&quot;&gt;open
source PostgreSQL&lt;/a&gt; database server
which was chosen because it was the only one with the primitives they needed.
The Puppet Enterprise Console is built on the &lt;a href=&quot;https://emberjs.com/sponsors/&quot;&gt;open source
Ember&lt;/a&gt; framework. I could go on listing the projects
that Puppet depends on, but that would easily triple the length of this article.&lt;/p&gt;

&lt;p&gt;There is very literally not a single part of Puppet Enterprise that stands
alone. More of the PE installer is open source packages than it’s Puppet
Enterprise itself. And yet how much do they contribute back? Do you think they
“pay their fair share?”&lt;/p&gt;

&lt;p&gt;No. It’s zero. When jRuby put out a desperate call for funding, do you think
Perforce chipped in? Have they ever contributed to OpenSSL? Has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@whack&lt;/code&gt; ever
seen a dime for the thousands of packages Puppet built with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fpm&lt;/code&gt;? No. None.
Perforce has never contributed a penny. They’ve just taken and taken.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Update, hey neat! Looks like raising the issue finally got jRuby some support.
Let’s hope that they continue funding their dependencies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote class=&quot;mastodon-embed&quot; data-embed-url=&quot;https://mastodon.social/@headius/114082394611145408/embed&quot; style=&quot;background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;&quot;&gt; &lt;a href=&quot;https://mastodon.social/@headius/114082394611145408&quot; target=&quot;_blank&quot; style=&quot;align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Oxygen, Ubuntu, Cantarell, &#39;Fira Sans&#39;, &#39;Droid Sans&#39;, &#39;Helvetica Neue&#39;, Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 79 75&quot;&gt;&lt;path d=&quot;M74.7135 16.6043C73.6199 8.54587 66.5351 2.19527 58.1366 0.964691C56.7196 0.756754 51.351 0 38.9148 0H38.822C26.3824 0 23.7135 0.756754 22.2966 0.964691C14.1319 2.16118 6.67571 7.86752 4.86669 16.0214C3.99657 20.0369 3.90371 24.4888 4.06535 28.5726C4.29578 34.4289 4.34049 40.275 4.877 46.1075C5.24791 49.9817 5.89495 53.8251 6.81328 57.6088C8.53288 64.5968 15.4938 70.4122 22.3138 72.7848C29.6155 75.259 37.468 75.6697 44.9919 73.971C45.8196 73.7801 46.6381 73.5586 47.4475 73.3063C49.2737 72.7302 51.4164 72.086 52.9915 70.9542C53.0131 70.9384 53.0308 70.9178 53.0433 70.8942C53.0558 70.8706 53.0628 70.8445 53.0637 70.8179V65.1661C53.0634 65.1412 53.0574 65.1167 53.0462 65.0944C53.035 65.0721 53.0189 65.0525 52.9992 65.0371C52.9794 65.0218 52.9564 65.011 52.9318 65.0056C52.9073 65.0002 52.8819 65.0003 52.8574 65.0059C48.0369 66.1472 43.0971 66.7193 38.141 66.7103C29.6118 66.7103 27.3178 62.6981 26.6609 61.0278C26.1329 59.5842 25.7976 58.0784 25.6636 56.5486C25.6622 56.5229 25.667 56.4973 25.6775 56.4738C25.688 56.4502 25.7039 56.4295 25.724 56.4132C25.7441 56.397 25.7678 56.3856 25.7931 56.3801C25.8185 56.3746 25.8448 56.3751 25.8699 56.3816C30.6101 57.5151 35.4693 58.0873 40.3455 58.086C41.5183 58.086 42.6876 58.086 43.8604 58.0553C48.7647 57.919 53.9339 57.6701 58.7591 56.7361C58.8794 56.7123 58.9998 56.6918 59.103 56.6611C66.7139 55.2124 73.9569 50.665 74.6929 39.1501C74.7204 38.6967 74.7892 34.4016 74.7892 33.9312C74.7926 32.3325 75.3085 22.5901 74.7135 16.6043ZM62.9996 45.3371H54.9966V25.9069C54.9966 21.8163 53.277 19.7302 49.7793 19.7302C45.9343 19.7302 44.0083 22.1981 44.0083 27.0727V37.7082H36.0534V27.0727C36.0534 22.1981 34.124 19.7302 30.279 19.7302C26.8019 19.7302 25.0651 21.8163 25.0617 25.9069V45.3371H17.0656V25.3172C17.0656 21.2266 18.1191 17.9769 20.2262 15.568C22.3998 13.1648 25.2509 11.9308 28.7898 11.9308C32.8859 11.9308 35.9812 13.492 38.0447 16.6111L40.036 19.9245L42.0308 16.6111C44.0943 13.492 47.1896 11.9308 51.2788 11.9308C54.8143 11.9308 57.6654 13.1648 59.8459 15.568C61.9529 17.9746 63.0065 21.2243 63.0065 25.3172L62.9996 45.3371Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt; &lt;div style=&quot;color: #787588; margin-top: 16px;&quot;&gt;Post by @headius@mastodon.social&lt;/div&gt; &lt;div style=&quot;font-weight: 500;&quot;&gt;View on Mastodon&lt;/div&gt; &lt;/a&gt; &lt;/blockquote&gt;
&lt;script data-allowed-prefixes=&quot;https://mastodon.social/&quot; async=&quot;&quot; src=&quot;https://mastodon.social/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;To be clear though, that’s not the part of this that’s so offensive. Perforce’s
sponsorship record is pretty standard for the corporate world. Puppet itself,
prior to Perforce, didn’t contribute all that much more. &lt;strong&gt;What &lt;em&gt;is&lt;/em&gt; utterly
offensive is this idea that somehow the very community that built the foundation
that Puppet sits on isn’t “paying their fair share”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let me tell you about “their fair share.” Many of you know that I spent thirteen
years at Puppet and I ran Community for a large part of that. So I can give you
actual real numbers. A full 96% of Puppet’s paid customers uses Vox Pupuli’s
open source community modules. And the remaining 4% would have except their
policies required them to write all their own content.&lt;/p&gt;

&lt;p&gt;When it comes to paid Puppet customers, we should acknowledge how many of them
are customers because of the community. I’ve attended many “vendor appreciation
dinners” where the point of me being there as a community representative was to
make sure that Puppet, Inc continued to remember that investing in community was
required. The single biggest deal Puppet ever closed hinged on a community
interaction that I and some Vox Pupuli members enabled. We talked through a
hypothetical solution to the problem at hand and then I built it on my spare
time as a community member.&lt;/p&gt;

&lt;p&gt;Then if you want, we can get down to raw contributor data and &lt;a href=&quot;https://gist.github.com/binford2k/81fcf15ff62a9bf2c0ed3ad7cec3718e&quot;&gt;I could show you
how many community members contribute to Puppet codebases more even than most
Puppet employees&lt;/a&gt;.
A quick count shows that only 54 of the top 100 were Puppet employees.&lt;/p&gt;

&lt;p&gt;Puppet’s community is the foundation for Puppet Enterprise and the whole
company. When we did the massive upgrade from 3 to 4 and needed all the tooling
to find all the weird edge cases, the community is who pointed out what we
needed to validate and built tools like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;catalog-diff&lt;/code&gt; to give us a starting
point. When we pulled core resources out to modules in Puppet 6 and broke every
testing pipeline in the world, the community worked with Josh to fix it. And
now that Perforce is insisting on shooting its own feet, the community is once
again stepping up to ensure that an open source community actually continues to
exist and help each other out. That community still includes their paid clients
and somehow Perforce continues to lose sight of that fact.&lt;/p&gt;

&lt;p&gt;The fact that the messaging that their representatives share is that open source
community users need to step up and “pay their fair share” is a slap in the face
to the community that has provided the foundation for the entire company to
exist on. Without these people Puppet by Perforce would not exist.&lt;/p&gt;

&lt;p&gt;Puppet by Perforce doesn’t pay their fair share in any way shape or form, and
for them to self-righteously demand that from their users demonstrates how little
they understand the space and how parasitic they are. I wish that they weren’t so
representative of the industry.&lt;/p&gt;

&lt;p&gt;The reality is that open source projects help your company. They accelerate your
GTM (go to market) velocity a TON. And open-sourcing your own product leads to
rapid adoption by open source community users who give you so much free marketing
and product feedback and are the final line of QA before changes hit
your paying customers. All in addition to the code and docs and expertise
contributions. &lt;em&gt;But this doesn’t come without cost to you&lt;/em&gt;. If you use the
benefits of open source software then you have to pay the costs of open source
software. It’s not a lot. It just means that you have to understand that the
value you derive from open source communities is inherently a function of your
product being freely available. When the vultures swoop in and try to alter that
power balance, then you lose the benefits of the open source community.&lt;/p&gt;

&lt;p&gt;And before you demand a “fair share” from your users, make sure that you’re
doing the same. If your company uses open source software then contribute back.
Either sponsor the project or contribute code, resources, or time. And if your
community contributes code, or docs, or troubleshooting, or anything then
freaking appreciate them for it.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: CfgMgmtCamp wrapup</title>
    <link href="https://overlookinfratech.com/2025/02/17/cfgmgmtcamp-wrapup/"/>
    <id>https://overlookinfratech.com/2025/02/17/cfgmgmtcamp-wrapup</id>
    <updated>2025-02-17T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Well, we’re back from Belgium after another successful week of conferences.
First we attended FOSDEM in Brussels, then we hustled down to Ghent for the
CfgMgmtCamp speaker’s dinner. This year is special to me because it’s the first
year that I’ve not gone to represent someone else. It was also special because
the atmosphere was so positive. There were a ton of great talks, more
sponsorship dollars, attendance was up, more people showed up for the optional
third day add-on than ever before, and people responded really well to the talk
lineup. Chef even showed up to try and rebuild their community engagement!&lt;/p&gt;

&lt;p&gt;In other words, it felt a bit like a revival. But I’ll come back to that. (Or you
can &lt;a href=&quot;#insights&quot;&gt;skip directly to it&lt;/a&gt;, just like online recipes!)&lt;/p&gt;

&lt;h2 id=&quot;main-stage-panel-at-fosdem&quot;&gt;Main stage panel at FOSDEM&lt;/h2&gt;

&lt;p&gt;It started on Jan. 21, 10 days before FOSDEM. There were some last minute schedule changes that I won’t
&lt;a href=&quot;https://web.archive.org/web/20250120113301/https://fosdem.org/2025/schedule/event/fosdem-2025-4507-infusing-open-source-culture-into-company-dna-a-conversation-with-jack-dorsey-and-manik-surtani-block-s-head-of-open-source/&quot;&gt;speculate&lt;/a&gt;
about
&lt;a href=&quot;https://web.archive.org/web/20250123225543/https://fosdem.org/2025/schedule/event/fosdem-2025-4507-infusing-open-source-culture-into-company-dna-a-conversation-with-jack-dorsey-and-manik-surtani-block-s-head-of-open-source/&quot;&gt;too much&lt;/a&gt;
&lt;sup&gt;(they’re the same link, just two days apart)&lt;/sup&gt;
and I was invited to participate in a panel discussion about project forks, why
they happen, what they can do to the community and so on. Read more about it and
see the recording right on the
&lt;a href=&quot;https://fosdem.org/2025/schedule/event/fosdem-2025-5258-forked-communities-project-re-licensing-and-community-impact/&quot;&gt;FOSDEM schedule page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What a fun experience! &lt;a href=&quot;https://hachyderm.io/@geekygirldawn&quot;&gt;Dawn&lt;/a&gt; was the
original architect of the Puppet community and was my boss for all of a hot
minute when I moved onto the team. But somehow this is the first time I’ve
shared a stage with her. Everyone on stage was super knowledgeable and I learned
a ton. In particular, Stephen Walli
&lt;a href=&quot;https://medium.com/@stephenrwalli/forked-communities-whose-property-is-it-anyway-2ddee71f2ef1&quot;&gt;explains that&lt;/a&gt;
statistically two criteria predict the success of a community fork&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The community is put into existential crisis by actions of the original IP holder, and&lt;/li&gt;
  &lt;li&gt;One or more respected non-employee members of the community step up to serve as anchors.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This bodes very well for the future of OpenVox.&lt;/p&gt;

&lt;h2 id=&quot;a-busy-week-in-ghent&quot;&gt;A busy week in Ghent&lt;/h2&gt;

&lt;p&gt;The main stage lineup at CfgMgmtCamp was fascinating. First,
&lt;a href=&quot;https://hachyderm.io/@hazelweakly&quot;&gt;Hazel Weakly&lt;/a&gt;
talked about the &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/39GGMS/&quot;&gt;humanity behind software, configuration, and infrastructure
management&lt;/a&gt;. This is an
interesting perspective to me. As I see it, humanity is the unsaid reason
behind the devops movement in the first place but we’ve now shifted far away
from it. It’s no surprise that Puppet’s slogan used to be the human-forward
&lt;em&gt;“freeing you to do what the robots can’t”&lt;/em&gt; and now it’s bland corporate
scare-words &lt;em&gt;“avoid business disruption and minimize downtime.”&lt;/em&gt;&lt;/p&gt;

&lt;iframe width=&quot;280&quot; height=&quot;157&quot; style=&quot;float: right; clear: both; margin: 0.15rem 0.5rem;&quot; src=&quot;https://www.youtube.com/embed/rt66RLqSBKI?si=SG-EXzgELadMEX0O&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;The thematic arc of the morning took us through these ideals we held when
starting the devops movement, to the nitty gritty of OpenTofu’s &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/T7JBSA/&quot;&gt;escape from the
corporate thumbscrews&lt;/a&gt; and
recapturing their own personal power, to the &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/R7CKYH/&quot;&gt;real value of a content
ecosystem&lt;/a&gt; and musing on a
better way to build sustainable ecosystems.&lt;/p&gt;

&lt;iframe width=&quot;280&quot; height=&quot;157&quot; style=&quot;float: right; clear: both; margin: 0.15rem 0.5rem;&quot; src=&quot;https://www.youtube.com/embed/UKqAYhV9bqk?si=aEdqPvh95zTGHztD&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Of course, the rest of the conference was just as good. We had two full days of
two solid Puppet / OpenVox tracks covering all kinds of topics, from the
technical to the speculative to the damage control. On the main stage Adam
talked about the
&lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/WRZL3U/&quot;&gt;hypergraph of the future&lt;/a&gt;.
James kept you on your toes by
&lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/DQUHEL/&quot;&gt;throwing fire again&lt;/a&gt;.
There were a &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/RTV7UM/&quot;&gt;couple&lt;/a&gt;
of &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/NXJTDG/&quot;&gt;talks&lt;/a&gt; about compliance.
Stahnke showed us how he put his EPEL and Puppet knowledge to work
&lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/YUVUTN/&quot;&gt;with NixOS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But there was still a vibe running through. Florian talked about
&lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/FGLTVM/&quot;&gt;toxic corporate environments&lt;/a&gt;
and Nick explained how &lt;a href=&quot;https://cfp.cfgmgmtcamp.org/ghent2025/talk/UWREUC/&quot;&gt;“source available” is such a woefully inadequate
substitute for open source&lt;/a&gt;
and ways to better appreciate your contributors. A lot of talks hinted at
disillusionment with corporatism and the current state of the world. Lunch
conversations debated the merits of various open source licenses and whether or
not the GPL would prevent the licensing smash-and-grab ploys that so many
once-great companies are pulling today. I lost track of how many people thanked
me for my “yes, I’m a dirty hippie” slide.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/posts/vox_dinner.jpeg&quot; alt=&quot;Vox Pupuli Dinner&quot; class=&quot;img left w30&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I even overheard someone at a table nearby talking about their challenges in
forming a tech union. I didn’t catch who or where it was, so if this was you,
I’d love to hear more about it.&lt;/p&gt;

&lt;p&gt;And that’s not even mentioning the overwhelming interest in
&lt;a href=&quot;https://voxpupuli.org/openvox/&quot;&gt;OpenVox&lt;/a&gt;. Our community dinner (traditionally
the &lt;em&gt;Puppet Community&lt;/em&gt; and now the &lt;em&gt;Vox Pupuli Community&lt;/em&gt;) filled the first
floor of the Royal India. This was a nice healthy increase from years past and
brought together several generations of Puppet / OpenVox community
collaborators.&lt;/p&gt;

&lt;p&gt;Some people were there for their first time and some had been there since the
beginning; back when CfgMgmtCamp was still called a Puppet Camp and none of us
had been acquired by private equity yet. But we were all united by two common
interests: 1) keeping a community alive despite the best efforts of Puppet’s
current owner to repeatedly shoot its own feet, and 2) excellent Indian cuisine
enjoyed with friends.&lt;/p&gt;

&lt;p class=&quot;callout right quote&quot;&gt;For context: the conference is two days long, but they book the venue for a
third day to make space for mini-events to tag along and colocate. Puppet has
traditionally used this for a Puppet Community Day; Ansible and Foreman have
similar events.&lt;/p&gt;

&lt;p&gt;The optional third day is where this enthusiasm really shone through though. Not
only were there more people who stayed for the third day than in years past, but
every single person left the Puppet Community Day to join the OpenVox working
session breakout. And as a measure of their commitment, at 5pm there were still
40-50 people in the room still hard at it, which is unheard of. Most people
trickle out to catch their trains during the day.&lt;/p&gt;

&lt;p&gt;We did not get quite as much done as we’d hoped, but everyone had the opportunity to
participate and we set the groundwork for a stable and healthy project. In short,
we adopted a &lt;a href=&quot;https://fedoraproject.org/wiki/Creating_a_Fedora_SIG&quot;&gt;Fedora SIG style governance&lt;/a&gt;
and decided to remain an independent project via
&lt;a href=&quot;https://opencollective.com/vox-pupuli&quot;&gt;OpenCollective&lt;/a&gt; for the time being. As we
mature a bit, we’ll continue investigating the Linux Foundation and other options.&lt;/p&gt;

&lt;p&gt;Notes for the entire third day, including the Puppet Community Day and the
OpenVox working session breakout can be seen &lt;a href=&quot;https://github.com/OpenVoxProject/planning/wiki/2025-Community-Day-at-CfgMgmtCamp&quot;&gt;in our wiki&lt;/a&gt;,
thanks to &lt;a href=&quot;https://fosstodon.org/@genebean&quot;&gt;Gene Liverman&lt;/a&gt; and &lt;a href=&quot;https://github.com/alexjfisher&quot;&gt;Alex Fisher&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;insights&quot;&gt;Insights&lt;/h2&gt;

&lt;p&gt;I think the positive vibes were mainly the result of three different factors.&lt;/p&gt;

&lt;h3 id=&quot;first-the-obvious&quot;&gt;First, the obvious&lt;/h3&gt;

&lt;p&gt;I don’t need to spend much time on this. With the media coverage and the furor
around the clumsy way Perforce is stumbling through its community changes and the
blatant money-grab, it’s no surprise to see excitement in our community. We shall
see how much of that excitement is lasting.&lt;/p&gt;

&lt;h3 id=&quot;whats-old-is-new-again&quot;&gt;What’s old is new again&lt;/h3&gt;

&lt;p&gt;For years, it seemed inevitable; cloud was eating our lunch. Every workload was
becoming cloud-native, whether or not it really fit there, and the idea of managing
configuration of long-lived instances was beginning to feel quaint. But there was
a lot of foreshadowing, especially for those of us who’d been around for a few
boom-bust cycles. A lot of the same problems being solved, a lot of the same challenges
being (re-)discovered, opaque builds, semi-reproducible artifacts. And so much of
it is tied together with not much more than the shell scripts I used to build production
&lt;a href=&quot;https://en.wikipedia.org/wiki/Chroot&quot;&gt;chroots&lt;/a&gt; back in the day.&lt;/p&gt;

&lt;p&gt;The expected savings from cloud-native workflows haven’t really materialized either.
If your workload is suited for, designed for, and optimized for the cloud then it
can often be cheaper. But costs that were trivial at testing or prototyping stages
have a tendency to unexpectedly balloon out of control at scale.&lt;/p&gt;

&lt;p&gt;It’s not that cloud is bad. We’re actually moving a lot of workflows (such as the
&lt;a href=&quot;https://github.com/OpenVoxProject/puppet-runtime/blob/main/.github/workflows/build.yml&quot;&gt;OpenVox build pipelines&lt;/a&gt;) to containers and it’s drastically simplifying what we’re doing. But
now that the craze is dying down, people are realizing that it’s not the be-all
end-all that it was promised to be. There are a ton of workflows well-suited to
cloud &amp;amp; containers, especially transient and stateless tasks. But there are also
a lot of workloads that benefit from being on-prem, or on long-lived stateful
instances, or just don’t need the layers and layers of complexity.&lt;/p&gt;

&lt;p&gt;And for these jobs, good old fashioned configuration management is right here
waiting with its predictable pricing. The pendulum is swinging back to a hybrid
infrastructure and we’ll all benefit from playing in the same sandbox. This year
we saw a resurgence in config management community investment. But we also had
a full Kubernetes track. And I find that very promising.&lt;/p&gt;

&lt;h3 id=&quot;the-industry-the-world-and-everything&quot;&gt;The industry, the world, and everything&lt;/h3&gt;

&lt;p&gt;There’s a lot going on right now. There’s somehow a blustery orange man-baby in
the American White House again. For some unexplained reason, that gives Elon
Musk the ability to go Twitter on the American government and slash essential
departments protecting everything from consumer rights to the environment. “AI”
is unsustainably disrupting industries and exploding the wealth gap. A lot of
companies &amp;amp; industries aren’t fully recovered from COVID yet.&lt;/p&gt;

&lt;p&gt;And right in the middle of that are company after company turning to corporate
vampires and sucking the open source communities that built them dry. Is it any
wonder that people are activating?&lt;/p&gt;

&lt;p&gt;Back in the early 2000’s, I regularly protested with the EFF on the Santa Monica
Pier in support of all sorts of digital rights and privacy protections. Not all
the bystanders knew who the EFF was or what we were talking about. But most did
or were curious to hear about it.&lt;/p&gt;

&lt;p&gt;I’m getting some of the same vibes now. People are realizing that they need to take
their interests into their own hands again. VC money has been cheap and easy for
a very long time. And along with the foosball tables, that meant quite a bit of
budget flexibility. It didn’t matter so much if you spent a few days polishing up
a patch to contribute upstream when the runway was so long.&lt;/p&gt;

&lt;p&gt;That’s been on the downswing for a while, but this last year really brought everything
to a head. The investors and board members have come calling and they want their
pound of flesh. And when it comes down to it, your boss cares more about the bottom
line and the market cap than they do about you or your silly open source ideals.
The days of founding a startup with a pair of dice are done. No more &lt;em&gt;Uber but for
dogwalking&lt;/em&gt; or &lt;em&gt;AirBnb but for retirement homes&lt;/em&gt; or &lt;em&gt;Tinder but for telling jokes&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Now it’s time for solidarity. People are realizing that the company they once trusted
doesn’t actually have their best interests in mind. They’re seeing through the
permissive license and contributor license agreement blinders. And they’re viscerally
coming to grips with the idea that very few businesses include &lt;em&gt;“provide something
of value”&lt;/em&gt; as an actual business goal.&lt;/p&gt;

&lt;p&gt;And they’re starting to do something about it.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Das neue Puppet Open Source Projekt heißt &quot;OpenVox&quot;</title>
    <link href="https://dev.to/betadots/das-neue-puppet-open-source-projekt-heisst-openvox-2330"/>
    <id>https://dev.to/betadots/das-neue-puppet-open-source-projekt-heisst-openvox-2330</id>
    <updated>2025-01-28T15:00:38+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;&lt;a href=&quot;https://puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet by Perforce&lt;/a&gt; hat &lt;a href=&quot;https://www.puppet.com/blog/open-source-puppet-updates-2025&quot; rel=&quot;noopener noreferrer&quot;&gt;angekündigt&lt;/a&gt;,&lt;br&gt;
dass die Open Source Pakete ab 2025 nur noch nach Zustimmung einer nicht weiter definierten EULA verfügbar gemacht werden sollen. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;In early 2025, Puppet will begin to ship any new binaries and packages developed by our team to a private, hardened, and controlled location. Community contributors will have free access to this private repo under the terms of an End-User License Agreement (EULA) for development use.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Dabei muss berücksichtigt werden, dass die kostenfreie Variante nur für maximal 25 Nodes gilt.&lt;br&gt;
Wer mehr Systeme mit Puppet Open Source verwalten will, benötigt eine &lt;code&gt;Puppet Labs Support Commercial License&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Die Open Source Community hat das Gespräch mit dem Puppet Management gesucht.&lt;/p&gt;

&lt;p&gt;Hier das Resultat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Das neue Puppet Open Source Projekt heißt &lt;code&gt;OpenVox&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Vorbereitung&lt;/li&gt;
&lt;li&gt;Aus Puppet wird OpenVox und Puppet Open Source&lt;/li&gt;
&lt;li&gt;Source Code, Pakete und Container&lt;/li&gt;
&lt;li&gt;Ich nutze Puppet Open Source. Was muss ich tun?&lt;/li&gt;
&lt;li&gt;Puppet Open Source&lt;/li&gt;
&lt;li&gt;OpenVox&lt;/li&gt;
&lt;li&gt;Die weitere Entwicklung&lt;/li&gt;
&lt;li&gt;Wie kann man helfen?&lt;/li&gt;
&lt;li&gt;Professioneller Support&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Vorbereitung
&lt;/h2&gt;

&lt;p&gt;Vor dem ersten Gespräch mit Puppet hat sich die Community im Vorfeld besprochen und analysiert, ob man als Open Source Projekt in der Lage sein kann, die weitere Entwicklung zu stemmen.&lt;br&gt;
Das Feedback aus der Open Source Community und von Open Source Usern war sehr positiv.&lt;/p&gt;

&lt;p&gt;Einige Nutzer haben sogar direkt angekündigt, dass man Entwickler für dedizierte Aufgaben zur Verfügung stellen möchte.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aus Puppet wird OpenVox und Puppet Open Source
&lt;/h2&gt;

&lt;p&gt;Die Open Source Community hat sich mit Puppet by Perforce darauf geeinigt, dass die weitere Entwicklung von Puppet in einem Fork unter der Verwaltung der Open Source Community erfolgen soll.&lt;/p&gt;

&lt;p&gt;Nach einer &lt;a href=&quot;https://github.com/OpenVoxProject/planning/discussions/9&quot; rel=&quot;noopener noreferrer&quot;&gt;öffentlichen Abstimmung Ende 2024&lt;/a&gt; innerhalb der Community, hat man sich für den Namen &lt;code&gt;OpenVox&lt;/code&gt; entschieden und beschlossen, dass die Entwicklung innerhalb der bereits existierenden &lt;a href=&quot;https://voxpupuli.org&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;Vox Pupuli&lt;/code&gt; Puppet Open Source Community&lt;/a&gt; auf &lt;a href=&quot;https://github.com/voxpupuli&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub&lt;/a&gt; stattfinden soll.&lt;/p&gt;

&lt;p&gt;Da Perforce die Namensrechte an &lt;code&gt;Puppet&lt;/code&gt; besitzt, hat man gemeinsam mit Perforce entschieden, dass die Pakete, die durch das OpenVox Projekt erstellt werden, nicht mehr &lt;code&gt;Puppet&lt;/code&gt; im Namen haben sollen.&lt;br&gt;
Dies ist auch in Hinsicht auf Differenzierbarkeit der Herkunft der Pakete sinnvoll.&lt;br&gt;
Die Open Source Pakete von Perforce werden weiterhin &lt;code&gt;puppet-agent&lt;/code&gt;, &lt;code&gt;puppetdb&lt;/code&gt; und &lt;code&gt;puppetserver&lt;/code&gt; heißen.&lt;/p&gt;

&lt;p&gt;Gemeinsam mit Perforce wurde vereinbart, dass man sowohl die Kommandozeile, wie auch Namen der Konfigurationsdateien und die Schnittstellen zur Erweiterung von Puppet ( &lt;code&gt;/lib/puppet&lt;/code&gt; in Modulen) zu existierenden Installation und Code kompatibel halten möchte.&lt;/p&gt;

&lt;p&gt;Die weitere Kompatibilität soll durch ein &lt;code&gt;Language Steering Committee&lt;/code&gt; sichergestellt werden, in dem alle Beteiligten gemeinsam über Änderungen an der Puppet DSL oder Inkompatibilitäten entscheiden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Code, Pakete und Container
&lt;/h2&gt;

&lt;p&gt;Der frei verfügbare &lt;a href=&quot;https://github.com/puppetlabs&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet Open Source Code&lt;/a&gt; wurde in das &lt;a href=&quot;https://github.com/openvoxproject&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenVox GitHub Projekt&lt;/a&gt; geforkt.&lt;/p&gt;

&lt;p&gt;Im ersten Schritt hat das Re-branding stattgefunden.&lt;br&gt;
&lt;strong&gt;Für Anwender ist es wichtig zu wissen, dass Kommandozeilen Tools, Bibliotheken und Puppet Erweiterungen auch unter OpenVox vollständig kompatibel zu Puppet Open Source sind.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Aktuell werden die Pipelines zur Erstellung der RedHat und Debian Pakete auf öffentliche GitHub Actions umgestellt und sollen sehr bald zur Verfügung gestellt werden.&lt;br&gt;
Erste reproduzierbare Build wurden bereits getetstet.&lt;br&gt;
Pakete für Windows und MacOS sind ebenfalls geplant.&lt;/p&gt;

&lt;p&gt;Die Puppet Container (&lt;a href=&quot;https://github.com/voxpupuli/container-puppetserver&quot; rel=&quot;noopener noreferrer&quot;&gt;puppetserver&lt;/a&gt; und &lt;a href=&quot;https://github.com/voxpupuli/container-puppetdb&quot; rel=&quot;noopener noreferrer&quot;&gt;puppetdb&lt;/a&gt;) werden nicht mehr weiter gepflegt!&lt;/p&gt;

&lt;p&gt;Statt dessen werden OpenVox Container zur Verfügung gestellt. Aktuell wird an &lt;a href=&quot;https://github.com/OpenVoxProject/container-openvoxserver&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenVox Server&lt;/a&gt; und &lt;a href=&quot;https://github.com/OpenVoxProject/container-openvoxdb&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenVoxDB&lt;/a&gt; gearbeitet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ich nutze Puppet Open Source. Was muss ich tun?
&lt;/h2&gt;

&lt;p&gt;Es gibt 2 Möglichkeiten:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Puppet Open Source&lt;/li&gt;
&lt;li&gt;OpenVox&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Puppet Open Source
&lt;/h3&gt;

&lt;p&gt;Man muss bei Puppet die EULA akzeptieren und sich ab 25 Nodes um eine Lizenz kümmern.&lt;/p&gt;

&lt;h3&gt;
  
  
  OpenVox
&lt;/h3&gt;

&lt;p&gt;Die Installations-Anleitung für die Migration findet man auf der&lt;br&gt;
&lt;a href=&quot;https://voxpupuli.org/openvox/install/&quot; rel=&quot;noopener noreferrer&quot;&gt;OpenVox Projekt Seite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Da die Konfigurationspfade und -dateien, sowie die Kommandos alle identisch sind, muss keine weitere Anpassung oder Umstellung vorgenommen werden.&lt;br&gt;
Auch der existierende Puppet Code funktioniert ohne Anpassungen.&lt;/p&gt;

&lt;p&gt;Nutzer von OpenVox werden gebeten die aktuellen Pakete und Container in einer Testumgebung zu validieren.&lt;/p&gt;

&lt;h2&gt;
  
  
  Die weitere Entwicklung
&lt;/h2&gt;

&lt;p&gt;Es gibt eine ganze Reihe von Wünschen aus der Community, die bisher leider nicht umgesetzt wurden.&lt;br&gt;
Zuerst möchte man aktuelle Betriebssystemversionen unterstützen. Hierbei hatte die Community oft nach Paketen für Debian stable und testing gefragt.&lt;/p&gt;

&lt;p&gt;Im nächsten Schritt soll der Code von alten Artefakten befreit werden.&lt;br&gt;
Nicht mehr gepflegte Ruby Erweiterungen sollen durch moderne Implementierungen ersetzt werden.&lt;br&gt;
Der Code Zoo (Jruby, Clojure, Java) soll aufgeräumt werden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wie kann man helfen?
&lt;/h2&gt;

&lt;p&gt;Die Puppet Community möchte sich als Professionelles Open Source Projekt etablieren.&lt;br&gt;
Dazu sucht man die finanzielle Unterstützung von Usern, Firmen und öffentlichen Einrichtungen für Open Source Arbeit.&lt;/p&gt;

&lt;p&gt;Auch individuelle Zuarbeit ist gerne gesehen. Es muss Dokumentation geschrieben werden, die Pakete müssen getestet werden.&lt;br&gt;
Die Community braucht die Unterstützung der Anwender, sei es durch Zuarbeit, Code Reviews oder Bug Reports.&lt;/p&gt;

&lt;p&gt;Im nächsten Schritt benötigt man Spiegel Server, Bandbreite und Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Professioneller Support
&lt;/h2&gt;

&lt;p&gt;Open Source Support ist für viele Kunden ein wichtiger Aspekt bei der Wahl der Mittel.&lt;br&gt;
Für OpenVox gibt es bereits jetzt schon 2 Firmen, die Support anbieten:&lt;/p&gt;

&lt;p&gt;Wir von der &lt;a href=&quot;https://www.betadots.de&quot; rel=&quot;noopener noreferrer&quot;&gt;betadots GmbH&lt;/a&gt; unterstützen Kunden bei Planung, Installation, Updates von OpenVox, Puppet Open Source und Puppet Enterprise und kümmern uns um die Weiterbildung der Mitarbeiter im Umfeld von Git und GitLab, Foreman/Katello, Puppet/OpenVox und Linux HA.&lt;/p&gt;

&lt;p&gt;In Amerikanischem Raum wird die Firma &lt;a href=&quot;https://overlookinfratech.com/&quot; rel=&quot;noopener noreferrer&quot;&gt;overlookinfratech&lt;/a&gt; Ansprechpartner für OpenVox Support sein.&lt;/p&gt;

&lt;p&gt;Weitere Firmen werden auf der &lt;a href=&quot;https://voxpupuli.org/openvox/support/&quot; rel=&quot;noopener noreferrer&quot;&gt;Support Übersicht von OpenVox&lt;/a&gt; gelistet werden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;OpenVox ist ein drop-in Ersatz für Puppet Open Source.&lt;/p&gt;

&lt;p&gt;Die Community hat gezeigt, dass sie den Wunsch nach Weiterentwicklung hat und in der Lage ist diese Arbeiten durchzuführen.&lt;/p&gt;

&lt;p&gt;Professioneller Support und Trainings stehen in den USA und in Europa zur Verfügung.&lt;/p&gt;

&lt;p&gt;Open Source ist die Basis für den Erfolg vieler Unternehmen. Deshalb: Unterstützt Eure Community.&lt;/p&gt;

&lt;p&gt;Für weitere Informationen stehen &lt;a href=&quot;//mailto:info@betadots.de&quot;&gt;wir&lt;/a&gt; gerne zur Verfügung.&lt;/p&gt;

&lt;p&gt;Datacenters need automation&lt;/p&gt;

&lt;p&gt;betadots GmbH&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: First release, hot off the presses!</title>
    <link href="https://overlookinfratech.com/2025/01/21/first-release-hot-off-the-presses/"/>
    <id>https://overlookinfratech.com/2025/01/21/first-release--hot-off-the-presses</id>
    <updated>2025-01-21T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;It’s been quite a journey, y’all. But we’re excited to announce the first
release of &lt;a href=&quot;https://voxpupuli.org/openvox/&quot;&gt;OpenVox&lt;/a&gt;, the community-maintained
open source implementation of Puppet. OpenVox 8.11 is functionally equivalent to
Puppet and should be a drop-in replacement. Be aware, of course, that even
though you can type the same commands, use all the same modules and extensions,
and configure the same settings, OpenVox is not yet tested to the same standard
that Puppet is.&lt;/p&gt;

&lt;p&gt;Migrating is fairly simple, just replace the packages following instructions on
the &lt;a href=&quot;https://voxpupuli.org/openvox/install/&quot;&gt;handy dandy new install page&lt;/a&gt;.
You’ll notice that they’re still using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt|yum.overlookinfratech.com&lt;/code&gt;
repositories. As we get our infrastructure built out, these will probably be
moved to the voxpupuli.org namespace. Please don’t use these packages on
critical production infrastructures yet, unless you’re comfortable with
troubleshooting and reporting back on the silly errors we’ve made while
rebranding and rebuilding.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;If you’d like professional assistance in the migration, check out our new
&lt;a href=&quot;/services/&quot;&gt;OpenVox migration service&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We consider OpenVox a soft-fork because we intend to maintain downstream
compatibility for as long as we are able. As such, Vox Pupuli is working with
Perforce create a Puppet™️ Standards Steering Committee to set the direction of
features and language evolutions.&lt;/p&gt;

&lt;p&gt;The OpenVox project goals are pretty straightforward and aim to alleviate pain
points observed by the community over the last few years:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Modernizing the OpenVox codebase and ecosystem including supporting current operating systems.&lt;/li&gt;
  &lt;li&gt;Recentering and focusing on community requirements; user needs will drive development.&lt;/li&gt;
  &lt;li&gt;Democratizing platform support by allowing community members to contribute what they need instead of waiting for business requirements to align.&lt;/li&gt;
  &lt;li&gt;Maintaining an active and responsive open source community like the rest of Vox Pupuli’s namespace.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;migration-service&quot;&gt;&lt;strong&gt;Migration Service&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Overlook InfraTech is an actual company with bills to pay and everything, so you
knew there was going to be a plug here eventually. But we’ll keep it short and
tasteful. We have been instrumental to the OpenVox fork right from the very
beginning and we know inside and out what changes to expect. We are best
situated to help you, so if you’d like professional assistance, or even just
someone to look over your (virtual) shoulder during the process, we have a very
&lt;a href=&quot;/services/&quot;&gt;reasonably priced migration service&lt;/a&gt;.
Whether you’re ready to jump in and try it out today, or you’d rather wait a bit
and let the dust settle, we’ll be here to help.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: The Ruby side of Puppet - Part 3 - Custom Types and Providers</title>
    <link href="https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma"/>
    <id>https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma</id>
    <updated>2025-01-20T18:50:29+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is the final post in a three-part serie, covering the concepts and best practices for extending &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; using &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;custom facts&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;custom functions&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;custom types&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7&quot;&gt;Part 1&lt;/a&gt; explores how to create custom facts, which allow nodes to send information to the Puppet Server.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7&quot;&gt;Part 2&lt;/a&gt; discusses building custom functions to process data or execute specific tasks.&lt;/p&gt;

&lt;p&gt;Part 3 (this post) focuses on custom types and providers, allowing you to extend Puppet&#39;s DSL and control system resources.&lt;/p&gt;




&lt;p&gt;Types are at the core of Puppet’s declarative DSL. Types describe the desired state of the system, targeting specific, configurable parts (sometimes OS-specific). Puppet provides a set of &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/type.html&quot; rel=&quot;noopener noreferrer&quot;&gt;core types&lt;/a&gt;, available with any Puppet agent installation.&lt;/p&gt;

&lt;p&gt;Some of the core types include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file&lt;/li&gt;
&lt;li&gt;user&lt;/li&gt;
&lt;li&gt;group&lt;/li&gt;
&lt;li&gt;package&lt;/li&gt;
&lt;li&gt;service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Create Custom Types and Providers&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;There are several reasons to develop custom types and providers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoid relying on the often unreliable or hard-to-maintain &lt;code&gt;exec&lt;/code&gt; resources&lt;/li&gt;
&lt;li&gt;Manage an application that requires CLI commands for configuration.&lt;/li&gt;
&lt;li&gt;Handle configurations with highly specific syntax that existing Puppet types cannot manage (like &lt;code&gt;file&lt;/code&gt; or &lt;code&gt;concat&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Content&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;General concepts&lt;/li&gt;
&lt;li&gt;Types and providers in modules&lt;/li&gt;
&lt;li&gt;Type description&lt;/li&gt;
&lt;li&gt;Provider implementation&lt;/li&gt;
&lt;li&gt;Using custom types in Puppet DSL&lt;/li&gt;
&lt;li&gt;Resource API&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  General concepts
&lt;/h2&gt;

&lt;p&gt;A custom type consists of two main parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type definiton&lt;/strong&gt;: This defines how the type will be used in the Puppet DSL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider implementation(s)&lt;/strong&gt;: One or more providers per type define how to interact with the system to manage resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Type definition
&lt;/h3&gt;

&lt;p&gt;The type describes how Puppet resources are declared in the DSL. For example, to manage an application’s configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;param&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the type definition, you typically have a &lt;strong&gt;namevar&lt;/strong&gt; (a key identifier) and mutiple &lt;strong&gt;parameters&lt;/strong&gt; and &lt;strong&gt;properties&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namevar&lt;/strong&gt;: The key identifying the resource instance in the type declaration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties&lt;/strong&gt;: These represent something measurable on the target system, such as a user’s UID or GID, or an application’s config value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameters&lt;/strong&gt;: Parameters influence how Puppet manages a resource but don’t directly map to something measurable on the system. For example, &lt;code&gt;manage_home&lt;/code&gt; in the &lt;code&gt;user&lt;/code&gt; type is a parameter that affects Puppet’s behavior but isn’t a property of the user account.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference between parameters and properties is nicely described on the &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types#custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet Type/Provider development page&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Properties:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&quot;Properties correspond to something measurable on the target system. For example, the UID and GID of a user account are properties, because their current state can be queried or changed. In practical terms, setting a value for a property causes a method to be called on the provider.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Parameters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&quot;Parameters change how Puppet manages a resource, but do not necessarily map directly to something measurable. For example, the user type’s managehome attribute is a parameter — its value affects what Puppet does, but the question of whether Puppet is managing a home directory isn’t an innate property of the user account.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our example, the property is the value of the config setting, whereas the parameter specifies application attributes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider implementations
&lt;/h3&gt;

&lt;p&gt;Providers define the mechanisms for managing the state of the resources described by types. They handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Determining whether the resource already exists.&lt;/li&gt;
&lt;li&gt;Creating or removing resources.&lt;/li&gt;
&lt;li&gt;Modifying resources.&lt;/li&gt;
&lt;li&gt;Optionally, listing all existing resources of the type.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Types and providers in modules
&lt;/h2&gt;

&lt;p&gt;Custom types and providers are placed within the &lt;code&gt;lib/puppet&lt;/code&gt; directory of a module.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The type file is located in &lt;code&gt;lib/puppet/type&lt;/code&gt; and named after the resource type (&lt;code&gt;app_config.rb&lt;/code&gt; in this case).&lt;/li&gt;
&lt;li&gt;Provider implementations go into the &lt;code&gt;lib/puppet/provider&lt;/code&gt; directory, inside a subdirectory named after the resource type. Each provider is named according to its provider implementation (e.g., &lt;code&gt;ruby.rb&lt;/code&gt;, &lt;code&gt;cli.rb&lt;/code&gt;, &lt;code&gt;cert.rb&lt;/code&gt;, &lt;code&gt;token.rb&lt;/code&gt;, &lt;code&gt;user.rb&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Type: app_config&lt;/li&gt;
&lt;li&gt;Providers:

&lt;ul&gt;
&lt;li&gt;cert&lt;/li&gt;
&lt;li&gt;token&lt;/li&gt;
&lt;li&gt;user
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# &amp;lt;modulepath&amp;gt;/&amp;lt;modulename&amp;gt;
modules/application/
    \- lib/
        \- puppet/
            |- type/
            |    \- app_config.rb
            \- provider/
                \- app_config/
                    |- cert.rb
                    |- token.rb
                    \- user.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Type description
&lt;/h2&gt;

&lt;p&gt;New custom types can be crated using two different API versions.&lt;br&gt;
APIv1 is the old, classic way using getters and setters within the providers.&lt;br&gt;
APIv2 is a new implementation which is integrated into PDK - this implementation is also called &lt;code&gt;Resource API&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the next section we introduce the APIv1 implementation. The Resource API implementation is handled later in this document.&lt;/p&gt;
&lt;h3&gt;
  
  
  APIv1
&lt;/h3&gt;

&lt;p&gt;The traditional method uses &lt;code&gt;Puppet::Type.newtype&lt;/code&gt;, which defines the type.&lt;br&gt;
It is recommended to add the type documentation into the code.&lt;br&gt;
This allows users to run &lt;code&gt;puppet describe &amp;lt;type&amp;gt;&lt;/code&gt; on their system to display the documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ... the code ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Management
&lt;/h3&gt;

&lt;p&gt;The most important feature of a type is to add or remove something from the system.&lt;br&gt;
This is usually handled with the &lt;code&gt;ensure&lt;/code&gt; property.&lt;br&gt;
To enable &lt;code&gt;ensure&lt;/code&gt;, a single line is needed: &lt;code&gt;ensurable&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Namevar
&lt;/h3&gt;

&lt;p&gt;When declaring the type, one must specify a title - one could refer to this as a type instance identifier.&lt;br&gt;
Usually this reflects for something the type is managing.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;betadots&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# &amp;lt;- title
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most simple implementation is to add a parameter with the name &lt;code&gt;:name&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app config setting to manage. see app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passing the &lt;code&gt;namevar: true&lt;/code&gt; key to the parameter is another way to identify a namevar:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;namevar: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;The app config setting key to manage. see app_cli conf --help&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Properties
&lt;/h3&gt;

&lt;p&gt;Next we add the other property. Each property can be validated by its content.&lt;br&gt;
In our demo case we expect a string value.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app config setting to manage. see app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The config value to set.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a valid value&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides this one can provide specific valid values which are validated automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that arrays as property values are validated in a different way:&lt;/p&gt;

&lt;p&gt;From &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types#tandp_properties_and_parameters&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet custom types&lt;/a&gt; website:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By default, if a property is assigned multiple values in an array:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is considered in sync if any of those values matches the current value.&lt;/li&gt;
&lt;li&gt;If none of those values match, the first one is used when syncing the property.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;If all array values should be matched, the property needs the&lt;br&gt;
&lt;code&gt;array_matching&lt;/code&gt;to be set to &lt;code&gt;:all&lt;/code&gt;. The default value is &lt;code&gt;:first&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing values can be done in two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;should&lt;/code&gt; for properties, &lt;code&gt;value&lt;/code&gt; for parameters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; for both, properties and parameters&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We prefer to use the explizit methods as this makes it more clear, whether we deal with a property or a parameter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameters
&lt;/h3&gt;

&lt;p&gt;Parameters are defined in a similar way:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        cli_args =&amp;gt; [&#39;-p&#39;], # persistency
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app config setting to manage. see app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The config value to set.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a valid value&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CLI options to use during command execution.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a an array&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;defaultto&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;-p&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Boolean parameters
&lt;/h3&gt;

&lt;p&gt;Parameter which get a bool value should be handled in a different way to avoid code repetition&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/parameter/boolean&#39;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:force&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:boolean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Parameter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automatic relationships
&lt;/h3&gt;

&lt;p&gt;Within the type we can specify soft dependencies between different types.&lt;/p&gt;

&lt;p&gt;E.g. the app_cli should use a user which must be available on the system&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;autorequire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From now on the new custom type can already be used in Puppet DSL, the compiler will create a catalog but the agent will produce an error, as there are no functional providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent side prerun evaluation
&lt;/h3&gt;

&lt;p&gt;It is possible to have the agent first check some things prior the catalog will be applied.&lt;br&gt;
This is what the &lt;code&gt;:pre_run_check&lt;/code&gt; method can be used for&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pre_run_check&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app.exe&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;App not installed&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;p&gt;When using multiple providers (similar to the package resource) we want to be sure that the provider has all required implementations (features).&lt;br&gt;
A type can require a feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Manage application config. There are three ways
    to authenticate: cert, token, user. cert is the default
    provider.

    Example:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        file     =&amp;gt; &#39;/opt/app/etc/app.cfg&#39;,
        cli_args =&amp;gt; [&#39;-p&#39;], # persistency
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# global feature&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# feature :cli, &quot;The cli feature requires some parameters&quot;, :methods =&amp;gt; [:cli]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app config setting to manage. see app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The config value to set.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a valid value&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# The property config_file must be set when using the cli &lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#   option to configure app.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# The cli provider will check for a feature.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:required_features&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w{cli}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The config file to use.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not an absolute path&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;defaultto: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/etc/app.cfg&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CLI options to use during command execution.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s is not a an array&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;defaultto: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;-p&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provider implementation
&lt;/h2&gt;

&lt;p&gt;Once the type is defined, the provider must handle how the resource is managed. Providers typically implement methods to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if the resource exists (&lt;code&gt;exists?&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Create a new resource (&lt;code&gt;create&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Read existing resource properties (&lt;code&gt;prefetch&lt;/code&gt; or a getter)&lt;/li&gt;
&lt;li&gt;Modify a resource (&lt;code&gt;flush&lt;/code&gt; or a setter).&lt;/li&gt;
&lt;li&gt;Delete a resource (&lt;code&gt;destroy&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types cli provider.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also possible to re-use and extend existing providers classes.&lt;br&gt;
Common code can be placed inside a generic provider (&lt;code&gt;lib/puppet/provider/app_config.rb&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;App_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types cli provider reuses shared/common code from app_config.rb.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to add shared functionality to multiple providers, you can place your code into the PuppetX module directory: &lt;code&gt;lib/puppet_x/&amp;lt;company_name&amp;gt;/&amp;lt;unique class name&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;../../puppet_x/betadots/app_api.rb&#39;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types cli provider reuses shared/common code from app_config.rb.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new provider can be created by inheriting from and extending an existing provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/token.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types token provideris an extension to the api provider.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally one can reuse any provider from any type:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/file.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ini_setting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ruby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types token provideris an extension to the api provider.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Selection of provider
&lt;/h3&gt;

&lt;p&gt;The Puppet Agent must know which provider to use, if multiple providers are present.&lt;br&gt;
You can use of the &lt;code&gt;provider&lt;/code&gt; meta parameterr or let providers perform checks to determine if they are valid for the system. The options include:&lt;/p&gt;

&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;commands&lt;/td&gt;
&lt;td&gt;&lt;code&gt;commands :app =&amp;gt; &quot;/opt/app/bin/app.exe&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the command &lt;code&gt;/opt/app/bin/app.exe&lt;/code&gt; exists. The command can be later used using &lt;code&gt;:app&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confine - exists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :exists =&amp;gt; &quot;/opt/app/etc/app.conf&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the file exists.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confine - bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :true =&amp;gt; /^10\./.match(%x{/opt/app/bin/app.exec version})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if Version is 10.x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confine - Fact&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine &#39;os.family&#39; =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the fact value matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;confing - feature&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :feature =&amp;gt; :cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Valid if the provider has the feature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;defaultfor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaultfor &#39;os.family&#39; =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use this provider as default for Debian-based systems.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Reading and applying configuration
&lt;/h3&gt;

&lt;p&gt;The provider needs the ability to create, read, update and delete individual configuration states (CRUD API).&lt;/p&gt;

&lt;p&gt;Each configuration property needs a &lt;code&gt;getter&lt;/code&gt; (read) and a &lt;code&gt;setter&lt;/code&gt; (modify) method.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   app_config: { &#39;enable_logging&#39;:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     ensure   =&amp;gt; present,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     value    =&amp;gt; true,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   }&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The app_config types cli provider.&quot;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# confine to existing command&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;commands&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe&quot;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;exists?&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;%r{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;%r{^&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ParserError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;found multiple config items found, please fix this&#39;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;set&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;destroy&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;rm&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# getter&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# setter&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;set&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is recommended to also create the &lt;code&gt;instances&lt;/code&gt; class method, which can collect all instances of a resource type into a hash.&lt;br&gt;
The namevar is the hash key, which has a hash of paramters and properties as value.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;yaml&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instances&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attributes_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;key: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;ensure: :present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sometimes it is not possible to collect all instances, such as with the &lt;code&gt;file&lt;/code&gt; resource. In such cases no &lt;code&gt;instance&lt;/code&gt; method is defined.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;instance&lt;/code&gt; method is used indirectly be each type when calling &lt;code&gt;prefetch&lt;/code&gt; to get the acutal configuration and returns the&lt;code&gt;@property_hash&lt;/code&gt; instance variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prefetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows the type getter and setter to read an manipulate the instance variable, instead of writing &lt;code&gt;getter&lt;/code&gt; and &lt;code&gt;setter&lt;/code&gt; methods for each type.&lt;br&gt;
This behavior is added by declaring the &lt;code&gt;mk_resource_methods&lt;/code&gt; class method.&lt;/p&gt;

&lt;p&gt;Once this is implemented you can run the command &lt;code&gt;puppet resource app_config&lt;/code&gt; to retrieve all existing configurations.&lt;/p&gt;
&lt;h3&gt;
  
  
  Refresh events
&lt;/h3&gt;

&lt;p&gt;In some cases it is required to &lt;code&gt;refresh&lt;/code&gt; a resource, for example, to restart a service, remount a mount, or rerun an exec resource. To allow a type/provider to react to a refresh event, it needs special handling.&lt;/p&gt;

&lt;p&gt;Within the type the &lt;code&gt;refreshable&lt;/code&gt; feature must be activated and a &lt;code&gt;refresh&lt;/code&gt; definition is added.&lt;/p&gt;

&lt;p&gt;The following examples are taken from puppet &lt;code&gt;service&lt;/code&gt; type and provider. The feature describes, which provider definition should be executed upon refresh event.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/services.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;feature&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:refreshable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;The provider can restart the service.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:methods&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;refresh&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Only restart if we&#39;re actually running&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;retrieve&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:running&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;restart&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Skipping restart; service is not running&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using custom types in Puppet DSL
&lt;/h2&gt;

&lt;p&gt;Once the custom type and provider are implemented, you can use them in your manifests as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Type declaration
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;enable_logging&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;require&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/etc/app.cfg&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;subscribe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Type reference
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;subscribe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;enable_logging&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Type declaration using lambda and splat operator
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$config_hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;err&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Virtual or exported resource
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app_mount&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{facts[&#39;networking&#39;][&#39;fqdn&#39;]}/srv/app_mount&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;tag&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;blog&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Resource collector
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;App_config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;blog&#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resource API
&lt;/h2&gt;

&lt;p&gt;The modern resource API has one limitation: one can not refresh types!&lt;br&gt;
Besides this it also consists of types and providers which must be placed at the same location as the existing implementation.&lt;/p&gt;
&lt;h3&gt;
  
  
  Resource API Type
&lt;/h3&gt;

&lt;p&gt;The resource API type uses an attributes hash to list all parameters and properties. Within the &lt;code&gt;type&lt;/code&gt; key one can use any of the built-in Puppet data types.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/resource_api&#39;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ResourceApi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app_config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;docs: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      @summary The type to manage app config settings
      @example
      app_config { &#39;key&#39;:
        ensure =&amp;gt; present,
        value  =&amp;gt; &#39;value&#39;,
      }

      This type provides Puppet with the capabilities to manage our application config
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    EOS&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;features: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;attributes: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;ensure: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type:    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Enum[present, absent]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc:    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Whether the setting should be added or removed&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;default: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;present&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type:     &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;String&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc:     &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;The setting to manage&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;behavior: :namevar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Variant[String, Integer, Array]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;The value to set&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Respource API Provider
&lt;/h3&gt;

&lt;p&gt;The most simple solution is to make use of the &lt;code&gt;SimpleProvider&lt;/code&gt; class.&lt;br&gt;
This provider needs up to 4 defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get&lt;/li&gt;
&lt;li&gt;create&lt;/li&gt;
&lt;li&gt;update&lt;/li&gt;
&lt;li&gt;delete
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/resource_api/simple_provider&#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;open3&#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Puppet::Provider::AppConfig::Cli&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ResourceApi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SimpleProvider&lt;/span&gt;
  &lt;span class=&quot;vg&quot;&gt;$app_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app.exe&#39;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Get: fetching all readable config settings into a hash&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Returning data from command&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app.exe list&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ParseError&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Error running command: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@command_result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;%r{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@command_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;---&#39;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;:&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;:&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strip&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&#39;ensure&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;present&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&#39;value&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Create: add a new config setting with name and should value&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Creating &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39; with &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Update: correct an existing setting with name and replace with should value&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Updating &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39; with &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Delete: remove a config setting&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Deleting &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe rm &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Missing error handling. This is just for demo&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Types and providers are not complex. They basically describe the CRUD behavior of Puppet.&lt;br&gt;
The type defines how to use the Puppet DSL, while the provider controls how to check and handle specific settings.&lt;/p&gt;

&lt;p&gt;Please stop using &lt;code&gt;exec&lt;/code&gt; for almost anything that is not super simple. In most cases, a simple type and provider will allow better analysis, error handling and control over what is happening. Besides this: &lt;strong&gt;custom types and providers execute faster&lt;/strong&gt; compared to &lt;code&gt;exec&lt;/code&gt; type usage - far more faster!&lt;/p&gt;

&lt;p&gt;The example application and the working types and providers are available on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The app: &lt;a href=&quot;https://github.com/betadots/workshop-demo-app&quot; rel=&quot;noopener noreferrer&quot;&gt;workshop demo app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The Puppet module with types and providers for the app: &lt;a href=&quot;https://github.com/betadots/workshop-demo-module/tree/ruby_workshop&quot; rel=&quot;noopener noreferrer&quot;&gt;workshop demo module&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;App usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install APP:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-app /opt/app
/opt/app/bin/app.exe

&lt;span class=&quot;c&quot;&gt;# Install Puppet module:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-module &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; ruby_workshop modules/app
&lt;span class=&quot;c&quot;&gt;# Type API V1&lt;/span&gt;
puppet resource app_config &lt;span class=&quot;nt&quot;&gt;--modulepath&lt;/span&gt; modules
&lt;span class=&quot;c&quot;&gt;# Type Resource API&lt;/span&gt;
puppet resource app_config2 &lt;span class=&quot;nt&quot;&gt;--modulepath&lt;/span&gt; modules

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy puppetizing,&lt;br&gt;
Martin&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Die Ruby-Seite von Puppet - Teil 3 - Benutzerdefinierte Typen und Provider</title>
    <link href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp"/>
    <id>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp</id>
    <updated>2025-01-20T18:48:32+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Dies ist der letzte Beitrag in einer dreiteiligen Serie, die die Konzepte und Best Practices für die Erweiterung von &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; mithilfe von &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Fakten&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Funktionen&lt;/a&gt; und &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Typen&lt;/a&gt; sowie &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;Providern&lt;/a&gt; behandelt.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj&quot;&gt;Teil 1&lt;/a&gt; untersucht, wie man benutzerdefinierte Fakten erstellt, die es Knoten ermöglichen, Informationen an den Puppet-Server zu senden.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3&quot;&gt;Teil 2&lt;/a&gt; behandelt den Aufbau benutzerdefinierter Funktionen zur Verarbeitung von Daten oder zur Ausführung spezifischer Aufgaben.&lt;/p&gt;

&lt;p&gt;Teil 3 (dieser Beitrag) konzentriert sich auf benutzerdefinierte Typen und Provider, mit denen Puppets DSL erweitert und Systemressourcen verwaltet werden können.&lt;/p&gt;




&lt;p&gt;Typen stehen im Zentrum der deklarativen DSL von Puppet. Typen beschreiben den gewünschten Zustand des Systems und zielen auf spezifische, konfigurierbare Teile ab (manchmal betriebssystemspezifisch). Puppet bietet eine Reihe von &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/type.html&quot; rel=&quot;noopener noreferrer&quot;&gt;Kern-Typen&lt;/a&gt;, die mit jeder Puppet-Agent-Installation verfügbar sind.&lt;/p&gt;

&lt;p&gt;Einige der Kern-Typen umfassen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file&lt;/li&gt;
&lt;li&gt;user&lt;/li&gt;
&lt;li&gt;group&lt;/li&gt;
&lt;li&gt;package&lt;/li&gt;
&lt;li&gt;service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Warum benutzerdefinierte Typen und Provider erstellen&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Es gibt mehrere Gründe, benutzerdefinierte Typen und Provider zu entwickeln:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vermeiden von den oft unzuverlässigen oder schwer zu wartenden &lt;code&gt;exec&lt;/code&gt;-Ressourcen.&lt;/li&gt;
&lt;li&gt;Verwalten einer Anwendung, die CLI-Befehle zur Konfiguration benötigt.&lt;/li&gt;
&lt;li&gt;Handhaben von Konfigurationen mit hochspezifischer Syntax, die bestehende Puppet-Typen nicht verwalten können (wie &lt;code&gt;file&lt;/code&gt; oder &lt;code&gt;concat&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Inhalt&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Allgemeine Konzepte&lt;/li&gt;
&lt;li&gt;Typen und Provider in Modulen&lt;/li&gt;
&lt;li&gt;Typbeschreibung&lt;/li&gt;
&lt;li&gt;Provider-Implementierung&lt;/li&gt;
&lt;li&gt;Verwendung benutzerdefinierter Typen in der Puppet DSL&lt;/li&gt;
&lt;li&gt;Resourcen-API&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Allgemeine Konzepte
&lt;/h2&gt;

&lt;p&gt;Ein Typ besteht aus zwei Hauptteilen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Typdefinition&lt;/strong&gt;: Diese definiert, wie der Typ in der Puppet DSL verwendet wird.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider-Implementierung(en)&lt;/strong&gt;: Ein oder mehrere Provider pro Typ definieren, wie mit dem System interagiert wird, um Ressourcen zu verwalten.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Typdefinition
&lt;/h3&gt;

&lt;p&gt;Der Typ beschreibt, wie Puppet-Ressourcen in der DSL deklariert werden. Zum Beispiel, um die Konfiguration einer Anwendung zu verwalten:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;param&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;auth_method&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typdefinition haben einen &lt;strong&gt;Namevar&lt;/strong&gt; (ein Schlüsselbezeichner) und mehrere &lt;strong&gt;Parameter&lt;/strong&gt; und &lt;strong&gt;Properties (Eigenschaften)&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Namevar&lt;/strong&gt;: Der Schlüssel, der die Ressourceninstanz in der Typdeklaration identifiziert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Properties (Eigenschaften)&lt;/strong&gt;: Diese repräsentieren etwas Messbares im Zielsystem, wie die UID oder GID eines Benutzers oder einen Konfigurationswert einer Anwendung.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameter&lt;/strong&gt;: Parameter beeinflussen, wie Puppet eine Ressource verwaltet, spiegeln jedoch nicht direkt etwas Messbares im System wider. Zum Beispiel ist &lt;code&gt;manage_home&lt;/code&gt; im &lt;code&gt;user&lt;/code&gt;-Typ ein Parameter, der Puppets Verhalten beeinflusst, aber keine Eigenschaft des Benutzerkontos ist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Der Unterschied zwischen Parametern und Eigenschaften wird auf der &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types#custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet-Typ/Provider-Entwicklungsseite&lt;/a&gt; gut beschrieben:&lt;/p&gt;

&lt;p&gt;Properties:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&quot;Eigenschaften entsprechen etwas Messbarem im Zielsystem. Zum Beispiel sind die UID und GID eines Benutzerkontos Eigenschaften, da ihr aktueller Zustand abgefragt oder geändert werden kann. Praktisch bedeutet das, dass das Festlegen eines Wertes für eine Eigenschaft eine Methode im Provider aufruft.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Parameter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&quot;Parameter ändern, wie Puppet eine Ressource verwaltet, entsprechen jedoch nicht unbedingt direkt etwas Messbarem. Zum Beispiel ist das managehome-Attribut des Benutzertyps ein Parameter – sein Wert beeinflusst, was Puppet tut, aber die Frage, ob Puppet ein Home-Verzeichnis verwaltet, ist keine angeborene Eigenschaft des Benutzerkontos.&quot;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In unserem Beispiel ist die Eigenschaft der Wert des Konfigurationseinstellungen, während der Parameter die Anwendungsattribute angibt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Provider-Implementierungen
&lt;/h3&gt;

&lt;p&gt;Provider definieren die Mechanismen zur Verwaltung des Zustands der durch Typen beschriebenen Ressourcen. Sie behandeln:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bestimmung, ob die Ressource bereits existiert&lt;/li&gt;
&lt;li&gt;Erstellen oder Entfernen von Ressourcen.&lt;/li&gt;
&lt;li&gt;Ändern von Ressourcen.&lt;/li&gt;
&lt;li&gt;Optional das Auflisten aller vorhandenen Ressourcen des Typs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Typen und Provider in Modulen
&lt;/h2&gt;

&lt;p&gt;Benutzerdefinierte Typen und Provider werden im Verzeichnis &lt;code&gt;lib/puppet&lt;/code&gt; eines Moduls platziert.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Die Typdatei befindet sich in &lt;code&gt;lib/puppet/type&lt;/code&gt; und ist nach dem Ressourcentyp benannt (&lt;code&gt;app_config.rb&lt;/code&gt; in diesem Fall).&lt;/li&gt;
&lt;li&gt;Provider-Implementierungen gehen in das Verzeichnis &lt;code&gt;lib/puppet/provider&lt;/code&gt;, in ein Unterverzeichnis, das nach dem Ressourcentyp benannt ist. Jeder Provider ist nach seiner Provider-Implementierung benannt (z.B. &lt;code&gt;ruby.rb&lt;/code&gt;, &lt;code&gt;cli.rb&lt;/code&gt;, &lt;code&gt;cert.rb&lt;/code&gt;, &lt;code&gt;token.rb&lt;/code&gt;, &lt;code&gt;user.rb&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beispiel:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Typ: app_config&lt;/li&gt;
&lt;li&gt;Provider:

&lt;ul&gt;
&lt;li&gt;cert&lt;/li&gt;
&lt;li&gt;token&lt;/li&gt;
&lt;li&gt;user
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# &amp;lt;modulpfad&amp;gt;/&amp;lt;modulname&amp;gt;
modules/application/
    \- lib/
        \- puppet/
            |- type/
            |    \- app_config.rb
            \- provider/
                \- app_config/
                    |- cert.rb
                    |- token.rb
                    \- user.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Typbeschreibung
&lt;/h2&gt;

&lt;p&gt;Neue benutzerdefinierte Typen können mit zwei verschiedenen API-Versionen erstellt werden. APIv1 ist die alte, klassische Methode, die Getter und Setter innerhalb der Provider verwendet. APIv2 ist eine neue Implementierung, die in PDK integriert ist – diese Implementierung wird auch als &lt;code&gt;Resource-API&lt;/code&gt; bezeichnet.&lt;/p&gt;

&lt;p&gt;Im nächsten Abschnitt stellen wir die APIv1-Implementierung vor. Die Implementierung der Ressourcen-API wird später in diesem Dokument behandelt.&lt;/p&gt;

&lt;h3&gt;
  
  
  APIv1
&lt;/h3&gt;

&lt;p&gt;Die traditionelle Methode verwendet &lt;code&gt;Puppet::Type.newtype&lt;/code&gt;, die den Typ definiert. Es wird empfohlen, die Typdokumentation in den Code einzufügen. Dies ermöglicht es den Benutzern, &lt;code&gt;puppet describe &amp;lt;type&amp;gt;&lt;/code&gt; auf ihrem System auszuführen, um die Dokumentation anzuzeigen.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten
    zur Authentifizierung: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ... der Code ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verwaltung
&lt;/h3&gt;

&lt;p&gt;Die wichtigste Funktion eines Typs besteht darin, etwas zum System hinzuzufügen oder zu entfernen. Dies wird normalerweise mit der &lt;code&gt;ensure&lt;/code&gt;-Eigenschaft behandelt. Um &lt;code&gt;ensure&lt;/code&gt; zu aktivieren, ist eine einzige Zeile erforderlich: &lt;code&gt;ensurable&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten
    zur Authentifizierung: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Namevar
&lt;/h3&gt;

&lt;p&gt;Bei der Deklaration des Typs muss ein Titel angegeben werden – dies könnte man als Typinstanz-Identifikator bezeichnen. In der Regel spiegelt dies wider, wofür der Typ verantwortlich ist.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;betadots&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# &amp;lt;- Titel
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Die einfachste Implementierung besteht darin, einen Parameter mit dem Namen &lt;code&gt;:name&lt;/code&gt; hinzuzufügen.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Das Übergeben des Schlüssels &lt;code&gt;namevar: true&lt;/code&gt; an den Parameter ist eine weitere Möglichkeit, einen Namevar zu identifizieren:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;namevar: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Der Schlüssel des Anwendungsconfig-Elements, das verwaltet werden soll. Siehe app_cli conf --help&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Eigenschaften
&lt;/h3&gt;

&lt;p&gt;Als Nächstes fügen wir die andere Eigenschaft hinzu. Jede Eigenschaft kann durch ihren Inhalt validiert werden. In unserem Demo-Fall erwarten wir einen Stringwert.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der zu setzende Konfigurationswert.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein gültiger Wert&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Außerdem kann man spezifische gültige Werte angeben, die automatisch validiert werden:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:enable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newvalue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bitte beachten, dass Arrays als Eigenschaftswerte auf andere Weise validiert werden:&lt;/p&gt;

&lt;p&gt;Von der Website &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types#tandp_properties_and_parameters&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet benutzerdefinierte Typen&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Standardmäßig wird eine Eigenschaft, die mehrere Werte in einem Array zugewiesen bekommt:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Als synchron betrachtet, wenn einer dieser Werte dem aktuellen Wert entspricht.&lt;/li&gt;
&lt;li&gt;Wenn keiner dieser Werte übereinstimmt, wird der erste Wert beim Synchronisieren der Eigenschaft verwendet.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wenn alle Array-Werte übereinstimmen sollen, muss die Eigenschaft &lt;code&gt;array_matching&lt;/code&gt; auf &lt;code&gt;:all&lt;/code&gt; gesetzt werden. Der Standardwert ist &lt;code&gt;:first&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:flags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Der Zugriff auf Werte kann auf zwei Arten erfolgen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;should&lt;/code&gt; für Eigenschaften, &lt;code&gt;value&lt;/code&gt; für Parameter.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; für sowohl Eigenschaften als auch Parameter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wir bevorzugen die expliziten Methoden, da dies klarer macht, ob wir es mit einer Eigenschaft oder einem Parameter zu tun haben.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameter
&lt;/h3&gt;

&lt;p&gt;Parameter werden auf ähnliche Weise definiert:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        cli_args =&amp;gt; [&#39;-p&#39;], # Persistenz
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der zu setzende Konfigurationswert.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein gültiger Wert&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CLI-Optionen, die während der Befehlsausführung verwendet werden sollen.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein Array&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;defaultto&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;-p&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Boolean-Parameter
&lt;/h3&gt;

&lt;p&gt;Parameter, die einen booleschen Wert erhalten, sollten auf andere Weise behandelt werden, um Codewiederholungen zu vermeiden.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/parameter/boolean&#39;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:force&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:boolean&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Parameter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automatische Abhängigkeiten
&lt;/h3&gt;

&lt;p&gt;Innerhalb des Typs können wir weiche Abhängigkeiten zwischen verschiedenen Typen angeben.&lt;/p&gt;

&lt;p&gt;Beispiel: Das &lt;code&gt;app_cli&lt;/code&gt; sollte einen Benutzer verwenden, der im System verfügbar sein muss.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;autorequire&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Von nun an kann der neue benutzerdefinierte Typ bereits in Puppet DSL verwendet werden, der Compiler wird ein Katalog erstellen, aber der Agent wird einen Fehler produzieren, da es keine funktionalen Provider gibt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vorabprüfung auf der Agentenseite
&lt;/h3&gt;

&lt;p&gt;Es ist möglich, dass der Agent zunächst einige Dinge überprüft, bevor der Katalog angewendet wird. Dafür kann die Methode &lt;code&gt;:pre_run_check&lt;/code&gt; verwendet werden.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pre_run_check&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app.exe&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;App nicht installiert&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Merkmale
&lt;/h3&gt;

&lt;p&gt;Bei der Verwendung mehrerer Provider (ähnlich der Ressourcenpakete) wollen wir sicherstellen, dass der Provider alle erforderlichen Implementierungen (Merkmale - Features) hat. Ein Typ kann ein Merkmal erfordern:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;newtype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%q{Verwalten der Anwendungsconfig. Es gibt drei Möglichkeiten,
    sich zu authentifizieren: Zertifikat, Token, Benutzer. Zertifikat ist der Standard
    Provider.

    Beispiel:

      app_config: { &#39;enable_logging&#39;:
        ensure   =&amp;gt; present,
        value    =&amp;gt; true,
        file     =&amp;gt; &#39;/opt/app/etc/app.cfg&#39;,
        cli_args =&amp;gt; [&#39;-p&#39;], # Persistenz
      }
  }&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ensurable&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# globales Merkmal&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# feature :cli, &quot;Das CLI-Merkmal erfordert einige Parameter&quot;, :methods =&amp;gt; [:cli]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Das Anwendungsconfig-Element, das verwaltet werden soll. Siehe app_cli conf --help&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der zu setzende Konfigurationswert.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^\w+/&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein gültiger Wert&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Die Eigenschaft config_file muss gesetzt werden, wenn die CLI&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#   Option zur Konfiguration von App verwendet wird.&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Der CLI-Provider wird nach einem Merkmal suchen.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newproperty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:required_features&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w{cli}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Die zu verwendende Konfigurationsdatei.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expand_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein absoluter Pfad&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;defaultto&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/etc/app.cfg&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;newparam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:array_matching&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;CLI-Optionen, die während der Befehlsausführung verwendet werden sollen.&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ArgumentError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%s ist kein Array&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;defaultto&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;-p&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provider-Implementierung
&lt;/h2&gt;

&lt;p&gt;Sobald der Typ definiert ist, muss der Anbieter steuern, wie die Ressource verwaltet wird. Anbieter implementieren typischerweise Methoden, um:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Überprüfen, ob die Ressource existiert (&lt;code&gt;exists?&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Eine neue Ressource erstellen (&lt;code&gt;create&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Vorhandene Ressourcenattribute lesen (&lt;code&gt;prefetch&lt;/code&gt; oder ein Getter).&lt;/li&gt;
&lt;li&gt;Eine Ressource ändern (&lt;code&gt;flush&lt;/code&gt; oder ein Setter).&lt;/li&gt;
&lt;li&gt;Eine Ressource löschen (&lt;code&gt;destroy&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der CLI-Anbieter des app_config-Typs.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es ist auch möglich, bestehende Anbieterklassen wiederzuverwenden und zu erweitern. Gemeinsamer Code kann in einem generischen Anbieter (&lt;code&gt;lib/puppet/provider/app_config.rb&lt;/code&gt;) platziert werden.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Provider&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;App_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der CLI-Anbieter des app_config-Typs verwendet gemeinsamen Code aus app_config.rb.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wenn eine gemeinsame Funktionalität für mehrere Anbieter hinzugefügt werden soll, kann man den Code im Puppet_X-Modulverzeichnis ablegen: &lt;code&gt;lib/puppet_x/&amp;lt;unternehmens_name&amp;gt;/&amp;lt;eindeutiger Klassenname&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require_relative&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;../../puppet_x/betadots/app_api.rb&#39;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der CLI-Anbieter des app_config-Typs verwendet gemeinsamen Code aus app_config.rb.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ein neuer Anbieter kann erstellt werden, indem er von einem bestehenden Anbieter erbt und diesen erweitert:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/token.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der Token-Anbieter des app_config-Typs ist eine Erweiterung des API-Anbieters.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zusätzlich kann man jeden Anbieter von jedem Typ wiederverwenden:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/file.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ini_setting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ruby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der Datei-Anbieter des app_config-Typs ist eine Erweiterung des API-Anbieters.&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auswahl des Anbieters
&lt;/h3&gt;

&lt;p&gt;Der Puppet-Agent muss wissen, welcher Anbieter zu verwenden ist, wenn mehrere Anbieter vorhanden sind. Man kann den &lt;code&gt;provider&lt;/code&gt;-Meta-Parameter verwenden oder die Anbieter Überprüfungen durchführen lassen, um festzustellen, ob sie für das System gültig sind. Die Optionen umfassen:&lt;/p&gt;

&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Überprüfung&lt;/th&gt;
&lt;th&gt;Beispiel&lt;/th&gt;
&lt;th&gt;Beschreibung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Befehle&lt;/td&gt;
&lt;td&gt;&lt;code&gt;commands :app =&amp;gt; &quot;/opt/app/bin/app.exe&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn der Befehl &lt;code&gt;/opt/app/bin/app.exe&lt;/code&gt; existiert. Der Befehl kann später mit &lt;code&gt;:app&lt;/code&gt; verwendet werden.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - existiert&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :exists =&amp;gt; &quot;/opt/app/etc/app.conf&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn die Datei existiert.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - bool&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :true =&amp;gt; /^10\./.match(%x{/opt/app/bin/app.exec version})&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn die Version 10.x ist.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - Fakt&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine &#39;os.family&#39; =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn der Faktwert übereinstimmt.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Einschränkung - Funktion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;confine :feature =&amp;gt; :cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gültig, wenn der Anbieter die Funktion hat.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;defaultfor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;defaultfor &#39;os.family&#39; =&amp;gt; :debian&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Anbieter als Standard für Debian-basierte Systeme.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Lesen und Anwenden von Konfigurationen
&lt;/h3&gt;

&lt;p&gt;Der Anbieter benötigt die Fähigkeit, einzelne Konfigurationszustände zu erstellen, zu lesen, zu aktualisieren und zu löschen (CRUD-API).&lt;/p&gt;

&lt;p&gt;Jede Konfigurationseigenschaft benötigt eine &lt;code&gt;getter&lt;/code&gt;- (lesen) und eine &lt;code&gt;setter&lt;/code&gt;- (ändern) Methode.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   app_config: { &#39;enable_logging&#39;:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     ensure   =&amp;gt; present,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     value    =&amp;gt; true,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   }&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:app_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provide&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der CLI-Anbieter des app_config-Typs.&quot;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Einschränkung auf bestehenden Befehl&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;commands&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe&quot;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;exists?&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;%r{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;grep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;%r{^&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ParserError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Mehrere Konfigurationselemente gefunden, bitte beheben Sie dies&#39;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;set&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;destroy&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;rm&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# getter&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# setter&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;set&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es wird empfohlen, auch die &lt;code&gt;instances&lt;/code&gt;-Klassenmethode zu erstellen, die alle Instanzen eines Ressourcentyps in einem Hash sammeln kann. Der Namevar ist der Hash-Schlüssel, der einen Hash von Parametern und Eigenschaften als Wert hat.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;yaml&#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;instances&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;YAML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attributes_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;key: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;ensure: :present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Manchmal ist es nicht möglich, alle Instanzen zu sammeln, z. B. beim &lt;code&gt;file&lt;/code&gt;-Ressourcentyp. In solchen Fällen wird keine &lt;code&gt;instance&lt;/code&gt;-Methode definiert.&lt;/p&gt;

&lt;p&gt;Die &lt;code&gt;instance&lt;/code&gt;-Methode wird indirekt von jedem Typ verwendet, wenn &lt;code&gt;prefetch&lt;/code&gt; aufgerufen wird, um die aktuelle Konfiguration zu erhalten und die &lt;code&gt;@property_hash&lt;/code&gt;-Instanzvariable zurückzugeben.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prefetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each_key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instances&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dies ermöglicht es dem Typ, Getter und Setter zu verwenden, um die Instanzvariable zu lesen und zu manipulieren, anstatt für jeden Typ &lt;code&gt;getter&lt;/code&gt;- und &lt;code&gt;setter&lt;/code&gt;-Methoden zu schreiben. Dieses Verhalten wird durch die Deklaration der &lt;code&gt;mk_resource_methods&lt;/code&gt;-Klassenmethode hinzugefügt.&lt;/p&gt;

&lt;p&gt;Sobald dies implementiert ist, kann man den Befehl &lt;code&gt;puppet resource app_config&lt;/code&gt; ausführen, um alle vorhandenen Konfigurationen abzurufen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Refresh-Ereignisse
&lt;/h3&gt;

&lt;p&gt;In einigen Fällen ist es erforderlich, eine Ressource zu &lt;code&gt;aktualisieren&lt;/code&gt; (refresh), beispielsweise um einen Dienst neu zu starten, ein Laufwerk erneut zu mounten oder eine Exec-Ressource erneut auszuführen. Damit ein Typ/Anbieter auf ein Aktualisierungsereignis reagieren kann, ist eine spezielle Behandlung erforderlich.&lt;/p&gt;

&lt;p&gt;Innerhalb des Typs muss die &lt;code&gt;refreshable&lt;/code&gt;-Funktion aktiviert werden, und eine &lt;code&gt;refresh&lt;/code&gt;-Definition wird hinzugefügt.&lt;/p&gt;

&lt;p&gt;Die folgenden Beispiele stammen vom Puppet &lt;code&gt;service&lt;/code&gt; Typ und Anbieter. Die Funktion beschreibt, welche Anbieterdefinition bei einem Aktualisierungsereignis ausgeführt werden soll.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/services.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;feature&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:refreshable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Der Anbieter kann den Dienst neu starten.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:methods&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:restart&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;refresh&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Nur neu starten, wenn wir tatsächlich laufen&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newattr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ensure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;retrieve&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:running&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;restart&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Neustart überspringen; Dienst läuft nicht&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verwendung benutzerdefinierter Typen in Puppet DSL
&lt;/h2&gt;

&lt;p&gt;Sobald der benutzerdefinierte Typ und Anbieter implementiert sind, kann man diesen in Puppet Manifesten wie folgt verwenden:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Typdeklaration
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;enable_logging&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;require&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/etc/app.cfg&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;subscribe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Typreferenz
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;subscribe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;App_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;enable_logging&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Typdeklaration mit Lambda und Splat-Operator
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$config_hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hash&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Virtuelle oder exportierte Ressource
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;@@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_config&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;app_mount&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;ensure&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;present&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;value&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{facts[&#39;networking&#39;][&#39;fqdn&#39;]}/srv/app_mount&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;tag&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;blog&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Ressourcen-Sammler
&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;App_config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;blog&#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&amp;gt;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resourcen-API
&lt;/h2&gt;

&lt;p&gt;Die moderne Ressourcen-API hat eine Einschränkung: Man kann Typen nicht aktualisieren! Außerdem besteht sie aus Typen und Anbietern, die am selben Ort wie die bestehende Implementierung platziert werden müssen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ressourcen-API-Typ
&lt;/h3&gt;

&lt;p&gt;Der Ressourcen-API-Typ verwendet einen Attributs-Hash, um alle Parameter und Eigenschaften aufzulisten. Innerhalb des &lt;code&gt;type&lt;/code&gt;-Schlüssels kann man jeden der integrierten Puppet-Datentypen verwenden.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/type/app_config.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/resource_api&#39;&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ResourceApi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;register_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;app_config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;docs: &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      @summary Der Typ zur Verwaltung von App-Konfigurationseinstellungen
      @example
      app_config { &#39;key&#39;:
        ensure =&amp;gt; present,
        value  =&amp;gt; &#39;value&#39;,
      }

      Dieser Typ bietet Puppet die Möglichkeit, unsere Anwendungsconfig zu verwalten.
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;    EOS&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;features: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;attributes: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;ensure: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type:    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Enum[present, absent]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc:    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Ob die Einstellung hinzugefügt oder entfernt werden soll&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;default: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;present&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type:     &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;String&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc:     &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Die Einstellung, die verwaltet werden soll&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;behavior: :namevar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;value: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;type: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Variant[String, Integer, Array]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;desc: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Der Wert, der gesetzt werden soll&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ressourcen-API-Anbieter
&lt;/h3&gt;

&lt;p&gt;Die einfachste Lösung ist die Verwendung der Klasse &lt;code&gt;SimpleProvider&lt;/code&gt;. Dieser Anbieter benötigt bis zu 4 Definitionen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;get&lt;/li&gt;
&lt;li&gt;create&lt;/li&gt;
&lt;li&gt;update&lt;/li&gt;
&lt;li&gt;delete
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# lib/puppet/provider/app_config/cli.rb&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/resource_api/simple_provider&#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;open3&#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Puppet::Provider::AppConfig::Cli&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ResourceApi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SimpleProvider&lt;/span&gt;
  &lt;span class=&quot;vg&quot;&gt;$app_command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app.exe&#39;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Get: Alle lesbaren Konfigurationseinstellungen in einen Hash abrufen&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Gibt Daten vom Befehl zurück&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app.exe list&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ParseError&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Fehler beim Ausführen des Befehls: &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Fehlende Fehlerbehandlung. Dies dient nur zur Demo&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@command_result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;%r{&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@command_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;---&#39;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;:&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;:&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strip&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&#39;ensure&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;present&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s1&quot;&gt;&#39;value&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Create: Eine neue Konfigurationseinstellung mit Name und Sollwert hinzufügen&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Erstelle &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39; mit &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Fehlende Fehlerbehandlung. Dies dient nur zur Demo&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Update: Eine vorhandene Einstellung mit Namen korrigieren und durch den Sollwert ersetzen&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Aktualisiere &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39; mit &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe set &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Fehlende Fehlerbehandlung. Dies dient nur zur Demo&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# Delete: Eine Konfigurationseinstellung entfernen&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Lösche &#39;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&#39;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_stdout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Open3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;capture3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/app/bin/app.exe rm &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Fehlende Fehlerbehandlung. Dies dient nur zur Demo&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;Typen und Anbieter sind nicht komplex. Sie beschreiben im Grunde das CRUD-Verhalten von Puppet. Der Typ definiert, wie man die Puppet DSL verwendet, während der Anbieter steuert, wie spezifische Einstellungen überprüft und behandelt werden.&lt;/p&gt;

&lt;p&gt;Eine exzessive Nutzung der &lt;code&gt;exec&lt;/code&gt; Resource ist zu vermweiden. In den meisten Fällen ermöglicht ein einfacher Typ und Anbieter eine bessere Analyse, Fehlerbehandlung und Kontrolle über das, was passiert. Darüber hinaus: &lt;strong&gt;benutzerdefinierte Typen und Anbieter führen schneller aus&lt;/strong&gt; im Vergleich zur Verwendung des &lt;code&gt;exec&lt;/code&gt;-Typs - viel schneller!&lt;/p&gt;

&lt;p&gt;Die Beispielanwendung und die funktionierenden Typen und Anbieter aus diesem Posting sind auf GitHub verfügbar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Die App: &lt;a href=&quot;https://github.com/betadots/workshop-demo-app&quot; rel=&quot;noopener noreferrer&quot;&gt;Workshop-Demo-App&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Das Puppet-Modul mit Typen und Anbietern für die App: &lt;a href=&quot;https://github.com/betadots/workshop-demo-module/tree/ruby_workshop&quot; rel=&quot;noopener noreferrer&quot;&gt;Workshop-Demo-Modul&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;App-Nutzung:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# APP installieren:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-app /opt/app
/opt/app/bin/app.exe

&lt;span class=&quot;c&quot;&gt;# Puppet-Modul installieren:&lt;/span&gt;
git clone https://github.com/betadots/workshop-demo-module &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; ruby_workshop modules/app
&lt;span class=&quot;c&quot;&gt;# Type API V1&lt;/span&gt;
puppet resource app_config &lt;span class=&quot;nt&quot;&gt;--modulepath&lt;/span&gt; modules
&lt;span class=&quot;c&quot;&gt;# Type Resource API&lt;/span&gt;
puppet resource app_config2 &lt;span class=&quot;nt&quot;&gt;--modulepath&lt;/span&gt; modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Viel Spaß beim Puppetisieren,&lt;br&gt;&lt;br&gt;
Martin&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: The Ruby side of Puppet - Part 2 - Custom Functions</title>
    <link href="https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7"/>
    <id>https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7</id>
    <updated>2025-01-14T16:04:08+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is the second post in a series of three, covering the concepts and best practices for extending &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; using &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;custom facts&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;custom functions&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;custom types&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7&quot;&gt;Part 1 covers Custom Facts&lt;/a&gt;, which explain how a node can provide information to the Puppet Server.&lt;/p&gt;

&lt;p&gt;Part 2 (this post) focuses on Custom Functions, demonstrating how to develop functions for data processing and execution.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma&quot;&gt;Part 3&lt;/a&gt; covers Custom Types and Providers, showing how to extend Puppet&#39;s DSL functionality.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Custom Functions in Puppet&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Functions in Puppet are executed on the Puppet server, specifically inside the compiler. These functions have access to all facts and Puppet variables, as long as they exist within the function’s namespace.&lt;/p&gt;

&lt;p&gt;There are two main types of functions in Puppet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Statement functions&lt;/li&gt;
&lt;li&gt;Return value functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Statement functions&lt;/strong&gt; can interact with Puppet internals. The most common statement functions are &lt;code&gt;include&lt;/code&gt; or &lt;code&gt;contain&lt;/code&gt;, which add classes to the catalog, or &lt;code&gt;noop&lt;/code&gt; and &lt;code&gt;notice&lt;/code&gt;, which set all resources into simulation mode or print entries into the Puppet server log file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Return value functions&lt;/strong&gt; such as &lt;code&gt;lookup&lt;/code&gt; or &lt;code&gt;read_url&lt;/code&gt;, return values from Hiera or external sources like web servers.&lt;br&gt;
Some return value functions can also manipulate data (&lt;code&gt;each&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;reduce&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Create Custom Functions?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are some reasons you might develop custom functions in Puppet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing a custom Hiera lookup function&lt;/li&gt;
&lt;li&gt;Converting data structures for specific needs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Content&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Function development&lt;/li&gt;
&lt;li&gt;Functions in Modules&lt;/li&gt;
&lt;li&gt;General API&lt;/li&gt;
&lt;li&gt;Dispatcher and Define&lt;/li&gt;
&lt;li&gt;Using functions in Puppet DSL&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Function development
&lt;/h2&gt;

&lt;p&gt;Since functions are executed only at compile time, it is advisable to first focus on their core functionality before integrating them with the Function API. This allows you to test the logic with local data, avoiding issues on the Puppet server that could break compilation or even crash the server.&lt;/p&gt;

&lt;p&gt;For example, imagine you have a data structure that outlines various aspects of a system&#39;s configuration. Using a function, you can expose specific parts of this data structure as variables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Data Structure&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;server_hash&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;postfix&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;transport&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mta&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssl&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&#39;apache&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;vhost&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;server.domain.tld&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;document_root&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/srv/www/html/server.domain.tld&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssl&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&#39;proxy_pass&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://localhost:8080&#39;&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tomcat&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;application&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;crm&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;service&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;frontend&#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assume that this data structure comes from Hiera, and you want to allow other teams, who may write their own modules, to reuse this data without performing additional lookups. This would allow you to modify the data structure internally while still providing teams with the required information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Function&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# test data&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;server_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;postfix&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;transport&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;mta&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;ssl&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;apache&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;vhost&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;server.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;document_root&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/srv/www/html/server.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;ssl&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;proxy_pass&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;http://localhost:8080&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;tomcat&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;application&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;crm&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;service&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;frontend&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# function definition&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read_service_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# testing the function&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;read_service_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;tomcat&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Functions in Modules
&lt;/h2&gt;

&lt;p&gt;Puppet provides an API for creating custom functions. To ensure the Puppet agent can locate these functions, they must be placed in specific directories within a module.&lt;/p&gt;

&lt;p&gt;Files inside the module&#39;s &lt;code&gt;lib&lt;/code&gt; directory are synchronized across all agents. Note that it is not possible to limit which files are synchronized.&lt;/p&gt;

&lt;p&gt;Custom functions can be added to one of two directory structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lib/puppet/parser/functions&lt;/code&gt; - Functions API v1&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lib/puppet/functions&lt;/code&gt; - Functions API v2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The newer Functions API v2 supports subdirectories, allowing you to namespace your functions. The filename should match the function name.&lt;/p&gt;

&lt;p&gt;In this post, we’ll focus on the modern API v2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Directory Structure&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# &amp;lt;modulepath&amp;gt;/stdlib/&lt;/span&gt;
  &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; lib/
    &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; puppet/
      &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; functions/
        &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; stdlib/
          &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; to_yaml.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# stdlib/lib/puppet/functions/stdlib/to_yaml.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;stdlib::to_yaml&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We recommend prefixing custom functions with the module name to avoid naming conflicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  General API
&lt;/h2&gt;

&lt;p&gt;To create custom functions in Puppet, use the following structure:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the namespace &lt;strong&gt;must&lt;/strong&gt; be identical to the module name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dispatcher and Define
&lt;/h2&gt;

&lt;p&gt;Within the &lt;code&gt;do ... end&lt;/code&gt; of the function, you need to add a dispatcher. The dispatcher validates the input data and links it to a named definition, which executes the corresponding Ruby code.&lt;/p&gt;

&lt;p&gt;The dispatcher can check data types using &lt;code&gt;param&lt;/code&gt; and map values to Ruby symbols. The definition uses the dispatcher’s name and the parameters provided in the dispatcher.&lt;/p&gt;

&lt;p&gt;The final command in the definition determines the return value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Dispatcher for an IPv4 Address&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s a table of common parameter methods used in dispatchers:&lt;/p&gt;

&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;param&lt;/code&gt; or &lt;code&gt;required_param&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A mandatory argument. May occur multiple times. &lt;strong&gt;Position&lt;/strong&gt;: All mandatory arguments must come first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optional_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An argument that can be omitted. You can use any number of these. When there are multiple optional arguments, users can only pass latter ones if they also provide values for the prior ones. This also applies to repeated arguments. &lt;strong&gt;Position&lt;/strong&gt;: Must come after any required arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;repeated_params&lt;/code&gt; or &lt;code&gt;optional_repeated_params&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A repeatable argument, which can receive zero or more values. A signature can only use one repeatable argument. &lt;strong&gt;Position&lt;/strong&gt;: Must come after any non-repeating arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;required_repeated_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A repeatable argument, which must receive one or more values. A signature can only use one repeatable argument. &lt;strong&gt;Position&lt;/strong&gt;: Must come after any non-repeating arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;block_param&lt;/code&gt; or &lt;code&gt;required_block_param&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A mandatory lambda (block of Puppet code). A signature can only use one block. &lt;strong&gt;Position&lt;/strong&gt;: Must come after all other arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optional_block_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An optional lambda. A signature can only use one block. &lt;strong&gt;Position&lt;/strong&gt;: Must come after all other arguments.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Adding a Definition&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Dispatcher&lt;/th&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dispatch :ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;def ip&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;param ..., :ipaddr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;def ip(ipaddr)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;resolv&#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ipaddr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Resolv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getname&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now additional dispatchers and definitions can be added:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;socket&#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:dnsname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/.*/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:dnsname&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:local_hostname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[//]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:local_hostname&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ipaddr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Addrinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tcp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ipaddr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;80&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getnameinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dnsname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dnsname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Addrinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dnsname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getnameinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;local_hostname&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gethostname&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using functions in Puppet DSL
&lt;/h2&gt;

&lt;p&gt;Within Puppet the functions usually gets executed during compile time.&lt;br&gt;
A special case is the &lt;a href=&quot;https://www.puppet.com/docs/puppet/8/deferring_functions.html&quot; rel=&quot;noopener noreferrer&quot;&gt;deferred function&lt;/a&gt; which gets executed on the agent.&lt;/p&gt;

&lt;p&gt;Let&#39;s consider two use-cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;using resolv function on the compiler&lt;/li&gt;
&lt;li&gt;using resolv function on the agent&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Use case 1: compiler execution
&lt;/h3&gt;

&lt;p&gt;The function can be used anywhere in the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;betadots::resolver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Stdlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Fqdn&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$local_hostname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;betadots::resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$ip_from_google&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;betadots::resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;www.google.com&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use case 2: agent execution
&lt;/h3&gt;

&lt;p&gt;The function must be declared to run in deferred mode in Puppet. Avoid using the function within the class header, as this could potentially overwrite the function call from hiera data.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;betadots::local_resolve&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$local_api_ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;api.int.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Puppet provides a stable API for creating custom functions.&lt;br&gt;
Before creating a new function, check whether a solutions already exists in &lt;a href=&quot;https://github.com/puppetlabs/puppetlabs-stdlib&quot; rel=&quot;noopener noreferrer&quot;&gt;Stdlib-&lt;/a&gt; or &lt;a href=&quot;https://github.com/voxpupuli/puppet-extlib&quot; rel=&quot;noopener noreferrer&quot;&gt;Extlib-Module&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Remember to prefix your functions with your module name to avoid conflicts and help identify their origin.&lt;/p&gt;

&lt;p&gt;Happy puppetizing,&lt;br&gt;
Martin&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Die Ruby-Seite von Puppet - Teil 2 - Benutzerdefinierte Funktionen</title>
    <link href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3"/>
    <id>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3</id>
    <updated>2025-01-14T16:01:03+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Dies ist der zweite Beitrag einer dreiteiligen Serie, in der die Konzepte und Best Practices zum Erweitern von &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; mithilfe von &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Facts&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Funktionen&lt;/a&gt; und &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Typen&lt;/a&gt; und &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;Providern&lt;/a&gt; behandelt werden.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj&quot;&gt;Teil 1 behandelt Benutzerdefinierte Facts&lt;/a&gt;, die erklären, wie ein Knoten Informationen an den Puppet-Server übermitteln kann.&lt;/p&gt;

&lt;p&gt;Teil 2 (dieser Beitrag) konzentriert sich auf Benutzerdefinierte Funktionen und zeigt, wie man Funktionen für Datenverarbeitung und Ausführung entwickelt.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp&quot;&gt;Teil 3&lt;/a&gt; stellt dar, wie man die DSL-Funktionalität von Puppet mit neuen Typen und Provider erweitert.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Benutzerdefinierte Funktionen in Puppet&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Funktionen in Puppet werden auf dem Puppet-Server, insbesondere im Compiler, ausgeführt. Diese Funktionen haben Zugriff auf alle Facts und Puppet-Variablen, solange sie mit dem Namensraum angegeben werden.&lt;/p&gt;

&lt;p&gt;Es gibt zwei Haupttypen von Funktionen in Puppet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anweisungsfunktionen&lt;/li&gt;
&lt;li&gt;Rückgabefunktionen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Anweisungsfunktionen&lt;/strong&gt; können mit den internen Mechanismen von Puppet interagieren. Die gebräuchlichsten Anweisungsfunktionen sind &lt;code&gt;include&lt;/code&gt; und &lt;code&gt;contain&lt;/code&gt;, die Klassen zum Katalog hinzufügen, oder &lt;code&gt;noop&lt;/code&gt; und &lt;code&gt;notice&lt;/code&gt;, die alle Ressourcen in den Simulationsmodus versetzen oder Einträge in die Protokolldatei des Puppet-Servers schreiben.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rückgabefunktionen&lt;/strong&gt; wie &lt;code&gt;lookup&lt;/code&gt; oder &lt;code&gt;read_url&lt;/code&gt; geben Werte aus Hiera oder externen Quellen wie Webservern zurück. Einige Rückgabefunktionen können auch Daten manipulieren (&lt;code&gt;each&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;reduce&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warum benutzerdefinierte Funktionen erstellen?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hier sind einige Gründe, warum man benutzerdefinierte Funktionen in Puppet entwickeln könnte:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Erstellen einer benutzerdefinierten Hiera-Suchfunktion&lt;/li&gt;
&lt;li&gt;Umwandeln von Datenstrukturen für spezifische Anforderungen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Inhalt&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Funktion-Entwicklung&lt;/li&gt;
&lt;li&gt;Funktionen in Modulen&lt;/li&gt;
&lt;li&gt;Allgemeine API&lt;/li&gt;
&lt;li&gt;Dispatcher und Definition&lt;/li&gt;
&lt;li&gt;Verwendung von Funktionen in der Puppet-DSL&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Funktion-Entwicklung
&lt;/h2&gt;

&lt;p&gt;Da Funktionen nur zur Kompilierzeit ausgeführt werden, empfiehlt es sich, sich zunächst auf ihre Kernfunktionalität zu konzentrieren, bevor sie in die Funktions-API integriert werden. Dadurch kann die Logik mit lokalen Daten getestet werden, ohne dass Probleme auf dem Puppet-Server auftreten, die die Kompilierung unterbrechen oder den Server sogar zum Absturz bringen könnten.&lt;/p&gt;

&lt;p&gt;Als Beispiel wird eine Datenstruktur definiert, die verschiedene Aspekte der Konfiguration eines Systems darstellt. Mit einer Funktion kann man bestimmte Teile dieser Datenstruktur als Variablen verfügbar machen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel für eine Datenstruktur&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;server_hash&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;postfix&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;transport&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mta&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssl&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&#39;apache&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;vhost&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;server.domain.tld&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;document_root&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/srv/www/html/server.domain.tld&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssl&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&#39;proxy_pass&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://localhost:8080&#39;&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;tomcat&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;config&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;application&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;crm&#39;&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;service&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;frontend&#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Angenommen, diese Datenstruktur stammt aus Hiera, und man möchte anderen Teams, die ihre eigenen Module schreiben, die Wiederverwendung dieser Daten ohne zusätzliche Abfragen ermöglichen. Dadurch kann man die interne Datenstruktur ändern und die Funktion anpassen, während die Teams weiterhin die benötigten Informationen zur Verfügung gestellt bekommen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel für eine Funktion&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Test Daten&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;server_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;postfix&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;transport&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;mta&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;ssl&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;apache&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;vhost&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;server.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;document_root&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/srv/www/html/server.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;ssl&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;proxy_pass&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;http://localhost:8080&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;tomcat&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;config&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;application&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;crm&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&#39;service&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;frontend&#39;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Funktion definition&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read_service_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Testen der Funktion&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;read_service_config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;tomcat&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Funktionen in Modulen
&lt;/h2&gt;

&lt;p&gt;Puppet stellt eine API zum Erstellen benutzerdefinierter Funktionen zur Verfügung. Damit der Puppet Compiler diese Funktionen finden kann, müssen sie in bestimmten Verzeichnissen innerhalb eines Moduls abgelegt werden.&lt;/p&gt;

&lt;p&gt;Dateien im &lt;code&gt;lib&lt;/code&gt;-Verzeichnis des Moduls werden auf alle Agenten synchronisiert. Es ist nicht möglich, zu steuern, welche Dateien synchronisiert werden.&lt;/p&gt;

&lt;p&gt;Benutzerdefinierte Funktionen können zu einer der beiden folgenden Verzeichnisstrukturen hinzugefügt werden:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lib/puppet/parser/functions&lt;/code&gt; - Funktionen API v1&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lib/puppet/functions&lt;/code&gt; - Funktionen API v2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Die neuere Funktionen-API v2 unterstützt Unterverzeichnisse, sodass man Funktionen in Namespaces organisieren können. Der Dateiname sollte mit dem Funktionsnamen übereinstimmen.&lt;/p&gt;

&lt;p&gt;In diesem Beitrag konzentrieren wir uns auf die moderne API v2.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel für eine Verzeichnisstruktur&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# &amp;lt;modulepath&amp;gt;/stdlib/&lt;/span&gt;
  &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; lib/
    &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; puppet/
      &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; functions/
        &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; stdlib/
          &lt;span class=&quot;se&quot;&gt;\-&lt;/span&gt; to_yaml.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# stdlib/lib/puppet/functions/stdlib/to_yaml.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;stdlib::to_yaml&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wir empfehlen, benutzerdefinierte Funktionen mit dem Modulnamen zu präfixen, um Namenskonflikte zu vermeiden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Allgemeine API
&lt;/h2&gt;

&lt;p&gt;Um benutzerdefinierte Funktionen in Puppet zu erstellen, verwendet man die folgende Struktur:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bitte beachten, dass der Funktions Namespace identisch zum Modulnamen (betadots) sein &lt;strong&gt;muss&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dispatcher und Definition
&lt;/h2&gt;

&lt;p&gt;Innerhalb des &lt;code&gt;do ... end&lt;/code&gt; Blocks der Funktion muss ein Dispatcher und eine Definition hinzugefügt werden. Der Dispatcher validiert die Eingabedaten und verknüpft sie mit einer benannten Definition, die den entsprechenden Ruby-Code ausführt.&lt;/p&gt;

&lt;p&gt;Der Dispatcher kann Datentypen mithilfe von &lt;code&gt;param&lt;/code&gt; prüfen und Werte Ruby-Symbolen zuordnen. Die Definition verwendet den Namen des Dispatchers und die im Dispatcher bereitgestellten Parameter.&lt;/p&gt;

&lt;p&gt;Der letzte Befehl in der Definition bestimmt den Rückgabewert.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Beispiel für einen Dispatcher für eine IPv4-Adresse&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hier ist eine Tabelle mit gängigen Methoden für Parameter in Dispatchern:&lt;/p&gt;

&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Methode&lt;/th&gt;
&lt;th&gt;Beschreibung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;param&lt;/code&gt; oder &lt;code&gt;required_param&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Ein obligatorisches Argument. Kann mehrfach verwendet werden. &lt;strong&gt;Position&lt;/strong&gt;: Alle Pflichtargumente müssen zuerst erscheinen.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optional_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ein Argument, das ausgelassen werden kann.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;repeated_params&lt;/code&gt; oder &lt;code&gt;optional_repeated_params&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Ein wiederholbares Argument, das null oder mehr Werte annehmen kann.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;required_repeated_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ein wiederholbares Argument, das einen oder mehr Werte annehmen muss.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;block_param&lt;/code&gt; oder &lt;code&gt;required_block_param&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Ein obligatorisches Lambda.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optional_block_param&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ein optionales Lambda.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Hinzufügen einer Definition&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Beschreibung&lt;/th&gt;
&lt;th&gt;Dispatcher&lt;/th&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dispatch :ip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;def ip&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;param ..., :ipaddr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;def ip(ipaddr)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;resolv&#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ipaddr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Resolv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getname&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nun können zusätzliche Dispatcher und Definitionen hinzugefügt werden:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# &amp;lt;modulepath&amp;gt;/betadots/lib/puppet/functions/betadots/resolve.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Functions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create_function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;socket&#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ip&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/^(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})$/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ipaddr&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:dnsname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[/.*/]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:dnsname&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;dispatch&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:local_hostname&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;param&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Regexp[//]&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:local_hostname&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ipaddr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Addrinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;tcp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ipaddr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;80&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getnameinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;dnsname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dnsname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Addrinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dnsname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getnameinfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;local_hostname&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;gethostname&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verwendung von Funktionen in der Puppet-DSL
&lt;/h2&gt;

&lt;p&gt;Innerhalb von Puppet werden Funktionen in der Regel während der Kompilierzeit ausgeführt.&lt;br&gt;
Eine besondere Ausnahme bildet die &lt;a href=&quot;https://www.puppet.com/docs/puppet/8/deferring_functions.html&quot; rel=&quot;noopener noreferrer&quot;&gt;deferred function&lt;/a&gt;, die auf dem Agenten ausgeführt wird.&lt;/p&gt;

&lt;p&gt;Im folgenden betrachten wir die zwei Anwendungsfälle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verwendung der &lt;code&gt;resolve&lt;/code&gt;-Funktion im Compiler&lt;/li&gt;
&lt;li&gt;Verwendung der &lt;code&gt;resolve&lt;/code&gt;-Funktion auf dem Agenten&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Anwendungsfall 1: Ausführung im Compiler
&lt;/h3&gt;

&lt;p&gt;Die Funktion kann an beliebiger Stelle im Code verwendet werden:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;betadots::resolver&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Stdlib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Fqdn&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$local_hostname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;betadots::resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$ip_from_google&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;betadots::resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;www.google.com&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Anwendungsfall 2: Ausführung auf dem Agenten
&lt;/h3&gt;

&lt;p&gt;Die Funktion muss in deferred mode deklariert werden, um in Puppet auf dem Agenten ausgeführt zu werden. Die Funktion sollte nicht als Parameter im Header der Klasse verwendet werden, da dies möglicherweise den Funktionsaufruf mit hiera-Daten überschreiben könnte.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;betadots::local_resolve&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$local_api_ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Deferred&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;betadots::resolve&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;api.int.domain.tld&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;Puppet bietet eine stabile API zum Erstellen benutzerdefinierter Funktionen.&lt;br&gt;
Bevor eine neue Funktion erstellt wird, bitte prüfen, ob bereits eine Lösung im &lt;a href=&quot;https://github.com/puppetlabs/puppetlabs-stdlib&quot; rel=&quot;noopener noreferrer&quot;&gt;Stdlib-&lt;/a&gt; oder &lt;a href=&quot;https://github.com/voxpupuli/puppet-extlib&quot; rel=&quot;noopener noreferrer&quot;&gt;Extlib-Modul&lt;/a&gt; vorhanden ist.&lt;/p&gt;

&lt;p&gt;Funktionen sollten mit dem Modulnamen zu präfixen, um Konflikte zu vermeiden und deren Herkunft zu kennzeichnen.&lt;/p&gt;

&lt;p&gt;Viel Erfolg beim Puppetisieren,&lt;br&gt;
Martin&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: The strength of community</title>
    <link href="https://overlookinfratech.com/2025/01/08/the-strength-of-community/"/>
    <id>https://overlookinfratech.com/2025/01/08/the-strength-of-community</id>
    <updated>2025-01-08T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Everything’s been super hectic with the holidays and so nobody noticed that I
didn’t post the last two weeks, right? I joke, but not really. We’ve been super
busy too and not just with the regular holiday stuff. In between the
celebrations and glühwein, we’ve been working on community building.&lt;/p&gt;

&lt;p&gt;As a community, we decided on the &lt;a href=&quot;https://github.com/openvoxproject&quot;&gt;name&lt;/a&gt; for
our new community maintained Puppet™️ fork and we decided that
&lt;a href=&quot;https://github.com/voxpupuli/plumbing/issues/294&quot;&gt;Vox Pupuli would adopt it&lt;/a&gt;.
Now we’re busy creating the organization governance, porting all the
repositories in, and setting up the pipelines and build tooling to streamline
package publication. You’ll see a lot more information about things soon when
everything’s done and we make the official announcement, but that’s a lot of
work for a few weeks over the holidays when people are usually checked out!&lt;/p&gt;

&lt;p&gt;Our biggest remaining challenge right now is determining release criteria that
balances establishing our own brand presence against ecosystem safety all while
respecting Perforce’s trademarks. I’m talking about agreeing on what names need
to be changed before we can release packages and call them our own. I’m grateful
for David, Jake, Sara, and others from Perforce who have given us loads of their
own time to help us get to a resolution in everyone’s benefit.&lt;/p&gt;

&lt;p&gt;This ability to pull together and work to find solutions, even over strong
disagreements, is one of the indicators of a strong community. I’m confident
that this spirit of collaboration is going to continue as we build the new
Puppet™️ Language Steering Committee to guide our shared future. I’m quite
excited for this steering committee because it’s got representatives from the
entire community, even including Perforce itself. It’s the first time that
community user needs sit at the same table as the business needs do without
anyone having veto power over the other.&lt;/p&gt;

&lt;p&gt;Being at the same table ensures that we don’t diverge too much and that we stay
compatible and interoperable. These efforts, along with others like converging on 
acceptable compatibility names, will help ensure that the same Puppet™️ Forge 
works for all of us, that Vox Pupuli developer tools remain usable on Puppet 
Enterprise, that the PDK builds modules for whatever Puppet™️ implementation 
you happen to be running, and that our entire ecosystem remains collaborative 
and engaging regardless of where we work.&lt;/p&gt;

&lt;p&gt;I’m kind of digging where we are. Some of it may have been born out of conflict
and there were some hurt feelings all around. But being able to talk things over, 
share our needs, and work together to resolve them means that together we are
a community and together we will build an ecosystem that’s stronger than it ever
has been before.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Die Ruby-Seite von Puppet - Teil 1 - Benutzerdefinierte Fakten</title>
    <link href="https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj"/>
    <id>https://dev.to/betadots/die-ruby-seite-von-puppet-teil-1-benutzerdefinierte-fakten-4nnj</id>
    <updated>2025-01-01T17:26:54+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Dies ist der erste von drei Beiträgen, die die Konzepte und Best Practices zur Erweiterung von &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; mit Hilfe von &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Fakten&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/8/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Funktionen&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;benutzerdefinierten Typen&lt;/a&gt; und &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;Providern&lt;/a&gt; behandeln.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Teil 1&lt;/strong&gt; (dieser Beitrag) erklärt benutzerdefinierte Fakten und wie ein Puppet Agent dem Puppet-Server Informationen bereitstellen kann.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-2-benutzerdefinierte-funktionen-4ph3&quot;&gt;&lt;strong&gt;Teil 2&lt;/strong&gt;&lt;/a&gt; konzentriert sich auf benutzerdefinierte Funktione und erläutert detailliert, wie Datenverarbeitungs- oder Ausführungsfunktionen entwickelt werden können.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/die-ruby-seite-von-puppet-teil-3-benutzerdefinierte-typen-und-provider-jbp&quot;&gt;&lt;strong&gt;Teil 3&lt;/strong&gt;&lt;/a&gt; deckt benutzerdefinierte Typen und Provider ab, die die Funktionalität der Puppet-DSL erweitern.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Benutzerdefinierte Fakten&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Es gibt mehrere Gründe, benutzerdefinierte Fakten zu entwickeln:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wann immer ein VM-Namensschema verwendet wird, empfehlen wir, das Namensschema als benutzerdefinierten Fakt bereitzustellen.&lt;/li&gt;
&lt;li&gt;Es ist manchmal erforderlich, den Zustand bestimmter Konfigurationen zu bestimmen oder ob bestimmte Software installiert ist.&lt;/li&gt;
&lt;li&gt;Die Rechenzentrumsabteilung benötigt möglicherweise einen Überblick über die physische Hardware, Details zu den Hardwareanbietern und End-of-Life (EOL)-Supportdaten. Darüber hinaus kann die Finanzabteilung Informationen über die Anzahl der verwendeten kommerziellen Lizenzen und die Versionen der installierten Software benötigen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wann immer Informationen von einem Puppet Agenten benötigt werden, bietet Puppet die Möglichkeit, benutzerdefinierte Fakten zu implementieren.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inhalt&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Faktenerstellung&lt;/li&gt;
&lt;li&gt;Fakten in Modulen&lt;/li&gt;
&lt;li&gt;Allgemeine API&lt;/li&gt;
&lt;li&gt;Einschränkungen&lt;/li&gt;
&lt;li&gt;Hilfsfunktionen&lt;/li&gt;
&lt;li&gt;Zugriff auf andere Fakten&lt;/li&gt;
&lt;li&gt;Rückgabewerte&lt;/li&gt;
&lt;li&gt;Windows-Fakten&lt;/li&gt;
&lt;li&gt;Weitere Konzepte&lt;/li&gt;
&lt;li&gt;Codierungsstrategien und strukturierte Daten&lt;/li&gt;
&lt;li&gt;Zusammenfassung&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Faktenerstellung
&lt;/h2&gt;

&lt;p&gt;Benutzerdefinierte Fakten können lokal auf einem Arbeitsplatz oder auf dem System entwickelt werden, auf dem der Fakt benötigt wird. Bevor der ungetestete Fakt in den Puppet-Code aufgenommen wird, kann der Faktladepfad auf zwei Arten angegeben und unabhängig gestestet werden.&lt;/p&gt;

&lt;p&gt;Die folgende Verzeichnisstruktur wird verwendet:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; ~/new_fact/
betadots_application_version.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Um den Fakt auszuführen, verwenden man einen der folgenden Befehle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;FACTERLIB=~/new_fact facter betadots_application_version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;facter --custom-dir ~/new_fact betadots_application_version&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fakten in Modulen
&lt;/h2&gt;

&lt;p&gt;Puppet stellt eine API zur Entwicklung benutzerdefinierter Fakten bereit. Um sicherzustellen, dass der Puppet-Agent sie findet, müssen benutzerdefinierte Fakten in einem bestimmten Verzeichnis innerhalb eines Moduls abgelegt werden.&lt;/p&gt;

&lt;p&gt;Alle Dateien im &lt;code&gt;lib&lt;/code&gt;-Verzeichnis von Modulen werden auf alle Agenten synchronisiert. Es ist nicht möglich, einzuschränken, welche Dateien synchronisiert werden.&lt;/p&gt;

&lt;p&gt;Benutzerdefinierte Fakten sollten im Verzeichnis &lt;code&gt;lib/facter&lt;/code&gt; eines Moduls abgelegt werden. Obwohl man einen beliebigen Dateinamen wählen kann, wird empfohlen, den Namen des Fakts als Dateinamen zu verwenden, um eine einfache Identifikation zu gewährleisten. Der Dateiname muss mit &lt;code&gt;.rb&lt;/code&gt; enden.&lt;/p&gt;

&lt;p&gt;Wir empfehlen außerdem, dem Namen des benutzerdefinierten Fakts einen Firmen- oder Abteilungspräfix hinzuzufügen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Allgemeine API
&lt;/h2&gt;

&lt;p&gt;Facter bietet eine API zum Erstellen neuer benutzerdefinierter Fakten.&lt;/p&gt;

&lt;p&gt;Beispiel:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code hier&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Der Block &lt;code&gt;setcode do ... end&lt;/code&gt; enthält den gesamten Ruby-Code für den Fakt. Das Ergebnis des letzten Befehls oder der letzten Variablen wird als Rückgabewert des Fakts verwendet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Einschränkungen
&lt;/h2&gt;

&lt;p&gt;Da alle benutzerdefinierten Fakten an alle Puppet-Agenten verteilt werden, ist es wichtig sicherzustellen, dass betriebssystem- oder anwendungsspezifische Fakten nur dort ausgeführt werden, wo es erforderlich ist.&lt;/p&gt;

&lt;p&gt;Dies wird durch Einschränkungen (confining) erreicht.&lt;/p&gt;

&lt;p&gt;Einschränkungen können auf einfache oder strukturierte Fakten oder auf anderen Ruby-Code wie &lt;code&gt;File.exist?&lt;/code&gt; angewendet werden.&lt;/p&gt;

&lt;p&gt;Einfache Fakt-Einschränkung:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;kernel: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code hier&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Einschränkung unter Verwendung eines Ruby-Blocks und Fakten:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code hier&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Einschränkung unter Verwendung eines Ruby-Blocks und Ruby-Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code hier&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hilfsfunktionen
&lt;/h2&gt;

&lt;p&gt;Facter bietet mehrere Hilfsmethoden, die verwendet werden können, ohne dass die Klasse &lt;code&gt;facter&lt;/code&gt; erforderlich ist:&lt;/p&gt;

&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hilfsfunktion&lt;/th&gt;
&lt;th&gt;Beschreibung&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter::Core::Execution::execute&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Führt ein ausführbares Programm auf dem System mit einer Timeout-Option aus, um das Aufhängen von Facter-Läufen zu verhindern.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter::Core::Execution::which&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prüft, ob ein ausführbares Programm verfügbar ist. Funktioniert auf jedem unterstützten Betriebssystem und durchsucht die Standardpfade &lt;code&gt;ENV[&#39;PATH&#39;] + [&#39;/sbin&#39;, &#39;/usr/sbin&#39;]&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter.value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Bietet Zugriff auf den Wert eines anderen Fakts. Achtung! Loops vermeiden!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Weitere Informationen findet man in der &lt;a href=&quot;https://www.rubydoc.info/gems/facter&quot; rel=&quot;noopener noreferrer&quot;&gt;API-Dokumentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Bitte beachten, dass &lt;code&gt;Facter::Core::Execution::exec&lt;/code&gt; zugunsten von &lt;code&gt;Facter::Core::Execution::execute&lt;/code&gt; veraltet ist. Dies ist wichtig, wenn man von älteren Versionen von Facter migriert.&lt;/p&gt;

&lt;p&gt;Die &lt;code&gt;timeout&lt;/code&gt;-Options-Hash kann auf zwei Arten festgelegt werden:&lt;/p&gt;

&lt;p&gt;Gültig für den gesamten Fakt:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&amp;lt;name&amp;gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;timeout: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pro Ausführungsmethode:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&amp;lt;cmd&amp;gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zum Beispiel, wenn eine Anwendung ihre Konfiguration über &lt;code&gt;/opt/app/bin/app config&lt;/code&gt; zurückgibt, kann man ein Timeout von 5 Sekunden festlegen:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Das Ergebnis des letzten Befehls oder die Angabe einer Variable wird als Faktenergebnis verwendet.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config_list&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Zugriff auf andere Fakten
&lt;/h2&gt;

&lt;p&gt;Es ist möglich, dass ein benutzerdefinierter Fakt den Wert eines anderen Fakts verwendet, indem er &lt;code&gt;Facter.value&lt;/code&gt; verwendet. Dies sollte jedoch vorsichtig geschehen, um zyklische Abhängigkeiten zwischen Fakten zu vermeiden.&lt;/p&gt;

&lt;p&gt;In unserem Beispiel hat die Anwendung unterschiedliche Konfigurationspfade für verschiedene Betriebssysteme.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RedHat - /etc/sysconfig/application&lt;/li&gt;
&lt;li&gt;Debian - /etc/default/application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beispiel mit Zugriff auf einen anderen Fakt:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Debian&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/default/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Das Beispiel ruft den &lt;code&gt;os&lt;/code&gt;-Fakt ab und verwendet den &lt;code&gt;family&lt;/code&gt;-Schlüssel, um das Betriebssystem zu vergleichen, und wendet dann eine Logik an, um zu bestimmen, welchen Anwendungskonfigurationspfad geprüft werden soll.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rückgabewerte
&lt;/h2&gt;

&lt;p&gt;Puppet erwartet, dass benutzerdefinierte Fakten einen gültigen Werttyp zurückgeben, insbesondere Strings, Ganze Zahlen oder Arrays.&lt;br&gt;
Wenn der Wert eines Fakts &lt;code&gt;nil&lt;/code&gt; oder &lt;code&gt;undef&lt;/code&gt; ist, wird der Fakt einfach nicht definiert.&lt;/p&gt;
&lt;h2&gt;
  
  
  Windows-Fakten
&lt;/h2&gt;

&lt;p&gt;Facter ist eine plattformübergreifende API und stellt die gleichen Funktionen auf allen Betriebssystemen bereit. Es ist jedoch wichtig zu beachten, dass Pfade und ausführbare Dateien in einer Windows-Umgebung unterschiedlich sein können.&lt;/p&gt;

&lt;p&gt;Mit Hilfe des &lt;code&gt;Facter::Core::Execution.execute&lt;/code&gt; werden üblicherweise Powershell Kommandos gestartet.&lt;/p&gt;

&lt;p&gt;Das folgende Beispiel erstellt einen Windows-Fakt:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/windows_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:windows_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;osfamily: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;reg query &quot;HKLM\Software\Microsoft\Internet Explorer&quot; /v svcVersion&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Das Beispiel verwendet den &lt;code&gt;reg query&lt;/code&gt;-Befehl, um die Version des Internet Explorers zu erhalten.&lt;/p&gt;

&lt;p&gt;Bitte beachten, dass die &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/&quot; rel=&quot;noopener noreferrer&quot;&gt;Win32 API calls&lt;/a&gt; seit Ruby 1.9 deprecated sind.&lt;br&gt;
Man kann statt dessen die &lt;a href=&quot;https://github.com/ruby/fiddle&quot; rel=&quot;noopener noreferrer&quot;&gt;Fiddle&lt;/a&gt; oder andere Ruby Bibliotheken nutzen.&lt;/p&gt;
&lt;h2&gt;
  
  
  Weitere Konzepte
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Protokollierung
&lt;/h3&gt;

&lt;p&gt;Zur Analyse eines Facts kann man auf die &lt;code&gt;debug&lt;/code&gt; Methode zugreifen.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Custom fact &#39;betadots_application_version&#39; running&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es sind folgende Log Level möglich: &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt; und &lt;code&gt;fatal&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aggregierung
&lt;/h3&gt;

&lt;p&gt;Eine weitere Option ist das Zusammenstellen von Facts als Hash mit Hilfe von &lt;code&gt;aggregates&lt;/code&gt; und &lt;code&gt;chunks&lt;/code&gt;.&lt;br&gt;
Jeder &lt;code&gt;chunk&lt;/code&gt;benötigt einen Namen (key) und einen Wert (value), wobei der Wert entweder ein Array oder ein Hash sein &lt;strong&gt;muss&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Nachdem Facter alle chunks eingelesen und validiert hat, werden diese aggregiert. Als default werden die Arrays oder Hashes gemerged.&lt;br&gt;
Es besteht die Möglichkeit, eine eigene Aggregierungs Funktion zu schreiben.&lt;/p&gt;

&lt;p&gt;Wir gehen davon aus, dass unsere Beispielanwendung es uns ermöglicht, Einstellungen auszulesen: &lt;code&gt;/opt/app/bin/app config &amp;lt;key&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Debian&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/default/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:config_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config version&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:config_cache_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cache_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config cache_size&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:config_log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:log_level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config log_level&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ergebnis:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;betadots_application_version =&amp;gt; {
  version =&amp;gt; &#39;1.2.4&#39;,
  cache_size =&amp;gt; &#39;400M&#39;,
  log_level =&amp;gt; &#39;info&#39;,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Gewichtung
&lt;/h3&gt;

&lt;p&gt;Facter ermöglicht es, den Rückgabe Wert auf unterschiedliche Arten zu ermitteln. Die einzelnen Arten bekommen eine Gewichtung, damit Facter danach entscheiden kann, welche Art (Methode) vals Rückgabewert verwendet werden soll.&lt;/p&gt;

&lt;p&gt;Wir gehen davon aus, dass unsere Besipielanwendung entweder über systemd oder über eine andere Art gestartet wurde (PID Datei).&lt;/p&gt;

&lt;p&gt;Hinweis: Externe Fakten haben eine Gewichtung von 1000. Man kann externe Fakten überschreiben, indem man für den Custom Fakt den gleichen Namen verwendet und eine Gewichtung über 1000 hinterlegt.&lt;/p&gt;

&lt;p&gt;Weitere Informationen findet man in der &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts#configuring_facts-fact-precedence&quot; rel=&quot;noopener noreferrer&quot;&gt;Fact precedence&lt;/a&gt; Sektion der &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;Custom facts overview&lt;/a&gt; Seite.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;application_running&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_weight&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;systemctl status app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;application_running&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_weight&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/var/run/application.pid&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Blocking und Caching
&lt;/h3&gt;

&lt;p&gt;Normalerweise werden Facts bei jedem Lauf eines Puppet Agenten erneut ermittelt. Dies kann bei manchen Facts dazu führen, dass Facter mehr Zeit benötigt, bis alle Facts geladen sind. Dies gilt insbesondere, wenn Facts eine Anwendung abfragen und diese Abfrage eine hohe Last erzeugt und lange dauert oder es gibt &quot;teure&quot; Facts, die gar nicht benötigt werden.&lt;br&gt;
Facter ermöglicht es, einen Cache zu verwenden, so dass Facts nur einmalig und danch erst nach der Lebensdauer des Cache Objektes erneut abgefragt werden und man kann Blacklisten mit unerwünschen Facts pflegen.&lt;/p&gt;

&lt;p&gt;Die Facter Konfiguration erfolgt in &lt;code&gt;/etc/puppetlabs/facter/facter.conf&lt;/code&gt; auf *nix Systemen oder in &lt;code&gt;C:\ProgramData\PuppetLabs\facter\etc\facter.conf&lt;/code&gt; auf Windows.&lt;br&gt;
Bitte beachten, dass die Konfiguration auf dem Puppet Agent erfolgen muss!&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# /etc/puppetlabs/facter/facter.conf
facts : {
  blocklist : [ &quot;file system&quot;, &quot;EC2&quot;, &quot;processors.isa&quot; ]
  ttls : [
    { &quot;timezone&quot;: 30 days },
    { &quot;my-fact-group&quot;: 30 days },
  ]
}
fact-groups : {
  my-fact-group : [ &quot;os&quot;, &quot;ssh.version&quot;]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Innerhalb des Elementes &lt;code&gt;blocklist&lt;/code&gt; kann man entweder einen Fact, ein Subelement eines Fact Hashes oder eine Facter Gruppe angeben.&lt;br&gt;
Existierende Facter Gruppen kann man aus Facter auslesen:&lt;/p&gt;
&lt;h4&gt;
  
  
  Facter block groups
&lt;/h4&gt;


&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;facter &lt;span class=&quot;nt&quot;&gt;--list-block-groups&lt;/span&gt;
EC2
  - ec2_metadata
  - ec2_userdata
file system
  - mountpoints
  - filesystems
  - partitions
hypervisors
  - hypervisors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Facter cache groups
&lt;/h4&gt;


&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;facter &lt;span class=&quot;nt&quot;&gt;--list-cache-groups&lt;/span&gt;
EC2
  - ec2_metadata
  - ec2_userdata
GCE
  - gce
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Außerdem kann man eigene Facter Gruppen innerhalb der &lt;code&gt;facts-group&lt;/code&gt; Sektion hinterlegen.&lt;/p&gt;

&lt;p&gt;Bitte beachten, dass das &lt;a href=&quot;https://forge.puppet.com/modules/dylanratcliffe/facter_cache/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;Dylan Ratcliffe&#39;s facter_cache Module&lt;/a&gt; seit Puppet 6 nicht mehr notwendig ist.&lt;/p&gt;

&lt;p&gt;Mehr Informationen findet man auf der  Puppet Webseite im Bereich &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/configuring_facter&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;facter.conf&lt;/code&gt; settings&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Codierungsstrategien und strukturierte Daten
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Anzahl dr Fakten redizieren&lt;/li&gt;
&lt;li&gt;Hashes verwenden&lt;/li&gt;
&lt;li&gt;Generischer Code und Ruby Module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wenn ein Fakt komplexe Daten zurückgeben muss, sollten strukturierte Daten verwendet werden, um hierarchische Informationen bereitzustellen. Weitere Informationen zur Strukturierung von Daten finden Sie in der Puppet-Dokumentation zu &lt;a href=&quot;https://www.puppet.com/docs/puppet/8/custom_facts.html#structured_data_in_custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;strukturierten Daten in benutzerdefinierten Fakten&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Eine bewährte Praxis besteht darin, sich für eine Struktur zu entscheiden, wenn ein Fakt mehr als eine einzelne Zeichenfolge zurückgeben soll. Solche Daten werden in der Regel in Hashes oder Arrays organisiert, die wahlweise direkt einen Hash liefern oder über aggregates zusammengesetzt werden:&lt;/p&gt;

&lt;p&gt;Lösung 1: Hash in Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_file_check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;c:/Program Data/Application/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;c:/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;:world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;world_writable?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Facter Ergebnis:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64328&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;438&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lösung 2: Hashes mit Hilfe von aggregate:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_file_check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;c:/Program Data/Application/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;c:/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file_world_writable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;world_writable?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Facter Ergebnis:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64328&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;438&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ein weiteres Facter Besipiel liest das Agent Zertifikat aus und liefert die Zertifikatserweiterungen als Fact Hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_cert_extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;openssl&#39;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet&#39;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/ssl/oids&#39;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# set variables&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;certdir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:certdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;certname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:certname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;certificate_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certdir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.pem&quot;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# get puppet ssl oids&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;oids&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SSL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Oids&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PUPPET_OIDS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;oids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# read the certificate&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;OpenSSL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;X509&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Certificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;certificate_file&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# cert extensions differs if we run via agent (numeric) or via facter (names)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;oid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r{^1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;34380&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;1}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;oid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;pp_preshared_key&#39;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r{^pp_}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;oid&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;pp_preshared_key&#39;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Zusammenfassung
&lt;/h2&gt;

&lt;p&gt;Dieser Beitrag hat die Grundlagen zur Erstellung von benutzerdefinierten Fakten in Puppet beschrieben. Benutzerdefinierte Fakten sind ein leistungsfähiges Werkzeug, um einem Puppet-Server oder einem Management-Tool Informationen zu liefern. Facts werden in Modulen hinterlegt und sollten bei Bedarf eingeschränkt (confine) werden. Die Rückgabewerte sollten als Hash oder Array und nur in seltenen Fällen als Bool, String oder Integer ausgegeben werden.&lt;/p&gt;

&lt;p&gt;Ein letzter Hinweis: Nur lokale Kommandos ausführen! Wenn man Remote Systeme in Kombination mit Facter nutzen möchte, müssen diese Hochverfügbar und skaliert aufgebaut sein.&lt;/p&gt;

&lt;p&gt;Happy puppetizing,&lt;br&gt;
Martin&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: The Ruby side of Puppet - Part 1 - Custom Facts</title>
    <link href="https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7"/>
    <id>https://dev.to/betadots/the-ruby-side-of-puppet-part-1-custom-facts-3hb7</id>
    <updated>2025-01-01T17:26:04+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is the first in a series of three posts covering the concepts and best practices for extending &lt;a href=&quot;https://www.puppet.com&quot; rel=&quot;noopener noreferrer&quot;&gt;Puppet&lt;/a&gt; using &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;custom facts&lt;/a&gt;, &lt;a href=&quot;https://www.puppet.com/docs/puppet/8/custom_functions_ruby&quot; rel=&quot;noopener noreferrer&quot;&gt;custom functions&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_types&quot; rel=&quot;noopener noreferrer&quot;&gt;custom types&lt;/a&gt; and &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/provider_development&quot; rel=&quot;noopener noreferrer&quot;&gt;providers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1&lt;/strong&gt; (this post) explains custom facts and how a node can provide information to the Puppet Server.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-2-custom-functions-7c7&quot;&gt;&lt;strong&gt;Part 2&lt;/strong&gt;&lt;/a&gt; focuses on custom functions, detailing how to develop data processing or execution functions.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dev.to/betadots/the-ruby-side-of-puppet-part-3-custom-types-and-providers-4hma&quot;&gt;&lt;strong&gt;Part 3&lt;/strong&gt;&lt;/a&gt; covers custom types and providers, extending Puppet DSL functionality.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Custom Facts&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;There are several reasons to develop custom facts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whenever a VM naming model is used, we recommend providing the naming schema as a custom fact.&lt;/li&gt;
&lt;li&gt;It is sometimes necessary to determine the state of certain configurations or whether specific software is installed.&lt;/li&gt;
&lt;li&gt;The datacenter department may need an overview of physical hardware, hardware vendor details, and end-of-life (EOL) support dates. Additionally, the finance department may require information on the number of commercial licenses in use and the versions of installed software.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever information from a node is needed, Puppet offers the option to deploy custom facts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fact Development&lt;/li&gt;
&lt;li&gt;Facts in Modules&lt;/li&gt;
&lt;li&gt;General API&lt;/li&gt;
&lt;li&gt;Confining&lt;/li&gt;
&lt;li&gt;Helpers&lt;/li&gt;
&lt;li&gt;Accessing Other Facts&lt;/li&gt;
&lt;li&gt;Return Values&lt;/li&gt;
&lt;li&gt;Windows Facts&lt;/li&gt;
&lt;li&gt;Additional Concepts&lt;/li&gt;
&lt;li&gt;Coding Strategies and Structured Data&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Fact Development
&lt;/h2&gt;

&lt;p&gt;You can develop custom facts locally on a workstation or on the system where the fact is needed. Before adding the untested fact to your Puppet code, you can specify the fact load path in two ways.&lt;/p&gt;

&lt;p&gt;The following directory structure is used:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;ls&lt;/span&gt; ~/new_fact/
betadots_application_version.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To execute the fact, use one of the following commands:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;FACTERLIB=~/new_fact facter betadots_application_version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;facter --custom-dir ~/new_fact betadots_application_version&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Facts in Modules
&lt;/h2&gt;

&lt;p&gt;Puppet provides an API to develop custom facts. To ensure the Puppet agent finds them, custom facts must be placed in a specific directory within a module.&lt;/p&gt;

&lt;p&gt;All files in the &lt;code&gt;lib&lt;/code&gt; directory of modules are synchronized to all agents. It’s not possible to restrict which files are synchronized.&lt;/p&gt;

&lt;p&gt;Custom facts should be placed in the &lt;code&gt;lib/facter&lt;/code&gt; directory of a module. Although you can choose any filename, it&#39;s recommended to use the fact name as the filename for easy identification. The filename must end with &lt;code&gt;.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We also recommend prefixing the custom fact name with a company or department identifier.&lt;/p&gt;

&lt;h2&gt;
  
  
  General API
&lt;/h2&gt;

&lt;p&gt;Facter provides an API to create new custom facts.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;setcode do ... end&lt;/code&gt; block contains all the Ruby code for the fact. The result of the last command or variable is used as the fact’s return value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confining
&lt;/h2&gt;

&lt;p&gt;Since all custom facts are distributed to all Puppet agents, it’s essential to ensure that OS- or application-specific facts are only executed where necessary.&lt;/p&gt;

&lt;p&gt;This is achieved by confining facts.&lt;/p&gt;

&lt;p&gt;Confinement can be applied to simple or structured facts or any other Ruby code, such as &lt;code&gt;File.exist?&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Simple fact confinement:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;kernel: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confinement using Ruby block and facts:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confinement using Ruby block and Ruby code:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Code here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Helpers
&lt;/h2&gt;

&lt;p&gt;Facter provides several helper methods that can be used without requiring the &lt;code&gt;facter&lt;/code&gt; class:&lt;/p&gt;

&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Helper&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter::Core::Execution::execute&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs an executable on the system with a timeout option to prevent hanging facter runs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter::Core::Execution::which&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks if an executable is available. Works on any supported OS, searching the default paths &lt;code&gt;ENV[&#39;PATH&#39;] + [&#39;/sbin&#39;, &#39;/usr/sbin&#39;]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Facter.value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Provides access to the value of any other fact. Be careful to avoid loops!&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;More information is available in the &lt;a href=&quot;https://www.rubydoc.info/gems/facter&quot; rel=&quot;noopener noreferrer&quot;&gt;API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please note that &lt;code&gt;Facter::Core::Execution::exec&lt;/code&gt; has been deprecated in favor of &lt;code&gt;Facter::Core::Execution::execute&lt;/code&gt;. This is important when migrating from older versions of Facter.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;timeout&lt;/code&gt; options hash can be set in two ways:&lt;/p&gt;

&lt;p&gt;Valid for the whole fact:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&amp;lt;name&amp;gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;timeout: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per execute method&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&amp;lt;cmd&amp;gt;&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, if an application returns its configuration via &lt;code&gt;/opt/app/bin/app config&lt;/code&gt;, you can set a timeout of 5 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result of the last command or a variable with content is used as fact result.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timeout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config_list&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Accessing Other Facts
&lt;/h2&gt;

&lt;p&gt;It’s possible for a custom fact to leverage the value of another fact by using &lt;code&gt;Facter.value&lt;/code&gt;.However, this should be done cautiously to avoid introducing cyclic dependencies between facts.&lt;/p&gt;

&lt;p&gt;In our example the application has to different config paths for different OS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RedHat - /etc/sysconfig/application&lt;/li&gt;
&lt;li&gt;Debian - /etc/default/application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example with access to another fact:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Debian&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/default/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example retreives the &lt;code&gt;os&lt;/code&gt; fact and uses the &lt;code&gt;family&lt;/code&gt; key to compare the OS, then applies logic to determine which application config path to check for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Return values
&lt;/h2&gt;

&lt;p&gt;Puppet expects custom facts to return a valid value type, specifically strings, integers, booleans, or structured data (such as arrays and hashes). If the fact cannot determine a valid value, it should return &lt;code&gt;nil&lt;/code&gt; or &lt;code&gt;undef&lt;/code&gt; to indicate that no fact value is available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows Facts
&lt;/h2&gt;

&lt;p&gt;Windows-based systems support custom facts just like Linux or other Unix-like systems.&lt;br&gt;
The main difference is that many Windows-specific tasks (such as checking installed software or reading from the registry)may require platform-specific Ruby code.&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;Facter::Core::Execution.execute&lt;/code&gt; usually powershell commands is used.&lt;/p&gt;

&lt;p&gt;Here&#39;s an example of a Windows custom fact that retrieves the version of Internet Explorer:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_internet_explorer_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_internet_explorer_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;kernel: &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;reg query &quot;HKLM\Software\Microsoft\Internet Explorer&quot; /v svcVersion&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we use the &lt;code&gt;reg query&lt;/code&gt;command to check the Internet Explorer version in the Windows registry.&lt;/p&gt;

&lt;p&gt;Please note that the &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/&quot; rel=&quot;noopener noreferrer&quot;&gt;Win32 API calls&lt;/a&gt; are deprecated since ruby 1.9.&lt;br&gt;
Please consider using &lt;a href=&quot;https://github.com/ruby/fiddle&quot; rel=&quot;noopener noreferrer&quot;&gt;Fiddle&lt;/a&gt; or other Ruby-based libraries for interacting with the system.&lt;/p&gt;
&lt;h2&gt;
  
  
  Additional Concepts
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Logging
&lt;/h3&gt;

&lt;p&gt;It&#39;s often useful to include logging within custom facts to help with troubleshooting and development. You can log messages using Puppet&#39;s built-in logging mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Custom fact &#39;betadots_application_version&#39; running&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logging levels include &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, and &lt;code&gt;fatal&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Aggregates
&lt;/h3&gt;

&lt;p&gt;Another option to create structured facts is the usage of chunks.&lt;br&gt;
Each chunk must have a name and returns a data structure which must be of type array or hash.&lt;br&gt;
After reading all chunks, the chunks will be aggregated. The default is to merge arrays or hashes.&lt;/p&gt;

&lt;p&gt;It is possible to write a different aggregate definition.&lt;/p&gt;

&lt;p&gt;We assume that our app can directly read config keys: &lt;code&gt;/opt/app/bin/app config &amp;lt;key&amp;gt;&lt;/code&gt; returns the value.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ~/new_fact/betadots_application_version.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_application_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;confine&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;RedHat&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/sysconfig/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Debian&#39;&lt;/span&gt;
                     &lt;span class=&quot;s1&quot;&gt;&#39;/etc/default/application&#39;&lt;/span&gt;
                   &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_cfg_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:config_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:version&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config version&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:config_cache_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cache_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config cache_size&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:config_log_level&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:log_level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app config log_level&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;betadots_application_version =&amp;gt; {
  version =&amp;gt; &#39;1.2.4&#39;,
  cache_size =&amp;gt; &#39;400M&#39;,
  log_level =&amp;gt; &#39;info&#39;,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Weight
&lt;/h3&gt;

&lt;p&gt;Facter offers the option to select a value based on priority of a fact.&lt;br&gt;
Let&#39;s assume that an application is either started form systemd or in any other way (so it has a pid file).&lt;/p&gt;

&lt;p&gt;Note: external facts have a built in weight value of 1000. Overriding external facts is possible by creating a fact with the same name and specifying a weight value over 1000.&lt;/p&gt;

&lt;p&gt;More details can be found in the &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts#configuring_facts-fact-precedence&quot; rel=&quot;noopener noreferrer&quot;&gt;Fact precedence&lt;/a&gt; section of the &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/custom_facts&quot; rel=&quot;noopener noreferrer&quot;&gt;Custom facts overview page&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;application_running&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_weight&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;systemctl status app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;application_running&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_weight&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;/var/run/application.pid&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Blocking and Caching
&lt;/h3&gt;

&lt;p&gt;By default, facts are recalculated each time they are queried. In certain scenarios, this might be inefficient, especially for computationally expensive facts or facts that rarely change. Facter offers a caching mechanism that allows you to persist fact values between Puppet runs.&lt;/p&gt;

&lt;p&gt;Besides this some facts might be of no interest, but take long time to read.&lt;br&gt;
In this case Facters blocklist can be used.&lt;/p&gt;

&lt;p&gt;Configuration takes place in &lt;code&gt;/etc/puppetlabs/facter/facter.conf&lt;/code&gt; on *nix systems or &lt;code&gt;C:\ProgramData\PuppetLabs\facter\etc\facter.conf&lt;/code&gt; on Windows.&lt;br&gt;
Please note that this configuratoin must be done on the Puppet agents!&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# /etc/puppetlabs/facter/facter.conf
facts : {
  blocklist : [ &quot;file system&quot;, &quot;EC2&quot;, &quot;processors.isa&quot; ]
  ttls : [
    { &quot;timezone&quot;: 30 days },
    { &quot;my-fact-group&quot;: 30 days },
  ]
}
fact-groups : {
  my-fact-group : [ &quot;os&quot;, &quot;ssh.version&quot;]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see: within the &lt;code&gt;blocklist&lt;/code&gt; we can mention the facts itself, a sub fact, or a fact group.&lt;br&gt;
Existing facter groups can be read on the command line:&lt;/p&gt;
&lt;h4&gt;
  
  
  Facter block groups
&lt;/h4&gt;


&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;facter &lt;span class=&quot;nt&quot;&gt;--list-block-groups&lt;/span&gt;
EC2
  - ec2_metadata
  - ec2_userdata
file system
  - mountpoints
  - filesystems
  - partitions
hypervisors
  - hypervisors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Facter cache groups
&lt;/h4&gt;


&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;facter &lt;span class=&quot;nt&quot;&gt;--list-cache-groups&lt;/span&gt;
EC2
  - ec2_metadata
  - ec2_userdata
GCE
  - gce
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Besides this we can add our own fact groups using the &lt;code&gt;facts-group&lt;/code&gt; setting.&lt;/p&gt;

&lt;p&gt;Please note that in the past people were using &lt;a href=&quot;https://forge.puppet.com/modules/dylanratcliffe/facter_cache/readme&quot; rel=&quot;noopener noreferrer&quot;&gt;Dylan Ratcliffe&#39;s facter_cache Module&lt;/a&gt;. This is no longer needed as of Puppet 6, when the facter.conf settings were introduced.&lt;/p&gt;

&lt;p&gt;The Puppet website provides more information on &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/configuring_facter&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;facter.conf&lt;/code&gt; settings&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Coding Strategies and Structured Data
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Limit the total number of facts&lt;/li&gt;
&lt;li&gt;Use data hashes&lt;/li&gt;
&lt;li&gt;Generic code and Ruby modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In more advanced cases, facts can return structured data, such as arrays or hashes. Structured facts allow for more complex data to be passed back to the Puppet server.&lt;/p&gt;

&lt;p&gt;It is absolutely required to limit the total number of facts for a node. Main reason is the performance of PuppetDB.&lt;/p&gt;

&lt;p&gt;Hashes or arrays can be built directly in Ruby code or via Facter aggregates.&lt;/p&gt;

&lt;p&gt;e.g. We need the state of several files.&lt;/p&gt;

&lt;p&gt;Solution 1: Hash in Ruby:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_file_check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;c:/Program Data/Application/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;c:/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;size&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;world_writable&#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;world_writable?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will return:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sr&quot;&gt;/opt/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64328&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;sr&quot;&gt;/etc/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;txt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;438&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Solution 2: Hashes using aggregate:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_file_check&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:aggregate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:kernel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;c:/Program Data/Application/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;c:/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;Linux&#39;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;/opt/app/bin/app&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;s1&quot;&gt;&#39;/etc/backup/state.txt&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;chunk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:file_world_writable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;file_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;world_writable?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will return:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;sr&quot;&gt;/opt/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64328&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;sr&quot;&gt;/etc/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;backup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;txt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;world_writable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;438&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another example reads the Puppet agent certificate and provides the certificate extensions as a Facter hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Facter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:betadots_cert_extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setcode&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;openssl&#39;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet&#39;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;puppet/ssl/oids&#39;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# set variables&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;certdir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:certdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;certname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:certname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;certificate_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certdir&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;certname&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.pem&quot;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# get puppet ssl oids&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;oids&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Puppet&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SSL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Oids&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PUPPET_OIDS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;oids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;o&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# read the certificate&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;OpenSSL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;X509&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Certificate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;certificate_file&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# cert extensions differs if we run via agent (numeric) or via facter (names)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;oid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_s&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r{^1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;34380&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;1}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;oid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;pp_preshared_key&#39;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r{^pp_}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;oid&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;..-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;short_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;pp_preshared_key&#39;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;extension_hash&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Custom facts are a powerful way to extend Puppet’s built-in facts with your organization’s specific information. Whether you’re retrieving application versions, monitoring system configuration, or providing custom details for a specific environment, custom facts help ensure that Puppet has the right data to apply the correct configuration.&lt;/p&gt;

&lt;p&gt;In this post, we’ve covered the basics of custom fact development, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to create custom facts in a local environment or as part of a module.&lt;/li&gt;
&lt;li&gt;The API for creating and confining facts.&lt;/li&gt;
&lt;li&gt;Accessing other facts and helper methods to enhance fact functionality.&lt;/li&gt;
&lt;li&gt;Returning structured data for complex requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One more advise: Run local commands only! If connections to remote systems are required you must ensure scalability and high availability.&lt;/p&gt;

&lt;p&gt;Happy puppetizing,&lt;br&gt;
Martin&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Letting go of control</title>
    <link href="https://overlookinfratech.com/2024/12/19/letting-go-of-control/"/>
    <id>https://overlookinfratech.com/2024/12/19/letting-go-of-control</id>
    <updated>2024-12-19T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Events of the last week have me thinking about something that’s true in a lot of
contexts. A lot of people like to have control of a relationship; control of a
situation; control of the message; or in this case, control of a community. I
get it, realizing that you don’t have control can be unsettling. But in reality,
not having control is the natural order of things and you’ll have a much better
time of it once you realize that.&lt;/p&gt;

&lt;p&gt;Let’s dive in. A controlling relationship is an easy start to the conversation,
since most of us have either been in one or have witnessed one. We may have even been
the offender; it’s very easy to accidentally fall into this role under certain
circumstances. A relationship controller is quite often someone who’s terrified
that the other(s) will leave or hurt them and so they tighten their grip more
and more and more trying to prevent this. We all know the usual outcome; that
grip becomes so tight that it &lt;em&gt;forces&lt;/em&gt; an escape. The inability to let go of
control leads to exactly the outcome that was most feared to begin with.&lt;/p&gt;

&lt;p&gt;The same thing happens when one tries too hard to control a message. I don’t
have to explain it to you. Google the
&lt;a href=&quot;https://en.wikipedia.org/wiki/Streisand_effect&quot;&gt;Barbra Streisand effect&lt;/a&gt; for that.&lt;/p&gt;

&lt;p&gt;As a seasoned community leader, I’m most interested in the context of community
engagement. And in particular, technical open source communities centered around
a product ecosystem — although this fundamentally applies to other community
types as well. See, a lot of Product communities have a power imbalance at their
very core. If the company can gatekeep merges, and packaging, and releases —
then they have an outsized say in where the product goes. If they don’t want to
invest in supporting Apple Silicon, it ain’t gonna happen &lt;em&gt;even if community
members volunteer to do the work.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Many companies mistake this imbalance as having control, even to the point of
treating community members as if they were unpaid employees. I’ll give you a
personal example of that. For most of our history, Puppet had an excellent
community relationship because we remembered that we built ourselves off of
their passion. As money got tight, some of the niceties got strained and some of
the technical engagement started lagging. But we always treated them with
respect and supported them as they grew and evolved the community. When they
created &lt;a href=&quot;https://voxpupuli.org&quot;&gt;Vox Pupuli&lt;/a&gt;, we celebrated and we never stopped
celebrating it. Just before the pandemic did us in, a large part of what turned out
to be Puppet’s final PuppetConf/Puppetize keynote centered Vox Pupuli as the
independent community group that our success hinged on.&lt;/p&gt;

&lt;p&gt;This changed rapidly after the Perforce acquisition. The community started being
treated as liabilities and resources. At one point, the VP of Product at the
time insisted on holding a community Zoom call &lt;em&gt;the very next day or two&lt;/em&gt; to get
feedback on some ideas he had. He said, “of course they’ll do it. They want to
have input on what we sell!”&lt;/p&gt;

&lt;p&gt;I’m sure they do. I collect feedback like that all the time. But community
members who are freely giving their time for something they enjoy in a space
where they feel welcomed and valued &lt;em&gt;do not want to feel obligated to do so!&lt;/em&gt;
That’s called a job and you collect a paycheck for it.&lt;/p&gt;

&lt;p&gt;He didn’t understand when I tried to explain that he didn’t &lt;em&gt;control them&lt;/em&gt;. They
weren’t employees and he couldn’t expect them to prioritize his demands. He
almost fired me when I refused.&lt;/p&gt;

&lt;p&gt;Most companies in this position don’t understand how fragile and tenuous this
power imbalance is. It only lasts so long as you own the roadmap and people
still care enough to put up with your bullshit. As soon as the frustrations
community feels about your behavior outweighs the work it is to just take on the
roadmap themselves, then this power imbalance evaporates in &lt;em&gt;an instant&lt;/em&gt;. And it’s
generally irreversible because you’ve already tipped your hand as to how you’ll
behave when you feel like you have the advantage. Your voice is no longer welcomed.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;&lt;em&gt;A Product company never has control over their open source user community.
They only have an illusion of it and by the time they realize this, it’s
generally too late to recover.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To build a healthier community, they should be actively relinquishing control
and cultivating influence instead. They will get very similar results, they just
have to be more patient, honest, open, transparent,… You know, all the things
that make a relationship work.&lt;/p&gt;

&lt;p&gt;When you realize that you have zero control but as much influence as you are
willing to develop, then you start behaving in ways that build a strong, loyal,
and supportive community that will help see you through your missteps and always
make sure that you have a seat at the table when decisions are being made.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You don’t lie to your community. You don’t always have to tell every single detail, and you can always couch things in softer terms or provide context. But you never lie.&lt;/li&gt;
  &lt;li&gt;You don’t make empty promises or excuses.  You’re clear about what you can or can’t do.&lt;/li&gt;
  &lt;li&gt;You actively listen to their feedback and you care about it. In specific, you hear the pain that they have more than the words they use or the solutions they offer. You never “empty listen” and then throw it in the trash.&lt;/li&gt;
  &lt;li&gt;You take action on that feedback if warranted, but you’re always clear what should be expected.&lt;/li&gt;
  &lt;li&gt;You get really good at hearing what community wants and getting there before they have to ask for it. This gives you more influence in the growth patterns, just like cultivating a garden.&lt;/li&gt;
  &lt;li&gt;You participate in the day-to-day of the community and get to know what people value.&lt;/li&gt;
  &lt;li&gt;You are a role model, not a dictator.&lt;/li&gt;
  &lt;li&gt;You remind rather than enforce. In all the years I participated in and then managed the Puppet community, I rarely had to moderate users more than reminding them of the code of conduct.&lt;/li&gt;
  &lt;li&gt;You meet community where they are and where they want to be. You help them build a better table rather than insisting that they sit at yours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, listen more than you proclaim, realize the value of your community
members and never take them for granted, and help them build the space that they
want — where they can feel welcome, accepted, and valued. Connect with people in
a human way, rather than as a corporation using carefully crafted corporate
doublespeak.&lt;/p&gt;

&lt;p&gt;And let go of this anxiety-driven idea of control. You never had it anyways, and
influence is far more valuable. Influence helps you build a lasting community
and solid open source project in which you have a respected seat at the table.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Contributions of ‘Ghost Engineers’</title>
    <link href="https://overlookinfratech.com/2024/12/05/contributions-of-ghost-engineers/"/>
    <id>https://overlookinfratech.com/2024/12/05/contributions-of-ghost-engineers</id>
    <updated>2024-12-05T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Last week was Thanksgiving for the USA and while I don’t personally celebrate
the holiday or the violent colonization it represents, it does encourage me to
reflect back on some of the things I’m most thankful for. Once again, you won’t
be surprised to hear that it’s people. Coworkers, friends, community members,
industry professionals that I learn from and am inspired by every day. People
that I’m lucky enough to collaborate with, which probably includes many of you
reading this. People in my communities are what I’m thankful for.&lt;/p&gt;

&lt;p&gt;Collaboration is one of the things that I’ve been thinking about a lot over the
last few months while getting this fun little venture off the ground. I think
that most companies don’t prioritize collaboration as much as they should and I
want to avoid that trap. And I’m definitely thinking about macro business
decisions like &lt;em&gt;why did Puppet, inc. invest &lt;strong&gt;so much&lt;/strong&gt; time and resources into
building an orchestration tool from scratch instead of integrating with one of
the many existing options, even Ansible itself?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔆 But even more, I’m thinking about micro level choices, because I think
those are how you define yourself as a people and then that culture bubbles up
into how your business runs, from top to bottom.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many of you know that I’ve been involved in the roller derby world for a very
long time. My partner has been a founding member of several teams and we are in
the home of one of the premiere leagues in the world. One of the things that
continues to draw me to the sport is their sense of community. The big dollars
haven’t invaded yet and so they’re just there to have fun. They’ll beat each
other up on the track and then share an afterparty. When a bout concludes, the
audience will rush to circle the track because every single player will
high-five every single fan who wants it. Then if you pay attention, there’s a
ceremony in which the teams give gifts to each other and award several MVP
awards &lt;em&gt;to the other team&lt;/em&gt;. It’s worth pointing out that these awards don’t go to
the player scoring the most points or other easily trackable stats. They almost
always go to the players who are &lt;em&gt;&lt;strong&gt;the best at holding their team together into
a formidable force&lt;/strong&gt;&lt;/em&gt; as observed by the people playing against them.&lt;/p&gt;

&lt;p&gt;Recently, someone at Stanford who apparently knows very little about teamwork
published a study that claimed to reveal that almost 10% of all software
engineers were lazy and ineffective and didn’t contribute anything to the bottom
line. Their rationale for this proclamation was they contributed few or no lines
of code and the author deemed them “ghost engineers.” 🤨&lt;/p&gt;

&lt;p&gt;A lot of people more qualified than I explained how LOC is maybe the least
useful metric possible so I won’t go there. Instead, I want to look at it more
holistically. This is a symptom of the &lt;em&gt;bleed ‘em dry&lt;/em&gt; stack-ranking mentality
that wants an easily measured metric so that toxic companies can reward those on
top and punish those on the bottom in the misguided idea that it will improve
performance and help the company hit revenue targets. Of course, trivially
measured and manipulated metrics like this have nothing to do with the success
of your company. If I have a choice between doing something to benefit the
business or something that will get me a raise, what do you think I’m going to
pick?&lt;/p&gt;

&lt;p&gt;The other day, &lt;a href=&quot;https://hachyderm.io/@mekkaokereke/113547029714350019&quot;&gt;Mekka Okereke posted a sports metric&lt;/a&gt;
that I’d never heard of and it’s super intriguing. We’ve all heard of the stats
like points scored, number of rebounds or blocks or assists. But he talks about
a new metric that makes me think about those roller derby MVP awards. It’s
called the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;plus-minus&lt;/code&gt; and seems to be a simple comparison of how well your
team does when you’re on the court vs. how it does when you’re not playing. 🤯&lt;/p&gt;

&lt;p&gt;Huh. I can see parallels in tech…. He goes on to say that&lt;/p&gt;

&lt;blockquote class=&quot;mastodon-embed&quot; data-embed-url=&quot;https://hachyderm.io/@mekkaokereke/113547086481399723/embed&quot; style=&quot;background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;&quot;&gt; &lt;a href=&quot;https://hachyderm.io/@mekkaokereke/113547086481399723&quot; target=&quot;_blank&quot; style=&quot;align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, &#39;Segoe UI&#39;, Oxygen, Ubuntu, Cantarell, &#39;Fira Sans&#39;, &#39;Droid Sans&#39;, &#39;Helvetica Neue&#39;, Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;&quot;&gt; &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; width=&quot;32&quot; height=&quot;32&quot; viewBox=&quot;0 0 79 75&quot;&gt;&lt;path d=&quot;M74.7135 16.6043C73.6199 8.54587 66.5351 2.19527 58.1366 0.964691C56.7196 0.756754 51.351 0 38.9148 0H38.822C26.3824 0 23.7135 0.756754 22.2966 0.964691C14.1319 2.16118 6.67571 7.86752 4.86669 16.0214C3.99657 20.0369 3.90371 24.4888 4.06535 28.5726C4.29578 34.4289 4.34049 40.275 4.877 46.1075C5.24791 49.9817 5.89495 53.8251 6.81328 57.6088C8.53288 64.5968 15.4938 70.4122 22.3138 72.7848C29.6155 75.259 37.468 75.6697 44.9919 73.971C45.8196 73.7801 46.6381 73.5586 47.4475 73.3063C49.2737 72.7302 51.4164 72.086 52.9915 70.9542C53.0131 70.9384 53.0308 70.9178 53.0433 70.8942C53.0558 70.8706 53.0628 70.8445 53.0637 70.8179V65.1661C53.0634 65.1412 53.0574 65.1167 53.0462 65.0944C53.035 65.0721 53.0189 65.0525 52.9992 65.0371C52.9794 65.0218 52.9564 65.011 52.9318 65.0056C52.9073 65.0002 52.8819 65.0003 52.8574 65.0059C48.0369 66.1472 43.0971 66.7193 38.141 66.7103C29.6118 66.7103 27.3178 62.6981 26.6609 61.0278C26.1329 59.5842 25.7976 58.0784 25.6636 56.5486C25.6622 56.5229 25.667 56.4973 25.6775 56.4738C25.688 56.4502 25.7039 56.4295 25.724 56.4132C25.7441 56.397 25.7678 56.3856 25.7931 56.3801C25.8185 56.3746 25.8448 56.3751 25.8699 56.3816C30.6101 57.5151 35.4693 58.0873 40.3455 58.086C41.5183 58.086 42.6876 58.086 43.8604 58.0553C48.7647 57.919 53.9339 57.6701 58.7591 56.7361C58.8794 56.7123 58.9998 56.6918 59.103 56.6611C66.7139 55.2124 73.9569 50.665 74.6929 39.1501C74.7204 38.6967 74.7892 34.4016 74.7892 33.9312C74.7926 32.3325 75.3085 22.5901 74.7135 16.6043ZM62.9996 45.3371H54.9966V25.9069C54.9966 21.8163 53.277 19.7302 49.7793 19.7302C45.9343 19.7302 44.0083 22.1981 44.0083 27.0727V37.7082H36.0534V27.0727C36.0534 22.1981 34.124 19.7302 30.279 19.7302C26.8019 19.7302 25.0651 21.8163 25.0617 25.9069V45.3371H17.0656V25.3172C17.0656 21.2266 18.1191 17.9769 20.2262 15.568C22.3998 13.1648 25.2509 11.9308 28.7898 11.9308C32.8859 11.9308 35.9812 13.492 38.0447 16.6111L40.036 19.9245L42.0308 16.6111C44.0943 13.492 47.1896 11.9308 51.2788 11.9308C54.8143 11.9308 57.6654 13.1648 59.8459 15.568C61.9529 17.9746 63.0065 21.2243 63.0065 25.3172L62.9996 45.3371Z&quot; fill=&quot;currentColor&quot; /&gt;&lt;/svg&gt; &lt;div style=&quot;color: #787588; margin-top: 16px;&quot;&gt;Post by @mekkaokereke@hachyderm.io&lt;/div&gt; &lt;div style=&quot;font-weight: 500;&quot;&gt;View on Mastodon&lt;/div&gt; &lt;/a&gt; &lt;/blockquote&gt;
&lt;script data-allowed-prefixes=&quot;https://hachyderm.io/&quot; async=&quot;&quot; src=&quot;https://hachyderm.io/embed.js&quot;&gt;&lt;/script&gt;

&lt;p&gt;This resonates so much with me. I’ve always worked towards being this person,
and I’ve always really valued others with this mindset. It’s a shame that it’s
so rare, but again, tech companies rarely incentivize it.&lt;/p&gt;

&lt;p&gt;But that brings me to the actual point of this ramble. The more we
micro-optimize and track things like lines of code, or commits, or tickets
closed, or time on the clock, or even story points, the more we lose track of
the bigger shared company success goal. And the more we incentivize people to
game the system. It’s easy to write more lines of code or chop up work into a
million tickets and commits. It’s much harder to enable actual value.&lt;/p&gt;

&lt;p&gt;I don’t know the answer. Not yet. And maybe I never will; this is a challenge
people have been grappling with for ages and in a backwards sort of way, it’s
why we have the terrible metrics we do today. But I do know that the answer is
based on building a culture of shared team success rather than on myopically
focusing on individual metrics. &lt;strong&gt;&lt;em&gt;Because these “ghost engineers” are the biggest
contributors to our actual success and it’s about damn time they get some MVP
awards of their own. And recognition and compensation to match.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Future proofing open source</title>
    <link href="https://overlookinfratech.com/2024/11/23/ulysses-pacts/"/>
    <id>https://overlookinfratech.com/2024/11/23/ulysses-pacts</id>
    <updated>2024-11-23T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;We had a slight disagreement with our attorney while drafting our articles of
incorporation  and related documents. He’d used some very aggressive language
around the ownership of intellectual property and whatnot. It was pretty
standard lingo but it just rubbed us both the wrong way and we asked him to tone
it down significantly. This prompted a long discussion about our open source
business model. We talked about the ownership of open source contributions, both
to our repositories and to those owned by Vox Pupuli or other community members.
We talked about contributor license agreements (CLAs) and the fact that our
business model involves more upstream contributions than internal development.
We explained that any future employees would almost certainly be established
open source contributors and that we didn’t believe it was ethical to try to
claim ownership of their personal OSS work whether it was done on “work time” or
not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At one point he asked, “if you don’t want to own anything, then how will you
build value in your company?”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ah, now that’s the problem, isn’t it? See, we don’t really ascribe to the
capitalistic idea that valuation is the only thing that matters. We’re more
interested in making the world a better place, in building value across the
ecosystem, and in making a decent living along the way. We’re not aiming to be a
billion dollar acquisition target. That’s why our company is registered as a
&lt;em&gt;public benefit corporation&lt;/em&gt; so that our &lt;a href=&quot;https://en.wikipedia.org/wiki/Fiduciary&quot;&gt;fiduciary duty&lt;/a&gt;
is not exclusively measured in dollars and shareholder return.&lt;/p&gt;

&lt;p&gt;That said, those are big words for current me. In a potential wildly successful
timeline, what’s future me going to do when staring down an investment offer
with a lot of zeros attached? We need to protect our ideals against not only
unethical venture capital &amp;amp; private equity vultures, but also against our own
temptations.&lt;/p&gt;

&lt;p&gt;Recently Jay Graber claimed that Bluesky was &lt;a href=&quot;https://boingboing.net/2024/11/22/bluesky-ceo-taunts-musk-with-billionaire-proof-design-claim.html&quot;&gt;billionaire proof&lt;/a&gt;
and I genuinely hope it’s true and wish them well. But all across the Internet,
experts had lots to say about it. In particular, Cory Doctorow talked about
&lt;a href=&quot;https://pluralistic.net/2023/08/06/fool-me-twice-we-dont-get-fooled-again/&quot;&gt;Ulysses pacts&lt;/a&gt;
which is recognizing that humans are fallible and protecting yourself against
temptation. Things like removing booze from your house to help you get through a
dry January or setting screen time limits so you go to bed instead of
doomscrolling all night.&lt;/p&gt;

&lt;p&gt;Open source licenses and eschewing greedy CLAs are Ulysses pacts. They mean
that when we are tempted, we literally cannot claw our projects back from the
community commons. Even more so, by staying OSS only and avoiding or limiting
&lt;a href=&quot;https://en.wikipedia.org/wiki/Open-core_model&quot;&gt;open core&lt;/a&gt; products, we reduce
our valuation potential to make that billion dollar investment or buyout offer
less likely in the first place.&lt;/p&gt;

&lt;p&gt;It comes with challenges, of course. It means that we have to work harder to
build a sustainable business model instead of taking the easy way out. It means
that hiring might be harder because nobody’s going to become a tech bro
billionaire on our stock options.&lt;/p&gt;

&lt;p&gt;But I think it’s doable. And I don’t quite have the hubris to say that we’re
“billionaire proof” but I think we’re building the right protections to ensure
that our community is and always will be open. And I do hope you’ll come along
with us.&lt;/p&gt;

&lt;p&gt;For what it’s worth, the attorney did end up rewriting things to focus on open
source. Small wins, but they’re important and they add up.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Alpha community package availability</title>
    <link href="https://overlookinfratech.com/2024/11/13/fork-announce/"/>
    <id>https://overlookinfratech.com/2024/11/13/fork-announce</id>
    <updated>2024-11-13T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Well, I’ve got some great news for you. Thanks to hard work by a few volunteers,
we have alpha community-built packages up and the starts of a download mirror
list, which you can find on the new “downloads” page. But let’s talk a bit
before you go racing off to install and make sure you know what you’re dipping
into.&lt;/p&gt;

&lt;p&gt;This project is kind of a preliminary state. It’s just a simple rebuild of the
packages, with Perforce’s blessing. There have been no alterations other than
build instructions, and no name or branding changes. The repositories are
currently just simple mirrors in the Overlook InfraTech namespace. There will
almost certainly be some organizational changes as we get going.&lt;/p&gt;

&lt;p&gt;You &lt;em&gt;should&lt;/em&gt; be able to simply install the package and end up with exactly the
same commands as before. However, these should be considered kick-the-tires
builds, not something to run in production just yet. We don’t yet have a fully
robust test pipeline operational, although with community help we’ll get there
soon.&lt;/p&gt;

&lt;p&gt;🥳 You can help out by trying these out on non-critical infrastructure. Let us
know of any installation failures or anything weird that you run into. And if you
have anything else to contribute or would like to be involved in other ways, just
drop us a message and let us know what’s on your mind.&lt;/p&gt;

&lt;p&gt;In other news, I realized something funny the other day. I’ve been writing in
this blog as if y’all knew who I was. And some of you probably connected the
dots because of how I shared the posts. But just for clarity’s sake, I wrote an
“about us” page with my name and face. If you go check it out, you’ll notice
another familiar face.&lt;/p&gt;

&lt;p&gt;I’d like to announce that Nick Burgan has joined this little endeavor! He left
Perforce a month or two ago, intending to take a nice peaceful break from the
corporate world. But the community packaging effort piqued his interest again.
He contributed the build system, so he’s to thank for the quick packaging
turnaround, and decided that he’d like to be part of the project long term. All
the better for us!&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: It was only a matter of time</title>
    <link href="https://overlookinfratech.com/2024/11/08/sequestered-source/"/>
    <id>https://overlookinfratech.com/2024/11/08/sequestered-source</id>
    <updated>2024-11-08T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Well, it looks like they did it. &lt;a href=&quot;https://www.puppet.com/blog/open-source-puppet-updates-2025&quot;&gt;Perforce has &lt;em&gt;all but&lt;/em&gt; closed the Puppet source&lt;/a&gt;.
To be fair, they didn’t actually change the license itself, but they’ve gone as
far as they could and still remain compliant. They’re forking projects
internally where all their development will happen and pinkie-swear promise that
….. &lt;em&gt;eventually&lt;/em&gt; ….. that work will make it to the public repositories.&lt;/p&gt;

&lt;p&gt;But we’ve seen how they keep promises. Heck, they never did get around to
paying out the survey compensation that y’all donated to Vox Pupuli a few years
ago.&lt;/p&gt;

&lt;p&gt;And as far as not changing the license goes, I have a little backstory to share
there too. See, when Perforce leadership first started flirting with the idea of
changing the license, I talked them out of it by explaining how the Contributor
License Agreement had been managed.&lt;/p&gt;

&lt;p&gt;First, let’s explain what a CLA is, if you haven’t run into one before. We’ll
start with just a commit. When you contribute to an OSS project with a given
license, your contribution is implicitly and hand-wavily licensed similarly. But
here’s the sticky point–you still &lt;strong&gt;&lt;em&gt;own&lt;/em&gt;&lt;/strong&gt; the code you contributed. So that 2
lines of code that I contributed to the Linux kernel a thousand years ago is
still mine and if they ever decide to do something wonky like &lt;em&gt;ahem&lt;/em&gt;, close the
source, they would need to get my permission or reimplement the patch.&lt;/p&gt;

&lt;p&gt;The CLA is designed to get around that. They’re all a little different and
generally they don’t take your ownership away, but they do grant perpetual
license to use and to re-license any code you contribute. If you’ve contributed
to Puppet and you had to go sign the thing to make the CLA Bot quit yelling at
you, that’s what you signed away.&lt;/p&gt;

&lt;p&gt;But here’s the problem. It was inconsistently maintained over the history of
the project. It didn’t even exist for the first years of Puppet and then later
on it would regularly crash or corrupt itself and months would go by without CLA
enforcement. If Perforce actually tried to change the license, it would require
a long and costly audit and then months or years of tracking down long-gone
contributors and somehow convincing them to agree to the license change.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I honestly think that’s the real reason they didn’t change the license&lt;/em&gt;.
Apache 2 allows them to close the source away and only requires that they tell
you what they changed. A changelog might be enough to stay technically
compliant. This lets them pretend to be open source without actually
participating.&lt;/p&gt;

&lt;p&gt;But we know what this sequestered source will mean. In short, from whenever
they implement this on, you’ll never again have assurance that the source code
you’re looking at in the repo is the same as what’s running on your servers. You
won’t know if they’re running intrusive trackers or exfiltrating private
information about your infrastructure. And you certainly won’t get access to any
new features. The Debian packages that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lavamind&lt;/code&gt; builds and the FreeBSD packages
that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;smortex&lt;/code&gt; maintains will never again be anything remotely resembling
current.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Which I imagine is how they want it.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But that honestly only touches the surface of what’s wrong with this approach.
What happens when community tooling, like Vox’s test pipelines for example,
break unexpectedly because they don’t know about internal EULA-only changes that
haven’t made it public yet? You might think that’s hyperbole, but it’s something
that regularly happened &lt;em&gt;even with the source being open!&lt;/em&gt; Many of you remember
the debacle of Puppet 6 demoting core resource types to installable modules.
With sequestered source, this is only going to get more common and worse. Even
paying customers are going to feel the pain. The community is really good at
catching all the small things before they snowball into big problems and before
they ship to customers. With this OSP-as-an-afterthought approach, that last bit
of QA is going to land firmly in paying customers’ laps.&lt;/p&gt;

&lt;p&gt;In any case, last week I told y’all that I was fiddling with a build pipeline
to get packages built from the repositories I’d mirrored. Several people reached
out to help and it looks like we can have the build pipeline cranking and
geographically distributed hosting for community-built packages next week.&lt;/p&gt;

&lt;p&gt;I’ve still got a lot of things to work out, but if you’re interested in helping
just give me a shout and let’s get the ball rolling. And you’re always welcome
to show up to the Vox Pupuli sync call
&lt;a href=&quot;https://www.timeanddate.com/worldclock/converter.html?iso=20241112T153000&amp;amp;p1=202&amp;amp;p2=179&amp;amp;p3=919&amp;amp;p4=307&amp;amp;p5=3332&amp;amp;p6=236&amp;amp;p7=240&quot;&gt;next Tuesday at an ungodly time of the morning for me&lt;/a&gt;
using the &lt;a href=&quot;https://perforce.zoom.us/j/92119937381?pwd=IK00nUw1GrmR9KNjtkbMSbQAzpboPu.1&quot;&gt;current Zoom URL&lt;/a&gt;.
We have a lot of details to work out; whether we simply build packages or
deviate from the published source and become a community uptream fork; how we
intend to stay ahead of CVEs and get timely security patches out; what sort of
community governance structure we’d like; and so on. But these are the early
days. We’ve got some time to work out the kinks.&lt;/p&gt;

&lt;p&gt;Cheers and stay well. This week has been a rollercoaster for a lot of us.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: A Flock of Flutter devs</title>
    <link href="https://overlookinfratech.com/2024/10/31/flutter-forking/"/>
    <id>https://overlookinfratech.com/2024/10/31/flutter-forking</id>
    <updated>2024-10-31T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Flutter is a neat little UI toolkit allowing you to build applications
targeting most major platforms. It started out mobile only, iOS and Android.
Today it supports web and desktop too. I poked at it back in the day when I was
considering some fun mobile app dev side projects. It made a pretty big splash
initially, with a lot of community interest. Was it to be the fabled universal
toolkit?&lt;/p&gt;

&lt;p&gt;But over the years, I heard less and less about it and at some point got the
impression that the project was defunct. How sad. So when &lt;a href=&quot;https://hachyderm.io/@hazelweakly&quot;&gt;Hazel Weakly&lt;/a&gt;
boosted a &lt;a href=&quot;https://flutterfoundation.dev/blog/posts/we-are-forking-flutter-this-is-why/&quot;&gt;blog post about a Flutter fork&lt;/a&gt;
the other day, it piqued my interest. So much of the story sounded oddly
familiar. It’s an “open source” project supported by a corporate entity that
claims to be an OSS cheerleader but doesn’t actually show up.
The symptoms are really easy to see. Things like:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;An understaffed core dev team&lt;/li&gt;
  &lt;li&gt;Pull requests and issues that get ignored for years&lt;/li&gt;
  &lt;li&gt;Weird infrastructure that makes it hard for external contributors to participate&lt;/li&gt;
  &lt;li&gt;Lack of interest in community features&lt;/li&gt;
  &lt;li&gt;Needlessly contentious reviews and community friction&lt;/li&gt;
  &lt;li&gt;Devs who don’t actually use the product&lt;/li&gt;
  &lt;li&gt;Few or no commits to upstream projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oh, wait. Are we still talking about Flutter?&lt;/p&gt;

&lt;p&gt;Anyways, it made me think that maybe it would benefit the Puppet community to
have their own fork that could advance at the pace that we need it to. This is a
model that’s worked out pretty well for Fedora and other upstream community
forks. Not everything makes it back downstream, but most of the things that
really matter do. And it achieves this goal without fragmenting the community.
This sounds like what Flock wants to do with the Flutter community and I’m
really excited for them.&lt;/p&gt;

&lt;p&gt;I don’t know that I am ready to take on such a large project, but the idea is
intriguing. It makes me wonder how many other people are having similar thoughts.&lt;/p&gt;

&lt;p&gt;Since I’ve already got the &lt;a href=&quot;https://github.com/orgs/overlookinfra/repositories&quot;&gt;public repos mirrored and syncing any updates&lt;/a&gt;,
I might as well try to get them building packages too. This seems like a fun
little baby step without actually &lt;em&gt;committing&lt;/em&gt; to anything. I’m mostly just
dipping my toes in the water and feeling things out. But if anyone is excited by
the idea and wants to help out, just let me know.&lt;/p&gt;

&lt;p&gt;And Happy Halloween for anyone celebrating. It’s my favorite holiday of the year.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Community stewardship matters</title>
    <link href="https://overlookinfratech.com/2024/10/21/community-stewardship/"/>
    <id>https://overlookinfratech.com/2024/10/21/community-stewardship</id>
    <updated>2024-10-21T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Some years ago I got caught up in a very annoying conflict with &lt;a href=&quot;https://mastodon.social/@purpleidea&quot;&gt;James&lt;/a&gt;
over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;r/puppet&lt;/code&gt;. I don’t remember if he’d created the space or not, but he’d been the
sole moderator for years. He’d been doing an excellent job, but recently he’d been
drifting away from the Puppet community and spending more of his time on his
&lt;a href=&quot;https://github.com/purpleidea/mgmt&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mgmt&lt;/code&gt;&lt;/a&gt; project.
(Which is totally cool and you should check it out sometime.)&lt;/p&gt;

&lt;p&gt;Some moderation tasks had begun slipping by. Spam posts took a while to get removed.
The look and feel of the forum hadn’t kept up to use the newer theming capabilities.
You know, minor things that felt like increasingly irritating papercuts over time.
I thought I’d take a little work off his hands and asked for mod status to which
he immediately replied that no company should ever have full administrative control
over its own community and denied my request. &lt;em&gt;What a jerk! 🤣&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We eventually came to a compromise by which he gave me limited mod status as long
as I brought along two committed community members to take full admin roles and have
veto power over me. And then some time later as we built trust, I also got full admin
and didn’t have to ask the other mods for help updating the forum theming. For some
eight years or so, this arrangement has worked out very well.&lt;/p&gt;

&lt;p&gt;But I’m here now to tell you that &lt;strong&gt;&lt;em&gt;James was right all along&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;community-stewardship-&quot;&gt;Community Stewardship 👍&lt;/h2&gt;

&lt;p&gt;Many people are passingly familiar with the idea of &lt;a href=&quot;https://en.wikipedia.org/wiki/Separation_of_powers#Checks_and_balances&quot;&gt;checks and balances&lt;/a&gt;
in which branches of the government can prevent excesses in the other branches.
The push &amp;amp; pull in opposition to each other creates a stability in the whole system
similarly to how cantilever truss bridges support weight. In contrast, governments
that concentrate all the power in a single branch are generally inherently unstable
and usually require totalitarian methods to prevent unrest.&lt;/p&gt;

&lt;p&gt;The parallel to community is obvious, although the consequences are rarely so brutal.
When a person is ready to leave a community, they &lt;em&gt;just do it&lt;/em&gt;. No police state stops
them at the border. And then the community collapses. Sometimes quickly and sometimes
in excruciating slow motion. It’s almost inevitable when a community doesn’t have
power over itself.&lt;/p&gt;

&lt;p&gt;We’ve seen examples of this for ages in the tech world. Right now we’re watching a
&lt;a href=&quot;https://en.m.wikipedia.org/wiki/WP_Engine#WordPress_dispute_and_lawsuit&quot;&gt;particularly drama-filled MiniMusk Mullenweg Meltdown&lt;/a&gt;
ravage the Wordpress community because he has nothing to keep him in check, but
that’s far from the only example. I was involved (very peripherally) with Andover
way back in the day (get offa my lawn you kids! ) when VA swooped in and booted
out all the original community volunteer SourceForge admins. Without the corrective
pushback of community volunteers, the place very quickly devolved into the first
case of &lt;a href=&quot;https://en.wikipedia.org/wiki/Enshittification&quot;&gt;enshittification&lt;/a&gt;
that I personally witnessed. SourceForge still exists, but it’s the barest shadow of
itself. I can’t remember the last time I even looked at it before five minutes ago.&lt;/p&gt;

&lt;p&gt;And closer to the Puppet ecosystem, just look at the roller coaster that SaltStack
has been on since the VMware/Broadcom acquisition. First in January they posted a
&lt;a href=&quot;https://saltproject.io/blog/salt-project-policy-changes/&quot;&gt;“no open source without direct revenues” manifesto&lt;/a&gt;.
Then in May, &lt;a href=&quot;https://saltproject.io/blog/tom-exits-broadcom-stays-with-salt/&quot;&gt;Tom Hatch, creator of Salt, stepped down&lt;/a&gt;.
And finally in September, &lt;a href=&quot;https://saltproject.io/blog/a-letter-from-chunga/&quot;&gt;the community manager, Jimmy Chunga, left the building&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Given that timeline, observe the difference in another VMware owned community forum between
&lt;a href=&quot;https://web.archive.org/web/20240519015825/https://community.broadcom.com/tanzu/browse/allrecentposts&quot;&gt;May&lt;/a&gt;
and &lt;a href=&quot;https://web.archive.org/web/20240909013847/https://community.broadcom.com/tanzu/browse/allrecentposts&quot;&gt;September&lt;/a&gt;.
Today, there’s a single orphaned post from 25 days ago with zero replies. When a
corporate entity &lt;em&gt;owns&lt;/em&gt; a community, there’s very little to keep them prioritizing
community needs, and people respond to that.&lt;/p&gt;

&lt;p&gt;Perhaps a better way of stating James’ objection is that&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;📌 A community always has ultimate power over itself, even if sometimes that power
is exercised by simply leaving.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;corporate-stewardship-&quot;&gt;“Corporate Stewardship” 🤢&lt;/h2&gt;

&lt;p&gt;This is why I don’t truly believe there’s such a thing as “corporate stewardship”
with implied ownership. The very definition of the word is (emphasis mine):&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;stewardship, n.&lt;/em&gt; the careful and responsible management of &lt;strong&gt;&lt;em&gt;something entrusted to one’s care&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One cannot be a steward of something you own and have iron control over! Being a
community steward means responsibly managing the community &lt;em&gt;on behalf of the community&lt;/em&gt;
instead of to “control the message” or mine it for leads or other kinds of monetization.
And when a corporate entity has unfettered power over a community, without the checks
and balances pushback to keep it in line, it’s an unstable system that inevitably
falls into enshittification. Corporate entities simply are not capable of telling
themselves no, especially when they believe that there’s money on the table.&lt;/p&gt;

&lt;h3 id=&quot;so-why-did-it-work-for-rpuppet-and-other-aspects-of-the-puppet-community-for-so-long&quot;&gt;So why did it work for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;r/puppet&lt;/code&gt; and other aspects of the Puppet Community for so long?&lt;/h3&gt;

&lt;p&gt;In reality it didn’t. Puppet’s community never fell under corporate stewardship.
It only appeared that way because some of the folk in admin/owner roles were employed
by Puppet. But the key is that &lt;em&gt;we were all community members first and foremost&lt;/em&gt;.
We participated in community, we helped people in the Slack and asked for help ourselves,
we built Puppet modules and other tools to satisfy our needs, we helped connect people
with needs to people with solutions. And then we also did our jobs, just like everyone
else in the community.&lt;/p&gt;

&lt;p&gt;Vox Pupuli leaders and other community members always set the ground rules for the
community and the Puppet Community Team just implemented them. That’s not to say
that the company didn’t have any say–we certainly did help steer the ship. But
Puppet’s influence was gentle and always followed the needs of the community itself.&lt;/p&gt;

&lt;p&gt;Our problem was that we never wrote down and codified these practices. For example,
we had a very firm rule that any Puppet employee with a leadership role or other
decision making influence would not run for the Vox Pupuli management committee
to avoid a conflict-of-interest and having undue control over community decisions.
That’s why you’ve never seen myself or David Sandilands on the PMC. But that was
an informal rule that we held ourselves to, it wasn’t part of the Vox bylaws.&lt;/p&gt;

&lt;h2 id=&quot;in-short&quot;&gt;In short&lt;/h2&gt;

&lt;p&gt;The Puppet Community has always been under community stewardship, even though it
wasn’t always obvious as such. And that’s the only way that a healthy vibrant
community can prosper. Unfortunately, we were sloppy and didn’t protect
against future iterations of the company that weren’t as responsible.&lt;/p&gt;

&lt;p&gt;Now as I’ve become exclusively a community member, I’m becoming more and more
convinced that we need to firmly re-establish community leadership of the Puppet
ecosystem. I’m still not quite sure what that means, but one thing’s for sure:
&lt;em&gt;we gotta write it down this time&lt;/em&gt;!&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Overlook InfraTech: Everything is on fire….</title>
    <link href="https://overlookinfratech.com/2024/10/16/everything-on-fire/"/>
    <id>https://overlookinfratech.com/2024/10/16/everything-on-fire</id>
    <updated>2024-10-16T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;I’ve been pretty quiet since leaving Puppet in July. And circumstances this week
aren’t exactly how I wanted to pop my head up again and tell you what I was up to.
I’ve done some private consulting with a few of y’all over the years, and had
planned to expand on that. Check out the &lt;em&gt;Services&lt;/em&gt; page for more information if
you’re interested. But that’s not what I want to talk about today.&lt;/p&gt;

&lt;p&gt;I want to talk a bit about Perforce’s actions and what I think that means for the
community &amp;amp; ecosystem. They &lt;a href=&quot;https://hachyderm.io/@binford2k/113307917523904182&quot;&gt;seized control of the Puppet Community Slack&lt;/a&gt;,
booted all the admins from the moderator team, and banned me from the space. For
context of how momentous that was, up until this week the Puppet Community has only
ever banned one single person &lt;em&gt;ever&lt;/em&gt;. To me, that implies that they’re gearing up
for something that they want super strict control over messaging for. We’ve all
seen this game before.&lt;/p&gt;

&lt;p&gt;I know from personal experience with the company that they do not value the community
for itself. In 2023, they flew me to Boston to explain the community to execs &amp;amp; VPs.
Halfway through my presentation, they cut me off to demand to know how we monetize
and then they pivoted into speculation on how we &lt;em&gt;would&lt;/em&gt; monetize. There was zero
interest in the idea that a healthy community supported a strong ecosystem from
which the entire value of the company and product was derived. None. If the community
didn’t literally hand them dollars, then they didn’t want to invest in supporting it.&lt;/p&gt;

&lt;p&gt;But that’s to be expected, I suppose. They are a &lt;a href=&quot;https://github.com/binford2k/private_equity&quot;&gt;private equity company&lt;/a&gt;
after all.&lt;/p&gt;

&lt;p&gt;Now here’s something that many people don’t realize: &lt;strong&gt;&lt;em&gt;the Apache 2 license does not
require that the source be kept open&lt;/em&gt;&lt;/strong&gt;. In fact, simply &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/release_notes_puppet.html&quot;&gt;publishing a changelog&lt;/a&gt;
might be enough to stay compliant.&lt;/p&gt;

&lt;p&gt;I might be paranoid, but it would not surprise me one bit if they were about to
close the source and make it really hard to use Open Source Puppet. That would
tie right into what I know about their values. Why should they invest in supporting
users who don’t hand them dollars?&lt;/p&gt;

&lt;p&gt;I’m obviously not super happy with that idea, so I’ve spent the last couple days
busily mirroring public OSS repositories from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;puppetlabs&lt;/code&gt;. I don’t quite know what
happens next, but if worse comes to worst, then I’ll at least be able to republish
packages.&lt;/p&gt;
  </content>
    <author>
      <name>Overlook InfraTech</name>
      <uri>https://overlookinfratech.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Laurent Domb Blog: Game Day Every Day: How AWS Prepares for the Unexpected</title>
    <link href="https://blog.domb.net/?p=2902"/>
    <id>https://blog.domb.net/?p=2902</id>
    <updated>2024-09-08T22:50:04+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;I had the pleasure to speak to John Gaffney from PYMNTS.com about resiliences on AWS, and how we help our customers to build a resilience posture that maps business requirements. &lt;/p&gt;



&lt;p&gt;Full interview here: &lt;a href=&quot;https://www.pymnts.com/news/security-and-risk/2024/chaos-engineering-aws-chief-technologist-on-preparing-for-the-unexpected/&quot;&gt;https://www.pymnts.com/news/security-and-risk/2024/chaos-engineering-aws-chief-technologist-on-preparing-for-the-unexpected/&lt;/a&gt;&lt;/p&gt;



&lt;figure class=&quot;wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio&quot;&gt;&lt;div class=&quot;wp-block-embed__wrapper&quot;&gt;
&lt;iframe title=&quot;Chaos Engineering: AWS Chief Technologist on Preparing for the Unexpected&quot; width=&quot;640&quot; height=&quot;360&quot; src=&quot;https://www.youtube.com/embed/5mCUgQqFzMU?feature=oembed&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;&lt;/figure&gt;
  </content>
    <author>
      <name>Laurent Domb Blog</name>
      <uri>http://blog.domb.net</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">R.I.Pienaar - www.devco.net: Lab Infra Rebuild Part 6</title>
    <link href="https://www.devco.net/posts/2024/07/31/lab-infra-rebuild-6/"/>
    <id>https://www.devco.net/posts/2024/07/31/lab-infra-rebuild-6/</id>
    <updated>2024-07-31T01:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is the final in a series of posts about rebuilding my lab infrastructure, see the initial post &lt;a href=&quot;https://www.devco.net/posts/2024/03/20/lab-infra-rebuild-1/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today we&amp;rsquo;ll wrap things up with a look at some SaaS tools I use and a general look through some small utilities and
things I use to bring it all together.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been enjoying my summer for the last 3 months hence the hiatus of posts.&lt;/p&gt;
&lt;h2 id=&quot;email&quot;&gt;Email&lt;/h2&gt;
&lt;p&gt;Long ago I used to run my own Zimbra but I gave up on that a few years ago, been with &lt;a href=&quot;https://fastmail.com&quot;&gt;Fastmail&lt;/a&gt;
ever since and really happy with their service.&lt;/p&gt;
&lt;p&gt;Delivering email from my 20+ VMs all over the world though is tricky, getting all the various DKIM and other
settings right for a big set of IP addresses and ranges quickly becomes a maintenance nightmare. But ther&amp;rsquo;s a
constant trickle of stuff from them - cron job, monitoring, backup statusses and more.&lt;/p&gt;
&lt;p&gt;After some looking around at options I found &lt;a href=&quot;https://www.smtp2go.com&quot;&gt;SMTP2GO&lt;/a&gt; who have a very generous 1000 /
month free tier.  This is usually fine for me but I ended up paying them anyway for a annual account. This way I
have just one egress point to consider in my various email policy setups and thus far, for delivering system emails,
this has been a great time saver.&lt;/p&gt;
&lt;p&gt;Read on about DNS, Git, SSO and more.&lt;/p&gt;
&lt;h2 id=&quot;dns&quot;&gt;DNS&lt;/h2&gt;
&lt;p&gt;Till this rebuild I hosted my own DNS, I&amp;rsquo;ve had a set of quite stable DNS servers on the same IP addresses for over
a decade so it&amp;rsquo;s worked well.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve always wanted to be rid of hosting this myself but most services are request based billing and I always felt
this would not go well since you have no way of controlling how many DNS requests you get.&lt;/p&gt;
&lt;p&gt;After some research I found &lt;a href=&quot;https://www.cloudns.net&quot;&gt;ClouDNS&lt;/a&gt; who have a 50 zone / 2000 record plan that allows 200
million queries a month for $5/month. This is plenty and incredibly cheap. They have Geo DNS servers, support is
responsive and knowledgeable and have a decent API.&lt;/p&gt;
&lt;p&gt;For comparison DNSimple is $30/month and charges per zone and 10c per million queries a month. That&amp;rsquo;s crazy.&lt;/p&gt;
&lt;p&gt;My usage is around 20 million queries a month so I am very comfortable in this level of service and for $5/month
there is simply no way to compete with this on any kind of self hosting setup.&lt;/p&gt;
&lt;p&gt;They have a bind zone file import feature and I use their API to do daily backups of all the zones into my local Git
server using a little tool I wrote call &lt;a href=&quot;https://github.com/ripienaar/cloudns-backup&quot;&gt;CloudDNS Backup&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;git&quot;&gt;Git&lt;/h2&gt;
&lt;p&gt;Speaking of Git hosting, I quite like &lt;a href=&quot;https://about.gitea.com&quot;&gt;Gitea&lt;/a&gt; though should probably move to &lt;a href=&quot;https://forgejo.org&quot;&gt;Foregejo&lt;/a&gt;. The
split from Gitea happened just as I was building things up so I am still on that.&lt;/p&gt;
&lt;p&gt;Gitea is pretty great, it does a reasonable job of being a GitHub facsimile and uses &lt;a href=&quot;https://github.com/nektos/act&quot;&gt;act&lt;/a&gt;
(also great for local action testing) to provide reasonably compatible self hosted GitHub Actions.&lt;/p&gt;
&lt;p&gt;Gitea and Foregejo are both single binary/single process tools so really easy to get going, for my needs even their
SQLite support is ideal.&lt;/p&gt;
&lt;h2 id=&quot;sso&quot;&gt;SSO&lt;/h2&gt;
&lt;p&gt;Getting all things authenticated and managing users is a nightmare, I use Okta free tier to front almost all my HTTP
stuff and it&amp;rsquo;s been great. With Apache &lt;code&gt;mod_auth_openidc&lt;/code&gt; it&amp;rsquo;s easy to stick that in-front of most things even
static sites.&lt;/p&gt;
&lt;h2 id=&quot;object-storage&quot;&gt;Object Storage&lt;/h2&gt;
&lt;p&gt;I used to use Digitalocean S3 compatible storage (still do a bit tbh), but I am slowly moving all my use of that
over to a private &lt;a href=&quot;https://minio.io&quot;&gt;Minio&lt;/a&gt; instance.  I do not need it to be super high available but do need the data
on it secure so this runs on my Hetzner Backup server with its 6 disk redundant storage setup.&lt;/p&gt;
&lt;p&gt;To be honest I think Minio is just unusable. It&amp;rsquo;s not that the tool is bad, it&amp;rsquo;s really great, I really want to love
it, it&amp;rsquo;s that the project is just crazy with releases. It&amp;rsquo;s in everyones interest to upgrade and run the latest software
but with 50 releases THIS YEAR ALONE (IT IS AUGUST!), I do not know how anyone use this in any seriousness. This is not
software made to be used by real teams in the real world with real pressures on their time. Further their Release
Notes are of the &lt;code&gt;git log --oneline&lt;/code&gt; variety which does not help, commit logs are developer UX not end user UX.&lt;/p&gt;
&lt;p&gt;So I am probably looking at an alternative soon. This is kind of why my migration to it have stalled until I can
figure out what to do with this.&lt;/p&gt;
&lt;h2 id=&quot;security&quot;&gt;Security&lt;/h2&gt;
&lt;p&gt;We all have to stay on top of security alerts, in the good old days you&amp;rsquo;d just read &lt;a href=&quot;https://en.wikipedia.org/wiki/Bugtraq&quot;&gt;Bugtraq&lt;/a&gt;
and know all there is to know. These days though things are much more complex with the amount of things we run and how
much goes on the internet.&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&quot;https://www.opencve.io&quot;&gt;OpenCVE&lt;/a&gt; to track and alert me of any CVEs on the main tools I care about.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also for the first time set my Enterprise Linux machines to auto update themselves, it&amp;rsquo;s a bit scary to be
honest and I exclude kernel updates, but so far it&amp;rsquo;s been ok. Once a httpd update messed me around a bit but Puppet
soon fixed that on next run so was a small inconvenience.  On the whole I&amp;rsquo;d strongly recommend using this.&lt;/p&gt;
&lt;h2 id=&quot;monitoring&quot;&gt;Monitoring&lt;/h2&gt;
&lt;p&gt;Apart from the obvious Grafana/Prometheus pair that I do self-host I use a few other things. Graphite to store my
IoT data in, it just seems a bit more suitable though alas it&amp;rsquo;s in a sad state and mostly dying. I might need to
revisit what I do there in time.&lt;/p&gt;
&lt;p&gt;I have deployed &lt;a href=&quot;https://github.com/caronc/apprise&quot;&gt;Apprise&lt;/a&gt; everywhere and it&amp;rsquo;s really great it can notify a huge
list of services and having it on every machine ready to use is great. I have it integrated in a reusable Gitea
action so any failing builds result in alerts.&lt;/p&gt;
&lt;p&gt;Apprise sends some alerts and statuses to a dedicated private Mastodon account others to Slack and others to
Victorops and to Pushover. This is well worth looking into. Geting Slack/Mastadon reach outs from
cron or other tools is really good.&lt;/p&gt;
&lt;p&gt;Victorops I use for my Prometheus alerts and some other things, I like its ability to silence and to give me a clear
view of the current state of things when I am not around computers.  Probably better options now but it&amp;rsquo;s cheap and
just works.&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&quot;https://pulsetic.com&quot;&gt;Pulsetic&lt;/a&gt; to do external checks on my websites, they have a very generous free tier
though I did recently upgrade to a paid plan. They allow you to make great external visible or private dashboard
like this one for my &lt;a href=&quot;https://status.choria.io&quot;&gt;Choria Project Infrastructure&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;letsencrypt&quot;&gt;Letsencrypt&lt;/h2&gt;
&lt;p&gt;I use LE for TLS like more or less everyone else. ClouDNS is supported for a DNS authenticator in &lt;a href=&quot;https://acme.sh&quot;&gt;acme.sh&lt;/a&gt;
so that&amp;rsquo;s a good fit.&lt;/p&gt;
&lt;p&gt;I have a difficult problem in that I want to do a globally redundant hosting of some static files on the same name
but I do not want to pay for a GSLB. So I ended up making an Action that will manage those certificates daily, renew
them and commit them to my Puppet repository from where they get rolled out to the webservers. This works great.&lt;/p&gt;
&lt;h2 id=&quot;awesome-lists&quot;&gt;Awesome Lists&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ll take liberty and do a plug for my &lt;a href=&quot;https://free-for.dev/#/&quot;&gt;Free-for-dev&lt;/a&gt; list that currently has 1500+ services
listed. It focus on services that provide generous free tiers generally in the devopsey world many of them suited for
home labs. Most of the ones above came from this list, this is literally why I maintain this list. Currently this is the
4th most popular Awesome List with nearly 90 000 stars. Built by 1600+ people collaboratively on GitHub&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not aware I also strongly recommend subscribing to &lt;a href=&quot;https://selfh.st/newsletter/&quot;&gt;This Week in Self-Hosted&lt;/a&gt;
if home labs is your thing this will be invaluable.&lt;/p&gt;
&lt;p&gt;In general if you are not yet on board with the whole Awesome List movement you really should, check out a giant
list in &lt;a href=&quot;https://github.com/sindresorhus/awesome&quot;&gt;sindresorhus/awesome&lt;/a&gt; and you&amp;rsquo;ll find many tools to track,
discover, search and more.&lt;/p&gt;
&lt;p&gt;These are the new Yahoo at grass roots level, it&amp;rsquo;s amazing and one of the most real and relevant resources out there
today.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Well that about sums it up.&lt;/p&gt;
&lt;p&gt;After this we&amp;rsquo;ll get back into some general blogging, there&amp;rsquo;s a fair bit I did not get into like my recent intro into
3D printing and more but this series related to my Home Lab build has more or less run its course.&lt;/p&gt;
  </content>
    <author>
      <name>R.I.Pienaar - www.devco.net</name>
      <uri>http://www.devco.net/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Puppet best practice</title>
    <link href="https://dev.to/betadots/puppet-best-practice-3b9c"/>
    <id>https://dev.to/betadots/puppet-best-practice-3b9c</id>
    <updated>2024-05-08T08:10:51+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;At &lt;a href=&quot;https://www.betados.de&quot;&gt;betadots&lt;/a&gt;, during our &lt;a href=&quot;https://puppet.com&quot;&gt;Puppet&lt;/a&gt; code reviews, we often receive requests for a comprehensive summary of best practices and guidelines.&lt;br&gt;
In response, we&#39;ve compiled this article to delve deep into Puppet&#39;s best practices and implementations.&lt;/p&gt;




&lt;p&gt;Table of content&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Control Repo

&lt;ul&gt;
&lt;li&gt;Test your control-repo!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Hiera&lt;/li&gt;
&lt;li&gt;Node Classification&lt;/li&gt;
&lt;li&gt;Library Module Usage&lt;/li&gt;
&lt;li&gt;Profiles: Doing Them right&lt;/li&gt;
&lt;li&gt;
Module Development

&lt;ul&gt;
&lt;li&gt;General Concepts and Structure&lt;/li&gt;
&lt;li&gt;Parameters and Variables&lt;/li&gt;
&lt;li&gt;Declaration and References&lt;/li&gt;
&lt;li&gt;Class and Resource Ordering&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;
Examples

&lt;ul&gt;
&lt;li&gt;environment.conf&lt;/li&gt;
&lt;li&gt;bin/config_script.sh&lt;/li&gt;
&lt;li&gt;manifests/site.pp&lt;/li&gt;
&lt;li&gt;Hiera Config&lt;/li&gt;
&lt;li&gt;Simple Hiera Data&lt;/li&gt;
&lt;li&gt;Module Classes&lt;/li&gt;
&lt;li&gt;Class Spec Tests&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Control Repo
&lt;/h2&gt;

&lt;p&gt;The control repo&#39;s layout is crucial. We scrutinize files such as &lt;code&gt;environment.conf&lt;/code&gt;, &lt;code&gt;Puppetfile&lt;/code&gt;, &lt;code&gt;manifests/site.pp&lt;/code&gt;, and &lt;code&gt;hiera.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;environment.conf&lt;/code&gt;, we focus on settings like &lt;code&gt;config_version&lt;/code&gt;, &lt;code&gt;modulepath&lt;/code&gt;, and &lt;code&gt;environment_timeout&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When examining &lt;code&gt;Puppetfile&lt;/code&gt;, we emphasize library modules, versions, and sources. While &lt;a href=&quot;https://forge.puppet.com/&quot;&gt;Puppet forge&lt;/a&gt; offers convenience, mirroring library module GIT repositories internally is advisable for control and security. Using git tags in &lt;code&gt;Puppetfile&lt;/code&gt; for module versioning facilitates pre-upgrade reviews and eliminates Puppet server internet dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your Control Repo
&lt;/h3&gt;

&lt;p&gt;Thorough testing validates code changes effectively. Our tests include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linting with &lt;a href=&quot;https://github.com/puppetlabs/puppet-lint&quot;&gt;puppet lint&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Unit testing with &lt;a href=&quot;https://github.com/puppetlabs/rspec-puppet&quot;&gt;rspec puppet&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Acceptance testing using &lt;a href=&quot;https://github.com/voxpupuli/beaker&quot;&gt;puppet beaker&lt;/a&gt; or &lt;a href=&quot;https://puppetlabs.github.io/litmus/&quot;&gt;puppet litmus&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Catalog diffing with &lt;a href=&quot;https://github.com/voxpupuli/puppet-catalog_diff&quot;&gt;catalog diff tool&lt;/a&gt; and &lt;a href=&quot;https://github.com/voxpupuli/puppet-catalog-diff-viewer&quot;&gt;catalog diff viewer&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider adopting &lt;a href=&quot;https://www.puppet.com/docs/pdk/3.x/pdk.html&quot;&gt;PDK - Puppet Development Kit&lt;/a&gt; or leveraging &lt;a href=&quot;https://github.com/voxpupuli/onceover&quot;&gt;onceover&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ensure you&#39;re using the right version of RSpec-Puppet, distinguishing between maintained projects by &lt;a href=&quot;https://github.com/rodjek/rspec-puppet&quot;&gt;Rodjek&lt;/a&gt; and &lt;a href=&quot;https://github.com/puppetlabs/rspec-puppet&quot;&gt;Puppet Inc.&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For your reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The official website for puppet-lint is now located at &lt;a href=&quot;https://puppetlabs.github.io/puppet-lint/&quot;&gt;https://puppetlabs.github.io/puppet-lint/&lt;/a&gt;.&lt;br&gt;
Please note that the older version is no longer maintained.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Similarly, for RSpec, we advise consulting the &lt;a href=&quot;https://github.com/puppetlabs/rspec-puppet/blob/main/README.md&quot;&gt;README&lt;/a&gt; file in the official GitHub repository. Be cautious not to mistake it for the project by Rodjek. While they may appear similar currently, this could change in the future.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Hiera
&lt;/h2&gt;

&lt;p&gt;Hiera is pivotal, covering node classification, library module API parameters, and infrastructure differences. We stress correct Hiera implementation, aligning hierarchy layers with infrastructure complexity while balancing diversity and maintainability.&lt;br&gt;
All Hiera layers should rely solely on Puppet or trusted facts.&lt;/p&gt;

&lt;p&gt;Next we analyze the data and check for data keys and their module namespace usage.&lt;/p&gt;

&lt;p&gt;If no module namespace is used, we can be sure that all hiera values are looked up using explizit lookup function.&lt;br&gt;
This has a huge impact on analyzing and understanding code and data, as class parameters are mapped to different names within hiera.&lt;/p&gt;




&lt;h2&gt;
  
  
  Node Classification
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;manifests/site.pp&lt;/code&gt;, we inspect resource defaults, node declarations, and data processing.&lt;/p&gt;

&lt;p&gt;Setting resource defaults simplifies coding, while following the roles and profiles pattern aids in managing numerous identically configured systems.&lt;/p&gt;

&lt;p&gt;For divergent infrastructures, Hiera-based node classification offers flexibility, shifting complexity from static code to dynamic data.&lt;/p&gt;

&lt;p&gt;We usually recommend to set the following resource defaults:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file : disable backup&lt;/li&gt;
&lt;li&gt;exec : path parameter default&lt;/li&gt;
&lt;li&gt;package : default provider (only needed in an environment with Windows nodes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another topic we look for is data processing based on node data like fqdn or network information. Here we ask to please move this to a custom fact.&lt;br&gt;
Nice side effect: this will drastically reduce the compilation time, as the node calculates its own data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Library Module Usage
&lt;/h2&gt;

&lt;p&gt;Leverage upstream library modules whenever possible to solve problems efficiently. However, consider community contributions or feature requests for missing functionalities rather than internal forks or rewrites.&lt;/p&gt;

&lt;p&gt;Modern libraries adopting the data-in-modules pattern facilitate configuration solely through Hiera data, minimizing Puppet code development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Profiles: Doing Them Right
&lt;/h2&gt;

&lt;p&gt;Profiles encapsulate infrastructure components like user management, authentication, and application deployment. Combining resource and library module declarations within profiles streamlines configuration management.&lt;/p&gt;

&lt;p&gt;Clear profile class naming aids in identifying responsibilities and maintaining a single source of truth.&lt;/p&gt;

&lt;p&gt;The main win over just using library module data is the capability to identify which profile declares the library class.&lt;/p&gt;

&lt;p&gt;Any subprofile which is split into multiple files - maybe due to better readability or maintainability - must be specified being a private subprofile.&lt;br&gt;
This can be achieved by placing &lt;code&gt;assert_private()&lt;/code&gt; into the beginning of the class body.&lt;/p&gt;

&lt;p&gt;The concept of profile module development is the same as for library modules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Module Development
&lt;/h2&gt;

&lt;p&gt;Module development involves several considerations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;General Concepts and Structure: Utilize PDK for module creation and consider modulesync for managing multiple modules.&lt;/li&gt;
&lt;li&gt;Parameters and Variables: Emphasize class parameters for flexibility and avoid unnecessary variable mapping.&lt;/li&gt;
&lt;li&gt;Declaration and References: Ensure resource references are in the same class as the declaration and use Puppet-lint plugins for reference checks.&lt;/li&gt;
&lt;li&gt;Class and Resource Ordering: Maintain proper class and resource ordering for predictable execution.&lt;/li&gt;
&lt;li&gt;Documentation: Leverage Puppet Strings for comprehensive module documentation, including parameter usage, limitations, and code examples.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  General Concepts and Structure
&lt;/h3&gt;

&lt;p&gt;To simply get started with a new module we recommend using PDK. It will help to ramp up the module skeleton.&lt;br&gt;
If you create a class with PDK, it will also create a simple test class for it.&lt;/p&gt;

&lt;p&gt;In case that many modules must be managed, we recommend to switch to &lt;a href=&quot;https://github.com/voxpupuli/modulesync&quot;&gt;modulesync&lt;/a&gt; from &lt;a href=&quot;https://github.com/voxpupuli&quot;&gt;Voxpupuli Puppet community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Every module must have a dedicated API.&lt;/p&gt;

&lt;p&gt;That means that there are specific classes to be used by others, which have parameter to allow adoptions.&lt;br&gt;
Any subclass which is not an API endpoint, must be marked as being &lt;code&gt;private&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Testing should be done on all classes, the private ones should verify for compiler error, whereas your API classes spec tests should also cover different parameters and operating systems.&lt;/p&gt;

&lt;p&gt;Differences between operating systems must be done via hiera data in library or profile modules.&lt;/p&gt;

&lt;p&gt;If you find self written profile class code repeated or copied within multiple modules you must check if building an additional internal library module can be used to reduce the multiple development overhead.&lt;/p&gt;

&lt;p&gt;Testing must at least cover linting and unit tests.&lt;br&gt;
Acceptance tests need VM automation or container technologies and should be added as soon as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameters and Variables
&lt;/h3&gt;

&lt;p&gt;Class Parameters allow flexible usage of profiles. Any parameter can be validated using a &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/lang_data_type.html&quot;&gt;Puppet Data Type&lt;/a&gt;. You also can create custom data types if the basic ones are not sufficient for you use case. In the past validation was often done with &lt;code&gt;assert_*&lt;/code&gt; or &lt;code&gt;validate_*&lt;/code&gt; functions in the class body. With data types you can specify the type of data you are expecting right in the class header and don&#39;t need to care about validation later on.&lt;/p&gt;

&lt;p&gt;With class parameters you can specify different settings for specific use cases like access to a dev and a prod machine via hiera.&lt;/p&gt;

&lt;p&gt;Please stop doing variable mapping!&lt;/p&gt;

&lt;p&gt;We often see that hash parameters are not used directly but that certain elements of a hash are placed into a new variable.&lt;br&gt;
This only makes sense if you need the specific value multiple times.&lt;br&gt;
Rule of thumb, if you need a special piece of data more then twice, you might do a mapping.&lt;br&gt;
But in most cases we recommend to not do it!&lt;/p&gt;

&lt;p&gt;Variable mapping produces code which is hard to understand, because you must always keep in mind the original variable and the mapped variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Declaration and References
&lt;/h3&gt;

&lt;p&gt;Using resource references, you can set default values and ordering.&lt;/p&gt;

&lt;p&gt;We sometimes see references on resources which are not local to the class.&lt;br&gt;
The class &lt;code&gt;config&lt;/code&gt; declares the file and a class &lt;code&gt;service&lt;/code&gt; declares the service.&lt;/p&gt;

&lt;p&gt;Whenever someone refactors on of the classes, you must also take care on the references.&lt;/p&gt;

&lt;p&gt;We highly recommend to make use of the &lt;a href=&quot;https://github.com/voxpupuli/puppet-lint-reference_on_declaration_outside_of_class-check&quot;&gt;reference on declaration outside of class&lt;/a&gt; puppet-lint plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Class and Resource Ordering
&lt;/h3&gt;

&lt;p&gt;We mentioned that references should be local to a declaration.&lt;br&gt;
But how to ensure resource ordering?&lt;/p&gt;

&lt;p&gt;Normally resources inside a class are always processed in the same order as they appear in the class.&lt;br&gt;
There are some resource types which have &lt;code&gt;soft autorequire dependecies&lt;/code&gt;, like user and their primary group.&lt;/p&gt;

&lt;p&gt;In general you must ensure that classes are always done in right order.&lt;br&gt;
Otherwise puppet tries to make an educated guess in which order resources and classes might be.&lt;/p&gt;

&lt;p&gt;Classes and resources ordering should be separate from declaration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation
&lt;/h3&gt;

&lt;p&gt;Classes can have many parameters. So where do you look for information on how to make use of them?&lt;br&gt;
This is where &lt;a href=&quot;https://www.puppet.com/docs/puppet/latest/puppet_strings.html&quot;&gt;Puppet strings&lt;/a&gt; will be used.&lt;br&gt;
Puppet strings allows you to render the file &lt;code&gt;REFERENCE.md&lt;/code&gt; using a rake task.&lt;/p&gt;

&lt;p&gt;Additionally it offers the option to run a documentation web server with access to your completely deployed control-repository.&lt;br&gt;
It will check for any file updates and update the documentation automatically.&lt;br&gt;
We recommend to put a web server with user authentication in front of the puppet strings web service.&lt;/p&gt;

&lt;p&gt;Next to class responsibility, limitations, usage and parameter documentation we recommend to document any complex code.&lt;br&gt;
We sometimes see multiple usages of &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt; or &lt;code&gt;reduce&lt;/code&gt; functions which parse and restructure data.&lt;br&gt;
Any new person working on the code must be able to understand why and what is done. Data examples can be useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Structured and documented code simplifies Puppet management. Adopting automated testing and coding principles from reputable library modules - like the ones from &lt;a href=&quot;https://forge.puppet.com/modules/puppetlabs&quot;&gt;Puppet Inc&lt;/a&gt; and &lt;a href=&quot;https://forge.puppet.com/modules/puppet&quot;&gt;Voxpupuli&lt;/a&gt; - enhances code quality and reliability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;Below are examples illustrating various Puppet practices.&lt;/p&gt;

&lt;h3&gt;
  
  
  environment.conf
&lt;/h3&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;config_version &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;bin/config_script.sh $environmentpath $environment&#39;&lt;/span&gt;
modulepath &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; site:modules:&lt;span class=&quot;nv&quot;&gt;$basemodulepath&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  bin/config_script.sh
&lt;/h3&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; /usr/bin/git &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENVGITDIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/environments/&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/.git&quot;&lt;/span&gt;
  /usr/bin/git &lt;span class=&quot;nt&quot;&gt;--git-dir&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ENVGITDIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; log &lt;span class=&quot;nt&quot;&gt;--pretty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;format:&lt;span class=&quot;s2&quot;&gt;&quot;%h - %an, %ad : %s&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;no git - environment &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fi
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  manifests/site.pp
&lt;/h3&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;File&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;backup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;Exec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;py&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$facts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;path&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$facts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;os&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;family&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;windows&#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Package&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;py&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;choco&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Node classification - sorted Hash
# e.g.
# classes_hash:
#   &#39;01_base&#39;: &#39;profile::base&#39;
#   &#39;02_security&#39;: &#39;profile::security&#39;
#   &#39;12_application&#39;: &#39;profile::application::billing&#39;
#   &#39;13_service&#39;:
#     - &#39;profile::application::billing::backend&#39;
#     - &#39;profile::application::billing::admin_ui&#39;
#
# overwriting identifier classes:
# classes_hash:
#   &#39;13_service&#39;: &#39;profile::application::billing2&#39;
#
# disabling identifier classes:
# classes_hash:
#   &#39;13_service&#39;: &#39;&#39;
#
# $element[0]: the hash key identifier
# $element[1]: the class name to load
#
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$classes_hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;classes_hash&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;value_type&#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;default_value&#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$classes_hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$classes_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&#39;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;contain&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$classes_hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;py&quot;&gt;message&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Class for &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;${key}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; on &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{facts[&#39;networking&#39;][&#39;fqdn&#39;]} is disabled&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;py&quot;&gt;withpath&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hiera Config
&lt;/h3&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;defaults&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;datadir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;data&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;lookup_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;eyaml_lookup_key&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pkcs7_private_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/etc/puppetlabs/puppet/keys/private_key.pkcs7.pem&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pkcs7_public_key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/etc/puppetlabs/puppet/keys/public_key.pkcs7.pem&quot;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;hierarchy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hierarchy&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;nodes/%{trusted.certname}.yaml&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hierarchy&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;role/%{trusted.extensions.pp_role}-%{trusted.extensions.pp_env}.yaml&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;role/%{trusted.extensions.pp_role}.yaml&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hierarchy&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;os/%{facts.os.family}-%{facts.os.release.major}.yaml&quot;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;os/%{facts.os.family}.yaml&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;zone&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;hierarchy&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;zone/%{trusted.extentions.pp_zone}.yaml&quot;&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Common&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;common.yaml&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Simple Hiera Data
&lt;/h3&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# data/common.yaml&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Node classification using classes_hash&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;classes_hash&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;00_base_ssh&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ssh&#39;&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;01_base_nft&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;profile::nftables&#39;&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;02_base_nft_rules&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;profile::nftables::rules::base&#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Commmon SSH data&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# using saz-ssh: https://forge.puppet.com/modules/saz/ssh/readme&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;ssh::server_options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Protocol&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;2&#39;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ListenAddress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;127.0.0.0&#39;&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;%{::facts.networking.hostname}&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;PasswordAuthentication&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;yes&#39;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;SyslogFacility&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;AUTHPRIV&#39;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;UsePAM&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;yes&#39;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;X11Forwarding&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;yes&#39;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;ssh::server::match_block&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;filetransfer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;group&#39;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ChrootDirectory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/home/sftp&#39;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ForceCommand&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;internal-sftp&#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Adding core resources&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# requires puppetlabs-stdlib : https://forge.puppet.com/modules/puppetlabs/stdlib/readme&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;stdlib::manage::create_resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;package&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;nano&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ensure&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;absent&#39;&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;vim&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ensure&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;present&#39;&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;user&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;monitoring&#39;&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ensure&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;present&#39;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;uid&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;11225&#39;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;gid&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;11225&#39;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;!&#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Module Classes
&lt;/h3&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight puppet&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# @summary Installs and configures the application
# @author betadots GmbH
# @param parameter1 Parameter to set thing 1
# @param parameter2 Parameter to set thing 2
# @example
#     include application
#   or
#     class { &#39;application&#39;:
#       parameter1 =&amp;gt; &#39;value,
#     }
#
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;application&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Datatype&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$parameter1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;Datatype&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$parameter2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;default&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;contain&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;application::install&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;contain&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;application::config&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;contain&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;application::service&lt;/span&gt;

  &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;application::install&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;application::config&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;~&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;application::service&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;application::install&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nf&quot;&gt;assert_private&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# Puppet DSL
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Class Spec Tests
&lt;/h3&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ruby&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# spec/classes/application_spec.rb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;application&#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;on_supported_os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os_facts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;on &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:facts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os_facts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with_all_deps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;contain_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&#39;application::install&#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# spec/classes/application_install_spec.rb&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;application::install&#39;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;on_supported_os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os_facts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;on &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:facts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os_facts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;and_raise_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%{private}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy puppetizing,&lt;br&gt;&lt;br&gt;
Martin&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">R.I.Pienaar - www.devco.net: Lab Infra Rebuild Part 5</title>
    <link href="https://www.devco.net/posts/2024/04/25/lab-infa-rebuild-5/"/>
    <id>https://www.devco.net/posts/2024/04/25/lab-infa-rebuild-5/</id>
    <updated>2024-04-25T06:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is an ongoing post about rebuilding my lab infrastructure, see the initial post &lt;a href=&quot;https://www.devco.net/posts/2024/03/20/lab-infra-rebuild-1/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today let&amp;rsquo;s look at VMs and Baremetals and Operating Systems.&lt;/p&gt;
&lt;h2 id=&quot;virtual-machines&quot;&gt;Virtual Machines&lt;/h2&gt;
&lt;p&gt;As I mentioned I&amp;rsquo;ve been a Linode customer since essentially their day one and have had many 100s of machines there for personal and client needs.&lt;/p&gt;
&lt;p&gt;I soured off them quite significantly after they botched their Kubernetes release by not having Europe side support past basic triage initially that
led to multiple multi hour outages and so I have been making some changes.&lt;/p&gt;
&lt;p&gt;My most recent incarnation had about 5 or 6 Linode machines - Puppet Server, Choria Repos * 3, 2 x DNS, General Services machine.&lt;/p&gt;
&lt;p&gt;Today I have 2 Linode machines left. I was reluctant to move them as they were DNS servers for many domains but I changed my way of hosting domains
also during this so less of a concern now.&lt;/p&gt;
&lt;p&gt;They are now just RPM/Deb repos for Choria and I will move those elsewhere soon also.  That&amp;rsquo;ll be the first time I am without Linode machines since
basically 2003, such a shame. One is in the US to provide a nearby mirror there, I might keep it and just scale it down to lower spec. But with the
recent changes at Linode it feels a bit like it&amp;rsquo;s time to consider alternatives.&lt;/p&gt;
&lt;p&gt;Previously I had Digital Ocean droplets x 3 for my Kubernetes cluster, as discussed that&amp;rsquo;s all gone now too.&lt;/p&gt;
&lt;p&gt;I used to have quite a selection of Vultr machines, I don&amp;rsquo;t recall why I left them really I think I felt Linode was just the rolls royce of this kind
of Cloud provider and so consolidated to simplify my business accounting etc&lt;/p&gt;
&lt;h2 id=&quot;baremetal&quot;&gt;Baremetal&lt;/h2&gt;
&lt;p&gt;In the previous iteration I had only 1 hosted physical machine and that was my Backups machine running Bacula on a Hetzner SX64 (64GB RAM Ryzen 5 3600)
with 4 x 16 TB SATA Enterprise HDD 7200rpm. I do not need much from this machine, it wakes up, do backups then sleep again till tomorrow.  So the spinning
rust is fine for that, I just need lots of it. I rebuilt on a new one just to get a hardware and OS refresh.&lt;/p&gt;
&lt;p&gt;Of course I do still use Virtual Machines just managed by Cockpit as per the &lt;a href=&quot;https://www.devco.net/posts/2024/03/21/lab-infra-rebuild-2/&quot;&gt;2nd part in this series&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I got a pair of Hetzner AX41-NVMe (64GB RAN Ryzen 5 3600) with 2 x 512 NVMe SSDs each. I was expecting to add a 3rd but really these 2 plus the Ryzen at
my office turns out to be plenty for my needs. They have some upgrades available - more RAM, Extra Disks, SATA can be added etc. I don&amp;rsquo;t know if Hetzner
supports upgrading running machines but this is nice little platform.  At EUR37 per machine that puts them between a 4GB and 8GB shared CPU Linode.
You really can&amp;rsquo;t complain. I might get a 3rd one just for the sake of it and spread my development machines out more. Something for after the summer.&lt;/p&gt;
&lt;p&gt;Performance wise moving from my Droplets and other VMs to these machines have been amazing, for an investment equalling 1 Linode I can run several VMs with
no additional cost to expand to another VM or to shuffle memory allocations - or just to allocate more since 64GB is way more than I need.  This really
is a no brainer for my needs.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been a Hetzner customer also since a very long time, it&amp;rsquo;s not clear how long but it feels like maybe 2008 or so. They&amp;rsquo;ve had their ups and downs and
dodgy datacenters, dodgy connectivity and dodgy hardware, bad english support, but in the past few years I think they&amp;rsquo;re really firmed up quite nicely and
my machines there have not given me trouble so I felt it&amp;rsquo;s safe to lean on them a bit more. A few months in now and I&amp;rsquo;ve not had one minute of problems.&lt;/p&gt;
&lt;p&gt;Read on about Operating Systems and more.&lt;/p&gt;
&lt;h2 id=&quot;operating-system&quot;&gt;Operating System&lt;/h2&gt;
&lt;p&gt;A bit of history is needed I guess.  I started with &lt;a href=&quot;https://en.wikipedia.org/wiki/Softlanding_Linux_System&quot;&gt;Softlanding Linux System&lt;/a&gt; around 1993, after
installing this on a 20MB HDD I removed from a Novell machine the disk promptly died. A bit of hits and misses later I eventually got it quite happy.&lt;/p&gt;
&lt;p&gt;Then I moved to Slackware which was the successor to SLS of course. I tried a few things but once RedHat released their first preview in October 1994 on
Halloween it was pretty much just RedHat all the way from there. I&amp;rsquo;ve used all the versions, even the 5.x series that was a total disaster after they moved
to ELF but kept at it.&lt;/p&gt;
&lt;p&gt;When RedHat removed their free editions I dabbled with Debian but I don&amp;rsquo;t like the inconsistencies, community, approach or really anything at all about Debian.&lt;/p&gt;
&lt;p&gt;Luckily of course CentOS came to the rescue, I even donated hardware to them - a IBM Bladecenter full of Blades. Of course then for some reason they decided
to join RedHat, I never did understand the thinking here.  Needless to say that did go about as well as it was obvious to anyone it would.&lt;/p&gt;
&lt;p&gt;This left a few choices, mainly Rocky Linux and AlmaLinux. I initially went with Rocky Linux it seemed to be on a good track and had the CentOS founder
on board, they did seem to loose steam a bit and had a few fits and starts. Now seems fairly solid so I wouldn&amp;rsquo;t have qualms using them but I settled
on AlmaLinux. AlmaLinux seemed at the time to have a bit more backers, a bit more polish and just worked better for my needs.&lt;/p&gt;
&lt;p&gt;Then of course RedHat removed the source repos from CentOS Stream this left things quite difficult. Rocky Linux, then, stated they are going to get their
SRPMs from a bunch of places to keep the bug-for-bug EL rebuild thing going by means which I really do not think is sustainable. From using paid-for cloud instances
and then getting SRPMs using those or using the UBI container images etc, basically they go spelunking all over the place to find source RPMs and then
somehow promise to be a complete like-for-like distribution of RHEL. I am not convinced.&lt;/p&gt;
&lt;p&gt;Almalinux went another way, they decided to drop the 1:1 RHEL promise and instead moving to &lt;a href=&quot;https://almalinux.org/blog/future-of-almalinux/&quot;&gt;promising ABI/Binary compatible with RHEL&lt;/a&gt;.
They probably also get their SRPMs from some interesting places but they are forging a path of innovation which already resulted in them returning some
old hardware support for example. I am quite happy with my choice.&lt;/p&gt;
&lt;p&gt;To complicate matters a bit more - or simplify them I guess - the &lt;a href=&quot;https://openela.org/&quot;&gt;Open Enterprise Linux Association&lt;/a&gt; was created to be a
source of SRPMs for EL rebuilders like Alma and Rocky. Their mission is to deliver &lt;code&gt;All sources necessary to achieve a 1:1 / bug-for-bug compatible  version of EL which will be distributed via Git, encouraging community collaboration&lt;/code&gt;. Hopefully this will help matters along. This is supported
by Oracle and SUSE. Oracle of course have their own Oracle Enterprise Linux that was dunked into the same problem - and no doubt the source of
all the headaches for RedHat.&lt;/p&gt;
&lt;p&gt;Anyway, so a lot of history, I like EL. I will keep using EL even if its a slightly different EL. I don&amp;rsquo;t think its inherently better or worse
than other alternatives - and I do not care. The best tool for the job is often the one you know best. I have 30 years of EL experience and
that works for me. It&amp;rsquo;s all just SystemdOS now anyway.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s about it really for hardware and OS. The consolidation, moving to Baremetals etc have ended up resulting in quite a bit of money saved.
Even if I got a 3rd VM host at Hetzner I&amp;rsquo;d still be more than a $100 down on monthly expenses but got a lot more.&lt;/p&gt;
&lt;p&gt;I have some travel coming up in the next few weeks so the next installment might be a while.&lt;/p&gt;
  </content>
    <author>
      <name>R.I.Pienaar - www.devco.net</name>
      <uri>http://www.devco.net/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Camptocamp Blog: Unveiling the Simplicity of Cluster Mesh for Kubernetes Deployments</title>
    <link href="https://dev.to/camptocamp-ops/unveiling-the-simplicity-of-cluster-mesh-for-kubernetes-deployments-1bfc"/>
    <id>https://dev.to/camptocamp-ops/unveiling-the-simplicity-of-cluster-mesh-for-kubernetes-deployments-1bfc</id>
    <updated>2024-04-16T13:30:57+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;h2&gt;
  
  
  Unveiling the Simplicity of Cluster Mesh for Kubernetes Deployments
&lt;/h2&gt;

&lt;p&gt;During Kubecon EU 2024, among a crowd of tech enthusiasts and Kubernetes aficionados, Liz Rice the Queen bee, demo’ed multi-cluster networking. This is Cluster Mesh 101 with Cilium. &lt;/p&gt;

&lt;p&gt;Here are a few paragraphs summarizing the experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;Cluster Mesh extends the networking plane across multiple clusters. It enables connection among endpoints of connected clusters. Two noticeable features are:&lt;br&gt;
i) Network Policy Enforcement as implemented by Cilium prevails even under this network setup&lt;br&gt;
ii) Services can load balance requests among clusters just by using annotations&lt;/p&gt;
&lt;h3&gt;
  
  
  Networking Adventures Begin
&lt;/h3&gt;

&lt;p&gt;To the surprise of many, Liz announces she will be running the demo over the venue&#39;s Wi-Fi.&lt;/p&gt;

&lt;p&gt;Presentation gets started with connectivity tests over VPN connections, routes propagation validation, checks of the BGP peering and visualization of routing tables. The setup is a running k8s cluster in GKE and another in EKS. Node to node Network connectivity is the final objective here, and do not forget all assigned IPs should be not overlapping. Cilium cannot create a bridge between two cloud providers. No black magic. &lt;/p&gt;

&lt;p&gt;This is foreplay preparing the ground for the demo. The steps for reproducing this can be found in official Cilium &lt;a href=&quot;https://docs.cilium.io/en/latest/network/clustermesh/clustermesh/#setting-up-cluster-mesh&quot; rel=&quot;noopener noreferrer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Enter Cilium&#39;s Cluster Mesh
&lt;/h3&gt;

&lt;p&gt;With the foundational work laid, it&#39;s time to kickstart the demo. Liz demonstrates how to enable all necessary components with &lt;code&gt;cilium clustermesh enable&lt;/code&gt; in both clusters. These trigger the deployment of the clustermesh-apiserver into each cluster, along with the generation of all required certificates. This component goes the extra mile by attempting to auto-detect the optimal service type for LoadBalancer, ensuring the Cluster Mesh control plane is efficiently exposed to other clusters. &lt;/p&gt;

&lt;p&gt;A simple &lt;code&gt;cilium clustermesh connect&lt;/code&gt; builds the bridge between clusters, just as if we had a single network plane between Pods across all clusters.&lt;/p&gt;

&lt;p&gt;Now &lt;code&gt;cilium clustermesh status&lt;/code&gt; echos:&lt;/p&gt;

&lt;p&gt;✅ All 2 nodes are connected to all clusters&lt;br&gt;
🔌 Cluster Connections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cilium-cli-ci-multicluster-2-168: 2/2 configured, 2/2 connected&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Traffic load balancing and failover in the hive:
&lt;/h3&gt;

&lt;p&gt;Till now demo showed and validated the piping between Pods. We can successfully communicate with a Pod on another cluster using its IP address for example. But this wouldn&#39;t be very practical in a real world-scenario. Moreover no DNS service can help us fetch this dynamic IP. In fact, this is the raison d&#39;être of k8s Service object.&lt;/p&gt;

&lt;p&gt;What Cilium proposes is extending native Service resources into a cluster-mesh-aware Service using annotations.&lt;/p&gt;

&lt;p&gt;Cilium provides a pragmatic solution through global services with auto discovery and failover mechanisms. &lt;/p&gt;

&lt;p&gt;By using &lt;code&gt;service.cilium.io/global=true&lt;/code&gt; makes a service global, which meansmatching Pods across clusters. Or in other words we extend service’s backends to use Pods in remote clusters. Then the service’s traffic is balanced across clusters. &lt;/p&gt;

&lt;p&gt;Questions? This is probably better explained by Ciilium documentation &lt;a href=&quot;https://docs.cilium.io/en/latest/network/clustermesh/services/#load-balancing-with-global-services&quot; rel=&quot;noopener noreferrer&quot;&gt;here&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rebel-base&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;service.cilium.io/global&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterIP&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rebel-base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;service.cilium.io/affinity=local|remote&lt;/code&gt; we can fine tune the global service to prefer local or remote Pods. With local we could designate our local cluster as the primary destination, while the remote Pods serve as a backup.&lt;/p&gt;

&lt;p&gt;The following represents a service, which is global and prefers using endpoints found locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rebel-base&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;service.cilium.io/global&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;service.cilium.io/affinity&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;local&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterIP&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rebel-base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Should the primary cluster encounter any issues or downtime, traffic seamlessly shifts to the backup cluster, ensuring continuity of service.&lt;br&gt;
In essence, Cilium offers a straightforward approach to traffic management, enhancing reliability by providing a failover mechanism that ensures service accessibility remains intact in the face of pod disruptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;A few interesting use cases arise when using Cluster Mesh. The one we focused on in this article is about leveraging remote clusters Pods as service&#39;s Pod backend as failover mechanism. We can also think of using a &lt;code&gt;global service&lt;/code&gt; for moving workloads around, for example for lowering computational costs into cheaper regions.&lt;/p&gt;

&lt;p&gt;So there you have it, folks. A whirlwind tour of multicluster networking traffic management with Cilium, served up with a dose of honey.&lt;br&gt;
Who knew cluster meshing would be that simple? &lt;/p&gt;

&lt;h3&gt;
  
  
  Contact us
&lt;/h3&gt;

&lt;p&gt;Needs a demo, or dig into some specifics on Cilium?&lt;br&gt;
Ping us here: &lt;a href=&quot;https://camptocamp.com/consulting&quot; rel=&quot;noopener noreferrer&quot;&gt;https://camptocamp.com/consulting&lt;/a&gt;&lt;/p&gt;
  </content>
    <author>
      <name>Camptocamp Blog</name>
      <uri>https://dev.to/camptocamp-ops</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">R.I.Pienaar - www.devco.net: Lab Infra Rebuild Part 4</title>
    <link href="https://www.devco.net/posts/2024/04/11/lab-infra-rebuild-4/"/>
    <id>https://www.devco.net/posts/2024/04/11/lab-infra-rebuild-4/</id>
    <updated>2024-04-11T09:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is an ongoing post about rebuilding my lab infrastructure, see the initial post &lt;a href=&quot;https://www.devco.net/posts/2024/03/20/lab-infra-rebuild-1/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today I&amp;rsquo;ll talk about my physical office and office hardware.&lt;/p&gt;
&lt;h2 id=&quot;office-space&quot;&gt;Office Space&lt;/h2&gt;
&lt;p&gt;When my son started going to school I did not look forward to all the driving so figured a office near his school would be good, I&amp;rsquo;d spend the days there and come home after pick up. I rented a nice place in a town called &lt;a href=&quot;https://en.wikipedia.org/wiki/Mosta&quot;&gt;Mosta&lt;/a&gt;, it was nice and had ample storage and would have made a really great maker space as it had about 4 car garages worth of underground storage that was well lit and ventilated.&lt;/p&gt;
&lt;p&gt;Unfortunately this place was opposite a school and parking was absolute hell. I ended up just not using it for months at a time since I could drive home in 12 minutes or spend 45 minutes finding parking, no thanks. I gave up trying to find a garage to rent around there, it&amp;rsquo;s just crazy.&lt;/p&gt;
&lt;p&gt;When I started looking the 2nd place in &lt;a href=&quot;https://en.wikipedia.org/wiki/%C5%BBebbu%C4%A1&quot;&gt;Żebbuġ&lt;/a&gt; I saw seemed perfect, a fantastic bright 7m x 7m office with a underground 1.5 car garage at a reasonable price. I took that and just recently extended my lease to 5 years. My sister-in-law is also moving her business to the same street and there&amp;rsquo;s a 3D printer shop around the corner, bonus. Parking is always easy even on the street and it&amp;rsquo;s like 5 minutes from my house.&lt;/p&gt;
&lt;p&gt;Here I am able to set up 3 workstations, my main desk, one for guests with a little Desktop for my son and big work station for soldering, assembling IoT projects and such. I also have a nice 2 seater sofa with a coffee table. With the garage I have space to put things like a lazer cutter and more in future - though I am eyeing a small space across the road as a workshop space also for that kind of thing.&lt;/p&gt;
&lt;p&gt;Location wise I could not want more, it&amp;rsquo;s a easy walk or cycle from my house through a beautiful car-free valley and the town is pleasant enough with food options and a small corner shop nearby.&lt;/p&gt;
&lt;p&gt;Read on for more about the hardware.&lt;/p&gt;
&lt;h2 id=&quot;desktop--laptop&quot;&gt;Desktop / Laptop&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been using using Apple desktops and laptops almost exclusively since about 2007. I do like the graphical UI but loathe the BSD based shell, so generally my mantra is: &lt;em&gt;MacOS Shells are for SSH to Linux machines&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve had every iMac since the &lt;a href=&quot;https://www.devco.net/archives/2006/01/18/apple_core_duo_imac.php&quot;&gt;very first plastic white ones&lt;/a&gt;, I really liked that form factor, so I just kept buying each model that came out. I especially liked the time when Apple sold visually complimentary displays for these machines and you could have a quite pleasing dual screen setup.&lt;/p&gt;
&lt;p&gt;Alas those days are gone, now to be honest, every iMac dual screen setup just looks like rubbish, so I just can&amp;rsquo;t with that anymore and so it was time to change. I now have 2 useless Display port old Cinema displays, guess they go to the trash.&lt;/p&gt;
&lt;p&gt;I have a MacBook 16&amp;quot; M2 PRO 32GB/1TB SSD, which I think is pretty much perfect - despite coffee damage knocking out all the ports on the right side, it&amp;rsquo;s still awesome.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve always had a disconnect between Laptop and Desktop speeds and this time round the stars aligned reasonably well that one can almost have parity between the 2, so after some looking around I figured a Mac Mini will give me near enough performance and good enough options for displays.&lt;/p&gt;
&lt;p&gt;To be honest I was not too convinced about moving to a Mini - but the iMacs were never really speed daemons so maybe it would be fine? So I picked up a Mac Mini M2 PRO 32GB/1TB SSD and really moving between the 2 just feels exactly the same performance wise. This has been a bigger deal than I anticipated for my general happiness when switching setup - something I do all the time.&lt;/p&gt;
&lt;p&gt;The mini sitting on my desk is a bit annoying, I&amp;rsquo;m considering options to mount it on the wall behind the display.&lt;/p&gt;
&lt;p&gt;For my display I went with a LG 40&amp;quot; Curved UltraWide 5K2K Nano IPS Monitor. I&amp;rsquo;ve used a 32 inch ultra wide for a few years, this has been a great upgrade.  It&amp;rsquo;s quite pricey around EUR1200, but it&amp;rsquo;s a really nice display and as it&amp;rsquo;s a Thunderbolt monitor it just works flawlessly with the Macs. I did buy &lt;a href=&quot;https://github.com/waydabber/BetterDisplay&quot;&gt;BetterDisplay&lt;/a&gt; to get some more control, well worth it - I&amp;rsquo;d say essential if you have a monitor like this. I do wish it was brighter though.&lt;/p&gt;
&lt;p&gt;I used to have a &lt;a href=&quot;https://moodeaudio.org/&quot;&gt;Moode Audio&lt;/a&gt; system with some high end DACs and Amp paired with JBL monitors for audio, I got those before HomePods existed. Now tossed all that out for just 2 HomePod Minis on my desk, they&amp;rsquo;re fantastic.&lt;/p&gt;
&lt;p&gt;I use a Logitech Brio 4K Ultra HD Webcam - works well on MacOS including it&amp;rsquo;s management software. My previous camera did not handle the sync frequency of my new office lights well and so was instantly useless.&lt;/p&gt;
&lt;p&gt;Today I am replacing my Razer Abyssus (2014) with a new Abyssus that doesn&amp;rsquo;t look like it&amp;rsquo;s been to war.&lt;/p&gt;
&lt;p&gt;Keyboard wise I use the MS Natural 4000 and have 4 more in boxes so I don&amp;rsquo;t need to think about keyboard for years, they last about 3 to 4 years each.&lt;/p&gt;
&lt;h2 id=&quot;storage&quot;&gt;Storage&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been a Qnap user since forever, like maybe 2005 or so. The first Qnap I bought was some weird ARM CPU and they ran a patched Linux Kernel to get big partition support. When the machine died I had quite a bit of trouble to access my files but did get there eventually - but did make me reconsider my backup strategy.&lt;/p&gt;
&lt;p&gt;Their next gen kit was Intel based and ran stock kernels so I gave them another go. What I got then was a 4 bay TS-439. This machine moved with me from London to Malta and now 14 years later it still got a security update just before I retired it, unbelievable.&lt;/p&gt;
&lt;p&gt;Later I got a TS-451+ as the old ones CPU was a bit slow, the old one moved to my office and I synced to it daily as a backup. But as the old one is now 14 years old it seems I am just pushing my luck with this box, so I retired it.  Moved the TS-451+ to the position of backup machine and got a new TS-453E.&lt;/p&gt;
&lt;p&gt;The TS-453E is interesting because it&amp;rsquo;s a kind of LTS hardware that will have full hardware support till 2029 and probably software far past that. I got it with an extended warranty also so this should set me up for 5 years at least on this platform.&lt;/p&gt;
&lt;p&gt;I set the primary NAS up in 2 x RAID-1 configuration and basically perform backups like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Real time RAID-1 mirror between 2 disk for the primary volume&lt;/li&gt;
&lt;li&gt;Daily sync my Dropbox onto the primary NAS&lt;/li&gt;
&lt;li&gt;Daily sync primary to the backup NAS that is running a RAID-5 setup in another location in Malta&lt;/li&gt;
&lt;li&gt;Daily sync the primary to a 5TB external USB drive that I can take to a 3rd offsite when I leave the office for months over summer&lt;/li&gt;
&lt;li&gt;Monthly I do a full bitrot check across all the files on the primary&lt;/li&gt;
&lt;li&gt;Monthly I do a manual sync between the RAID-1 volumes of the primary NAS&lt;/li&gt;
&lt;li&gt;Monthly I sync the full primary NAS to Finland on a machine with a RAID-6&lt;/li&gt;
&lt;li&gt;My primary desktop does TimeMachine backups to the primary regularly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This achieves the following redundancy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;+1 disk redundancy for any change I made now thanks to the first RAID-1&lt;/li&gt;
&lt;li&gt;daily same-country offsite to a RAID-5 storage&lt;/li&gt;
&lt;li&gt;monthly OS-level integrity checks of all files and the chance to restore any to the primary&lt;/li&gt;
&lt;li&gt;monthly on-site backup for long term recovery should I notice a accidental file deletion or corruption&lt;/li&gt;
&lt;li&gt;monthly backup to another country.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the end every file lands on about 11 disks in duplicate, 3-4 locations, 2 countries, different raid levels and with bitrot detection at multiple RAID levels both standard Linux kernel and NAS kernels and at the OS level. I could make the final Finland archive a 3 month rolling archive since I have enough space there for it.&lt;/p&gt;
&lt;p&gt;The machine I mention in Finland is a Hetzner SX64 (64GB RAM Ryzen 5 3600) with 4 x 16 TB SATA Enterprise HDD 7200rpm drives it runs Bacula for my server backups, but for above I just rsync to it.  It&amp;rsquo;s a slow crappy machine tbh but for the purpose of mostly just keepting files on disks mostly idle, it&amp;rsquo;s perfectly fine and the price is great. I&amp;rsquo;ve had similar machines for a decade or more, I cycle them every 3 years to new hardware.&lt;/p&gt;
&lt;h2 id=&quot;linux-shell&quot;&gt;Linux Shell&lt;/h2&gt;
&lt;p&gt;I mentioned earlier that I live by: &lt;em&gt;MacOS Shells are for SSH to Linux machines&lt;/em&gt;, so I need a Linux machine.&lt;/p&gt;
&lt;p&gt;I used to use the NUC machines but as they were ancient and dying I needed something else. Slack suggested I look at the Asus Mini PC PN51 and what a great suggestion that was.&lt;/p&gt;
&lt;p&gt;I have the Ryzen 7 5700U based machine with 64GB RAM and 1TB M.2 SSD in it, this machine runs CentOS and later Almalinux flawlessly. I used to use it bare but that was a bit of a waste now it runs &lt;a href=&quot;https://www.devco.net/posts/2024/03/21/lab-infra-rebuild-2/&quot;&gt;Cockpit as per my previous post&lt;/a&gt; and I have a 16GB VM there dedicated as my shell box.&lt;/p&gt;
&lt;p&gt;Along with that I have 6-10 VMs elsewhere making up a dev environment.&lt;/p&gt;
&lt;h2 id=&quot;miscellaneous-hardware&quot;&gt;Miscellaneous Hardware&lt;/h2&gt;
&lt;p&gt;At my office I have a few other things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One of my old NUC machines is a Linux Desktop for my son, mainly for minecraft/youtube when he visits&lt;/li&gt;
&lt;li&gt;A Krups Nescafe Dolce Gusto Infinissima Touch Black Automatic Coffee Machine - it&amp;rsquo;s rubbish, don&amp;rsquo;t buy these coffee machines&lt;/li&gt;
&lt;li&gt;A bar fridge&lt;/li&gt;
&lt;li&gt;Xiaomi air purifier&lt;/li&gt;
&lt;li&gt;Xiaomi robot vacuum - the nice one with auto bag empty feature so the place keeps clean when I am away for months at a time over summer&lt;/li&gt;
&lt;li&gt;A Prusa MK4 and it&amp;rsquo;s many accessories (this is a whole blog post to be made)&lt;/li&gt;
&lt;li&gt;A 15 year old Brother printer that is enormous and has finally now probably met it&amp;rsquo;s end, probably fixable, but I want an excuse to buy a smaller printer&lt;/li&gt;
&lt;li&gt;Ubiquity Dream Machine and a 8 port switch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything is new except the Brother printer, but we&amp;rsquo;ll fix that soon.&lt;/p&gt;
&lt;p&gt;I try to not clutter the place up so I keep things to a minimum here and as mentioned I might move my workshop across the road to another unit and get some more 3D printers, Lazer cutters and so.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s about it for the physical location and physical hardware. I&amp;rsquo;ll need to redo my home office as I gutted it and brought most things here but that&amp;rsquo;s for later, it&amp;rsquo;s mainly used now for its real purpose of being a media room with a 100 inch 4K project Dolby Atmos audio setup.&lt;/p&gt;
  </content>
    <author>
      <name>R.I.Pienaar - www.devco.net</name>
      <uri>http://www.devco.net/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Camptocamp Blog: Beyond the Buzz: Embracing the Magic of eBPF in Kubernetes</title>
    <link href="https://dev.to/camptocamp-ops/beyond-the-buzz-embracing-the-magic-of-ebpf-in-kubernetes-45md"/>
    <id>https://dev.to/camptocamp-ops/beyond-the-buzz-embracing-the-magic-of-ebpf-in-kubernetes-45md</id>
    <updated>2024-04-10T14:02:32+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;h2&gt;
  
  
  Beyond the Buzz: Embracing the Magic of eBPF in Kubernetes
&lt;/h2&gt;

&lt;p&gt;In a time where the buzz around Artificial Intelligence (AI) seems to overshadow everything else, this year&#39;s KubeCon Europe offered a refreshing perspective. While AI continues to be a hot topic, some in the Kubernetes community are starting to feel a bit tired of it. With all the hype and uncertainty surrounding AI, another hero has emerged: eBPF (Extended Berkeley Packet Filter).&lt;/p&gt;

&lt;h3&gt;
  
  
  AI: A Distant Shining Horizon
&lt;/h3&gt;

&lt;p&gt;AI has certainly added some excitement to discussions about cloud-native technologies, from automating cluster troubleshooting to hosting AI on Kubernetes. But not everyone is fully on board. While AI has proved to be of great assistance - e.g. suggesting, fixing and reviewing code written by humans-  some folks worry that too much focus on AI might distract from more practical, ready-to-implement advancements. The feeling is clear: while AI offers lots of possibilities, the horizon is a bit uncertain and overly hyped.&lt;/p&gt;

&lt;h3&gt;
  
  
  eBPF: Here and Now in Kubernetes Innovation
&lt;/h3&gt;

&lt;p&gt;In contrast, eBPF is all about practical innovation. It&#39;s a technology that delivers real results, right here, right now. We need solutions for network security, observability, and performance today, and that&#39;s where eBPF shines. Unlike the abstract promises of AI, eBPF offers concrete tools and methods to improve Kubernetes environments immediately. For example, when it comes to network security, eBPF-powered tools like Cilium can do the job without needing the complexity of AI.&lt;br&gt;
This shift towards valuing what&#39;s immediately useful over what&#39;s exciting but distant was noticeable at KubeCon. As we dive deeper into what eBPF can do, it becomes clear why this technology has captured the attention of the Kubernetes community.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Way Forward with eBPF
&lt;/h3&gt;

&lt;p&gt;By embracing eBPF, the Kubernetes community isn&#39;t just adopting new tools; it&#39;s championing a philosophy of practical, tangible progress. As we explore the latest eBPF innovations – from better security to revolutionary observability tools – we see the benefits eBPF brings to Kubernetes. It&#39;s a journey grounded in reality, offering not just a vision of the future, but a roadmap to get there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redefining Efficiency: eBPF&#39;s Radical Return to Linux&#39;s Roots
&lt;/h3&gt;

&lt;p&gt;eBPF represents a refreshing departure from the conventional approach of stacking layer upon layer in pursuit of functionality, often resulting in bloated, resource-intensive systems, even for simple tasks like rendering a webpage or routing network packets between nodes or clusters. With eBPF, we&#39;re venturing back into the depths of the Linux system, where innovation meets efficiency. Here, we witness a paradigm shift—a departure from the status quo. The results speak for themselves: a &lt;a href=&quot;https://isovalent.com/blog/post/tetragon-release-10/#process-execution-tracking-at-less2percent-overhead&quot;&gt;nearly negligible overhead&lt;/a&gt; and remarkable responsiveness. In fact, eBPF brings us so close to real-time processing that it&#39;s revolutionizing how we think about performance in cloud-native environments. We&#39;re not just optimizing; we&#39;re redefining what&#39;s possible, and eBPF is leading the charge.&lt;/p&gt;

&lt;h3&gt;
  
  
  eBPF talks from KubeCon 2024
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cilium: Connecting, Observing, and Securing Service Mesh and Beyond with eBPF&lt;br&gt;
Speakers: Liz Rice, Christine Kim, Nico Meisenzahl, Vlad Ungureanu&lt;br&gt;
&lt;a href=&quot;https://youtu.be/wq1TxZw1AaY?si=JTyhE333QfsGht0T&quot;&gt;https://youtu.be/wq1TxZw1AaY?si=JTyhE333QfsGht0T&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dealing with eBPF’s Observability Data Deluge&lt;br&gt;
Speaker: Anna Kapuścińska&lt;br&gt;
&lt;a href=&quot;https://youtu.be/yWB8n_e4N14?si=OyMJEKzbxS5zxA5P&quot;&gt;https://youtu.be/yWB8n_e4N14?si=OyMJEKzbxS5zxA5P&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unlock Energy Consumption in the Cloud with eBPF&lt;br&gt;
Speaker: Leonard Pahlke&lt;br&gt;
&lt;a href=&quot;https://youtu.be/lW9pZoKRJVs?si=rX5CQMaFuZBm8bBT&quot;&gt;https://youtu.be/lW9pZoKRJVs?si=rX5CQMaFuZBm8bBT&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fast and Efficient Log Processing with Wasm and eBPF&lt;br&gt;
Speaker: Michael Yuan&lt;br&gt;
&lt;a href=&quot;https://youtu.be/4u7nUpZxr3g?si=pkcpoEwOeDaH5HcE&quot;&gt;https://youtu.be/4u7nUpZxr3g?si=pkcpoEwOeDaH5HcE&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No &#39;Soup&#39; for You! Enforcing Network Policies for Host Processes via eBPF&lt;br&gt;
Speaker: Vinay Kulkarni&lt;br&gt;
&lt;a href=&quot;https://youtu.be/AWAf3H4Qwq8?si=qVqQfWb3J_905BCJ&quot;&gt;https://youtu.be/AWAf3H4Qwq8?si=qVqQfWb3J_905BCJ&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;eBPF’s Abilities and Limitations: The Truth&lt;br&gt;
Speakers: Liz Rice &amp;amp; John Fastabend&lt;br&gt;
&lt;a href=&quot;https://youtu.be/tClsqnZMN6I?si=TyMFTMk4Q45K6T2v&quot;&gt;https://youtu.be/tClsqnZMN6I?si=TyMFTMk4Q45K6T2v&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
  </content>
    <author>
      <name>Camptocamp Blog</name>
      <uri>https://dev.to/camptocamp-ops</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">R.I.Pienaar - www.devco.net: Lab Infra Rebuild Part 3</title>
    <link href="https://www.devco.net/posts/2024/04/07/lab-infra-rebuild-3/"/>
    <id>https://www.devco.net/posts/2024/04/07/lab-infra-rebuild-3/</id>
    <updated>2024-04-07T08:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;This is an ongoing post about rebuilding my lab infrastructure, see the initial post &lt;a href=&quot;https://www.devco.net/posts/2024/03/20/lab-infra-rebuild-1/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Today I&amp;rsquo;ll talk a bit about Configuration Management having previously mentioned I am &lt;a href=&quot;https://www.devco.net/posts/2024/03/21/lab-infra-rebuild-2/&quot;&gt;ditching Kubernetes&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;server-management&quot;&gt;Server Management&lt;/h2&gt;
&lt;p&gt;The general state of server management is pretty sad, you have Ansible or Puppet and a long tail of things that just can&amp;rsquo;t work or are under terrible corporate control that you just can&amp;rsquo;t touch them.&lt;/p&gt;
&lt;p&gt;I am, as most people are aware, a very long term Puppet user since almost day 1 and have contributed significant features like Hiera and the design of Data in Modules. I&amp;rsquo;ve not had much need/interest in being involved in that community for ages but I want to like Puppet and I want to keep using it where appropriate.&lt;/p&gt;
&lt;p&gt;I think Puppet is more or less finished - there are bug fixes and stuff of course - but in general core Puppet is stable, mature and does what one wants and have extension points one needs. There&amp;rsquo;s not really any reason not to use if it fits your needs/tastes and should things go pear shaped its a easy fork. One can essentially stay on a current version for years at this point and it&amp;rsquo;s fine. There used to be some issues around packaging but even this is Apache-2 now. If you have the problem it solves it does so very well, but the industry have moved on so not much scope for extending it imo.&lt;/p&gt;
&lt;p&gt;All the action is in the content - modules on the forge. &lt;a href=&quot;https://voxpupuli.org/&quot;&gt;Vox Pupuli&lt;/a&gt; are doing an amazing job, I honestly do not know how they do so much or maintain so many modules, it&amp;rsquo;s really impressive.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a general state of rot though, modules I used to use are almost all abandoned with a few moved to Vox. I wonder if stats about this is available, but I get the impression that content wise things are also taking a huge dive there and Vox are holding everything afloat with Puppet hoping to make money from selling commercial modules - I doubt it will sustain a business their size but it&amp;rsquo;s a good idea.&lt;/p&gt;
&lt;p&gt;Read on for more about Puppet today in my infra.&lt;/p&gt;
&lt;p&gt;Given the general state of things in the server management world I decided to use Puppet again for this round of infrastructure rebuild. I can&amp;rsquo;t see this lasting alas. Most people are aware that Puppet have been bought by Perforce and have had a huge shift in people and such. It&amp;rsquo;s inevitable that revenue generation is the main push at the moment.&lt;/p&gt;
&lt;p&gt;Unfortunately the way this play out is pretty unpleasant. Here&amp;rsquo;s an example: I have &lt;a href=&quot;https://github.com/ripienaar/monitoring-scripts/blob/master/puppet/check_puppet.rb&quot;&gt;an ancient, and crappy, monitoring script&lt;/a&gt; that runs on the node and checks &lt;code&gt;last_run_summary.yaml&lt;/code&gt; to infer the current status of runs - are they failing etc. It&amp;rsquo;s worked for a very long time and &lt;code&gt;last_run_summary.yaml&lt;/code&gt; is a contract that&amp;rsquo;s to be maintained.
Something in recent Puppet broke this script (the &lt;code&gt;last_run_summary.yaml&lt;/code&gt; now behaves differently) so I thought I&amp;rsquo;d ask on their Slack if there is something newer/maintained before I fixed mine.&lt;/p&gt;
&lt;p&gt;Immediately from Puppet people you get a message saying to use Puppet Enterprise for this. In a community Slack where people are just asking questions about a 200 line script. The suggestion is to move to a price-not-disclosed product vs a 200 line script. Without even so much as a question about needs or environment or if the suggestion would be relevant. It&amp;rsquo;s just corporate enshitification. Eventually I got some good answers from the community, despite Puppets best efforts.&lt;/p&gt;
&lt;p&gt;This outcome is of course entirely predictable, I can only hope Vox doesn&amp;rsquo;t get burned in the inevitable slide into the sewer.&lt;/p&gt;
&lt;h2 id=&quot;managing-puppet&quot;&gt;Managing Puppet&lt;/h2&gt;
&lt;p&gt;I have 2 old EL based machines that could not make the trip to Puppet 8 due some legacy there, the rest got moved to EL9 with a Puppet Server. I am though a big fan of running &lt;code&gt;puppet apply&lt;/code&gt; based builds and will likely move to that instead of the server. Apply based workflows present a few problems though, primarily how you get the code on the nodes and how the workflow is around that.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d like a git based flow where I commit a change, CI package it and puts it on a repo and the fleet updates to it. Ideally the fleet updates to it asap. Further I want visibility into the runs, node-side monitoring which fits my event based world-view and allows me central control to trigger runs and do scheduled maintenance.&lt;/p&gt;
&lt;p&gt;So I built a system to orchestrate Puppet that&amp;rsquo;ll release soon called &lt;code&gt;Puppet Control&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provides a nice cli for triggering runs, querying runs etc&lt;/li&gt;
&lt;li&gt;Git+CI based flow handles fetching, validating and deploying code to nodes for &lt;code&gt;apply&lt;/code&gt;, including tamper detection&lt;/li&gt;
&lt;li&gt;Has concurrently control built in for fine-grained resource management of file servers used to deliver code bundles&lt;/li&gt;
&lt;li&gt;Includes a run scheduler with concurrency controls that include ability to say for eg: only 1 database server out of all database server can run Puppet at a time but webservers can run 10 concurrently&lt;/li&gt;
&lt;li&gt;Can do on-demand runs as soon as possible subject to concurrency control to ensure the shared infra performs at peak&lt;/li&gt;
&lt;li&gt;Has various ways to find nodes in certain states like &lt;code&gt;pctl nodes failing&lt;/code&gt; to find nodes with failing resources&lt;/li&gt;
&lt;li&gt;Can show real-time events of Puppet runs&lt;/li&gt;
&lt;li&gt;Can have maintenance declared that will stop all scheduled Puppet runs&lt;/li&gt;
&lt;li&gt;Expose run statistics to Prometheus for runtimes, changes, health and more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There&amp;rsquo;s an optional video below with more details and show the code release flow etc:&lt;/p&gt;
&lt;div style=&quot;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&quot;&gt;
      &lt;iframe allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&quot; loading=&quot;eager&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; src=&quot;https://www.youtube.com/embed/Bt9K5J3x3ac?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&quot; style=&quot;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&quot; title=&quot;YouTube video&quot;&gt;&lt;/iframe&gt;
    &lt;/div&gt;

&lt;p&gt;I&amp;rsquo;ll release this eventually, it&amp;rsquo;s dependent on some work happening in Choria at the moment.&lt;/p&gt;
&lt;p&gt;The concurrency control is a big deal. Scheduling Puppet runs is quite a difficult task. Usually the solution Puppet users do is just to spread the runs in cron by some random time distribution.&lt;/p&gt;
&lt;p&gt;That leaves the problem of fast Puppet runs during maintenance windows though. In the past we had a command &lt;code&gt;mco puppet runall 100&lt;/code&gt; which would discover all the nodes then in a loop ask their state and schedule more as some stopped - the goal being to keep as close to 100 running at a time. The choice of 100 nodes is related to the capacity of the Puppet Server infrastructure.&lt;/p&gt;
&lt;p&gt;This worked fine but it was very resource intensive on the Choria/MCollective network as 1000s of RPC requests have to be made to know the current state and it was not suited to using in an ongoing fashion. With &lt;code&gt;Puppet Control&lt;/code&gt; every run is a run that happens at the desired concurrency, but without a central orchestrator trying to make all the choices. It&amp;rsquo;s significantly cheaper on the network.&lt;/p&gt;
&lt;p&gt;More significantly by allowing the concurrency group name to be configured on a per node basis one can have different policies by type of machine. This is a big deal, let&amp;rsquo;s say we are using &lt;code&gt;puppet apply&lt;/code&gt; but we do not wish to have our 5 database machines all do concurrent runs and potentially restarting at the same time. By creating a concurrency governor for just those machines set to 1 we prevent that.&lt;/p&gt;
&lt;p&gt;With this in place triggering all the nodes to run at their groups configured concurrency is a simple &lt;code&gt;pctl nodes trigger&lt;/code&gt; which takes 2 seconds to complete. From there the nodes will run without overwhelming the Servers.&lt;/p&gt;
&lt;p&gt;Another interesting thing here is that this model maps well onto Ansible local mode as well. So in theory, unexplored theory, this same central controller and scheduler could be made for Ansible.&lt;/p&gt;
&lt;p&gt;This is built on &lt;a href=&quot;https://choria.io/docs/streams/governor/&quot;&gt;Choria Concurrency Governors&lt;/a&gt; which is an amazing distributed system building block.&lt;/p&gt;
&lt;h2 id=&quot;choria&quot;&gt;Choria&lt;/h2&gt;
&lt;p&gt;No surprise that I am using Choria for a large part of this, with a bit of a twist though. Choria, as released on &lt;a href=&quot;https://choria.io&quot;&gt;choria.io&lt;/a&gt;, is actually a distribution of a much larger system that is tailored for Puppet users.  That official Choria release &lt;strong&gt;requires&lt;/strong&gt; Puppet Agent and will not support unofficial builds or unsupported deviations from that.&lt;/p&gt;
&lt;p&gt;With the writing being on the wall though for Puppet this leaves me with a problem, I have no easy to use Public distribution of Choria for non Puppet users. Puppet provides the following to Choria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deployment of the packages and files to nodes&lt;/li&gt;
&lt;li&gt;Management of policies and plugins on nodes&lt;/li&gt;
&lt;li&gt;Certificate Authority with certs on every node&lt;/li&gt;
&lt;li&gt;Optional discovery source of truth in PuppetDB&lt;/li&gt;
&lt;li&gt;Libraries for managing packages and services&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are actually quite significant hurdles to cross to create a fully Puppet-free Choria distribution.&lt;/p&gt;
&lt;p&gt;That said for a long time Choria have had another life as a &lt;a href=&quot;https://choria.io/docs/concepts/large_scale/&quot;&gt;large scale orchestrator&lt;/a&gt; that is not Puppet related. This implies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It can &lt;a href=&quot;https://choria.io/blog/post/2018/08/13/server-provisioner/&quot;&gt;self-provision at a rate of 1000+ nodes / minute&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Deploy its own plugins at a rate of multiple plugins delivered to millions of nodes in minutes&lt;/li&gt;
&lt;li&gt;Manage its own security both integrated with a CA or using a new JWT+ed25519 based approach&lt;/li&gt;
&lt;li&gt;Integrate with non Puppet data sources using external extension points&lt;/li&gt;
&lt;li&gt;Can upgrade itself in place without any help from Puppet in an Over-The-Air type self-upgrade system&lt;/li&gt;
&lt;li&gt;Has centralised RBAC integrated with tools like Open Policy Agent&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the last 2 years I was on a related contract and all these components have been firmed up and made much more capable, reliable and horizontally scalable and used in anger in the real world in some quite serious mission critical builds.&lt;/p&gt;
&lt;p&gt;Thus, my Choria infrastructure is actually a highbrid between Puppet and Non Puppet. Puppet places the RPMs and plugins that require Puppet (package, service, legacy plugins), but Choria self provisions everything else and owns the life of the agent and more. I am running the new Protocol and with Open Policy Agent based RBAC. I&amp;rsquo;ve made some changes to the various Puppet models to enable this and will start looking for some early adopters.&lt;/p&gt;
&lt;p&gt;This means my many Raspberry PI - from Xmas lights to sensors and HVAC control - are all now managed by Choria as well as the provisioner caters for them since those are without Puppet.&lt;/p&gt;
&lt;p&gt;I wrote about the &lt;a href=&quot;https://choria-io.github.io/go-choria/previews/protov2/index.html&quot;&gt;new protocol in an ADR&lt;/a&gt; and you can see there is scope for integration into TPMs and more. This is the future world view of Choria and already in use on some 100s of thousands of machines.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So that&amp;rsquo;s a bit about managing the machines without Kubernetes or a ISP managing them.&lt;/p&gt;
&lt;p&gt;As I&amp;rsquo;ve been out of active Puppet use for a few years it&amp;rsquo;s been interesting to come back with some semi-fresh eyes and rethink some of the old things I believed was true when I used it constantly.&lt;/p&gt;
&lt;p&gt;Choria will play a critical role in the path forward as I&amp;rsquo;ll move much into containers managed as per the previous blog post leaving the problem Puppet solves to quite a thin layer. I&amp;rsquo;ve some thoughts on doing something to at least replace the most basic package-config-service trio of CM with something in Choria long term.&lt;/p&gt;
  </content>
    <author>
      <name>R.I.Pienaar - www.devco.net</name>
      <uri>http://www.devco.net/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">R.I.Pienaar - www.devco.net: Lab Infra Rebuild Part 2</title>
    <link href="https://www.devco.net/posts/2024/03/21/lab-infra-rebuild-2/"/>
    <id>https://www.devco.net/posts/2024/03/21/lab-infra-rebuild-2/</id>
    <updated>2024-03-21T08:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;&lt;a href=&quot;https://www.devco.net/posts/2024/03/20/lab-infra-rebuild-1/&quot;&gt;Previously&lt;/a&gt; I blogged about rebuilding my personal infra, focussing on what I had before.&lt;/p&gt;
&lt;p&gt;Today we&amp;rsquo;ll start into what I used to replace the old stuff. It&amp;rsquo;s difficult to know where to start but I think a bit about VM and Container management is as good as any.&lt;/p&gt;
&lt;h2 id=&quot;kubernetes&quot;&gt;Kubernetes&lt;/h2&gt;
&lt;p&gt;My previous build used a 3 node Kubernetes Cluster hosted at Digital Ocean. It hosted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Public facing websites like this blog (WordPress in the past), Wiki, A few static sites etc&lt;/li&gt;
&lt;li&gt;Monitoring: Prometheus, Grafana, Graphite&lt;/li&gt;
&lt;li&gt;A bridge from &lt;a href=&quot;https://www.thethingsnetwork.org&quot;&gt;The Things Network&lt;/a&gt; for my LoRaWAN devices&lt;/li&gt;
&lt;li&gt;3 x redundant Choria Brokers and AAA&lt;/li&gt;
&lt;li&gt;Container Registry backed by Spaces (Digital Ocean object storage)&lt;/li&gt;
&lt;li&gt;Ingress and Okta integration via Vouch&lt;/li&gt;
&lt;li&gt;Service discovery and automatic generation of configurations for Prom, Ingress etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apart from the core cluster I had about 15 volumes, 3 Spaces, an Ingress load balancer with a static IP and a managed MySQL database.&lt;/p&gt;
&lt;p&gt;I never got around to go full GitOps on this setup, it just seemed too much to do for a one man infra to both deploy all that and maintain the discipline. Of course I am not a stranger to the discipline required being from Puppet world, but something about the whole GitOps setup just seemed like A LOT.&lt;/p&gt;
&lt;p&gt;I quite liked all of this, when Kubernetes works it is a pleasant experience, some highlights:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Integration with cloud infra like LBs is an amazing experience&lt;/li&gt;
&lt;li&gt;Integration with volumes to provide movable storage is really great and hard to repeat&lt;/li&gt;
&lt;li&gt;I do not mind YAML and the diffable infrastructure is really great, no surprise there. I hold myself largely to blame for the popularity of YAML in infra tools at large thanks to Hiera etc, so I can&amp;rsquo;t complain.&lt;/li&gt;
&lt;li&gt;Complete abstraction of node complexities is a double-edged sword but I think in the end I come to appreciate it&lt;/li&gt;
&lt;li&gt;I do like the container workflow and it was compatible with some &lt;a href=&quot;https://www.devco.net/archives/2015/03/30/some-thoughts-on-operating-containers.php&quot;&gt;pre-k8s thoughts I had on this&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Easy integration between CI and infrastructure with the &lt;code&gt;kubectl rollout&lt;/code&gt; abstraction&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some things I just did not like, I will try to mention some things that not the usual gripes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Access to managed k8s infra is great, but not knowing how its put together for the particular cloud can make debugging things hard. I had some Cilium failures that was a real pain&lt;/li&gt;
&lt;li&gt;API deprecations are constant, production software rely on Beta APIs and will just randomly break. I expected this, but over the 3 years this happened more than I expected. You really have to be on top of all the versions of all the things&lt;/li&gt;
&lt;li&gt;The complimentary management tooling is quite heavy like I mentioned around GitOps. Traditional CM had a quick on-ramp and was suitable at small scale, I miss that&lt;/li&gt;
&lt;li&gt;I had to move from Linode K8s to Digital Ocean K8s. The portability promises of pure kubernetes is lost if you do not take a lot of care&lt;/li&gt;
&lt;li&gt;Logging from the k8s infra is insane, ever-changing, unusable unless you really really are into this stuff like very deep and very on-top of every version change&lt;/li&gt;
&lt;li&gt;Digital Ocean does forced upgrades of the k8s, this is fine. The implication is that all the nodes will be replaced so Prometheus polling source will change with big knock on effect. The way DO does it though involves 2 full upgrades for every 1 upgrade doubling the pain&lt;/li&gt;
&lt;li&gt;It just seem like no-one wants to even match the features Hiera have in terms of customization of data&lt;/li&gt;
&lt;li&gt;Helm&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the end it all just seemed like a lot for my needs and was ever slightly fragile. I went on a 3 month sabbatical last year and the entire infra went to hell twice, all on its own, because I neglected some upgrades during this time and when Digital Ocean landed their upgrade it all broke. It&amp;rsquo;s a big commitment.&lt;/p&gt;
&lt;p&gt;See the full entry for detail of what I am doing instead.&lt;/p&gt;
&lt;h2 id=&quot;container-management&quot;&gt;Container Management&lt;/h2&gt;
&lt;p&gt;So let&amp;rsquo;s look at what I am working on for container management. My current R&amp;amp;D focus in Choria is around Autonomous Agents that can manage one thing forever, one thing like a Container. So I am dog-fooding some of this work where I need containers and will move things into containers as I progress down this path.&lt;/p&gt;
&lt;p&gt;Looking at my likes list from the Kubernetes section above we can imagine where I am focussing with the tooling I am building, lets just jump right in with what I have today:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre tabindex=&quot;0&quot; class=&quot;chroma&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;nt&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;tally&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;registry.choria.io/choria/tally&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;image_tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;latest&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;syslog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;kv_update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;restart_files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;/etc/tally/config/choria.conf&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;- &lt;span class=&quot;l&quot;&gt;/etc/tally/config:/tally/config&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;- &lt;span class=&quot;m&quot;&gt;9010&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;register_ports&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;      &lt;/span&gt;- &lt;span class=&quot;nt&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;tally&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;%{facts.networking.ip}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;l&quot;&gt;%{facts.location}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;9010&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;w&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;prometheus.io/scrape&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is Puppet Hiera data that defines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A running Container for a Choria related service that passively listens to events and expose metrics to Prometheus&lt;/li&gt;
&lt;li&gt;It will watch a file on the host file system and restart the container if the file changes, Puppet manages the file in question&lt;/li&gt;
&lt;li&gt;It supports rolling upgrades via Key-Value store updates but defaults to &lt;code&gt;latest&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;It exposes the port to service discovery with some port specific annotations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This creates a &lt;a href=&quot;https://choria.io/docs/autoagents/&quot;&gt;Choria Autonomous Agent&lt;/a&gt; that will forever manage the container. If health checks fail the port will not be published to Service Discovery anymore and remediation will kick in etc.&lt;/p&gt;
&lt;p&gt;Of course Puppet is an implementation detail - anything that can press the YAML file and place it into Choria can do this. Choria can also download and deploy these automations as a plugin at runtime via securely signed artifacts. So this supports a fully CI/CD driven GitOps like flow that has no Puppet involvement.&lt;/p&gt;
&lt;p&gt;To replace the &lt;code&gt;kubectl rollout&lt;/code&gt; process I support KV updates like &lt;code&gt;choria kv put HOIST container.tally.tag 0.0.4&lt;/code&gt; (we have Go APIs for this also), the container will listen for this kv update and perform an upgrade. Upgrades support rolling strategies like say 2 at at time out of a cluster of 20 etc.  The &lt;code&gt;kv put&lt;/code&gt; is the only interaction needed to do this and no active orchestration is needed.&lt;/p&gt;
&lt;p&gt;Service discovery is also in a Choria Key-Value bucket and I can query it:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ sd find
[1] tally @ ve2 [http://192.168.1.10:9010]
   prometheus.io/port: 9010
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Or generate Prometheus configurations:&lt;/p&gt;
&lt;pre tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nohighlight&quot; data-lang=&quot;nohighlight&quot;&gt;$ sd prometheus
- targets:
    - 192.168.1.10:9010
  labels:
    __meta_choria_cluster_name: ve2
    __meta_choria_ip: 192.168.1.10
    __meta_choria_port: &amp;#34;9010&amp;#34;
    __meta_choria_priority: &amp;#34;1&amp;#34;
    __meta_choria_protocol: http
    __meta_choria_service: tally
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So one can imagine how that integrates with Prometheus file based SD or HTTP based. In future I&amp;rsquo;ll add things to manage ingress configurations automatically etc, and of course Service Discovery -&amp;gt; file flow can ge managed using Autonomous Agents also.&lt;/p&gt;
&lt;p&gt;Actual building of containers have not changed much from &lt;a href=&quot;https://www.devco.net/archives/2015/02/24/moving-a-service-from-puppet-to-docker-2.php&quot;&gt;earlier&lt;/a&gt; &lt;a href=&quot;https://www.devco.net/archives/2015/03/30/some-thoughts-on-operating-containers.php&quot;&gt;thoughts&lt;/a&gt; about this and the above system - called Hoist - will focus on strengthening those thoughts.&lt;/p&gt;
&lt;h2 id=&quot;virtualization&quot;&gt;Virtualization&lt;/h2&gt;
&lt;p&gt;Previously I ran some various mixes of KVM things: I had Puppet code to generate &lt;code&gt;libvirt&lt;/code&gt; configuration files, later I got lazy and used the RedHat graphical machines manager - I just don&amp;rsquo;t change my VMs much to be honest so don&amp;rsquo;t need a lot of clever things.&lt;/p&gt;
&lt;p&gt;As I was looking to run 3 or 4 baremetal machines with VMs on-top I wanted something quite nice with a nice UI and started looking around and found numerous Youtubers going on like crazy about Proxmox. I tried Proxmox for a week on some machines and had some thoughts about this experience.&lt;/p&gt;
&lt;p&gt;It is nice with a broad feature set that is quite a good all round product in this space, I can see if done right this will be formidable tool to use and would consider it in future again.&lt;/p&gt;
&lt;p&gt;Is seems like this is a company who has engineers, paid to engineer, and they will engineer things all day long. Ditto product owners etc. There&amp;rsquo;s a lot there and lots of it feels half-baked, awkward or incomplete or in-progress. Combined with there just being A LOT it seemed like a mess. I bet this is a company who just love sprint based work and fully embrace the sprints being quite isolated approach. It shows in obvious ways.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s Debian based. I have used EL from their first Beta. Slackware before. SLS before (1993!). In kubernetes you can get away with not caring so much about your hosts but in a virtualized environment you will need to make sure you have ways to manage updates, backups and such of those baremetals. I do not have any tooling specifically built for Debian so I really do not want that lift. I also just do not care even remotely for Debian or its community.&lt;/p&gt;
&lt;p&gt;Ultimately I do not need integration with Ceph etc, I don&amp;rsquo;t need (oh hell no do I not need) a &lt;code&gt;database-driven file system developed by Proxmox&lt;/code&gt; and for my needs I do not need SDN. All these things are useful but it seemed like it would tick some of the boxes I listed in Kubernetes conns.&lt;/p&gt;
&lt;p&gt;After some looking around at options I came across &lt;a href=&quot;https://cockpit-project.org/&quot;&gt;Cockpit&lt;/a&gt; which comes integrated already into EL based distros and while it&amp;rsquo;s not as full featured as Proxmox I find that to be a feature rather than a shortcoming. It does just the right things and I can easily just not install things I do not want.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.devco.net/img/cockpit-vm-create.webp&quot; alt=&quot;Cockpit VM management&quot;&gt;&lt;/p&gt;
&lt;p&gt;I think its firewall management needs a bunch of work still, but that&amp;rsquo;s ok I do not want to manage firewalls in this manner (more later) so that is just fine, I also do not need its package management - no problem, just uninstall the feature.&lt;/p&gt;
&lt;p&gt;Really not much to say or complain about here - and having nothing to say is a huge feature - it makes virtual machines, allow me to edit their configurations, see their consoles etc. Just what I need and no more. One annoying thing with it is that I cannot figure out how to trigger a re-install of a machine, I did not look too deep though.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;EDIT:&lt;/em&gt; I do have a few things to say about it after all:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It took like 2 minutes to install on an EL machine.&lt;/li&gt;
&lt;li&gt;When you are not using it, there is nothing in the process tree. It&amp;rsquo;s fully based on socket activation. No resources wasted.&lt;/li&gt;
&lt;li&gt;It does not take over and camp on everything. It uses the same commands and APIs Puppet/Ansible/You does, you can keep using CLI tools you know. Or progressively learn.&lt;/li&gt;
&lt;li&gt;It uses your normal system users via PAM so not much to deploy regarding authentication etc.&lt;/li&gt;
&lt;li&gt;It support EL/Debian/Ubuntu and more.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is huge, it just is super lightweight and gets out of your way and does not prescribe much. It does not invent new things and does not invent new terminology. Huge.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So that is roughly what I am doing for Virtualization and Container management after Kubernetes.  Container management is a work in progress so a lot of my components are now just stuff running on VMs but as I progress to improve Hoist a bit I&amp;rsquo;ll gradually move into it for more things, this has been something I&amp;rsquo;ve wanted to finish for a long time so I am glad to get the chance.&lt;/p&gt;
&lt;p&gt;Further as my Lab is really that after all, a place for R&amp;amp;D, using the area I am focussing on in Choria in anger to solve some real problems has been invaluable and I wanted more of that, this influenced some of these decisions.&lt;/p&gt;
  </content>
    <author>
      <name>R.I.Pienaar - www.devco.net</name>
      <uri>http://www.devco.net/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">binford2k.com: Cranky community members</title>
    <link href="https://binford2k.com/2024/03/21/cranky-community-members/"/>
    <id>https://binford2k.com/2024/03/21/cranky-community-members</id>
    <updated>2024-03-21T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;You know the kind. There’s that one person who always has all the opinions. Sometimes
there are many of them. But they’re critical about that thing you built, or the workflow
you designed, or that issue that you haven’t responded to since they filed it last year.
Especially if you hear it long enough, this criticism can cut deeply.&lt;/p&gt;

&lt;p&gt;Technical Open Source community members always have something to complain about. Maybe
they don’t feel like their work is appreciated. Maybe bugs or pull requests aren’t
addressed in a timely manner. Maybe they have opinions about your product’s functionality.
Maybe your product is genuinely bad and they’re trying to tell you that.&lt;/p&gt;

&lt;p&gt;Whatever the reasons, you’ll soon come to learn that few communities all agree on everything.
There is always some amount of dissent and so there will always be someone complaining about
something. You’ll also find that corporate and community interests are rarely perfectly
aligned. Engaging in a technical community will always require navigating these imperfect alignments.&lt;/p&gt;

&lt;p&gt;So what are we to do? Luckily, there are some strategies you can employ to engage with
unruly community members, primarily by understanding them better and learning how to make
the jabs less hurtful.&lt;/p&gt;

&lt;h2 id=&quot;work-is-not-your-identity&quot;&gt;Work is not your identity&lt;/h2&gt;

&lt;p&gt;The most helpful strategy is to emotionally separate yourself from your work and from your
company.  Chances are that someone else is responsible for your product’s architecture, for
its design, for its featureset, its roadmap, its priorities, and so on. Chances are that you
are building something that’s largely dictated by others, and that their decision making
is driven by business needs and priorities.&lt;/p&gt;

&lt;p&gt;These business needs can be driven by all sorts of things. Maybe there’s a market play going
on. Or portfolio diversification. Maybe the company is preparing for IPO or an acquisition.
Maybe it’s working for a new certification. Perhaps there just aren’t resources to do it the
right way and corners had to be cut. Maybe you don’t even know what’s happening behind the scenes.&lt;/p&gt;

&lt;p&gt;The key is that it’s unlikely that you’re responsible for any of this, so you shouldn’t own
the complaint either. The most you can really do is to ensure that the community member files
a ticket to get the feedback in their own words and then raise the ticket to the appropriate
party. And don’t feel obligated to defend the underlying business need. Chances are that’s not
your job either.&lt;/p&gt;

&lt;h2 id=&quot;community-members-raise-concerns-because-they-care&quot;&gt;Community members raise concerns because they care&lt;/h2&gt;

&lt;p&gt;With a few exceptions, people complain because they care and want to help you improve something.
They aren’t complaining just to be jerks.&lt;/p&gt;

&lt;p&gt;The ones who are complaining to be jerks are usually readily identifiable – often because other
community members will call them out on their toxic behaviors. You don’t have to give these
people the time of the day. Report them to your community managers, share the code of conduct,
or just flat out ignore them.&lt;/p&gt;

&lt;p&gt;Spend your quality time with the ones who care and are trying to help. Remember that the words
they say aren’t always what they mean. Listen for the problem underneath and remember that
people are often reacting out of frustration, especially if it’s an ongoing problem they’re
referring to.&lt;/p&gt;

&lt;h2 id=&quot;communication-styles-are-hard&quot;&gt;Communication styles are hard&lt;/h2&gt;

&lt;p&gt;And that brings me to my last point. Most of us are engaging in global communities or diverse
communities of people from all walks of life. The life story that shaped your communication style
is likely not the same as that of the customer support engineer from the deep Bronx and that of
the SRE from Berlin or Melbourne and that of the infrastructure architect from Johannesburg and
that of the OSS contributor from Atlanta.&lt;/p&gt;

&lt;p&gt;We all have different stories and life experiences. Some of us went to school and some learned
from the school of hard knocks. Some of us took computer classes as school kids and some of us
are busy learning a new profession and how to manage effective blameless code reviews. Some of
us were trained on the American shit sandwich feedback style and some of us don’t mince words
and just say what we mean. Some of us are neurospicy in the way that makes us speak bluntly and
some of us are neurospicy in the way that finds that incredibly offensive.&lt;/p&gt;

&lt;p&gt;The point is that what you find to be insulting and snarky is usually not meant that way at all.
When the German SRE provides curt feedback that’s direct and to the point, they’re not doing it
to be an asshole; they’re doing it out of kindness. Because to them, that’s the most effective
way to communicate their needs and help you do your job better. Now it’s on you to choose how you
want to hear it.&lt;/p&gt;

&lt;h2 id=&quot;to-recap&quot;&gt;To recap&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;If you aren’t personally responsible for intentionally making something bad, then don’t take feedback personally.&lt;/li&gt;
  &lt;li&gt;Remember that people are providing feedback because they want to help make things better.&lt;/li&gt;
  &lt;li&gt;You don’t have to choose to hear negativity in the way people communicate. It probably wasn’t meant that way.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Happy open sourcing. Go forth and collaborate!&lt;/p&gt;
  </content>
    <author>
      <name>binford2k.com</name>
      <uri>http://binford2k.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">R.I.Pienaar - www.devco.net: Lab Infra Rebuild Part 1</title>
    <link href="https://www.devco.net/posts/2024/03/20/lab-infra-rebuild-1/"/>
    <id>https://www.devco.net/posts/2024/03/20/lab-infra-rebuild-1/</id>
    <updated>2024-03-20T08:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;I&amp;rsquo;ve been posting on socials a bit about rebuilding my lab and some opinions I had on tools, approaches and more. Some people have asked for a way to keep up with my efforts, so I figured it might be time to post here for the first time since 2018!&lt;/p&gt;
&lt;p&gt;In this post I&amp;rsquo;ll focus on what came before, a bit of a recap of my previous setup. Additionally, to a general software refresh I have also been in Malta now 8 years and a lot of my office hardware was purchased around the time of moving here, so we&amp;rsquo;ll also cover replacing NAS servers and more.&lt;/p&gt;
&lt;p&gt;My previous big OS rebuilt was around CentOS 7 days, so that&amp;rsquo;s about 3 years ago now, high time to revisit some choices.&lt;/p&gt;
&lt;p&gt;My infra falls in the following categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Development machines: usually 10 or so Virtual Machines that I use mainly in developing &lt;a href=&quot;https://choria.io&quot;&gt;Choria.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Office support equipment: NAS, Printers, Desktops, Laptops etc&lt;/li&gt;
&lt;li&gt;Networking equipment: mainly home networking stuff for my locations&lt;/li&gt;
&lt;li&gt;Hosting publicly visible items: This blog, DNS, Choria Package repos etc&lt;/li&gt;
&lt;li&gt;Management infrastructure: Choria, Puppet, etc&lt;/li&gt;
&lt;li&gt;Monitoring infrastructure: Prometheus and friends&lt;/li&gt;
&lt;li&gt;Backups: for everything&lt;/li&gt;
&lt;li&gt;General all-purpose things like source control etc&lt;/li&gt;
&lt;li&gt;My actual office&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below I&amp;rsquo;ll do a quick run through all the equipment, machines, devices etc. I use regularly. I&amp;rsquo;ve largely replaced it all and will detail that in the following posts. It&amp;rsquo;s not huge infra or anything, all told about 20 to 30 instances in 5 or 6 locations.&lt;/p&gt;
&lt;h2 id=&quot;development-machines&quot;&gt;Development Machines&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve had 3 x Intel NUC machines, each with 512GB solid state and 8GB RAM since 2016. They have served me well and really I could see them going for a while longer.&lt;/p&gt;
&lt;p&gt;They were each a KVM host and had on them 3 to 8 CentOS VMs. One had some bigger ones as that was my general shell machine and the other were mainly there to add some servers to my node count for Choria development. Choria being inherently a distributed system having some more nodes help.&lt;/p&gt;
&lt;p&gt;For my needs they were great but unfortunately they have a hardware problem, their BIOS battery die and it is soldered on to the board so not exactly user serviceable.&lt;/p&gt;
&lt;p&gt;Two of these are just retiring - I will remove the SSD and see what it can be used for, more on that later - but otherwise they are done.&lt;/p&gt;
&lt;p&gt;One of them is actually a bit newer so it&amp;rsquo;s finding a new home as a little desktop for my 6 y/o mainly for Scratch and Minecraft etc.&lt;/p&gt;
&lt;p&gt;I have been using RedHat since version 0.9 Halloween Beta release then used CentOS for ages - even donated hardware - and after they seemed to be intent on self-destruction I started moving to Alma Linux.&lt;/p&gt;
&lt;h2 id=&quot;public-hosting-and-management&quot;&gt;Public Hosting and Management&lt;/h2&gt;
&lt;p&gt;I host my blog and a few other public bits myself. When I rebuilt all my things 3 years ago I wanted to get some more real Kubernetes experience so I got 3 x 8GB droplets from Digital Ocean with a Kubernetes cluster.&lt;/p&gt;
&lt;p&gt;There I hosted the sites, used their managed MySQL, used their Object store and volumes.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s been fine generally, I don&amp;rsquo;t think I like the complexity for what I needed but it was good experience - more on this later. This cluster started with Linode managed KE but it was early days for them in Kubernetes and so I bailed - their support was terrible at the time wrt Kubernetes.&lt;/p&gt;
&lt;p&gt;This was surprising as I&amp;rsquo;ve been a Linode customer since almost their day one and they have been consistently amazing. But when they launched managed Kubernetes it seemed their EU timezone support people were like level 1 only and I always ended up waiting till US time to get things resolved, several times many hour outages. It&amp;rsquo;s a sad outcome, at the time I moved all I felt comfortable away from them fearing it was a start of a downward slide in quality. Apart from things I am reluctant to move Linode has essentially lost me as a customer.&lt;/p&gt;
&lt;p&gt;So I moved to Digital Ocean. It was fine, I have only really one pain with them - they do forced updates of the Kubernetes version (fine) but the way they do it always result in 2 full upgrades and node replacements. This means my outgoing IPs for things like Prometheus kept changing which was extremely annoying.&lt;/p&gt;
&lt;p&gt;The Kubernetes infra also ran a 3 node Choria Broker cluster, Prometheus+Alert Manager and Graphite. Alerts to Victorops that I pay for.&lt;/p&gt;
&lt;p&gt;Apart from that I had Linode machines for Puppet, Name Servers, Choria Package repos and some general use machines that I should have killed years ago.&lt;/p&gt;
&lt;p&gt;I guess the bills for this came to like $400 a month, helps to have a company to bill this to.&lt;/p&gt;
&lt;h2 id=&quot;office-equipment&quot;&gt;Office Equipment&lt;/h2&gt;
&lt;p&gt;My main office desktop is one of the last Intel iMacs with an old, like 2009 era, Apple Thunderbolt display. I&amp;rsquo;ve had iMacs since the very first one and they were one of my favourite form factors. It&amp;rsquo;s a bit sad about this one as it was a really nice machine but Apple is moving fast to stop supporting Intel so now there are no more OS updates.&lt;/p&gt;
&lt;p&gt;The screen being ridiculously old and Thunderbolt only - no HDMI - is now just useless.&lt;/p&gt;
&lt;p&gt;The current iMacs do look nice but the problem is there is no screen I can put next to them that don&amp;rsquo;t look like crap and I want either a big monitor or 2, it&amp;rsquo;s just not working anymore to use iMacs.&lt;/p&gt;
&lt;p&gt;I have an old tank Brother printer that is maybe 15 to 20 years old. It just refuses to die but I think its paper feed has now dried up so maybe time to go.&lt;/p&gt;
&lt;p&gt;Other than things actually stuck to my office I use a range of new Macbooks - currently a 16 inch M2 Pro with 32GB RAM.&lt;/p&gt;
&lt;p&gt;For file storage I use Dropbox of course but QNAP devices for larger storage. I have an old TS-439 that is now 14 years old and still getting security updates (!!!), this is at my office. At home I have a TS-451+. Each provide around 4TB of usable space.&lt;/p&gt;
&lt;p&gt;At home I also have a 34 inch ultra-wide display that I put my Laptop on when I sit at the desk here. I got this 7 years ago now so getting a bit old.&lt;/p&gt;
&lt;h2 id=&quot;networking-equipment&quot;&gt;Networking Equipment&lt;/h2&gt;
&lt;p&gt;I have 4 locations I work from often with bits of equipment scattered everywhere. Some time ago I had a mix of networking equipment but I&amp;rsquo;ve been standardising on Ubiquiti. I am still sad about Apple retiring their Wi-Fi range. Mikrotik was nice enough but I wanted something a bit more polished.&lt;/p&gt;
&lt;p&gt;In Malta I have 3 Dream Machines (the round tube one) and around 16 switches, APs, etc. My house is 450 year old with 1.5 meter thick walls so every room gets an AP. I like the single vendor system as all have a single management pane and generally things like VPNs etc. just work.&lt;/p&gt;
&lt;p&gt;In Latvia I have a Dream Machine Pro as I have a set of surveillance cameras there, I am considering updating to the Pro at my main house at least also as I&amp;rsquo;d like to add a camera outside.&lt;/p&gt;
&lt;p&gt;In Malta I used to be on Melita for everything, now on Go who have a really great new all fibre to the house network. I have one location left to move and then I&amp;rsquo;ll have 1Gb links everywhere. In Latvia a 4G link that&amp;rsquo;s actually quite surprisingly good (190 Mbps down 11 up).&lt;/p&gt;
&lt;p&gt;Firewalls tended to be a mix of the Ubiquiti devices and iptables.&lt;/p&gt;
&lt;h2 id=&quot;general-infra&quot;&gt;General Infra&lt;/h2&gt;
&lt;p&gt;My mail has been hosted at Fastmail for years and they are amazing. The web ui is a bit of a mess to be honest but at least it doesn&amp;rsquo;t keep changing. Their mail hosting features are rock solid though.&lt;/p&gt;
&lt;p&gt;I used GitHub for everything since they introduced unlimited Private repos. I do pay for GitHub though.&lt;/p&gt;
&lt;h2 id=&quot;backups&quot;&gt;Backups&lt;/h2&gt;
&lt;p&gt;I have a 30TB Hetzner machine that runs Bacula. It does daily backups of everything I have and does a full 3 month rotation of Full backups with 1 month of Incrementals onto a RAID-10 disk setup.&lt;/p&gt;
&lt;p&gt;The main QNAP syncs my Dropbox onto its disks daily.&lt;/p&gt;
&lt;p&gt;The main QNAP is set up as 2 x RAID-1 volumes. The main volume is synchronised daily to the office QNAP and I do monthly manual disk rot checks and sync between the RAID-1 volumes.  This gives me a 1 month old full backup of the entire QNAP at home and daily off-sites. Monthly I also sync the QNAP to the Hetzner machine.&lt;/p&gt;
&lt;p&gt;This means every file hits 9 drives in 3 locations over 2 countries with access to file versions going back 3 months. Ample time to recover from user error like deleting the wrong files and also a lot of redundancy built in.&lt;/p&gt;
&lt;h2 id=&quot;physical-office&quot;&gt;Physical Office&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve had an Office in a town called Mosta here for 4 years now, I have not used it much because parking around it was hell. On paper it was 5 minutes from School and I would stay there while the boy is at School - in practise it could take me 40 minutes to get parking and 12 to drive back home. And when I found parking it could be very far from the office - walking along pavements in Summer is no joke here.&lt;/p&gt;
&lt;p&gt;It just never worked, I ended up working from home most of the time. I should have bailed out of it years ago but just never got around to it.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So that about rounds it up, keep reading to hear what literally everything is being replaced with and what new things are being added to the mix to boot!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 2 covers &lt;a href=&quot;https://www.devco.net/posts/2024/03/21/lab-infra-rebuild-2/&quot;&gt;Kubernetes, Virtualization and Containers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3 covers &lt;a href=&quot;https://www.devco.net/posts/2024/04/07/lab-infra-rebuild-3/&quot;&gt;Server Management, Puppet and Choria&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 4 covers &lt;a href=&quot;https://www.devco.net/posts/2024/04/11/lab-infra-rebuild-4/&quot;&gt;My Office and Office Hardware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 5 covers &lt;a href=&quot;https://www.devco.net/posts/2024/04/25/lab-infa-rebuild-5/&quot;&gt;VMs, Baremetals and Operating Systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 6 wraps up the series with a look at &lt;a href=&quot;https://www.devco.net/posts/2024/07/31/lab-infra-rebuild-6/&quot;&gt;SaaS and other tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
  </content>
    <author>
      <name>R.I.Pienaar - www.devco.net</name>
      <uri>http://www.devco.net/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">binford2k.com: Recovering archived Puppet blog posts</title>
    <link href="https://binford2k.com/2024/02/27/recover-archived-puppet-blog-posts/"/>
    <id>https://binford2k.com/2024/02/27/recover-archived-puppet-blog-posts</id>
    <updated>2024-02-27T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;The Puppet blog has long been a treasure trove of content. You never knew what you might find; a product announcement, 
an industry analysis, a user interview, a technical post. And it never deleted content, so people got into the habit of
linking to blog posts to use as reference or documentation.&lt;/p&gt;

&lt;p&gt;This was really great in a lot of ways, but it came with its downsides. Outdated content didn’t always get updated
expediently and the amount of content just kept growing so there really wasn’t a good way to manage updates. Only the
content that was actively noticed and complained about was updated. So links across the web often pointed to old and
outdated content….&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔔 Unfortunately due to how Google indexing works, that really meant that the old, outdated, and often inaccurate content
surfaced at the top of search results way too darn often!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;During the acquisition, the new marketing team made the decision to declare bankruptcy and start over. They decided to
refocus the blog on mostly industry news and product updates and asked the Community and Engineering teams to republish
still-relevant content onto the &lt;a href=&quot;https://dev.to/puppet&quot;&gt;engineering blog&lt;/a&gt;. They kept some of the old content that was
performing well from an SEO standpoint and still relevant, but archived most of it.&lt;/p&gt;

&lt;p&gt;Understandably, this was dismaying for those of us using these posts for documentation! But don’t fret, there is a blog
archive located at https://prod-puppet-blog.netlify.app/blog/.&lt;/p&gt;

&lt;p&gt;I’ve created a shortcut a method for retrieving pages from the archive if you have the URL.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;First drag &lt;a href=&quot;javascript:(function(){let url=window.prompt(&#39;Enter the old Puppet blog URL&#39;,&#39;https://puppet.com/blog/…&#39;);window.location.href=url.replace(&#39;puppet.com&#39;,&#39;prod-puppet-blog.netlify.app&#39;);})()&quot;&gt;this link&lt;/a&gt; to your bookmarks folder and give it a reasonable name like “retrieve archived Puppet blog posts.”&lt;/li&gt;
  &lt;li&gt;Then when you see a link that leads to an archived post that you’d like to recover right click and copy it.&lt;/li&gt;
  &lt;li&gt;Click the bookmark and paste the URL into the dialog you see.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you click OK, it will take you to directly the archived content. Save the page for your own reference or
republish it as long as you respect copyright. &lt;em&gt;&lt;strong&gt;🚨 Please don’t link to the archive&lt;/strong&gt;&lt;/em&gt;, as there’s no guarantee how long it
will stay running.&lt;/p&gt;

&lt;p&gt;Happy excavating!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(image from https://www.worldhistory.org/image/1353/archaeology/)&lt;/em&gt;&lt;/p&gt;
  </content>
    <author>
      <name>binford2k.com</name>
      <uri>http://binford2k.com</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Meine erste Woche bei Betadots</title>
    <link href="https://dev.to/betadots/meine-erste-woche-bei-betadots-41h9"/>
    <id>https://dev.to/betadots/meine-erste-woche-bei-betadots-41h9</id>
    <updated>2024-02-19T08:54:41+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;2024-02-13&lt;/p&gt;

&lt;p&gt;Die erste Woche fing fulminant an mit einem Besuch beim &lt;strong&gt;Configuration Management Camp in Belgien&lt;/strong&gt; in Gent. Ich bin in CCC-Kreisen unterwegs und das CfgMgmtCamp ist den CCC-Veranstaltungen ziemlich ähnlich - haufenweise Leute in ungezwungener Kleidung, an jeder Ecke ein Gesprächspartner, der mehr zu deinem Thema weiß als du selbst. Die Vortragsauswahl war deutlich schwankender, zwei Vorträge mag ich hervorheben:&lt;/p&gt;

&lt;p&gt;Der erste Vortrag ist &lt;strong&gt;Adam Jacob&lt;/strong&gt;. Das ist der Erfinder von &lt;strong&gt;Chef&lt;/strong&gt;. Chef wurde vor zwei Jahren (+/-) an Progress verkauft und seitdem ist es still um die Software geworden. Eigentlich lohnen sich die 45min Einführung aus diesem Podcast&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://corecursive.com/configuring-identity-adam-jacob/&quot;&gt;https://corecursive.com/configuring-identity-adam-jacob/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ich mag den Podcast mit eigenen Worten zusammenfassen: Auch als Chef weißt du manchmal nicht, wo der Weg hinführt. Du musst mit deiner eigenen Unsicherheit klar kommen und an deine Angestellten kommunizieren.&lt;br&gt;
Wie dem auch sei, Adam hat die Firma verkauft. Danach endet der Podcast. Man hört ja gelegentlich gerüchte von Startupgründern, die sich eine Insel kaufen und sich zur Ruhe setzen, aber Adam hat einen Vortrag gehalten über ein neues Stück Software, an dem er arbeitet.&lt;br&gt;
Der Vortrag hatte es in sich -- und zwar nicht vom Inhalt, sondern vom Präsentationsstil. Der längste Verkaufsmonolog, den ich je gehört habe. Das fing an mit 20 Minuten Smalltalk, wie geil ChatGPT sei, dass wir (das Publikum) das geil finden, dass Adam das geil findet. Der obligatorische SAP-Witz, dass ChatGPT kein SAP installieren kann und dann die Pointe ins Publikum, ob jemand schon mal manuell durch diese Hölle gegangen sei. Und dann der lange Anlauf, er habe eine GUI geschaffen, die nach unten Terraform für Amazon-Web-Services (AWS) und Google-Cloud-Plattform (GCP) schaffen kann.&lt;br&gt;
Das war ein emotivierender Vortrag. Danach hast du das Gefühl &quot;cooler Typ. Los, packen wir es an!&quot;; Inhaltlich bin ich nicht zufrieden, aber auf die Präsentation bin ich neidisch. Wenn ich ein Unternehmen gründen würde, würde ich Adam als &lt;em&gt;Chief Evangelist&lt;/em&gt; einstellen. 🥲&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workshop&lt;/strong&gt; mit Rafael zum &lt;strong&gt;eBPF&lt;/strong&gt;. Bei eBPF muss ich erstmal ausholen wofür man das braucht -- ich hatte mal ein Problem, dass mir ein Prozess eine Logdatei weggelöscht hat. Immer und immer wieder. Ich fand aber den Prozess nicht in Cron und nicht in DBUS und nicht in ...; Wie löst man das Problem? Nun ja, du schreibst ein Kernelmodul &quot;Wenn jemand auf diese Pfadangabe ein Dateihandle erzeugt oder einen Syscall-unlink auslöst, dass logge mir den Prozessnamen und den Prozessowner!&quot;; Weil Kernelmodule kein Zuckerschlecken sind, gibt es dafür Helferchen - zum Beispiel SystemTap. Das ist eine Hilfestellung um Kernelmodule zu schreiben.&lt;br&gt;
Manchmal braucht es aber nicht das ganze Kernelmodul. Es geht einfacher - mit eBPF. Die Abkürzung stand mal für &lt;em&gt;extended-Berkeley-Packet-Filter&lt;/em&gt; und will seitdem nicht mehr ausgesprochen werden, weil das Projekt über seine Idee hinausgewachsen ist. Du schreibst ein kleines C-Programm und lädst das in den Kernel. Anschließend rufst du auf: Wann immer jemand den Syscall-Open ausführen will, führe mein Programm aus! Es gibt Syscalls, Tracepoints, Kernel-Probes und Netzwerkpakete. Es ist sehr einfach verglichen mit Kernelmodulen, aber unterschätze den Lernaufwand dennoch nicht!&lt;br&gt;
Der Vortrag begann mit Kubernetes-Architekturen und wie sich Kunden mit Netzwerkdebuggen und Failover und Netzwerk-vs-Georedundanz ins Knie geschossen haben. Schon allein die Probemhinführung hat sich gelohnt um den Vortrag zu besuchen! Dann ging es um die Packet-Filter und die Abstraktionsebene darüber, die Rafaels Firma verkauft um den Netzwerkstack durchdebuggen zu können. Anschließend gab es das Tutorial zum selber durcharbeiten mit Mentor im Raum. Das Tutorial kann ich übrigens empfehlen!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://isovalent.com/labs/getting-started-with-ebpf/&quot;&gt;https://isovalent.com/labs/getting-started-with-ebpf/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Abendprogramm&lt;/strong&gt;: Der erste Tag in einer &lt;strong&gt;Bar&lt;/strong&gt; nahe des Campus. Die Bar war deutlich überfüllt, dafür gab es wieder diese Unmenge &lt;strong&gt;schlauer Gesprächspartner&lt;/strong&gt;. Also haben wir über 5G-Standard gesprochen, über Google-freie Androiden, über Bier brauen.&lt;br&gt;
Der zweite Tag war ein Abendessen mit bekannten Persönlichkeiten in der Welt des Puppet. Die Community hat ein Gesicht und hier sitzt ein wichtiger Teil davon am gleichen Tisch. Abschließend in einer Flipper-Bar. Ich habe seit meiner Kindheit nicht mehr geflippert 😇&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stadt: Gent&lt;/strong&gt; lohnt allein schon wegen der &lt;strong&gt;Architektur&lt;/strong&gt;. Die beleuchteten Sakralbauwerke bei Nacht am Kanal sind beeindruckend.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1p9gniswcjmmvzpazdu.jpg&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc1p9gniswcjmmvzpazdu.jpg&quot; alt=&quot;Eine Kirche vor einem Kanal sowie eine Brücke, die Bildelemente beleuchtet durch Straßenlaternen bei Nacht mit Lichtreflektionen im Wasser&quot; width=&quot;800&quot; height=&quot;600&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Neuer Laptop&lt;/strong&gt;: Ich habe mir einen &lt;strong&gt;Framework 13&lt;/strong&gt; als Dienstgerät gewünscht.&lt;br&gt;
Die Do-It-Yourself-Edition kommt als Paket mit mehreren Kleinpaketen. Gehäuse, Mainboard, Deckel und Display sind eine Einheit. Tastatur-und-Unterhälfte-Oberseite kommen als Einheit, RAM, NVMe, Bezel und eine Kiste mit Erweiterungskarten. Der Zusammenbau war leicht - ausgenommen die Festplattenschraube. Die war zu fest angezogen und wir konnten sie im ersten Anlauf nicht lösen. Wir mussten zum Elektronikladen laufen und das kleine iFix-it-Schraubendreherset kaufen. Beim RAM hat es zwei Steckversuche gebraucht, bis der Laptop ihn akzeptiert hat.&lt;br&gt;
Das Gerät ist leicht und schick. Das Bios ist beeindruckend schlank, so etwas habe ich noch nicht gesehen. Software und Setup sind nochmal einen &lt;strong&gt;eigenen Artikel&lt;/strong&gt; wert, da habe ich über Jahre entwickelte Vorstellungen, wie ich mein Arbeitsgerät mag. &lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlix30amtzjyf7z8pux2.jpg&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlix30amtzjyf7z8pux2.jpg&quot; alt=&quot;Laptopkompontenten ausgebreitet auf einem Hotel-Tisch&quot; width=&quot;800&quot; height=&quot;1066&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nach dem geglückten Start freue ich mich auf die nächsten Tage. Wenn es eure Nerdseele befriedigt, was ich mit der Laptopsoftware treibe, wäre das nochmal ein eigener Artikel. Bis zum nächsten Mal!&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Scaling Puppet Infrastructure</title>
    <link href="https://dev.to/betadots/scaling-puppet-infrastructure-3p2o"/>
    <id>https://dev.to/betadots/scaling-puppet-infrastructure-3p2o</id>
    <updated>2024-02-05T09:55:29+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;In large environments with many nodes one must take care of Puppet server scaling.&lt;br&gt;
When and if scaling is needed, depends on the number of nodes and on code complexity and size.&lt;br&gt;
This article describes the different ways of tuning Puppet server infrastructure.&lt;/p&gt;

&lt;p&gt;Table of content:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
Puppet Server tuning

&lt;ol&gt;
&lt;li&gt;Performance tuning a single node&lt;/li&gt;
&lt;li&gt;Scaling horizontally&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;PuppetDB tuning&lt;/li&gt;
&lt;li&gt;Analyzing performance win&lt;/li&gt;
&lt;li&gt;Distributed Puppet agent runs&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Scaling the hard way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scaling beyond JRuby limits&lt;/li&gt;
&lt;li&gt;Multiple Puppet Server instances&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Puppet Server tuning
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Performance tuning single node
&lt;/h3&gt;

&lt;p&gt;We sometimes see the usage of horizontal scaling, even when there is no need to do it.&lt;br&gt;
As scaling horizontally needs further infrastructure components like Load Balancer and Puppet Server Compilers, we usually first ask to do performance tuning on the single node instance.&lt;/p&gt;

&lt;p&gt;A properly configured and optimized single Puppet server should be able to handle &lt;a href=&quot;https://www.puppet.com/docs/pe/2023.5/hardware_requirements#hardware_requirements_standard&quot;&gt;up to 2500 nodes&lt;/a&gt; - using default runinterval of 30 minutes.&lt;/p&gt;

&lt;p&gt;There are three different possibilities for tuning, which sometimes rely upon each other:&lt;/p&gt;
&lt;h4&gt;
  
  
  Java Version
&lt;/h4&gt;

&lt;p&gt;Puppet server package has a dependency on java openjdk (headless).&lt;br&gt;
On most Linux distributions this will install Java 1.8.&lt;/p&gt;

&lt;p&gt;Please ensure to upgrade to Java 17 and set the java alternative accordingly:&lt;/p&gt;

&lt;p&gt;e.g (on Almalinux 8)&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;alternatives &lt;span class=&quot;nt&quot;&gt;--set&lt;/span&gt; java /usr/lib/jvm/java-17-openjdk-17.0.9.0.9-2.el8.x86_64/bin/java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Number of JRuby instances
&lt;/h4&gt;

&lt;p&gt;With its default configuration a Puppet Server spins up 2 JRuby instances.&lt;br&gt;
Each instance is able to handle a single Puppet Agent request.&lt;br&gt;
When there are more requests, these get queued.&lt;/p&gt;

&lt;p&gt;Each JRuby instance needs 512 MB of Java Heap RAM.&lt;br&gt;
Just to be sure: it is not possible to run more than 32 JRuby instances on a single node!&lt;/p&gt;

&lt;p&gt;Adopting the number of JRuby instances takes place in &lt;code&gt;/etc/puppetlabs/puppetserver/conf.d/puppetserver.conf&lt;/code&gt; within the &lt;code&gt;jruby-puppet&lt;/code&gt; section by setting the &lt;code&gt;max-active-instances&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;jruby-puppet: {
    ...
    max-active-instances: 8   # &amp;lt;------- JRuby instances
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that increasing the number of JRuby instances causes the Java process to need more Java HEAP RAM.&lt;/p&gt;

&lt;h4&gt;
  
  
  Puppet Server Java Heap Size
&lt;/h4&gt;

&lt;p&gt;The Java engine should have enough RAM to be able to spin up and maintain the JRuby instances.&lt;br&gt;
If you forget to increase Java Heap size, you will see Java out-of-memory error messages in the Puppet server logfile.&lt;/p&gt;

&lt;p&gt;Increasing Java Heapsize takes place in &lt;code&gt;/etc/default/puppetserver&lt;/code&gt; (Debian based) or &lt;code&gt;/etc/sysconfig/puppetserver&lt;/code&gt; (RedHat based).&lt;/p&gt;

&lt;p&gt;The Java Heap size is provided as a Java argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# /etc/[sysconfig|default]/puppetserver
JAVA_ARGS=&quot;-Xms18g -Xmx18g -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we have set the Java Heap size (upper and lower limit) to 18 GB RAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# Lower limit
-Xms18g
# Upper limit
-Xmx18g
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is considered best practice to set upper and lower limit to the same value, so Java reserves the RAM upon start up.&lt;/p&gt;

&lt;p&gt;Please note that the maximum possible value is limited by the amount of the system RAM.&lt;br&gt;
If you increase the Java heap size beyond system RAM, you will find Kernel out-of-memory errors in the journal.&lt;/p&gt;

&lt;p&gt;Besides this one must be aware that Java switches memory handling when using more than 32 GB of heap, which reduce the amount of objects. Also see the &lt;a href=&quot;https://www.codecentric.de/wissens-hub/blog/35gb-heap-less-32gb-java-jvm-memory-oddities&quot;&gt;blog post from Codecentric&lt;/a&gt; or &lt;a href=&quot;https://confluence.atlassian.com/jirakb/do-not-use-heap-sizes-between-32-gb-and-47-gb-in-jira-compressed-oops-1167745277.html#:~:text=Java%20disables%20Compressed%20Oops%20for,can%20store%20in%20the%20heap.&quot;&gt;Atlassian&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Reserved Code Cache
&lt;/h4&gt;

&lt;p&gt;When Puppet servers receive a request from a node the Puppet Code is loaded into code cache in memory.&lt;br&gt;
The default value is set to 512 MB RAM.&lt;/p&gt;

&lt;p&gt;A larger Puppet code base or complex code might need a larger Code cache setting.&lt;br&gt;
Code cache is configured as Java argument in &lt;code&gt;/etc/[sysconfig|default]/puppetserver&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# /etc/[sysconfig|default]/puppetserver
JAVA_ARGS=&quot;-Xms18g -Xmx18g -XX:ReservedCodeCacheSize=1024m -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we have set the code cache size to 1024 MB RAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;-XX:ReservedCodeCacheSize=1024m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that the maximum possible value is at &lt;code&gt;2048m&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Please ensure that the Puppet server process was restarted after setting all the required configuration options.&lt;/p&gt;

&lt;p&gt;A short mathematical sizing rule of thumb:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;Java Heap size (M) = ( No of JRuby instances * 512M ) + 512M
Reserved Code Cache Size (M) = No of JRuby instances * 128M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Scaling horizontally
&lt;/h2&gt;

&lt;p&gt;If a single Puppet server is not able to handle the amount of requests - e.g. in large infrastructures exceeding 2000 nodes - one has the option to set up additional compilers.&lt;/p&gt;

&lt;p&gt;Please note that each Puppet server infrastructure component should receive the performance tuning settings!&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure setup
&lt;/h3&gt;

&lt;p&gt;A high performance Puppet infrastructure consists of the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CA Server&lt;/li&gt;
&lt;li&gt;Compiler(s)&lt;/li&gt;
&lt;li&gt;Load balancer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Puppet agent sends the request to the Load balancer.&lt;br&gt;
The load balancer passes the request to a free compiler.&lt;/p&gt;

&lt;p&gt;From a Puppet agent point of view, the request is sent to the Load balancer and the response is received from the compiler.&lt;br&gt;
This has a special meaning when it comes to SSL certificates and strict SSL validation.&lt;/p&gt;

&lt;p&gt;We will discuss this when we come to the compiler setup.&lt;/p&gt;
&lt;h3&gt;
  
  
  Puppet CA Server
&lt;/h3&gt;

&lt;p&gt;The Puppet CA server is a standalone single instance, which is used to spin up and maintain additional Puppet compilers.&lt;br&gt;
From CA server point of view a compiler is just a node.&lt;/p&gt;

&lt;p&gt;All new CSR&#39;s must be signed on the CA server.&lt;br&gt;
When you want to scale your Puppet infrastructure the CA Server will be your existing Puppet server.&lt;/p&gt;

&lt;p&gt;When we want to sign the compilers certificates including dns_alt_names, we must configure the CA instance, to be able to do this by modifying the &lt;code&gt;/etc/puppetlabs/puppetserver/conf.d/ca.conf&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;We must allow subject alt names setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# /etc/puppetlabs/puppetserver/conf.d/ca.conf
certificate-authority: {
    # allow CA to sign certificate requests that have subject alternative names.
    allow-subject-alt-names: true  # &amp;lt;----- enable SAN cert signing

    # allow CA to sign certificate requests that have authorization extensions.
    # allow-authorization-extensions: false

    # enable the separate CRL for Puppet infrastructure nodes
    # enable-infra-crl: false
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please ensure that the Puppet server process was restarted after doing all the required changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding compilers
&lt;/h3&gt;

&lt;p&gt;Compilers should &lt;strong&gt;not&lt;/strong&gt; act as CA Server!&lt;br&gt;
The CA functionality is managed in &lt;code&gt;/etc/puppetlabs/puppetserver/services.d/ca.cfg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now we need to change the settings so that a compiler does not act as CA server, but pass all CA related requests to the CA server:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# To enable the CA service, leave the following line uncommented
# puppetlabs.services.ca.certificate-authority-service/certificate-authority-service
# To disable the CA service, comment out the above line and uncomment the line below
puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service
puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please not that all compilers (and the CA server) should receive the Puppet code. In most environments we see that the compilers and the CA server have a NFS mount on which the code is deployed on the CA server and used on all compilers.&lt;/p&gt;

&lt;p&gt;The NFS share is beyond scope of this document.&lt;/p&gt;

&lt;p&gt;The Puppet agent will not connect to a compiler but to the load balancer.&lt;/p&gt;

&lt;p&gt;If we keep the default setup, the Puppet agent will refuse to connect to the load-balancer, if DNS ALT Names are missing in the compilers certificate.&lt;/p&gt;

&lt;p&gt;The compiler &lt;strong&gt;MUST&lt;/strong&gt; have the Load balancer DNS name configured, prior generating the CSR.&lt;/p&gt;

&lt;p&gt;This can be achieved by adding the &lt;code&gt;dns_alt_names&lt;/code&gt; configuration setting into &lt;code&gt;7etc/puppetlabs/puppet/puppet.conf&lt;/code&gt; into the &lt;code&gt;agent&lt;/code&gt; section.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;[agent]
dns_alt_names = loadbalancer.domain.tld,compiler-fqdn.domain.tld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding load balancer
&lt;/h3&gt;

&lt;p&gt;Each request to a Puppet Server starts a compilation process with different runtimes. One should &lt;strong&gt;not&lt;/strong&gt; configure the roundrobin distribution algorithm on the load balancer.&lt;/p&gt;

&lt;p&gt;Instead we want to distribute the connections to the Puppet compiler with the least work to do - which means the one with the least connections. In HAproxy this setting is called &lt;code&gt;leastconn&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Besides this you do not want to rely on Layer 3 IP connection, but on Layer 7 functionality. Within HAproxy one should add the &lt;code&gt;SSL&lt;/code&gt; directive to each backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  PuppetDB tuning
&lt;/h2&gt;

&lt;p&gt;In most environments PuppetDB is used to store facts, reports, catalogs and exported resources.&lt;br&gt;
The more nodes you have in your infrastructure, the higher the load and the memory requirement on the PuppetDB process.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tuning PuppetDB
&lt;/h3&gt;

&lt;p&gt;PuppetDB is a HTTP(S) Rest API in front of a PostgreSQL database.&lt;br&gt;
One can say that PuppetDB also is a kind of web service.&lt;/p&gt;
&lt;h4&gt;
  
  
  Java Heap size
&lt;/h4&gt;

&lt;p&gt;The most important setting is Java heap size.&lt;br&gt;
Per default a PuppetDB is configured to use 512MB Heap size.&lt;/p&gt;

&lt;p&gt;Configuration takes place in &lt;code&gt;/etc/sysconfig/puppetdb&lt;/code&gt; (RedHat/SLES) or &lt;code&gt;7etc/default/puppetdb&lt;/code&gt; (Debian).&lt;/p&gt;

&lt;p&gt;The Java Heap size is provided as a Java argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# /etc/[sysconfig|default]/puppetdb
JAVA_ARGS=&quot;-Xms1g -Xmx1g ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we have set the Java Heap size (upper and lower limit) to 1 GB RAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# Lower limit
-Xms1g
# Upper limit
-Xmx1g
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is considered best practice to set upper and lower limit to the same value, so Java reserves the RAM upon start up.&lt;/p&gt;

&lt;p&gt;We usually receive good results with 1GB or 2GB heap site (depending on the number of nodes)&lt;/p&gt;

&lt;h4&gt;
  
  
  Database connection pool
&lt;/h4&gt;

&lt;p&gt;Within the PuppetDB configuration file, which is located at &lt;code&gt;/etc/puppetlabs/puppetdb/conf.d/database.conf&lt;/code&gt;, one can set the maximum numbers of idle and active PostgreSQL connections within the &lt;code&gt;database&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;maximum-pool-size = 25 # default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that PuppetDB uses two connection pools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;read pool&lt;/li&gt;
&lt;li&gt;write pool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The read pool can be set individually by setting &lt;code&gt;maximum-pool-size&lt;/code&gt; in the &lt;code&gt;read-database&lt;/code&gt; section. The default value is &lt;code&gt;10&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;maximum-pool-size&lt;/code&gt; should be set to the sum of both.&lt;/p&gt;

&lt;p&gt;Please check your PostgreSQL configuration (especially the max connection setting) prior increasing the connection pool.&lt;/p&gt;

&lt;h4&gt;
  
  
  Block facts
&lt;/h4&gt;

&lt;p&gt;Another possibility is to lower the number of facts stored.&lt;/p&gt;

&lt;p&gt;This can be achieved by setting &lt;code&gt;facts-blocklist&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;facts-blocklist = [&quot;fact1&quot;, &quot;fact2&quot;, &quot;fact3&quot;]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Command processing threads
&lt;/h4&gt;

&lt;p&gt;Besides this PuppetDB lets you configure the number of threads by setting &lt;code&gt;threads&lt;/code&gt; within the &lt;code&gt;command-processing&lt;/code&gt; section in &lt;code&gt;/etc/puppetlabs/puppetdb/conf.d/config.ini&lt;/code&gt;.&lt;br&gt;
As default PuppetDB will use half the number of cores of the system.&lt;/p&gt;
&lt;h4&gt;
  
  
  Web server threads
&lt;/h4&gt;

&lt;p&gt;Last but least one can set the &lt;code&gt;max-threads&lt;/code&gt; in &lt;code&gt;/etc/puppetlabs/puppetdb/conf.d/jetty.ini&lt;/code&gt;.&lt;br&gt;
This specifies the number if parallel possible HTTP and HTTPS connections.&lt;/p&gt;
&lt;h3&gt;
  
  
  Scaling PuppetDB
&lt;/h3&gt;

&lt;p&gt;In former times, it was best practice to have a single PuppetDB instance on the Puppet Server.&lt;br&gt;
If you have the need for scaling Puppet Servers, one should consider having PuppetDB on each Puppet infrastructure node, which connect to a centralized PostgreSQL database.&lt;/p&gt;
&lt;h2&gt;
  
  
  Analyzing performance win
&lt;/h2&gt;

&lt;p&gt;Every time one does performance tuning, one also wants to get proof that the new setting has improved performance.&lt;br&gt;
The most simple check is by getting the compile times from puppetserver logfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/Compiled catalog/ {print $12&quot; &quot;$14}&#39;&lt;/span&gt; /var/log/puppetlabs/puppetserver/puppetserver.log

production 0.07
production 0.05
production 0.05
production 0.04
production 0.04
production 0.03
production 0.03
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also get highest and lowest compile time&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/Compiled catalog/ {print $12&quot; &quot;$14}&#39;&lt;/span&gt; /var/log/puppetlabs/puppetserver/puppetserver.log | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;NR==1; END{print}&#39;&lt;/span&gt;

production 0.03
production 0.07
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need an average compile time the following command will be helpful&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;/Compiled catalog/ {print $12&quot; &quot;$14}&#39;&lt;/span&gt; /var/log/puppetlabs/puppetserver/puppetserver.log | &lt;span class=&quot;nb&quot;&gt;sort&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;{total += $2; count++ } END { print total/count }&#39;&lt;/span&gt;

0.0442857
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another option is provided by the &lt;a href=&quot;https://forge.puppet.com/modules/puppetlabs/puppet_operational_dashboards/readme&quot;&gt;Puppet Operational Dashboards&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This will setup a Grafana frontend which reads data from an InfluxDB, which gets its content from a Telegraf agent which connects via HTTPS to the Puppet server metrics endpoints.&lt;/p&gt;

&lt;p&gt;In Puppet Enterprise the PostgreSQL database also uses SSL certificates, which also allows fetching PostgreSQL metrics.&lt;br&gt;
When running Puppet Open Source, one needs to add an additional telegraf configuration to query the PostgreSQL database using a user and password and push the data into InfluxDB.&lt;/p&gt;
&lt;h2&gt;
  
  
  Distributed Puppet agent runs
&lt;/h2&gt;

&lt;p&gt;Within the introduction we mentioned that a single server can handle up to 2500 nodes.&lt;br&gt;
This number of nodes requires that Puppet agent runs must be evenly distributed over time.&lt;/p&gt;

&lt;p&gt;The default way for a Puppet agent is running as a daemon, which will schedule a Puppet agent run upon start and triggers the next runs every 30 minutes (&lt;code&gt;runinterval&lt;/code&gt; config option).&lt;/p&gt;

&lt;p&gt;Due to the reason that systems might be starting in parallel one will still see overloaded Puppet server infrastructure.&lt;br&gt;
As a result one will recognize that the Puppet agent run takes very long time. If the Agent did not receive a reply from Puppet server within 5 minutes (&lt;code&gt;http_read_timeout&lt;/code&gt; config option), it will stop the actual request and repeat again in 30 minutes.&lt;/p&gt;

&lt;p&gt;The config options can be modified in &lt;code&gt;puppet.conf&lt;/code&gt; config file in section &lt;code&gt;agent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Please reconsider if Puppet should run every 30 minutes.&lt;br&gt;
In some infrastructure Puppet is running in &lt;code&gt;noop&lt;/code&gt; mode as changes may only be deployed during maintenance window.&lt;br&gt;
In this case one still wants the node to regular check its configuration, but maybe 4 times a day only, so people can identify within the reporting that the systems are still in desired state.&lt;br&gt;
Doubling the &lt;code&gt;runinterval&lt;/code&gt; option reduces the load on the Puppet server to its half, so the Puppet server can handle double number of nodes.&lt;/p&gt;

&lt;p&gt;But one still will see huge and many load spikes on the Puppet server.&lt;br&gt;
It still can happen that to many nodes try to request for their configuration in parallel.&lt;/p&gt;

&lt;p&gt;We usually recommend to customers to run the agent via cron.&lt;br&gt;
One run will be done at reboot and the other runs will take place as recurring cron entries.&lt;br&gt;
To distribute the agent runs over time one can make use of the &lt;code&gt;fqdnrand&lt;/code&gt; function inside Puppet. This function generates a unique number for a hostame or FQDN.&lt;/p&gt;

&lt;p&gt;One can use the &lt;a href=&quot;https://forge.puppet.com/modules/reidmv/puppet_run_scheduler/readme&quot;&gt;reidmv puppet_run_scheduler module&lt;/a&gt; to configure Puppet accordingly.&lt;/p&gt;

&lt;p&gt;Please note that within Puppet Enterprise environments the Puppet agent on the Puppet servers &lt;strong&gt;must&lt;/strong&gt; be running as agent daemon service!&lt;/p&gt;
&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Tuning and scaling a Puppet server is very simple, as it is a single Java process and a web service only.&lt;/p&gt;

&lt;p&gt;Tuning is mostly related to the Java process and memory handling and is usually limited by the amount of RAM and numbers of CPUs being available.&lt;/p&gt;

&lt;p&gt;Scaling can be achieved by using simple, already established solutions to distribute web traffic amongst several servers.&lt;/p&gt;

&lt;p&gt;Distributing the Puppet agent runs ensures evenly used Puppet servers which allows more nodes to get managed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Scaling beyond JRuby limits
&lt;/h2&gt;

&lt;p&gt;We already mentioned that you can not spin up more than 32 JRuby instances.&lt;/p&gt;

&lt;p&gt;We tried it. Be sure. It is not working at all.&lt;/p&gt;

&lt;p&gt;But what to do if you have high end (e.g. HPC) hardware?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;72 cores&lt;/li&gt;
&lt;li&gt;248 GB RAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Usually you want to slice the big hardware e.g using KVM or Docker and run multiple VMs or &lt;a href=&quot;https://dev.to/betadots/puppet-containers-are-back-alive-5c2n&quot;&gt;Puppet server containers&lt;/a&gt; for example by using &lt;a href=&quot;https://github.com/voxpupuli/crafty&quot;&gt;Voxpupuli CRAFTY&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Normally we see such a hardware only within virtualization or container based environments or within a HPC platform.&lt;/p&gt;

&lt;p&gt;In our specific use case the usage of KVM, Docker, Podman or Kubernetes was not possible. Don&#39;t ask.&lt;/p&gt;

&lt;p&gt;We talked about this in the &lt;a href=&quot;https://puppetcommunity.slack.com/archives/C0W298S9G/p1697392478214749&quot;&gt;Puppet community Slack channel&lt;/a&gt; and people told us that the only option is to spin up multiple Puppet server instances by &quot;some specific symlinking and copying of config files&quot;.&lt;/p&gt;

&lt;p&gt;Just to be sure: this is not something you can configure out-of-the-box, but needs lots of changes also to Puppet internal scripts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Multiple Puppet Server instances
&lt;/h2&gt;

&lt;p&gt;You want it the hard way. Here we go..&lt;/p&gt;

&lt;p&gt;Please note that this is just first findings, not automated and of course unsupported!&lt;/p&gt;

&lt;p&gt;Requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;big hardware&lt;/li&gt;
&lt;li&gt;multiple IP addresses on the node

&lt;ul&gt;
&lt;li&gt;one for the main Puppet CA server&lt;/li&gt;
&lt;li&gt;one for the HAproxy loadbalancer&lt;/li&gt;
&lt;li&gt;and one for each compiler&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DNS configured for each IP&lt;/li&gt;
&lt;li&gt;Puppet CA server configured and running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Assumptions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the main installation is used for the Puppet CA server&lt;/li&gt;
&lt;li&gt;compilers will use copy of this installation&lt;/li&gt;
&lt;li&gt;main CA server FQDN: puppetserver.domain.tld&lt;/li&gt;
&lt;li&gt;compiler FQDN: puppetcompiler1.domain.tld&lt;/li&gt;
&lt;li&gt;load balancer FQDN: puppetbalancer.domain.tld&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First we need to stop the running Puppet Server, then we can copy several directories and make modifications to configuration files and scripts.&lt;br&gt;
Afterwards we can start the Puppet server processes and then we can install and configure HAproxy.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;systemctl stop puppetserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Files and directories needed
&lt;/h3&gt;

&lt;p&gt;Each Puppet server instance needs several files and directories copied from the main installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Service config&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-pr&lt;/span&gt; /etc/sysconfig/puppetserver /etc/sysconfig/puppetservercompiler1
&lt;span class=&quot;c&quot;&gt;# Puppet agent config&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-pr&lt;/span&gt; /etc/puppetlabs/puppet /etc/puppetlabs/puppetcompiler1
&lt;span class=&quot;c&quot;&gt;# Puppet server config&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-pr&lt;/span&gt; /etc/puppetlabs/puppetserver /etc/puppetlabs/puppetservercompiler1
&lt;span class=&quot;c&quot;&gt;# Puppet server application&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-pr&lt;/span&gt; /opt/puppetlabs/server /opt/puppetlabs/servercompiler1
&lt;span class=&quot;c&quot;&gt;# Puppet server logging&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-pr&lt;/span&gt; /var/log/puppetlabs/puppetserver /var/log/puppetlabs/puppetservercompiler1
&lt;span class=&quot;c&quot;&gt;# Puppet server service&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-pr&lt;/span&gt; /usr/lib/systemd/system/puppetserver.service /usr/lib/systemd/system/puppetservercompiler1.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Config files
&lt;/h3&gt;

&lt;h4&gt;
  
  
  /etc/puppetlabs/puppet/puppet.conf
&lt;/h4&gt;

&lt;p&gt;The main Puppet configuration from the main Puppet CA Server should only receive an entry for the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;puppet config &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--section&lt;/span&gt; main server puppetserver.domain.tld
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/sysconfig/puppetservercompiler1
&lt;/h4&gt;

&lt;p&gt;Change the paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;...
INSTALL_DIR=&quot;/opt/puppetlabs/servercompiler1/apps/puppetserver&quot;
CONFIG=&quot;/etc/puppetlabs/puppetservercompiler1/conf.d&quot;
# Bootstrap path
BOOTSTRAP_CONFIG=&quot;/etc/puppetlabs/puppetservercompiler1/services.d/,/opt/puppetlabs/servercompiler1/apps/puppetserver/config/services.d/&quot;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/services.d/ca.cfg
&lt;/h4&gt;

&lt;p&gt;Disable CA&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;# To enable the CA service, leave the following line uncommented
#puppetlabs.services.ca.certificate-authority-service/certificate-authority-service
# To disable the CA service, comment out the above line and uncomment the line below
puppetlabs.services.ca.certificate-authority-disabled-service/certificate-authority-disabled-service
puppetlabs.trapperkeeper.services.watcher.filesystem-watch-service/filesystem-watch-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/conf.d/global.conf
&lt;/h4&gt;

&lt;p&gt;Adopt path&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;global: {
    # Path to logback logging configuration file; for more
    # info, see http://logback.qos.ch/manual/configuration.html
    logging-config: /etc/puppetlabs/puppetservercompiler1/logback.xml
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/conf.d/metrics.conf
&lt;/h4&gt;

&lt;p&gt;Adopt the server id&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;metrics: {
    # a server id that will be used as part of the namespace for metrics produced
    # by this server
    server-id: puppetservercompiler1
    registries: {
        ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/conf.d/puppetserver.conf
&lt;/h4&gt;

&lt;p&gt;Adopt paths&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;jruby-puppet: {
    ruby-load-path: [/opt/puppetlabs/puppet/lib/ruby/vendor_ruby]

    # Adopt path
    gem-home: /opt/puppetlabs/servercompiler1/data/puppetserver/jruby-gems

    # Adopt paths
    gem-path: [${jruby-puppet.gem-home}, &quot;/opt/puppetlabs/servercompiler1/data/puppetserver/vendored-jruby-gems&quot;, &quot;/opt/puppetlabs/puppet/lib/ruby/vendor_gems&quot;]

    # Adopt path
    server-conf-dir: /etc/puppetlabs/puppetcompiler1

    server-code-dir: /etc/puppetlabs/code

    # Adopt path
    server-var-dir: /opt/puppetlabs/servercompiler1/data/puppetserver

    # Adopt path
    server-run-dir: /var/run/puppetlabs/puppetservercompiler1

    # Adopt path
    server-log-dir: /var/log/puppetlabs/puppetservercompiler1

    ...
}
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/conf.d/webserver.conf
&lt;/h4&gt;

&lt;p&gt;Adopt Path and IP and/or Port&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;webserver: {
    access-log-config: /etc/puppetlabs/puppetservercompiler1/request-logging.xml
    client-auth: want
    ssl-host: 10.110.10.102 # &amp;lt;---- add compiler1 IP and/or
    ssl-port: 8141          # &amp;lt;---- use another port
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /etc/puppetlabs/puppetservercompiler1/logback.xml
&lt;/h4&gt;

&lt;p&gt;Adopt paths&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight xml&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;scan=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;scanPeriod=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;60 seconds&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;STDOUT&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ch.qos.logback.core.ConsoleAppender&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;encoder&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSSXXX} %-5p [%t] [%c{2}] %m%n&lt;span class=&quot;nt&quot;&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/encoder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;F1&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ch.qos.logback.core.rolling.RollingFileAppender&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- TODO: this path should not be hard-coded --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;file&amp;gt;&lt;/span&gt;/var/log/puppetlabs/puppetservercompiler1/puppetserver.log&lt;span class=&quot;nt&quot;&gt;&amp;lt;/file&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;append&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/append&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;rollingPolicy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- rollover daily --&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;fileNamePattern&amp;gt;&lt;/span&gt;/var/log/puppetlabs/puppetservercompiler1/puppetserver-%d{yyyy-MM-dd}.%i.log.gz&lt;span class=&quot;nt&quot;&gt;&amp;lt;/fileNamePattern&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- each file should be at most 200MB, keep 90 days worth of history, but at most 1GB total--&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;maxFileSize&amp;gt;&lt;/span&gt;200MB&lt;span class=&quot;nt&quot;&gt;&amp;lt;/maxFileSize&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;maxHistory&amp;gt;&lt;/span&gt;90&lt;span class=&quot;nt&quot;&gt;&amp;lt;/maxHistory&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;totalSizeCap&amp;gt;&lt;/span&gt;1GB&lt;span class=&quot;nt&quot;&gt;&amp;lt;/totalSizeCap&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/rollingPolicy&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;encoder&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%d{yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSSXXX} %-5p [%t] [%c{2}] %m%n&lt;span class=&quot;nt&quot;&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/encoder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;STATUS&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ch.qos.logback.core.rolling.RollingFileAppender&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;file&amp;gt;&lt;/span&gt;/var/log/puppetlabs/puppetservercompiler1/puppetserver-status.log&lt;span class=&quot;nt&quot;&gt;&amp;lt;/file&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;append&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/append&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;rollingPolicy&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- rollover daily --&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;fileNamePattern&amp;gt;&lt;/span&gt;/var/log/puppetlabs/puppetservercompiler1/puppetserver-status-%d{yyyy-MM-dd}.%i.log.gz&lt;span class=&quot;nt&quot;&gt;&amp;lt;/fileNamePattern&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- each file should be at most 200MB, keep 90 days worth of history, but at most 1GB total--&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;maxFileSize&amp;gt;&lt;/span&gt;200MB&lt;span class=&quot;nt&quot;&gt;&amp;lt;/maxFileSize&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;maxHistory&amp;gt;&lt;/span&gt;90&lt;span class=&quot;nt&quot;&gt;&amp;lt;/maxHistory&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;totalSizeCap&amp;gt;&lt;/span&gt;1GB&lt;span class=&quot;nt&quot;&gt;&amp;lt;/totalSizeCap&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/rollingPolicy&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;encoder&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- note that this will only log the JSON message (%m) and a newline (%n)--&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;pattern&amp;gt;&lt;/span&gt;%m%n&lt;span class=&quot;nt&quot;&gt;&amp;lt;/pattern&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/encoder&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/appender&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- without additivity=&quot;false&quot;, the status log messages will be sent to every other appender as well--&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;logger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;puppetlabs.trapperkeeper.services.status.status-debug-logging&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;debug&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;additivity=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;STATUS&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/logger&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;logger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.eclipse.jetty&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;INFO&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;logger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;org.apache.http&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;INFO&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;logger&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;jruby&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;info&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;root&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;level=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;info&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!--&amp;lt;appender-ref ref=&quot;STDOUT&quot;/&amp;gt;--&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- ${logappender} logs to console when running the foreground command --&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;${logappender}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;appender-ref&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ref=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;F1&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /usr/lib/systemd/system/puppetservercompiler1.service
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;#
# Local settings can be configured without being overwritten by package upgrades, for example
# if you want to increase puppetserver open-files-limit to 10000,
# you need to increase systemd&#39;s LimitNOFILE setting, so create a file named
# &quot;/etc/systemd/system/puppetservercompiler1.service.d/limits.conf&quot; containing:
#   [Service]
#   LimitNOFILE=10000
# You can confirm it worked by running systemctl daemon-reload
# then running systemctl show puppetserver | grep LimitNOFILE
#
[Unit]
Description=puppetserver compiler1 Service
After=syslog.target network.target nss-lookup.target

[Service]
Type=forking
EnvironmentFile=/etc/sysconfig/puppetservercompiler1
User=puppet
TimeoutStartSec=300
TimeoutStopSec=60
Restart=on-failure
StartLimitBurst=5
PIDFile=/run/puppetlabs/puppetservercompiler1/puppetserver.pid

# https://tickets.puppetlabs.com/browse/EZ-129
# Prior to systemd v228, TasksMax was unset by default, and unlimited. Starting in 228 a default of &#39;512&#39;
# was implemented. This is low enough to cause problems for certain applications. In systemd 231, the
# default was changed to be 15% of the default kernel limit. This explicitly sets TasksMax to 4915,
# which should match the default in systemd 231 and later.
# See https://github.com/systemd/systemd/issues/3211#issuecomment-233676333
TasksMax=4915

#set default privileges to -rw-r-----
UMask=027


ExecReload=/opt/puppetlabs/servercompiler1/apps/puppetserver/bin/puppetserver reload
ExecStart=/opt/puppetlabs/servercompiler1/apps/puppetserver/bin/puppetserver start
ExecStop=/opt/puppetlabs/servercompiler1/apps/puppetserver/bin/puppetserver stop

KillMode=process

SuccessExitStatus=143

StandardOutput=syslog

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scripts to adopt
&lt;/h3&gt;

&lt;p&gt;Unluckily several scripts must be modified, too.&lt;/p&gt;

&lt;p&gt;Mostly due to hardcoded paths.&lt;/p&gt;

&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/bin/puppetserver
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#set default privileges to -rw-r-----&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;umask &lt;/span&gt;027

&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/default/puppetservercompiler1&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /etc/default/puppetservercompiler1
&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/etc/sysconfig/puppetservercompiler1&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; /etc/sysconfig/puppetservercompiler1
&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;uname&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;OpenBSD&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;JAVA_BIN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;javaPathHelper &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; puppetserver&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;JAVA_ARGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-Xms2g -Xmx2g -Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger&quot;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;TK_ARGS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_puppet&quot;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;INSTALL_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/puppetlabs/servercompiler1/apps/puppetserver&quot;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/etc/puppetlabs/puppetservercompiler1/conf.d&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;You seem to be missing some important configuration files; could not find /etc/default/puppetservercompiler1 or /etc/sysconfig/puppetservercompiler1&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class=&quot;nb&quot;&gt;exit &lt;/span&gt;1
&lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/apps/foreground
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;restartfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/puppetlabs/servercompiler1/data/puppetserver/restartcounter&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;cli_defaults&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;INSTALL_DIR&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;/cli/cli-defaults.sh
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/apps/reload
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; +e

&lt;span class=&quot;nv&quot;&gt;restartfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/puppetlabs/servercompiler1/data/puppetserver/restartcounter&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;reload_timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RELOAD_TIMEOUT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$reload_timeout&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;realname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;puppetservercompiler1&quot;&lt;/span&gt;

...

&lt;span class=&quot;nv&quot;&gt;initial&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 1 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$restartfile&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;pgrep &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetservercompiler1/conf.d&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-HUP&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/apps/start
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; +e
&lt;span class=&quot;nb&quot;&gt;env

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;pgrep &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetservercompiler1/conf.d&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;restartfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/puppetlabs/servercompiler1/data/puppetserver/restartcounter&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;start_timeout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;START_TIMEOUT&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;real_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;puppetservercompiler1&quot;&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/apps/stop
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; +e

&lt;span class=&quot;nv&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;pgrep &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetservercompiler1/conf.d&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;realname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;puppetservercompiler1&quot;&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/servercompiler1/apps/puppetserver/cli/cli-defaults.sh
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;INSTALL_DIR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/opt/puppetlabs/servercompiler1/apps/puppetserver&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$JRUBY_JAR&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Warning: the JRUBY_JAR setting is no longer needed and will be ignored.&quot;&lt;/span&gt; 1&amp;gt;&amp;amp;2
&lt;span class=&quot;k&quot;&gt;fi

&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CLASSPATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;CLASSPATH&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/facter.jar:/opt/puppetlabs/servercompiler1/data/puppetserver/jars/*&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adoptions to scripts from main installation
&lt;/h3&gt;

&lt;p&gt;As we now have changed the way on how to determine the correct pid, we must also do this adoption at the main puppet server cli commands.&lt;/p&gt;

&lt;h4&gt;
  
  
  /opt/puppetlabs/server/apps/puppetserver/cli/apps/reload
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;...

&lt;span class=&quot;nv&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;pgrep &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetserver/conf.d&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/server/apps/puppetserver/cli/apps/start
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;...

&lt;span class=&quot;nv&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;pgrep &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetserver/conf.d&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  /opt/puppetlabs/server/apps/puppetserver/cli/apps/stop
&lt;/h4&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;...

&lt;span class=&quot;nv&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;pgrep &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;puppet-server-release.jar.* -m puppetlabs.trapperkeeper.main --config /etc/puppetlabs/puppetserver/conf.d&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start the stack
&lt;/h3&gt;



&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;systemctl start puppetserver
systemctl start puppetservercompiler1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy hacking and success on performance tuning your Puppet server infrastructure.&lt;/p&gt;

&lt;p&gt;Martin Alfke&lt;br&gt;
&lt;a href=&quot;mailto:ma@betadots.de&quot;&gt;ma@betadots.de&lt;/a&gt;&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: HDM Foreman integration</title>
    <link href="https://dev.to/betadots/hdm-foreman-integration-3ge4"/>
    <id>https://dev.to/betadots/hdm-foreman-integration-3ge4</id>
    <updated>2024-01-25T08:55:17+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Analyzing Hiera Data can be very difficult.&lt;br&gt;
&lt;a href=&quot;https://github.com/betadots/hdm&quot; rel=&quot;noopener noreferrer&quot;&gt;HDM - Hiera Data manager&lt;/a&gt; is a web UI, which let&#39;s you analyze Hiera Data in a user-friendly way.&lt;/p&gt;

&lt;p&gt;We now have built and released the HDM Foreman integration, which consists of two packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href=&quot;https://github.com/betadots/smart_proxy_hdm&quot; rel=&quot;noopener noreferrer&quot;&gt;Foreman HDM Smart Proxy plugin&lt;/a&gt;: smart_proxy_hdm&lt;/li&gt;
&lt;li&gt;
&lt;a href=&quot;https://github.com/betadots/foreman_hdm/&quot; rel=&quot;noopener noreferrer&quot;&gt;Foreman HDM plugin&lt;/a&gt;: foreman_hdm&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  HDM Foreman Smart Proxy
&lt;/h2&gt;

&lt;p&gt;Foreman needs to read data from HDM. HDM offers an API endpoint to allow remote systems to query for data and structure.&lt;br&gt;
The &lt;a href=&quot;https://github.com/betadots/smart_proxy_hdm&quot; rel=&quot;noopener noreferrer&quot;&gt;Foreman HDM Smart Proxy plugin&lt;/a&gt; needs to know where it can find the HDM API endpoint and how to authorize.&lt;/p&gt;
&lt;h2&gt;
  
  
  HDM Foreman Plugin
&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/betadots/foreman_hdm/&quot; rel=&quot;noopener noreferrer&quot;&gt;Foreman HDM plugin&lt;/a&gt; is an extension to the node view and adds a new tab which shows node hiera data.&lt;br&gt;
Node data are fetched via HDM Smart Proxy from HDM API endpoint.&lt;/p&gt;
&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;First you need a &lt;a href=&quot;https://github.com/betadots/hdm&quot; rel=&quot;noopener noreferrer&quot;&gt;HDM - Hiera Data manager&lt;/a&gt; installation. The installation can take place on an individual node or a Puppet Server.&lt;br&gt;
HDM needs access to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PuppetDB to read environments, nodes and facts&lt;/li&gt;
&lt;li&gt;Puppet Code to read hiera config file and hiera data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Besides this HDM requires to &lt;strong&gt;only&lt;/strong&gt; make use of facts and trusted variables in hiera.yaml file.&lt;br&gt;
Any variable which gets set during catalog compilation can not be evaluated by HDM and HDM will display these hierarchies as unresolvable.&lt;/p&gt;

&lt;p&gt;The HDM Foreman integration needs at least Foreman 3.6 or newer. We don&#39;t build packages for older versions.&lt;br&gt;
Older versions must use HDM integration from ruybgems.org.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;You must install the packages from the Foreman Plugins repository and initialize the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;dnf &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; rubygem-foreman_hdm rubygem-smart_proxy_hdm
foreman-rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next you need to configure the HDM Smart Proxy:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# /etc/foreman-proxy/settings.d/hdm.yml&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# HDM Smart Proxy&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;:enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;:hdm_url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://&amp;lt;HDM&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;IP&amp;gt;:&amp;lt;HDM&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Port&amp;gt;&#39;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;:hdm_user&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;HDM&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;API&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Email&amp;gt;&#39;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;:hdm_password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;HDM&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;API&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Password&amp;gt;&#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HDM User and Password must be taken from an HDM API user!&lt;br&gt;
You can omit these settings if you have disabled HDM user authentication.&lt;/p&gt;

&lt;p&gt;Now the smart proxy must be restarted:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;systemctl restart foreman-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Screenshots
&lt;/h2&gt;

&lt;p&gt;HDM Data view&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbetadots%2Fhdm%2Fmain%2Fscreenshots%2FHDM-12-node_data_details_2.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbetadots%2Fhdm%2Fmain%2Fscreenshots%2FHDM-12-node_data_details_2.png&quot; alt=&quot;HDM Data View&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Foreman Data View&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbetadots%2Fhdm%2Fmain%2Fscreenshots%2Fforeman%2FForeman-Host-3.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbetadots%2Fhdm%2Fmain%2Fscreenshots%2Fforeman%2FForeman-Host-3.png&quot; alt=&quot;Foreman Data View&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy hacking on Puppet and Hiera data,&lt;/p&gt;

&lt;p&gt;Martin&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Emerging from a year long apnoea</title>
    <link href="https://blog.example42.com/2024/01/19/emerging-from-a-year-long-apnoea/"/>
    <id>https://blog.example42.com/2024/01/19/emerging-from-a-year-long-apnoea</id>
    <updated>2024-01-19T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    The last post on example42’s blog was from December 2022, more than a year ago.

I was introducing tp desktop which is a quick and easy way to manage your desktops with Puppet and I was actively working on the new version of Tiny Puppet which was expecting to deliver the huge promise of being able to install EVERY application on EVERY Operating System (this is already happening for years) in EVERY way (OS packages, upstream repo packages, source code, release tarballs, containers…) with a single Puppet module.

Works on this release peaked at last Configuration Management Camp and then have been brutally interrupted by new activities we had to deliver for customers and these are the main reasons why also this blog has been silent for so long.

2023 has been a particular year for example42: almost no public activities, but a lot of work for customers, which resulted in the best year ever, commercially speaking, for example42’s mother company Lab42 srl (who said that Puppet is dead?).

Still, I think that lack of time is the excuse of the procrastinator, and even if this year has begun as active and busy as the previous one, I want to commit more time to public (and open source, as always) activities on Puppet.

So, what’s next?

No promises yet, let facts speak for themselves, when they will be ready.

For the moment I can anticipate the conferences where I will be present in the next months, see you there:


  AIDays  in Milan, Italy, January 30, 2024
  FOSDEM  in Brussels, Belgium, February 3-4, 2024
  Config Management Camp  in Gent, Belgium, February 5-7, 2024
  Incontro Devops Italia 2024  in Bologna, Italy, March 15, 2024
  KubeCon Europe2024  in Paris, Frances, March 1922, 2024
  Google Next’24  in Las Vegas, USA, April 9-11, 2024


At the Config Management Camp I’ll be presenting a talk about Strategies for Puppet code upgrade and refactoring, join me there if you want to know more about how to handle code upgrades and refactoring in a sane way.

Keep puppetizing, because if you have to manage a system whose lifecycle is longer than a few weeks, Puppet is still the best way to do it.

Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Emerging from a year long apnoea</title>
    <link href="https://www.example42.com/2024/01/19/emerging-from-a-year-long-apnoea/"/>
    <id>https://www.example42.com/2024/01/19/emerging-from-a-year-long-apnoea</id>
    <updated>2024-01-19T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    The last post on example42’s blog was from December 2022, more than a year ago.

I was introducing tp desktop which is a quick and easy way to manage your desktops with Puppet and I was actively working on the new version of Tiny Puppet which was expecting to deliver the huge promise of being able to install EVERY application on EVERY Operating System (this is already happening for years) in EVERY way (OS packages, upstream repo packages, source code, release tarballs, containers…) with a single Puppet module.

Works on this release peaked at last Configuration Management Camp and then have been brutally interrupted by new activities we had to deliver for customers and these are the main reasons why also this blog has been silent for so long.

2023 has been a particular year for example42: almost no public activities, but a lot of work for customers, which resulted in the best year ever, commercially speaking, for example42’s mother company Lab42 srl (who said that Puppet is dead?).

Still, I think that lack of time is the excuse of the procrastinator, and even if this year has begun as active and busy as the previous one, I want to commit more time to public (and open source, as always) activities on Puppet.

So, what’s next?

No promises yet, let facts speak for themselves, when they will be ready.

For the moment I can anticipate the conferences where I will be present in the next months, see you there:


  AIDays  in Milan, Italy, January 30, 2024
  FOSDEM  in Brussels, Belgium, February 3-4, 2024
  Config Management Camp  in Gent, Belgium, February 5-7, 2024
  Incontro Devops Italia 2024  in Bologna, Italy, March 15, 2024
  KubeCon Europe2024  in Paris, Frances, March 1922, 2024
  Google Next’24  in Las Vegas, USA, April 9-11, 2024


At the Config Management Camp I’ll be presenting a talk about Strategies for Puppet code upgrade and refactoring, join me there if you want to know more about how to handle code upgrades and refactoring in a sane way.

Keep puppetizing, because if you have to manage a system whose lifecycle is longer than a few weeks, Puppet is still the best way to do it.

Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Emerging from a year long apnoea</title>
    <link href="https://www.example42.com/blog/2024/01/19/emerging-from-a-year-long-apnoea/"/>
    <id>https://www.example42.com/blog/2024/01/19/emerging-from-a-year-long-apnoea</id>
    <updated>2024-01-19T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    The last post on example42’s blog was from December 2022, more than a year ago.

I was introducing tp desktop which is a quick and easy way to manage your desktops with Puppet and I was actively working on the new version of Tiny Puppet which was expecting to deliver the huge promise of being able to install EVERY application on EVERY Operating System (this is already happening for years) in EVERY way (OS packages, upstream repo packages, source code, release tarballs, containers…) with a single Puppet module.

Works on this release peaked at last Configuration Management Camp and then have been brutally interrupted by new activities we had to deliver for customers and these are the main reasons why also this blog has been silent for so long.

2023 has been a particular year for example42: almost no public activities, but a lot of work for customers, which resulted in the best year ever, commercially speaking, for example42’s mother company Lab42 srl (who said that Puppet is dead?).

Still, I think that lack of time is the excuse of the procrastinator, and even if this year has begun as active and busy as the previous one, I want to commit more time to public (and open source, as always) activities on Puppet.

So, what’s next?

No promises yet, let facts speak for themselves, when they will be ready.

For the moment I can anticipate the conferences where I will be present in the next months, see you there:


  AIDays  in Milan, Italy, January 30, 2024
  FOSDEM  in Brussels, Belgium, February 3-4, 2024
  Config Management Camp  in Gent, Belgium, February 5-7, 2024
  Incontro Devops Italia 2024  in Bologna, Italy, March 15, 2024
  KubeCon Europe2024  in Paris, Frances, March 1922, 2024
  Google Next’24  in Las Vegas, USA, April 9-11, 2024


At the Config Management Camp I’ll be presenting a talk about Strategies for Puppet code upgrade and refactoring, join me there if you want to know more about how to handle code upgrades and refactoring in a sane way.

Keep puppetizing, because if you have to manage a system whose lifecycle is longer than a few weeks, Puppet is still the best way to do it.

Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">Example 42: Emerging from a year long apnoea</title>
    <link href="https://example42.com/blog/2024/01/19/emerging-from-a-year-long-apnoea/"/>
    <id>https://example42.com/blog/2024/01/19/emerging-from-a-year-long-apnoea</id>
    <updated>2024-01-19T00:00:00+00:00</updated>
    <content type="html" xml:lang="en">
    The last post on example42’s blog was from December 2022, more than a year ago.

I was introducing tp desktop which is a quick and easy way to manage your desktops with Puppet and I was actively working on the new version of Tiny Puppet which was expecting to deliver the huge promise of being able to install EVERY application on EVERY Operating System (this is already happening for years) in EVERY way (OS packages, upstream repo packages, source code, release tarballs, containers…) with a single Puppet module.

Works on this release peaked at last Configuration Management Camp and then have been brutally interrupted by new activities we had to deliver for customers and these are the main reasons why also this blog has been silent for so long.

2023 has been a particular year for example42: almost no public activities, but a lot of work for customers, which resulted in the best year ever, commercially speaking, for example42’s mother company Lab42 srl (who said that Puppet is dead?).

Still, I think that lack of time is the excuse of the procrastinator, and even if this year has begun as active and busy as the previous one, I want to commit more time to public (and open source, as always) activities on Puppet.

So, what’s next?

No promises yet, let facts speak for themselves, when they will be ready.

For the moment I can anticipate the conferences where I will be present in the next months, see you there:


  AIDays  in Milan, Italy, January 30, 2024
  FOSDEM  in Brussels, Belgium, February 3-4, 2024
  Config Management Camp  in Gent, Belgium, February 5-7, 2024
  Incontro Devops Italia 2024  in Bologna, Italy, March 15, 2024
  KubeCon Europe2024  in Paris, Frances, March 1922, 2024
  Google Next’24  in Las Vegas, USA, April 9-11, 2024


At the Config Management Camp I’ll be presenting a talk about Strategies for Puppet code upgrade and refactoring, join me there if you want to know more about how to handle code upgrades and refactoring in a sane way.

Keep puppetizing, because if you have to manage a system whose lifecycle is longer than a few weeks, Puppet is still the best way to do it.

Alessandro Franceschi
  </content>
    <author>
      <name>Example 42</name>
      <uri>http://www.example42.com/</uri>
    </author>
  </entry>


  <entry xml:lang="en">
    <title type="html" xml:lang="en">betadots: Puppet container version schema update</title>
    <link href="https://dev.to/betadots/puppet-container-version-schema-update-4n42"/>
    <id>https://dev.to/betadots/puppet-container-version-schema-update-4n42</id>
    <updated>2024-01-03T16:08:30+00:00</updated>
    <content type="html" xml:lang="en">
    &lt;p&gt;Community: we hear you!&lt;/p&gt;

&lt;p&gt;Several people have asked to please change the version schema on &lt;a href=&quot;https://github.com/voxpupuli/container-puppetserver/&quot;&gt;puppetserver-container&lt;/a&gt; and &lt;a href=&quot;https://github.com/voxpupuli/container-puppetdb/&quot;&gt;puppetdb-container&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Puppet Inc version schema
&lt;/h2&gt;

&lt;p&gt;Prior the repositories have been handed over from &lt;a href=&quot;https://puppet.com&quot;&gt;Puppet Inc&lt;/a&gt; to &lt;a href=&quot;https://voxpupuli.org&quot;&gt;Puppet Community&lt;/a&gt;, the container images were using the Puppet server and PuppetDB versions, which were used inside the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;&amp;lt;puppet.major&amp;gt;.&amp;lt;puppet.minor&amp;gt;.&amp;lt;puppet.patch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; puppet &lt;span class=&quot;nt&quot;&gt;--hostname&lt;/span&gt; puppet puppetlabs-puppetserver:7.2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;puppet.major&lt;/td&gt;
&lt;td&gt;Describes the contained major Puppet version (7 or 8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet.minor&lt;/td&gt;
&lt;td&gt;Describes the contained minor Puppet version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet.patch&lt;/td&gt;
&lt;td&gt;Describes the contained patchlevel Puppet version&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Initial voxpupuli version schema
&lt;/h2&gt;

&lt;p&gt;The original version schema did not show if changes to the container image have been done.&lt;br&gt;
At &lt;a href=&quot;https://voxpupuli.org&quot;&gt;Voxpupuli&lt;/a&gt; it was decided to use a new version schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;v&amp;lt;major&amp;gt;.&amp;lt;minor&amp;gt;.&amp;lt;patch&amp;gt;-&amp;lt;puppet release&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; puppet &lt;span class=&quot;nt&quot;&gt;--hostname&lt;/span&gt; puppet ghcr.io/voxpupuli/container-puppetserver:v1.0.0-7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;major&lt;/td&gt;
&lt;td&gt;Describes the major version of the base container (Ubunutu 22.04) or incompatible changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;minor&lt;/td&gt;
&lt;td&gt;Describes new features or refactoring with backward compatibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;patch&lt;/td&gt;
&lt;td&gt;Describes if minor changes or bugfixes have been implemented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet release&lt;/td&gt;
&lt;td&gt;Describes the contained major Puppet release (7 or 8)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At &lt;a href=&quot;https://github.com/voxpupuli/crafty.git&quot;&gt;Voxpupuli Crafty&lt;/a&gt; we had a &lt;a href=&quot;https://github.com/voxpupuli/crafty/discussions/22&quot;&gt;discussion&lt;/a&gt; on how to improve the version schema.&lt;br&gt;
Additionally we received feedback from customers, that they were unhappy with the new version schema as it was unclear, which exact Puppet version is inside the containers.&lt;/p&gt;
&lt;h2&gt;
  
  
  New version schema
&lt;/h2&gt;

&lt;p&gt;The new version schema has the following layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight plaintext&quot;&gt;&lt;code&gt;&amp;lt;puppet.major&amp;gt;.&amp;lt;puppet.minor&amp;gt;.&amp;lt;puppet.patch&amp;gt;-v&amp;lt;container.major&amp;gt;.&amp;lt;container.minor&amp;gt;.&amp;lt;container.patch&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; puppet &lt;span class=&quot;nt&quot;&gt;--hostname&lt;/span&gt; puppet ghcr.io/voxpupuli/container-puppetserver:7.13.0-v1.2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class=&quot;table-wrapper-paragraph&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;puppet.major&lt;/td&gt;
&lt;td&gt;Describes the contained major Puppet version (7 or 8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet.minor&lt;/td&gt;
&lt;td&gt;Describes the contained minor Puppet version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;puppet.patch&lt;/td&gt;
&lt;td&gt;Describes the contained patchlevel Puppet version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;container.major&lt;/td&gt;
&lt;td&gt;Describes the major version of the base container (Ubunutu 22.04) or incompatible changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;container.minor&lt;/td&gt;
&lt;td&gt;Describes new features or refactoring with backward compatibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;container.patch&lt;/td&gt;
&lt;td&gt;Describes if minor changes or bugfixes have been implemented&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Many thanks to the community for the great feedback and ideas for improvement.&lt;/p&gt;

&lt;p&gt;Happy hacking on Puppet in containers.&lt;/p&gt;

&lt;p&gt;Martin Alfke&lt;/p&gt;
  </content>
    <author>
      <name>betadots</name>
      <uri>https://dev.to/betadots</uri>
    </author>
  </entry>


</feed>