Skip to main content

Generating Kubernetes Manifests

Now that we have created a new Anemos project, let's write the code to generate Kubernetes manifests for our application.

Main Script

Anemos has already created a main script for us, which contains a basic example of how to use Anemos to generate Kubernetes manifests. Let's take a look at the code:

index.ts
import * as anemos from "@ohayocorp/anemos";

This line imports the Anemos library using TypeScript's standard import syntax. Although imported like any other JavaScript library, the core library is implemented in native code, eliminating the need for dependencies.

index.ts
const builder = new anemos.Builder("1.31", anemos.KubernetesDistribution.Minikube, anemos.EnvironmentType.Development);

Here, we instantiate the anemos.Builder class. The Builder takes information about the target Kubernetes cluster and the deployment environment (e.g., development, staging, production). This context allows Anemos packages to tailor the generated manifests. For instance, they might generate OpenShift Routes instead of standard Ingress' if the distribution is OpenShift, or adjust replica counts based on the environment type.

info

Anemos generates manifests based only on the information provided to the Builder. It does not interact with or require access to an actual Kubernetes cluster during generation.

index.ts
builder.addDocument(
`pod.yaml`,
`
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
`);

The template generated by anemos new includes a basic example of adding a simple Nginx Pod manifest using builder.addDocument(). This demonstrates a simple mechanism for adding resources. For our application, however, we'll replace this Pod definition with a Deployment and a Service.

index.ts
builder.build();

This line invokes the build method, which orchestrates the manifest generation process based on the components and actions added to the builder instance. We'll explore the components and actions in the next section.

Generating the Manifests

The simplest way to define the manifests is using the builder.addDocument() method. This method accepts a Document object (or a path and a YAML string) (or a path and a JavaScript object), which represents a single Kubernetes manifest file (like a Deployment or a Service).

Let's create a Deployment and a Service for a sample Nginx application.

First, define some constants for reuse within our manifests:

index.ts
const name = "example-app";
const namespace = "default";
const image = "nginx";
const replicas = 1;

Next, add the Deployment document using builder.addDocument():

index.ts
builder.addDocument(
`deployment.yaml`,
`
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${name}
namespace: ${namespace}
spec:
replicas: ${replicas}
selector:
matchLabels:
app: ${name}
template:
metadata:
labels:
app: ${name}
spec:
containers:
- name: app
image: ${image}
ports:
- containerPort: 80
`);

The builder.addDocument() function creates a Document object by parsing a YAML string.

  • The first argument (`deployment.yaml`) specifies the filename for the generated manifest.
  • The second argument is a YAML string defining the resource.

This approach leverages JavaScript template literals (backticks ``) for easy variable injection (${name}, ${replicas}, etc.) directly into the YAML structure.

Note the indentation in the YAML string. Anemos automatically removes common leading whitespace from each line before parsing, allowing you to format the YAML for readability within your code.

Now, let's add the Service document by passing a JavaScript object to builder.addDocument():

index.ts
builder.addDocument(
`service.yaml`,
{
apiVersion: "v1",
kind: "Service",
metadata: {
name: name,
namespace: namespace,
},
spec: {
selector: {
app: name
},
ports: [
{
protocol: "TCP",
port: 80,
targetPort: 80
}
]
}
});

Here, instead of a YAML string, we pass:

  • The desired filename (`service.yaml`).
  • A standard JavaScript object representing the Kubernetes Service resource.

This method offers a more structured way to define resources. For enhanced type safety and IDE autocompletion, consider using libraries like kubernetes-models or kubernetes-types to define these objects (package management will be covered later).

info

When creating a Document with a JavaScript object, Anemos converts the object into its YAML representation internally. Subsequent modifications to the original JavaScript object will not automatically update the Document instance.

That's it! To generate the manifests, run the Anemos CLI (or press Ctrl+F5 in VSCode if you have added the keybinding):

anemos build --tsc . dist/index.js

This command executes the dist/index.js file, which is generated by compiling index.ts file, processes the added documents, and writes the resulting deployment.yaml and service.yaml files to the output/manifests directory.

Below are the complete source files and the generated manifests:

index.ts
import * as anemos from "@ohayocorp/anemos";

const builder = new anemos.Builder("1.31", anemos.KubernetesDistribution.Minikube, anemos.EnvironmentType.Development);

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

builder.addDocument(
`deployment.yaml`,
`
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${name}
namespace: ${namespace}
spec:
replicas: ${replicas}
selector:
matchLabels:
app: ${name}
template:
metadata:
labels:
app: ${name}
spec:
containers:
- name: app
image: ${image}
ports:
- containerPort: 80
`);

builder.addDocument(
`service.yaml`,
{
apiVersion: "v1",
kind: "Service",
metadata: {
name: name,
namespace: namespace,
},
spec: {
selector: {
app: name
},
ports: [
{
protocol: "TCP",
port: 80,
targetPort: 80
}
]
}
});

builder.build();
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: example-app
template:
metadata:
labels:
app: example-app
spec:
containers:
- name: app
image: nginx
ports:
- containerPort: 80