Skip to content

seh/rules_kustomize

Repository files navigation

kustomize rules for Bazel

Integrate the kustomize tool_ into your Bazel projects to build Kubernetes-style manifests.

depth

2


Overview

These Bazel rules allow you to define kustomizations within your project workspace and capture the output of building those kustomizations with the kustomize tool_. Using kustomize within a Bazel project orchestrates both the preparation of the input files—for when static files won't do—and consumption of the resulting YAML document stream, situating kustomize as a transformer in the middle of that data flow.

kustomize operates on the Kubernetes Resource Model (KRM), and most often comes into play just ahead of Kubernetes API clients like kubectl, sending the YAML document stream to the Kubernetes API to apply the resources to the cluster. These rules do no include any such interaction with the Kubernetes API. Instead, they focus solely on emitting the resources defined by the kustomization.

Simple Example

Here we'll walk through a simple example to give a feel for how to use these Bazel rules.

Assume you have a kustomization that defines a single Kubernetes ConfigMap, taking its data entries from both a file containing "environment variable"-style line-by-line definitions and some inline definitions in the kustomization.yaml file. First, the kustomization.yaml file:

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

configMapGenerator:
- name: translations
  envs:
  - config-bindings
  literals:
  - HELLO=hola
  - GOODBYE=adios

Note that the "envs" field references a sibling file named config-bindings, with content as follows:

THANK_YOU=gracias

If we run the kustomize build command against the directory containing these two files, it emits the following YAML document:

apiVersion: v1
data:
  GOODBYE: adios
  HELLO: hola
  THANK_YOU: gracias
kind: ConfigMap
metadata:
  name: translations-74424tmdd8

Now, let's teach Bazel to produce the same document.

By convention, we'll add a couple of Bazel targets to the BUILD.bazel file in the same directory. First, we define the kustomization itself, indicating where the kustomization file sits and which other files it depends on. For this, we use the kustomization rule.

load(
     "@rules_kustomize//kustomize:kustomize.bzl",
     "kustomization",
 )

 kustomization(
     name = "base",
     srcs = [
         "config-bindings",
     ],
 )

By default, the kustomization rule assumes the kustomization file is named kustomization.yaml, but you can also point it at other file names, such as the kustomization.yml or kustomization alternatives that the kustomize tool accepts.

This "base" target we've defined doesn't produce any artifacts. It prepares the recipe for building artifacts, in the same way that a kustomization's files are inert input for the kustomize tool. When we'd like to build the kustomization using particular options—as we would by invoking kustomize build—we define another target in a BUILD.bazel file. Here we'll add to the same Bazel package, this time using the kustomized_resources rule.

kustomized_resources(
    name = "simple",
    kustomization = ":base",
)

When we tell Bazel to build this new "simple" target, it will invoke kustomize build and write the output to a file named simple.yaml. That's the default mapping from target name to output file name, but you can change it with the kustomized_resources rule's result attribute. Other Bazel targets can then demand this file as input, forcing Bazel to rebuild the file whenever—and only when—any of the input files change.

Integrating Into Your Project

In order to use these rules in your Bazel project, you must instruct Bazel to download the source and run the functions that make the rules available. Add the following to your project's MODULE.bazel file.

bazel_dep(name = "rules_kustomize", version = "0.3.10")

This declaration registers a particular version of the helm and kustomize tools, respectively. By default, it registers the latest version known to the rules. You can specify a preferred version for each tool by supplying the known version slug (e.g. "v4.5.7") as an argument to the respective module extension's download tag.

bazel_dep(name = "rules_kustomize", version = "0.3.10")

kustomize = use_extension("@rules_kustomize//kustomize:extensions.bzl", "kustomize")
kustomize.download(version = "v5.3.0")
helm = use_extension("@rules_kustomize//kustomize:extensions.bzl", "helm")
helm.download(version = "v3.12.1")

If any number of modules wind up specifying different version values for these tags, the latest version—per Semantic Versioning sorting—among the proposed candidate versions wins. If any of the tags also include the tolerate_newer attribute with a value of False, then no competing version newer than that tag's proposed version can win.

With those calls in place, you're now ready to use the rules in your Bazel packages.

Tool Versions

At present, these rules can load the following versions of these tools:

