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.
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
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.
const builder = new anemos.Builder();
Here, we instantiate the anemos.Builder class. Builder drives the entire manifest generation process.
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.
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.
apiVersion: "v1"
kind: "Pod"
metadata:
name: "example-app"
namespace: "default"
spec:
containers:
- name: "app"
image: "nginx"
ports:
- containerPort: 80
Defining Manifests Using JavaScript Objects
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:
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.
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.
- index.js
- pod-example-app.yaml
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();
apiVersion: "v1"
kind: "Pod"
metadata:
name: "example-app"
namespace: "default"
labels:
app: "example-app"
spec:
containers:
- name: "app"
image: "nginx"
ports:
- containerPort: 80