<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://holos.run/blog/</id>
    <title>Holos Blog</title>
    <updated>2024-11-25T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://holos.run/blog/"/>
    <subtitle>Holos Blog</subtitle>
    <icon>https://holos.run/img/favicon.ico</icon>
    <rights>Copyright © 2026, The Holos Authors</rights>
    <entry>
        <title type="html"><![CDATA[Validators added in Holos v0.101.0]]></title>
        <id>https://holos.run/blog/validators-feature/</id>
        <link href="https://holos.run/blog/validators-feature/"/>
        <updated>2024-11-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Validators are useful to enforce policy and catch Helm errors.]]></summary>
        <content type="html"><![CDATA[<p>We've added support for <a href="https://holos.run/docs/v1alpha5/tutorial/validators/" target="_blank" rel="noopener noreferrer">Validators</a> in <a href="https://github.com/holos-run/holos/releases/tag/v0.101.0" target="_blank" rel="noopener noreferrer">v0.101.0</a>.  Validators are useful to
enforce policies and ensure consistency early in the process.  This feature
addresses two primary use cases:</p>
<ol>
<li>Prevent insecure configuration early in the process.  For example, prevent
Helm from rendering a <code>Secret</code> which would otherwise be committed to version control.</li>
<li>Prevent unsafe configuration by validating manifests against Kubernetes core
and custom resource type definitions.</li>
</ol>
<p>Check out the <a href="https://holos.run/docs/v1alpha5/tutorial/validators/" target="_blank" rel="noopener noreferrer">Validators</a> tutorial for examples of both use cases.</p>
<!-- -->
<!-- -->
<p>Validators complete the core functionality of the Holos manifest rendering
pipeline.  We are seeking design partners to help enhance Generators and
Transformers.  Validators are implemented using a generic <code>Command</code> kind, and
we're considering a similar kind for Generators and Transformers.  Please
connect with us if you'd like to help design these enhancements.</p>]]></content>
        <author>
            <name>Jeff McCune</name>
            <uri>https://github.com/jeffmccune</uri>
        </author>
        <category label="Holos" term="Holos"/>
        <category label="feature" term="feature"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why CUE for Configuration]]></title>
        <id>https://holos.run/blog/why-cue-for-configuration/</id>
        <link href="https://holos.run/blog/why-cue-for-configuration/"/>
        <updated>2024-10-28T16:00:00.000Z</updated>
        <summary type="html"><![CDATA[Why we use CUE for configuration in Holos]]></summary>
        <content type="html"><![CDATA[<p>We selected <a href="https://cuelang.org/" target="_blank" rel="noopener noreferrer">CUE</a> as the configuration language in Holos
for a number of reasons described in this post.  The process was a combination
of process by elimination and the unique way CUE <em>unifies</em> configuration.</p>
<p>We evaluated a number of domain specific and general purpose languages before
deciding on CUE.  The CUE website, GitHub issues, and Marcel's videos do a great
job of explaining most of these reasons, so I'll summarize and cite them here.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="dsl-or-gpl">DSL or GPL<a href="https://holos.run/blog/why-cue-for-configuration/#dsl-or-gpl" class="hash-link" aria-label="Direct link to DSL or GPL" title="Direct link to DSL or GPL">​</a></h2>
<p>The first decision was if we should use a turing complete general purpose
language, or a domain specific language (DSL).  We decided to use a DSL because
we knew from hard won experience configuration with general purpose languages
invites too many problems over time.</p>
<ol>
<li>Configuration easily becomes non-deterministic, especially when remote procedure calls are involved.</li>
<li>Many general purpose languages support type checking, but few support constraints and validation of data.  We must write our own validation logic which often means validation happens haphazardly, if at all.</li>
<li>Data is usually mutable, making it difficult to know where an output value came from.</li>
<li>Configuration code is read much more frequently, and at more critical times like an outage, than it's written.  I felt this pain and I don't want anyone using Holos to feel that way.</li>
</ol>
<p>For these reasons we sought a domain specific language that focused on
simplicity, readability, and data validation.  This quote from Marcel got my attention focused on CUE.</p>
<blockquote>
<p>I would argue that for configuration languages maintainability and readability are more important even than for programming languages, because they are ofter viewed by a larger group, often need to be changed in emergency conditions, and also as they are supposed to convey a certain contract. Most configuration languages, like GCL (my own doing), are more like scripting languages, making it easier to crank out definitions of large swats of data compactly, but being harder to comprehend and modify later.</p>
</blockquote>
<p>Source: <a href="https://github.com/cuelang/cue/discussions/669#discussioncomment-306811" target="_blank" rel="noopener noreferrer">Comparisons between CUE, Jsonnet, Shall, OPA, etc.</a></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="other-dsls">Other DSLs<a href="https://holos.run/blog/why-cue-for-configuration/#other-dsls" class="hash-link" aria-label="Direct link to Other DSLs" title="Direct link to Other DSLs">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="template-engines">Template Engines<a href="https://holos.run/blog/why-cue-for-configuration/#template-engines" class="hash-link" aria-label="Direct link to Template Engines" title="Direct link to Template Engines">​</a></h3>
<p>Template engines are not exactly a domain specific language, but they're
similar.  We already used Go templates in Helm to produce YAML, and previously
used Jinja2 and ERB templates extensively for configuration tasks.</p>
<p>The fundamental problem with text template engines is that they manipulate text,
not data.  As a result, output is often rendered without error or indication the
configuration is invalid until it is applied to the live system.  Errors need
to be handled faster and earlier, ideally immediately as we're writing in our
editor.</p>
<p>For these reasons we can set aside all tools based on text templating.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="jsonnet">Jsonnet<a href="https://holos.run/blog/why-cue-for-configuration/#jsonnet" class="hash-link" aria-label="Direct link to Jsonnet" title="Direct link to Jsonnet">​</a></h3>
<p>Marcel and the CUE website explain this much better than I can.  We used Jsonnet
to configure the kubernetes prometheus stack and experienced Jsonnet's lack of
validation features first hand.</p>
<blockquote>
<p>Like Jsonnet, CUE is a superset of JSON. They also are both influenced by GCL. CUE, in turn is influenced by Jsonnet. This may give the semblance that the languages are very similar. At the core, though, they are very different.</p>
<p>CUE’s focus is data validation whereas Jsonnet focuses on data templating (boilerplate removal). Jsonnet was not designed with validation in mind.</p>
<p>Jsonnet and GCL can be quite powerful at reducing boilerplate. The goal of CUE is not to be better at boilerplate removal than Jsonnet or GCL. CUE was designed to be an answer to two major shortcomings of these approaches: complexity and lack of typing. Jsonnet reduces some of the complexities of GCL, but largely falls into the same category. For CUE, the tradeoff was to add typing and reduce complexity (for humans and machines), at the expense of giving up flexibility.</p>
</blockquote>
<p>Source: <a href="https://cuelang.org/docs/concept/configuration-use-case/#jsonnet-gcl" target="_blank" rel="noopener noreferrer">CUE Configuration Use Case - Jsonnet / GCL</a></p>
<p>Marcel answered this question in more depth earlier:</p>
<blockquote>
<p>Jsonnet is based on BCL, an internal language at Google. It fixes a few things relative to BCL, but is mostly the same. This means it copies the biggest mistakes of BCL. Even though BCL is still widely used at Google, its issues are clear. It was just that the alternatives weren't that much better.</p>
<p>There are a myriad of issues with BCL (and Jsonnet and pretty much all of its descendants), but I will mention a couple:</p>
<ol>
<li>Most notably, the basic operation of composition of BCL/Jsonnet, inheritance, is not commutative and idempotent in the general case. In other words, order matters. This makes it, for humans, hard to track where values are coming from. But also, it makes it very complicated, if not impossible, to do any kind of automation. The complexity of inheritance is compounded by the fact that values can enter an object from one of several directions (super, overlay, etc.), and the order in which this happens matters. The basic operation of CUE is commutative, associative and idempotent. This order independence helps both humans and machines. The resulting model is much less complex.</li>
<li>Typing: most of the BCL offshoots do not allow for schema definitions. This makes it hard to detect any kind of typos or user errors. For a large code bases, no one will question a requirement to have a compiled/typed language. Why should we not require the same kind of rigor for data? Some offshoots of BCL internal to Google and also external have tried to address this a bit, but none quite satisfactory. In CUE types and values are the same thing. This makes things both easier than schema-based languages (less concepts to learn), but also more powerful. It allows for intuitive but also precise typing.</li>
</ol>
<p>There are many other issues, like handling cycles, unprincipled workarounds for hermeticity, poor tooling and so forth that make BCL and offsprings often awkward.</p>
<p>So why CUE? Configuration is still largely an unsolved problem. We have tried using code to generate configs, or hybrid languages, but that often results in a mess. Using generators on databases doesn't allow keeping it sync with revision control. Simpler approaches like HCL and Kustomize recognize the complexity issue by removing a lot of it, but then sometimes become too weak, and actually also reintroduce some of this complexity with overlays (a poor man's inheritance, if you will, but with some of the same negative consequences). Other forms of removing complexity, for instance by just introducing simpler forms/ abstraction layers of configuration, may work within certain context but are domain-specific and relatively hard to maintain.</p>
<p>So inheritance-based languages, for all its flaws, were the best we had. The idea behind CUE is to recognize that a declarative language is the best approach for many (not all) configuration problems, but to tackle the fundamental issues of these languages.</p>
<p>The idea for CUE is actually not new. It was invented about 30 years ago and has been in use and further developed since that time in the field of computational linguistics, where the concept is used to encode entire lexicons as well as very detailed grammars of human languages. If you think about it, these are huge configurations that are often maintained by both computer scientists and linguists. You can see this as a proof of concept that large-scale, declarative configuration for a highly complex domain can work.</p>
<p>CUE is a bit different from the languages used in linguistics and more tailored to the general configuration issue as we've seen it at Google. But under the hood it adheres strictly to the concepts and principles of these approaches and we have been careful not to make the same mistakes made in BCL (which then were copied in all its offshoots). It also means that CUE can benefit from 30 years of research on this topic. For instance, under the hood, CUE uses a first-order unification algorithm, allowing us to build template extractors based on anti-unification (see issue #7 and #15), something that is not very meaningful or even possible with languages like BCL and Jsonnet.</p>
</blockquote>
<p>Source: <a href="https://github.com/cuelang/cue/issues/33#issuecomment-483615374" target="_blank" rel="noopener noreferrer">how CUE differs from jsonnet</a></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="dhall">Dhall<a href="https://holos.run/blog/why-cue-for-configuration/#dhall" class="hash-link" aria-label="Direct link to Dhall" title="Direct link to Dhall">​</a></h3>
<blockquote>
<p>Dhall addresses some of the issues of GCL and Jsonnet (like lack of typing), but lacks the detailed typing of CUE. But it still misses the most important property of CUE: its model of composability. Some of the benefits are explained in the above link. Conceptually, CUE is an aspect-oriented and constraint-based language. It allows you to specify fine-grained constraints on what are valid values. These constraints then double as templates, allowing to remove boilerplate often with the same efficacy as inheritance, even if it works very differently.</p>
</blockquote>
<p>Source <a href="https://github.com/cuelang/cue/discussions/669#discussioncomment-306811" target="_blank" rel="noopener noreferrer">Comparisons between CUE, Jsonnet, Dhall, OPA, etc.</a></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="rego-opa">Rego (OPA)<a href="https://holos.run/blog/why-cue-for-configuration/#rego-opa" class="hash-link" aria-label="Direct link to Rego (OPA)" title="Direct link to Rego (OPA)">​</a></h3>
<blockquote>
<p>CUE also can be used for policy specification, like Rego (OPA).CUE unifies values, types, and constraints in a single continuum. As it is a constraint-based language first and foremost, it is well suited for defining policy. It is less developed in that area than Rego, but it I expect it will ultimately be better suited for policy. Note that Rego is based on Datalog, which is more of a query language at hart, giving it quite a different feel for defining policy than CUE. Both are logic programming languages, though, and share many of the same properties.</p>
</blockquote>
<p>Source <a href="https://github.com/cuelang/cue/discussions/669#discussioncomment-306811" target="_blank" rel="noopener noreferrer">Comparisons between CUE, Jsonnet, Dhall, OPA, etc.</a></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="pkl">PKL<a href="https://holos.run/blog/why-cue-for-configuration/#pkl" class="hash-link" aria-label="Direct link to PKL" title="Direct link to PKL">​</a></h3>
<p>I didn't look deeply into <a href="https://github.com/apple/pkl" target="_blank" rel="noopener noreferrer">Pkl</a> primarily because
CUE, like Holos, is written in Go.  It was straight forward to integrate CUE
into Holos.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="hcl">HCL<a href="https://holos.run/blog/why-cue-for-configuration/#hcl" class="hash-link" aria-label="Direct link to HCL" title="Direct link to HCL">​</a></h3>
<p>I have extensive experience with HCL and found it challenging to work with at medium to large scales.</p>
<p>See also: <a href="https://cuelang.org/docs/concept/configuration-use-case/#hcl" target="_blank" rel="noopener noreferrer">CUE Configuration Use Case - HCL</a></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="editor-integration">Editor Integration<a href="https://holos.run/blog/why-cue-for-configuration/#editor-integration" class="hash-link" aria-label="Direct link to Editor Integration" title="Direct link to Editor Integration">​</a></h2>
<p>CUE has good support today for Visual Studio Code, and better support coming,
see the <a href="https://github.com/orgs/cue-lang/projects/15" target="_blank" rel="noopener noreferrer">CUE LSP Roadmap</a></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="additional-resources">Additional Resources<a href="https://holos.run/blog/why-cue-for-configuration/#additional-resources" class="hash-link" aria-label="Direct link to Additional Resources" title="Direct link to Additional Resources">​</a></h2>
<p>The video <a href="https://www.youtube.com/watch?v=jSRXobu1jHk" target="_blank" rel="noopener noreferrer">Large-Scale Engineering of Configuration with Unification (Marcel van
Lohuizen)</a> motivated me to go
deeper and invest significant time into CUE.</p>]]></content>
        <author>
            <name>Jeff McCune</name>
            <uri>https://github.com/jeffmccune</uri>
        </author>
        <category label="Holos" term="Holos"/>
        <category label="cue" term="cue"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Announcing Holos]]></title>
        <id>https://holos.run/blog/announcing-holos/</id>
        <link href="https://holos.run/blog/announcing-holos/"/>
        <updated>2024-10-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Holistically manage Helm and Kustomize with CUE]]></summary>
        <content type="html"><![CDATA[
<p>I'm excited to share Holos, a Go command line tool we developed to make it
easier to manage a platform built on Kubernetes.  Holos implements the rendered
manifests pattern as a data pipeline to fully render manifests generated from
<a href="https://helm.sh/" target="_blank" rel="noopener noreferrer">Helm</a>, <a href="https://kustomize.io/" target="_blank" rel="noopener noreferrer">Kustomize</a>, or <a href="https://cuelang.org/" target="_blank" rel="noopener noreferrer">CUE</a> in a holistic way.</p>
<!-- -->
<!-- -->
<p>At the start of the pandemic I was migrating our platform from VMs managed by
Puppet to Kubernetes.  My primary goal was to build an observability system
similar to what we had when we managed Puppet at Twitter prior to the
acquisition.  I started building the observability system with the official
<a href="https://github.com/prometheus-community/helm-charts" target="_blank" rel="noopener noreferrer">prometheus community charts</a>, but quickly ran into issues where the
individual charts didn’t work with each other.  I was frustrated with how
complicated and difficult to configure these charts were.  They weren’t well
integrated, so I switched to the <a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack" target="_blank" rel="noopener noreferrer">kube-prometheus-stack</a> umbrella chart which
attempts to solve this integration problem.</p>
<p>The umbrella chart got us further, as long as we didn’t stray too far from the
default values, but we quickly ran into operational challenges.  Upgrading the
chart introduced breaking changes we couldn’t see until they were applied,
causing incidents.  We needed to manage secrets securely so we mixed in
ExternalSecrets with many of the charts.  We decided to handle these
customizations by implementing the <a href="https://akuity.io/blog/the-rendered-manifests-pattern/" target="_blank" rel="noopener noreferrer">rendered manifests pattern</a> using scripts in
our CI pipeline.</p>
<p>These scripts got us further, but we found them costly to maintain.
Teammates needed to be careful to execute them with the same context they were
executed in CI.  We realized we were reinventing Hiera to manage a hierarchy of
helm values.yaml files to inject into multiple charts.</p>
<p>At this point I started looking for a more holistic solution to this problem of
integrating multiple charts together.  We saw the value in the rendered
manifests pattern, but we couldn’t find an agreed upon implementation.  We built
a Go command line tool to implement the pattern as a data pipeline.  I’d been
thinking about the comments from the <a href="https://hn.algolia.com/?dateRange=all&amp;page=0&amp;prefix=false&amp;query=https%3A%2F%2Fleebriggs.co.uk%2Fblog%2F2019%2F02%2F07%2Fwhy-are-we-templating-yaml&amp;sort=byDate&amp;type=story" target="_blank" rel="noopener noreferrer">Why are we templating YAML</a> posts and
wondering what an answer to this question would look like.</p>
<!-- -->
<p>The Go command line tool was an incremental improvement over the CI scripts, but
we still didn’t have a good way to handle the data values.  We were still
templating YAML which didn’t catch errors early enough.  It was too easy to
render invalid resources Kubernetes rejected, causing deployment problems.  I
searched for a solution to manage helm values, something like Hiera which we
knew well from Puppet, but not hierarchical because we knew it was important to
trace where config values came from in an outage.  A few HN comments mentioned
CUE, and an engineer we worked with at Twitter used CUE to configure Envoy at
scale, so I gave it a try.  I quickly appreciated how CUE provides both strong
type checking and validation of constraints, unifies all configuration data, and
provides clarity into where values originate from.</p>
<p>Take a look at Holos if you’re looking to implement the rendered manifests
pattern or can’t shake that feeling it should be easier to integrate third party
software into Kubernetes like we felt.</p>
<ol>
<li><a href="https://holos.run/docs/v1alpha5/tutorial/overview/">Tutorial</a> takes a tour of Holos features starting from scratch.</li>
<li><a href="https://holos.run/docs/v1alpha5/topics/">Topics</a> cover how to customize and tailor Holos to your unique needs.</li>
</ol>]]></content>
        <author>
            <name>Jeff McCune</name>
            <uri>https://github.com/jeffmccune</uri>
        </author>
        <category label="Holos" term="Holos"/>
        <category label="launch" term="launch"/>
    </entry>
</feed>