Rules

kustomization

This defines a kustomization from a set of source files and other kustomizations, intended for referencing from one or more dependent kustomized_resources targets.

Providers

Attributes

Name Type Default value
name string mandatory value
A unique name for the :term:`kustomizatio package (assuming that the target is in t like "base" is fitting. n`. As there is usually only o he same package as the :term:` ne such target defined per Bazel kustomization` file), a simple name
deps label_list []
The set of `kustomizations <kustomization _>`_ referenced as resources by this kustomization.
file label :value:kustomization.yaml
:file:`kustomization.yaml, :file:`kustom :term:`kustomization. ization.yml`, or :file:`kustom ization` file for this
requires_exec_functions bool False

Whether this kustomization requir --enable-exec :tool:`kustomize

Even if this kustomization's top-level resources don't require such use but any of its base kustomizations do, this value is effectively True.

es use of exec functions (raw ` flag). executables) (per the
requires_helm bool False

Whether this kustomization requir --enable-helm :tool:`kustomize

Even if this kustomization's top-level resources don't require such use but any of its base kustomizations do, this value is effectively True.

es use of the Helm chart infla ` flag). tor generator (per the
requires_plugins bool False

Whether this kustomization requir --enable-alpha-plugins :tool:`

Even if this kustomization's top-level resources don't require such use but any of its base kustomizations do, this value is effectively True.

es use of kustomize pl kustomize` flag). ugins (per the
requires_starlark_functions bool False

Whether this kustomization requir kustomize flag).

Even if this kustomization's top-level resources don't require such use but any of its base kustomizations do, this value is effectively True.

es use of Starlark functions ( per the --enable-star
srcs label_list :value:[]
Files referenced as resources for this : term:kustomization.

Example

kustomization(
    name = "overlay",
    deps = [
        # This target "base:base" is another kustomization.
        "//apps/base"
    ],
    # We can omit the "file" attribute because our kustomization
    # file is named "kustomization.yaml," matching the default.
    srcs = [
        # This target "charts:charts" is a filegroup.
        "//apps/base/charts",
        "extras.yaml",
    ],
    requires_helm = True,
)

kustomized_resources

This defines an invocation of the kustomize build command against a kustomization target, creating a resulting set of resources (collectively, a variant).

See the Difficulties section below for considerations both with kustomizations that involve use of the helmCharts Kustomization (or Component) field and when executing Bazel actions on some computers.

Attributes

Name Type Default value
name string mandatory value
A unique name for the variant.
env_bindings string_dict {}

Names and values of environment variabl kustomize flag).

These bindings specify a value for each through instead, use the :ruleattr:`env

es to be used by functions (pe

environment variable. To forw

_exports` attribute.

r the --env

ard an exported environment variable's

env_exports string_list []

Names of exported environment variables flag).

These bindings forward each exported en variable instead, use the :ruleattr:`en

to be used by functions (per

vironment variable's value. To v_bindings` attribute.

the --env kustomize

specify a value for each environment

kustomization label mandatory value

The kustomization to build.

This may refer to a target using the ku provider.

stomization rule or another r ule that yields a KustomizationInfo
load_restrictor string RootOnly

Control whether kustomizations --load-restrictor :tool:kust

See the Difficulties section for cases Bazel when you could normally get by wi kustomizations from loading fil

may load files from outsider t omize flag). May be one of :va

where you may need to set thi

th the kustomize tool' es from outside their root.

heir root directory (per the lue:None or RootOnly.

s value to None within s default behavior of preventing

result output <name>.yaml
The built result, as a YAML stream of K kustomize flag). RM resources in separate docum ents (per the --output

Example

kustomized_resources(
    name = "production",
    env_bindings = {
        "CLUSTER_NAME": "prod1234",
        "ENVIRONMENT": "production",
    },
    kustomization = ":overlay",
)

Providers

KustomizationInfo

KustomizationInfo summarizes a kustomization root, as provided by the kustomization rule.

Fields

Name Type
requires_exec_functions :type:bool
Whether this kustomization requ --enable-exec :tool:`kustomi ires use of exec functions (raw executables) (per the ze` flag).
requires_helm bool
Whether this kustomization requ --enable-helm :tool:`kustomi ires use of the Helm chart inflator generator (per the ze` flag).
requires_plugins bool
Whether this kustomization requ --enable-alpha-plugins :tool ires use of kustomize plugins (per the :kustomize flag).
requires_starlark_functions bool
Whether this kustomization requ --enable-star :tool:`kustomi ires use of Starlark functions (per the ze` flag).
target_file string
The top-level kustomization fil e defining this kustomization.
transitive_resources depset of File
The set of files (including other :term kustomization. :kustomizations) referenced by this

