Skip to main content

Generating Kubernetes Manifests

In Anemos, it is possible to define all your Kubernetes manifests in a single JavaScript file. You can import other local and remote files, use variables, and create functions to generate complex manifests. You can also use NPM packages and a more structured approach to define your manifests, but that's not the focus of this tutorial.

Let's start with a simple index.js file that will generate a Kubernetes manifest for us and then we will inspect how it works.

index.js
const anemos = require("@ohayocorp/anemos");

const builder = new anemos.Builder();

builder.addDocument(`
apiVersion: v1
kind: Pod
metadata:
name: example-app
namespace: default
spec:
containers:
- name: app
image: nginx
ports:
- containerPort: 80
`);

builder.build();

Explaining the Code

index.js
const anemos = require("@ohayocorp/anemos");

This line imports the Anemos library. Although imported like any other JavaScript library, the core library is implemented in native code, eliminating the need for dependencies.

index.js
const builder = new anemos.Builder();

Here, we instantiate the anemos.Builder class. Builder drives the entire manifest generation process.

index.js
builder.addDocument(`
apiVersion: v1
kind: Pod
metadata:
name: example-app
namespace: default
spec:
containers:
- name: app
image: nginx
ports:
- containerPort: 80
`);

This code snippet defines a Pod manifest using a YAML string. Documents that are added to the Builder are output as Kubernetes manifests. We will see how to customize these manifests in the below section.

index.js
builder.build();

This line invokes the build method, which orchestrates the manifest generation process.

Outputing the Manifests

To generate the Kubernetes manifests, run the following command in your terminal:

anemos build index.js

This command will execute the index.js file and Anemos will generate the Kubernetes manifests based on the defined documents. The output will be written into the output directory by default. Our pod definition will be the same as the one defined in the index.js file, since we did not customize it further.

pod-example-app.yaml
apiVersion: "v1"
kind: "Pod"
metadata:
name: "example-app"
namespace: "default"
spec:
containers:
- name: "app"
image: "nginx"
ports:
- containerPort: 80

Defining Manifests Using JavaScript Objects

index.js
const anemos = require("@ohayocorp/anemos");

const builder = new anemos.Builder();

builder.addDocument(new anemos.k8s.Service({
metadata: {
name: "example-app",
namespace: "default"
},
spec: {
ports: [
{
port: 80,
targetPort: 80
}
]
}
}));

builder.build();

This code snippet shows another way to define Kubernetes manifests using JavaScript objects. Here, we define a Service manifest using the anemos.k8s.Service class. Note that the apiVersion and kind fields are not required when using the provided classes, as they are inferred from the class itself.

This approach allows you to leverage JavaScript data structures and Kubernetes API types. Kubernetes object schemas are built into Anemos, enabling features like auto-completion and type checking when defining manifests.

Using Helm Charts

You can add Helm charts to the builder using the addHelmChart method. Here is an example of how to add a Helm chart to your index.js file:

index.js
const anemos = require("@ohayocorp/anemos");

const builder = new anemos.Builder();

const certManagerChartUrl = "https://charts.jetstack.io/charts/cert-manager-v1.19.1.tgz";
const certManagerReleaseName = "cert-manager";
const certManagerValues = `
replicaCount: 2
crds:
enabled: true
`;

builder.addHelmChart(certManagerChartUrl, certManagerReleaseName, certManagerValues);

builder.build();

This code snippet adds the cert-manager Helm chart to the builder. When you run the anemos build index.js command, Anemos will fetch the Helm chart, render it with the provided values, and include the generated manifests in the output directory along with the other manifests. For more detail on using Helm charts with Anemos, check out the Helm Interoperability page.

Customizing the Manifests

It is possible to customize the generated manifests in two ways: by using variables when defining the manifests or by modifying the document during the modify step. Steps are explained in more detail in the Components and Actions tutorial.

Using Variables

You can use variables to define dynamic values in your manifests. For example, you can define variables for name, namespace, and image and use it in your Pod definition. JavaScript template literals, text enclosed in backticks (``), allow you to embed expressions within strings (${expression}), making it easy to use variables in your manifests.

index.js
const anemos = require("@ohayocorp/anemos");

const builder = new anemos.Builder();

const name = "example-app";
const namespace = "default";
const image = "nginx";

builder.addDocument(`
apiVersion: v1
kind: Pod
metadata:
name: ${name}
namespace: ${namespace}
spec:
containers:
- name: app
image: ${image}
ports:
- containerPort: 80
`);

builder.build();

Modifying the Document

You can also modify the document during the modify step. This allows you to change the manifest after it has been generated but before it is output. You can use the onModify method of the Builder to add a callback that will be executed during the modify step.

The callback receives a context object that you can use to access the generated documents. Here we get the Pod document we have defined earlier and add a label to it.

const anemos = require("@ohayocorp/anemos");

const builder = new anemos.Builder();

builder.addDocument(`
apiVersion: v1
kind: Pod
metadata:
name: example-app
namespace: default
spec:
containers:
- name: app
image: nginx
ports:
- containerPort: 80
`);

builder.onModify(context => {
// Add a label to the Pod.
context
.getDocument("pod-example-app.yaml")
.setLabel("app", "example-app");
});

builder.build();