diff --git a/docs/README.md b/docs/README.md index 710d0d743..e34d7a5eb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,29 +3,45 @@ ![GitHub Sponsors](https://img.shields.io/github/sponsors/kapicorp?style=for-the-badge) ![GitHub Stars](https://img.shields.io/github/stars/kapicorp/kapitan?style=for-the-badge) -**`Kapitan`** aims to be your *one-stop tool* to help you manage the ever growing complexity of your configurations. +**`Kapitan`** aims to be your *one-stop **Platform Engineering** tool* to help you manage the ever growing complexity of your configurations. + +Empower your engineers to make changes to your infrastructure whilst maintaining full control, with a GitOps approach and full transparency. * :fontawesome-brands-slack: Join the community [`#kapitan`](https://kubernetes.slack.com/archives/C981W2HD3) * :fontawesome-brands-github: Help us grow: [give us a star](https://github.com/kapicorp/kapitan/stargazers) or even better [sponsor our project](pages/contribute/sponsor/) ## Why do I need **Kapitan**? -> I use [`Helm`](https://helm.sh)/[`Kustomize`](https://kustomize.io/)/that-new-kid-on-the-block +**Kapitan** allows you to bring all your configuration needs under one home, enabling **Platform Engineering** by creating a uniform way to manage your configuration that no other tool provides. -**Kapitan** allows you to bring all your configuration needs under one home, creating a uniform way to manage your configuration that no other tool provides. Seamlessly manage configurations for Kubernetes, Terraform and any other application. Integrate with Helm (and even Kustomize). Safely store your secrets using a range of Secret Backends +Seamlessly manage: -[Longer answer](pages/blog/2022-12-04.md#why-do-i-need-kapitan) +* Kubernetes +* Terraform +* Ansible +* Documentation +* Any other tool you can think of! + +Powerful integrations -!!! info - We are working hard to update all our documentation. Please reach out if you notice something that needs improving or you have other questions or comments. +* Integrate with Helm (and even Kustomize). +* Secret Backends + * gkms + * vault + * awskms + * azurekms + * ...and more + +[Longer answer](pages/blog/2022-12-04.md#why-do-i-need-kapitan) -## Dazzle me with a demo -![demo](images/kapitan-demo.gif) +## Video Tutorials to get started +[Kapitan Youtube Channel](https://t.co/djBtelEPPH) ## Related projects +* [Generators](https://github.com/kapicorp/generators) - Kapitan's powerful generators libraries * [Tesoro](https://github.com/kapicorp/tesoro) - Kubernetes Admission Controller for Kapitan Secrets * [Kapitan Reference](https://github.com/kapicorp/kapitan-reference) - our reference repository to get started with Kapitan diff --git a/docs/pages/examples/kubernetes.md b/docs/pages/examples/kubernetes.md deleted file mode 100644 index cc0c3b048..000000000 --- a/docs/pages/examples/kubernetes.md +++ /dev/null @@ -1,226 +0,0 @@ ---- -tags: - - kubernetes ---- -# :kapitan-logo: **Kubernetes examples** - -!!! danger - This documentation is not up-to-date. - - Please refer to the [documentation](/pages/kapitan_overview/), [Getting started](/getting_started/) or look at the [Kapitan Reference](https://github.com/kapicorp/kapitan-reference) repository. - -Here, we walk through how kapitan could be used to help create kubernetes manifests, whose values are customized for each target according to the inventory structure. The example folder can be found in our repository on Github at . - -## Directory structure - -The following tree shows what this directory looks like (only showing tree level 1): - -```text -├── components -├── docs -├── inventory -├── lib -├── scripts -├── refs -└── templates -``` - -We will describe the role of each folder in the following sections. - -### inventory - -This folder contains the inventory values used to render the templates for each target. The structure of this folder is as follows: - -```text -. -├── classes -│ ├── cluster -│ │ ├── common.yml -│ │ └── minikube.yml -│ ├── common.yml -│ └── component -│ ├── elasticsearch.yml -│ ├── mysql.yml -│ ├── namespace.yml -│ └── nginx.yml -└── targets - ├── minikube-es.yml - ├── minikube-mysql.yml - └── minikube-nginx.yml - -``` - -The required sub-folder is `targets`: during compile, kapitan searches for the yaml files under `targets` in order to identify the targets. In this example, there are three targets: - -- minikube-es -- minikube-mysql -- minikube-nginx - - Therefore, when you run `kapitan compile`, under the `compiled` folder that kapitan generates, you will see three folders named after these targets. - -`classes` is a folder that contains yaml files used as the "base class" in the hierarchical inventory database. The values defined here are inherited by the target files. For more explanation on how this works, look at the [inventory documentation](/inventory.md). Notice how the classes are nicely divided up into components and clusters, such as nginx and mysql, in order to clearly define what components each target should contain and to make the classes reusable. - -For example, take a look at `targets/nginx.yml`: - -```yaml -classes: - - common - - cluster.minikube - - component.namespace - - component.nginx - -parameters: - target_name: minikube-nginx - namespace: ${target_name} -``` - -This target inherits values from four files under `classes` folder: - -- common.yml -- cluster/minikube.yml -- component/namespace.yml -- component/nginx.yml - -*Note: that some of these classes themselves may inherit from other classes.* - -And the way classes are defined makes it easy to identify what components and clusters this target should contain and belong to! - -Let's take a close look now at `component/namespace.yml`: - -```yaml -parameters: - namespace: ${target_name} - kapitan: - compile: - - output_path: pre-deploy - input_type: jsonnet - output_type: yaml - input_paths: - - components/namespace/main.jsonnet -``` - -As we see, this file declares a `kapitan.compile` item whose input path (i.e. the template file) is `components/namespace/main.jsonnet` which, when rendered, will generate yaml file(s) under `compiled/minikube-nginx/pre-deploy`. - -Don't confuse the `components` folder with `inventory/classes/components` folder: the former contains the actual templates, while the latter contains inventory classes. - -### components - -This folder contains the template files as discussed above, typically jsonnet and kadet files. The tree of this directory looks as follows: - -```text -. -├── elasticsearch -│ ├── elasticsearch.container.jsonnet -│ ├── elasticsearch.statefulset.jsonnet -│ └── main.jsonnet -├── mysql -│ ├── main.jsonnet -│ ├── secret.jsonnet -│ ├── service.jsonnet -│ └── statefulset.jsonnet -├── namespace -│ └── main.jsonnet -└── nginx - └── __init__.py -``` - -Notice how the directory structure corresponds to that of `inventory/classes/components` in order to make it easy to identify which templates are used for which components. - -As mentioned above, we know that the target **minikube-nginx** inherits from `component.namespace`. Let's take a look at `components/namespace/main.jsonnet`: - -```json -local kube = import "lib/kube.libjsonnet"; -local kap = import "lib/kapitan.libjsonnet"; -local inventory = kap.inventory(); -local p = inventory.parameters; - -{ - "00_namespace": kube.Namespace(p.namespace), - "10_serviceaccount": kube.ServiceAccount("default") -} -``` - -The first two lines import libjsonnet files under `lib` folder: this is the folder that contains helper files used inside templates. For example, `kapitan.libjsonnet` allows you to access inventory values inside jsonnet templates, and `kube.libjsonnet` defines functions to generate popular kubernetes manifests. - -The actual object defined in `components/namespace/main.jsonnet` looks like this: - -```json -{ - "00_namespace": kube.Namespace(p.namespace), - "10_serviceaccount": kube.ServiceAccount("default") -} -``` - -We have "00_namespace" and "10_serviceaccount" as the keys. These will become files under `compiled/minikube-nginx/pre-deploy`, since `pre-deploy` is the `input_paths` declared in the inventory. For instance, `00_namespace.yml` would look like this: - -```yaml -apiVersion: v1 -kind: Namespace -metadata: - annotations: {} - labels: - name: minikube-nginx - name: minikube-nginx - namespace: minikube-nginx -spec: {} -``` - -### templates, docs, scripts - -These folders contain jinja2 template files. For example, `component.elasticsearch` contains: - -```yaml -kapitan: - compile: - # other items abbreviated for clarity - - output_path: scripts - input_type: jinja2 - input_paths: - - scripts - - output_path: . - input_type: jinja2 - input_paths: - - docs/elasticsearch/README.md -``` - -Since `component.elasticsearch` is inherited by the target **minikube-es**, this generates files under `compiled/minikube-es/scripts` and `compiled/minikube-es/README.md`. - -### References - -This folder contains references created manually by the user, or automatically by kapitan. Refer to [references management](/references.md) for how it works. - -In this example, the configuration, such as the recipients, is declared in `inventory/classes/common.yml`: - -```yaml -parameters: - kapitan: - vars: - target: ${target_name} - namespace: ${target_name} - secrets: - gpg: - recipients: - - name: example@kapitan.dev - fingerprint: D9234C61F58BEB3ED8552A57E28DC07A3CBFAE7C -``` - -The references to the secrets are declared in `inventory/classes/component/mysql`, which is inherited by the target **minikube-mysql**. After running `kapitan compile`, some of the generated manifests contain the references to secrets. For example, have a look at `compiled/minikube-mysql/manifests/mysql_secret.yml`: - -```yaml -apiVersion: v1 -data: - MYSQL_ROOT_PASSWORD: ?{gpg:targets/minikube-mysql/mysql/password:ec3d54de} - MYSQL_ROOT_PASSWORD_SHA256: ?{gpg:targets/minikube-mysql/mysql/password_sha256:122d2732} -kind: Secret -metadata: - annotations: {} - labels: - name: example-mysql - name: example-mysql - namespace: minikube-mysql -type: Opaque -``` - -`MYSQL_ROOT_PASSWORD` refers to the secret stored in `refs/targets/minikube-mysql/mysql/password` and so on. - -You may reveal the secrets by running `kapitan refs --reveal -f mysql_secret.yml` and use the manifest by piping the output to kubectl! diff --git a/docs/pages/examples/terraform.md b/docs/pages/examples/terraform.md deleted file mode 100644 index a0cc49394..000000000 --- a/docs/pages/examples/terraform.md +++ /dev/null @@ -1,206 +0,0 @@ ---- -tags: - - terraform ---- -# :kapitan-logo: **Terraform example** - -!!! danger - This documentation is not up-to-date. - - Please refer to the [documentation](/pages/kapitan_overview/), [Getting started](/getting_started/) or look at the [Kapitan Reference](https://github.com/kapicorp/kapitan-reference) repository. - -We will be looking at how to use Kapitan to compile terraform files with Jsonnet as the input type. It's possible to use other input types, however, Jsonnet is recommended. -For example, we could use the Kadet input to generate terraform files but this would require templates to be written in YAML then rendered into JSON. -It is possible to allow Kadet to consume JSON as an input. This enables you to integrate your organizations pre-existing terraform JSON file's as templates. -Jsonnet is the most straightforward input type as you will see due to its functional nature. The only appropriate output type is JSON since this is the format that Terraform consumes. - -## Directory structure - -There are several examples available in `examples/terraform`. This will be our working directory for this documentation. The directory structure is as follows: - -```text -├── inventory -└── templates -``` - -It is possible to further extend this locally to include a `lib` directory where a `terraform.libjsonnet` file can be stored for use. This is generally dependent on the project scope and organizational patterns. -We will describe in more detail the role of each of these folders in the following sections. - -### inventory - -This folder contains the inventory files used to render the templates for each target. The structure of this folder is as follows: - -```text -. -├── classes -│   ├── env -│   │   ├── develop.yml -│   │   ├── prod.yml -│   │   └── sandbox.yml -│   ├── provider -│   │   └── gcp.yml -│   └── type -│   └── terraform.yml -├── reclass-config.yml -└── targets - ├── develop - │   └── project1.yml - ├── prod - │   └── project2.yml - └── sandbox - └── project3.yml -``` - -The `targets` directory enables us to define various projects. We can specify each project as an environment such as `dev`, `staging` and `production` with each having unique parameters. - -The following is an example targets file. `type.terraform` is what defines the entry point into the main Jsonnet template file. The parameters in the file `inventory/targets/develop/project1.yml` will then be utilized to set the environmental specific provider/resource configuration. -We define the default region and zone for terraform's provider configuration. The default DNS TTL for the DNS resource is also configured for the development environment. - -```yaml -classes: - - type.terraform - -parameters: - name: project1 - region: europe-west2 - zone: europe-west2-a - - dns_default_ttl: 300 -``` - -In the following example, we use a reclass configuration file to specify further parameters that we would like to merge into our project files. Thus we define nodes, which are stored in targets and environmental mandatory parameters stored in `classes/env/`. The reclass config is shown below: - -```yaml -storage_type: yaml_fs -pretty_print: true -output: yaml -inventory_base_uri: . -nodes_uri: targets -classes_uri: classes -compose_node_name: false -class_mappings: - - develop/* env.develop - - prod/* env.prod - - sandbox/* env.sandbox -``` - -The following class `provider.gcp` will be found in all files in this path since it is a common configuration for the cloud authentication module. - -```yaml -classes: - - provider.gcp -``` - -Further classes that group parameters together can be included. To assist in further refining the configuration. - -### components - -We tend to use components as a method to organize Jsonnet files. This is not mandatory since it is possible to configure Kapitan to look for input files wherever you would like. -You can have these in any path just ensure you define that path in `inventory/classes/type/terraform.yml`. - -The templates folder is where the Jsonnet is located in this instance as shown below: - -```text -. -├── cloudbuild.jsonnet -├── dns.jsonnet -├── iam.jsonnet -├── iam_service_account.jsonnet -├── kms.jsonnet -├── kubernetes.jsonnet -├── logging.jsonnet -├── main.jsonnet -├── monitoring.jsonnet -├── org_iam.jsonnet -├── output.jsonnet -├── provider.jsonnet -├── pubsub.jsonnet -├── README.md.j2 -└── storage.jsonnet -``` - -The main thing to understand about terraform components is that they are strictly handled by Jsonnet for simplicity. The rendering logic is as follows: - -```json5 -{ - "output.tf": output, - "provider.tf": provider, - [if name_in_resoures("cloudbuild") then "cloudbuild.tf"]: cloudbuild, - [if name_in_resoures("container") then "kubernetes.tf"]: kubernetes, - [if name_in_resoures("dns") then "dns.tf"]: dns, - [if name_in_resoures("iam") && "serviceaccounts" in p.resources.iam then "iam_service_account.tf"]: iam_service_account, -... - [if name_in_resoures("pubsub") then "pubsub.tf"]: pubsub, - [if name_in_resoures("storage") then "storage.tf"]: storage, -} -``` - -Each Jsonnet file defines a resource and then it is imported. Jsonnet then filters through all the inventory parameters to find specific keys that have been defined. Let's take for example the cloud build resource: - -```json5 -local cloudbuild = import "cloudbuild.jsonnet"; -... -{ - "output.tf": output, - "provider.tf": provider, - [if name_in_resoures("cloudbuild") then "cloudbuild.tf"]: cloudbuild, -... -} -``` - -Assuming that one of the configuration files for a specific environment has the parameter key `cloudbuild` set. -These parameters will then be interpreted by the `cloudbuild.jsonnet` template. -A file named `cloudbuild.tf.json` will then be compiled using the parameters associated with the `cloudbuild` parameter key. - -It is important to understand that once you have a deeper understanding of Kapitan's capabilities, you can organize these files to a style and logic suitable for your organization. - -### templates, docs, scripts - -Jinja2 is used to generate documentation that sources information from kapitan's inventory. This enables the ability to have dynamic documentation based on your infrastructure configuration changes. -In `templates/terraform` you will find `README.md.j2`. This is used to generate a `README.md` template to be utilized by terraform's output module. - -The following is what generates the documentation: - -```json5 - data: { - template_file: { - readme: { - template: kap.jinja2_template("templates/terraform/README.md.j2", inv), - }, - }, - }, - - output: { - "README.md": { - value: "${data.template_file.readme.rendered}", - sensitive: true, - }, - }, -``` - -The function `kap.jinja2_template()` (imported from `kapitan.libjsonnet`) is used to convert and interpreter the `README.md.j2` file into a raw string using the inventory for evaluation logic. - -Based on the various parameters jinja2 decides which sections of the readme should be included. -When terraform runs it will use the output module to generate your desired `README.md` using information from terraform's state. - -Scripts are located in the `scripts` directory. They are compiled using jinja2 as the templating language. An example is as follows: - -```shell -export TF_DATA_DIR=$(realpath -m ${DIR}/../../../.TF_DATA_DIR/{{inventory.parameters.name}}) # Folder for TF initialization (preferable outside of compiled) -export OUTPUT_DIR=$(realpath -m ${DIR}/../../../output/{{inventory.parameters.name}}) # Folder for storing output files (preferable outside of compiled) -``` - -It is good practice to utilize this method to improve integration with various CLI based tools. Scripts help to ensure terraform and -kapitan can function with your CI/CD systems. It generally depends on your organizational workflows. - -### References - -Although there are no particular references in this instance. It is possible to utilize Kapitan references as defined in [referemce management](/references.md). - -### Collaboration - -In some situations you may find teams that are used to writing terraform in HCL. In such situations it may be difficult to adopt Kapitan into the companies workflows. -We can however use terraform modules to simplify the integration process. This means teams which are used to writing in HCL will not need to completely adopt Jsonnet. - -Modules can be imported into projects by defining them under the `modules` parameter key as shown in `inventory/targets/sandbox`. This means teams will only have to worry about coordinating parameter inputs for different projects. -Jsonnet provides the ability to specify conventions and validation of input parameters. This provides peace of mind to infrastructure administrators around the tools usage. diff --git a/docs/pages/kapitan_overview.md b/docs/pages/kapitan_overview.md index 86cff2692..ec6379c93 100644 --- a/docs/pages/kapitan_overview.md +++ b/docs/pages/kapitan_overview.md @@ -86,110 +86,35 @@ graph LR ``` -## Essential concepts +**Kapitan** is a powerful configuration management tool designed to help engineers manage complex systems through code. It centralizes and simplifies the management of configurations with a structured approach that revolves around a few core concepts. -### **Inventory** +Let's explore these concepts in a way that's accessible to new users: -The **Inventory** is a hierarchical database of variables, defined in yaml files, that are passed to the targets during compilation. +## Core Concepts of Kapitan -The **Inventory** is the heart of **Kapitan**. +### Inventory: The Backbone -Using simple reusable `yaml` files (classes), you can represent as a SSOT -everything that matters in your setup, for instance you can define: +At the core of **Kapitan** lies the Inventory, a structured database of variables meticulously organized in YAML files. +This hierarchical setup serves as the single source of truth (SSOT) for your system's configurations, making it easier to manage and reference the essential components of your infrastructure. Whether you're dealing with Kubernetes configurations, Terraform resources, or even business logic, the Inventory allows you to define and store these elements efficiently. This central repository then feeds into **Kapitan**'s templating engines, enabling seamless reuse across various applications and services. -* kubernetes `components` definitions -* terraform resources -* business concepts -* documentation and tooling -* ...anything else you want! +### Templating with Input Types -After defining it, you can make this data available to the various templating engines [***Input types***](#input-types) offered by Kapitan, allowing you to reuse it. +**Kapitan** takes the information stored in the Inventory and brings it to life through its templating engines upon compilation. This process transforms static data into dynamic configurations, capable of generating a wide array of outputs like Kubernetes manifests, Terraform plans, documentation, and scripts. It's about making your configurations work for you, tailored to the specific needs of your projects. -Find more detaled explanation in the [inventory](inventory/introduction.md) section of the documentation. +#### Getting Started with Generators -### Input types +Generators offer a straightforward entry point into using **Kapitan**, requiring minimal to no coding experience. These are essentially pre-made templates that allow you to generate common configuration files, such as Kubernetes manifests, directly from your Inventory data. **Kapitan** provides a wealth of resources, including the **Kapitan** Reference repository and various blog posts, to help users get up and running with generators. -On compilation, **Kapitan** "renders" the **Inventory** and makes it available to templates that can generate any configuration you want, including **Kubernetes manifests**, documentation/playbooks, **Terraform configuration** or even scripts. +#### Kadet: Advanced Configuration with Python -#### [**Generators**](https://medium.com/kapitan-blog/keep-your-ship-together-with-kapitan-d82d441cc3e7) +For those looking to leverage the full power of **Kapitan**, Kadet introduces a method to define and reuse complex configurations through Python. This internal library facilitates the creation of JSON and YAML manifests programmatically, offering a higher degree of customization and reuse. Kadet empowers users to craft intricate configurations with the simplicity and flexibility of Python. -> Generators are simplest way of getting started with **Kapitan** and require no code at all. Check out our [**Kapitan** Reference](https://github.com/kapicorp/kapitan-reference) repository to get started or our Read our blog post [**Keep your ship together with Kapitan**](https://medium.com/kapitan-blog/keep-your-ship-together-with-kapitan-d82d441cc3e7). +### **Kapitan** References: Your Flexible Configuration Superpower -The simplest way to get started with Kapitan. -Generators are ***universal templates*** that are a simplified way to generate configuration -files (for instance, Kubernetes manifests) without using any templating at all. - -#### [**Kadet**](https://github.com/kapicorp/kadet) - -> Easily define and reuse complex Python objects that serialize into JSON or YAML - -Use **kadet**, our home built Python library, to easily generate json and yaml manifests for your applications. - -Using **kadet** is simple as using Python - -???+ example "`examples/kubernetes/components/nginx-kadet/__init__.py`" - ```python - --8<-- "kubernetes/components/nginx-kadet/__init__.py" - ``` - -**kadet** is what [**generators**](#generators) are being built with. See and example - -Head over to [**kapicorp/kadet**](https://github.com/kapicorp/kadet) for more details - -Find help in :fontawesome-brands-slack: [`#kapitan`](https://kubernetes.slack.com/archives/C981W2HD3) - -#### [**Jsonnet**](https://jsonnet.org/) - -> A powerful DSL for elegant description of JSON data - -Use the **jsonnet** input type to compile `jsonnet` code, and have access to a large amount of available `jsonnet` libraries like [**bitnami-labs/kube-libsonnet**](https://github.com/bitnami-labs/kube-libsonnet) - -Find help in :fontawesome-brands-slack: [`#kapitan`](https://kubernetes.slack.com/archives/C981W2HD3) or :fontawesome-brands-slack: [`#jsonnet`](https://kubernetes.slack.com/archives/C6JLE4L9X) - -???+ example "`examples/kubernetes/components/nginx-jsonnet/main.jsonnet`" - ```python - --8<-- "kubernetes/components/nginx-jsonnet/main.jsonnet" - ``` - -Head over to [jsonnet](https://jsonnet.org/) to learn more - -#### [***Jinja2***](http://jinja.pocoo.org/) - -> Jinja is a fast, expressive, extensible templating engine - -Good old Jinja to create text based templates for scripts and documentation. - -Don't underestimate the power of this very simple approach to create templated scripts and documentation! - -???+ example "`examples/kubernetes/scripts/setup_cluster.sh`" - ```shell - --8<-- "kubernetes/scripts/setup_cluster.sh" - ``` - -Find help in :fontawesome-brands-slack: [`#kapitan`](https://kubernetes.slack.com/archives/C981W2HD3) - -#### :simple-helm: [***Helm***](https://helm.sh/) - -> The package manager for Kubernetes - -Kapitan can also be used to manage **Helm**, giving you access to its enourmous catalogues of [**Helm charts**](https://artifacthub.io/packages/search?kind=0). - -???+ example "`examples/kubernetes/inventory/classes/component/nginx-helm.yml`" - !!! note "" - - [external dependencies](external_dependencies.md) are used to automatically fetch helm charts in this example. - - Please use the `kapitan compile --fetch` flag if the chart has not been downloaded already - - ```yaml - --8<-- "kubernetes/inventory/classes/component/nginx-helm.yml" - ``` - -Find help in :fontawesome-brands-slack: [`#kapitan`](https://kubernetes.slack.com/archives/C981W2HD3) - -### [References](https://medium.com/kapitan-blog/declarative-secret-management-for-gitops-with-kapitan-b3c596eab088) - -Use Kapitan to securely generate and manage secrets with GPG, AWS KMS, gCloud KMS and Vault. +**Kapitan** references provide a secure way to store passwords, settings, and other essential data within your project. Think of them as special code placeholders. +Flexibility: Update a password once, and Kapitan updates it everywhere automatically. +Organization: References tidy up your project, especially when you're juggling multiple settings or environments (dev, staging, production). +Security: Protect sensitive information like passwords with encryption !!! tip diff --git a/mkdocs.yml b/mkdocs.yml index 4a59c8d19..047e69c04 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -113,9 +113,7 @@ nav: - searchvar: pages/commands/kapitan_searchvar.md - validate: pages/commands/kapitan_validate.md - kapitan dotfile: pages/commands/kapitan_dotfile.md - - Examples: - - Kubernetes: pages/examples/kubernetes.md - - Terraform: pages/examples/terraform.md + - Generators: https://generators.kapitan.dev/ - Blog: - 2023: - Kapitan Generators: pages/blog/2023-08-27.md