Difficulties

These rules attempt to make using kustomize with Bazel easier, but there are a few features of the tools that interact poorly, or at least surprisingly, even when they're individually doing their job as intended. We can work around each problem once we know better what to expect.

Bazel's Sandbox and Load Restrictors

kustomize prefers to load files only from the kustomization root directory—the one containing the kustomization.yaml file—or any of its subdirectories. The kustomize build subcommand runs with a load restrictor to enforce this restrictive policy. By default, the --load-restrictor flag uses the value LoadRestrictionsRootOnly. With that value in effect, kustomize build will refuse to read any files referenced by a kustomization that lie outside of the kustomization root directory tree, per this FAQ entry.

Bazel can execute the actions for its build and test commands in a restricted environment called a sandbox, using a technique called sandboxing. On some operating systems, Bazel uses symbolic links to make only some files available to programs it runs in its actions. These symbolic links point upward and outward to files that lie outside of the kustomization root in the sandbox. Even though the links are within the kustomization root, their target files are not. kustomize considers this to transgress its LoadRestrictionsRootOnly load restriction and blocks the attempt to load the referenced file.

There are three ways around this problem:

  • Relax kustomize's load restrictor by passing LoadRestrictionsNone to its --load-restrictor flag, by way of specifying the value None for the kustomized_resources rule's load_restrictor attribute.
  • Use a Bazel sandboxing implementation that doesn't rely on symbolic links, such as its sandboxfs FUSE file system. With the sandboxfs tool installed, pass the --experimental_use_sandboxfs flag to bazel build, bazel test, or bazel run.

    NB: As of Bazel version 7.0.0, per commit b6e2693f83a7ece37c902416de26a3807b541ceb, the --experimental_use_sandboxfs flag is no longer available. Bazel no longer supports use of the sandboxfs tool. See kustomize issue 5216 for an alternate proposed workaround, which is implemented but not merged.

  • Disable Bazel sandboxing entirely by omitting sandboxed from the values supplied via its --spawn_strategy flag. With sandboxing disabled, Bazel will present the input files to kustomize as regular files. So long as those files lie within the kustomization root, the LoadRestrictionsRootOnly load restrictor will not intervene.

Downloading Helm Charts

The kustomize tool can expand Helm charts using the Kustomization manifest's helmCharts field. If the Helm chart's source files are not available already locally, kustomize can fetch the chart archive and unpack within the directory specified in the helmGlobals.chartHome field. By default, this chartHome field's value is charts, meaning that kustomize will download and expand chart archives in the charts/<chart name> directory within the kustomization root.

Now, first, let's acknowledge that the kustomize maintainers do not recommend downloading Helm charts automatically, nor even relying on Helm for repeated expansion at all. The capability is there in kustomize today, though, so let's clarify how Bazel might interfere.

What could go wrong? Consider:

  • If Bazel sandboxing is enabled, you can't have kustomize download and write files within the sandbox directory tree.

    Instead, you can set the Kustomization helmGlobals.chartHome field to a directory to which Bazel is allowed to write, such as /tmp. Alternately, you can disable sandboxing entirely.

  • If your kustomization directs kustomize to store Helm chart files outside of the kustomization root, or even just refers to such distant files, the default load restrictor will block kustomize from reading them.

    You must relax the default load restrictor by specifying the value None for the kustomized_resources rule's load_restrictor attribute.

Given that your chosen use of Bazel likely implies a preference for hermetic and repeatable builds, it's best to at least acquire and unpack the Helm chart archives beforehand, committing the resulting files for future use. Expanding the Helm chart as manifests outside of kustomize is even better, though it's then harder to include artifacts generated by other Bazel rules. Finding the right balance will take some experimentation.