Skip to main content

Kubernetes App

Generate a small Kubernetes application bundle from one typed service definition. This example emits a Deployment, Service, ConfigMap, Ingress, and HorizontalPodAutoscaler as separate YAML files.

Source

examples/kubernetes-app/types.rank
use std::collections::{ length }

pub Environment = `dev` | `staging` | `prod`

pub LogLevel = `debug` | `info` | `warn` | `error`

pub FeatureFlag = `enabled` | `disabled`

pub AppRuntimeConfig = Object {
APP_ENV: Environment,
LOG_LEVEL: LogLevel,
FEATURE_LEDGER_EXPORT: FeatureFlag,
JVM_OPTS: string,
}

pub AppSpec = Object {
@constraint(cond: |self| length(self) > 0)
name: string,
@constraint(cond: |self| length(self) > 0)
namespace: string,
@constraint(cond: |self| length(self) > 0)
image: string,
@constraint(cond: |self| self >= 1 && self <= 65535)
containerPort: number,
@constraint(cond: |self| self >= 1 && self <= 65535)
servicePort: number,
@constraint(cond: |self| length(self) > 0)
host: string,
@constraint(cond: |self| self >= 1)
minReplicas: number,
@constraint(cond: |self| self >= 1)
maxReplicas: number,
@constraint(cond: |self| self >= 1 && self <= 100)
cpuTargetUtilization: number,
env: AppRuntimeConfig,
}

pub LabelMap = Object {
...: string,
}

pub Metadata = Object {
name: string,
namespace?: string,
labels?: LabelMap,
annotations?: LabelMap,
}

pub LabelSelector = Object {
matchLabels: LabelMap,
}

pub ConfigMapEnvSource = Object {
configMapRef: Object {
name: string,
},
}

pub ContainerPort = Object {
containerPort: number,
}

pub Container = Object {
name: string,
image: string,
ports: [ContainerPort],
envFrom?: [ConfigMapEnvSource],
}

pub PodSpec = Object {
containers: [Container],
}

pub PodTemplate = Object {
metadata: Object {
labels: LabelMap,
},
spec: PodSpec,
}

pub DeploymentSpec = Object {
replicas: number,
selector: LabelSelector,
template: PodTemplate,
}

pub Deployment = Object {
apiVersion: `apps/v1`,
kind: `Deployment`,
metadata: Metadata,
spec: DeploymentSpec,
}

pub ServicePort = Object {
port: number,
targetPort: number,
}

pub ServiceSpec = Object {
type?: `ClusterIP` | `LoadBalancer`,
selector: LabelMap,
ports: [ServicePort],
}

pub Service = Object {
apiVersion: `v1`,
kind: `Service`,
metadata: Metadata,
spec: ServiceSpec,
}

pub ConfigMap = Object {
apiVersion: `v1`,
kind: `ConfigMap`,
metadata: Metadata,
data: Object {
...: string,
},
}

pub IngressServiceBackend = Object {
name: string,
port: Object {
number: number,
},
}

pub IngressBackend = Object {
service: IngressServiceBackend,
}

pub HTTPIngressPath = Object {
path: string,
pathType: `Prefix` | `Exact`,
backend: IngressBackend,
}

pub HTTPIngressRule = Object {
paths: [HTTPIngressPath],
}

pub IngressRule = Object {
host: string,
http: HTTPIngressRule,
}

pub IngressSpec = Object {
ingressClassName: string,
rules: [IngressRule],
}

pub Ingress = Object {
apiVersion: `networking.k8s.io/v1`,
kind: `Ingress`,
metadata: Metadata,
spec: IngressSpec,
}

pub CpuMetricTarget = Object {
type: `Utilization`,
averageUtilization: number,
}

pub ResourceMetricSource = Object {
name: `cpu`,
target: CpuMetricTarget,
}

pub MetricSpec = Object {
type: `Resource`,
resource: ResourceMetricSource,
}

pub ScaleTargetRef = Object {
apiVersion: `apps/v1`,
kind: `Deployment`,
name: string,
}

pub HorizontalPodAutoscalerSpec = Object {
scaleTargetRef: ScaleTargetRef,
minReplicas: number,
maxReplicas: number,
metrics: [MetricSpec],
}

pub HorizontalPodAutoscaler = Object {
apiVersion: `autoscaling/v2`,
kind: `HorizontalPodAutoscaler`,
metadata: Metadata,
spec: HorizontalPodAutoscalerSpec,
}
examples/kubernetes-app/main.rank
/// Emits a small Kubernetes application bundle from one typed service
/// definition. Shows shared types, derived manifests, and Emit::manifest.

use std::Emit
use std::Path
use root::types::{
AppSpec,
ConfigMap,
Deployment,
HorizontalPodAutoscaler,
Ingress,
LabelMap,
Metadata,
Service,
}

app: AppSpec = {
name: `payments-api`,
namespace: `platform`,
image: `ghcr.io/acme/payments-api:1.4.0`,
containerPort: 8080,
servicePort: 80,
host: `payments.example.internal`,
minReplicas: 2,
maxReplicas: 6,
cpuTargetUtilization: 70,
env: {
APP_ENV: `staging`,
LOG_LEVEL: `info`,
FEATURE_LEDGER_EXPORT: `enabled`,
JVM_OPTS: `-Xms256m -Xmx512m`,
},
}

labels: LabelMap = {
`app.kubernetes.io/name`: app.name,
`app.kubernetes.io/component`: `api`,
`app.kubernetes.io/part-of`: `payments-platform`,
`app.kubernetes.io/managed-by`: `rank`,
}

config_map_name = `${app.name}-config`
service_name = app.name
ingress_name = `${app.name}-ingress`
hpa_name = `${app.name}-hpa`

metadata_for = |name: string| -> Metadata {
return {
name: name,
namespace: app.namespace,
labels: labels,
}
}

config_map: ConfigMap = {
apiVersion: `v1`,
kind: `ConfigMap`,
metadata: metadata_for(config_map_name),
data: {
APP_ENV: app.env.APP_ENV,
LOG_LEVEL: app.env.LOG_LEVEL,
FEATURE_LEDGER_EXPORT: app.env.FEATURE_LEDGER_EXPORT,
JVM_OPTS: app.env.JVM_OPTS,
},
}

deployment: Deployment = {
apiVersion: `apps/v1`,
kind: `Deployment`,
metadata: metadata_for(app.name),
spec: {
replicas: app.minReplicas,
selector: {
matchLabels: labels,
},
template: {
metadata: {
labels: labels,
},
spec: {
containers: [
{
name: app.name,
image: app.image,
ports: [
{
containerPort: app.containerPort,
},
],
envFrom: [
{
configMapRef: {
name: config_map_name,
},
},
],
},
],
},
},
},
}

service: Service = {
apiVersion: `v1`,
kind: `Service`,
metadata: metadata_for(service_name),
spec: {
type: `ClusterIP`,
selector: labels,
ports: [
{
port: app.servicePort,
targetPort: app.containerPort,
},
],
},
}

ingress: Ingress = {
apiVersion: `networking.k8s.io/v1`,
kind: `Ingress`,
metadata: metadata_for(ingress_name),
spec: {
ingressClassName: `nginx`,
rules: [
{
host: app.host,
http: {
paths: [
{
path: `/`,
pathType: `Prefix`,
backend: {
service: {
name: service_name,
port: {
number: app.servicePort,
},
},
},
},
],
},
},
],
},
}

hpa: HorizontalPodAutoscaler = {
apiVersion: `autoscaling/v2`,
kind: `HorizontalPodAutoscaler`,
metadata: metadata_for(hpa_name),
spec: {
scaleTargetRef: {
apiVersion: `apps/v1`,
kind: `Deployment`,
name: app.name,
},
minReplicas: app.minReplicas,
maxReplicas: app.maxReplicas,
metrics: [
{
type: `Resource`,
resource: {
name: `cpu`,
target: {
type: `Utilization`,
averageUtilization: app.cpuTargetUtilization,
},
},
},
],
},
}

pub main = || Emit::manifest({
entries: [
{
path: Path::join([`k8s`, `configmap.yaml`]),
format: `yaml`,
value: config_map,
},
{
path: Path::join([`k8s`, `deployment.yaml`]),
format: `yaml`,
value: deployment,
},
{
path: Path::join([`k8s`, `service.yaml`]),
format: `yaml`,
value: service,
},
{
path: Path::join([`k8s`, `ingress.yaml`]),
format: `yaml`,
value: ingress,
},
{
path: Path::join([`k8s`, `hpa.yaml`]),
format: `yaml`,
value: hpa,
},
]
})

Output

Running the example with --file-root writes these files:

  • k8s/configmap.yaml
  • k8s/deployment.yaml
  • k8s/service.yaml
  • k8s/ingress.yaml
  • k8s/hpa.yaml
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: payments-api
namespace: platform
labels:
app.kubernetes.io/name: payments-api
app.kubernetes.io/component: api
app.kubernetes.io/part-of: payments-platform
app.kubernetes.io/managed-by: rank
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: payments-api
app.kubernetes.io/component: api
app.kubernetes.io/part-of: payments-platform
app.kubernetes.io/managed-by: rank
template:
metadata:
labels:
app.kubernetes.io/name: payments-api
app.kubernetes.io/component: api
app.kubernetes.io/part-of: payments-platform
app.kubernetes.io/managed-by: rank
spec:
containers:
- name: payments-api
image: ghcr.io/acme/payments-api:1.4.0
ports:
- containerPort: 8080
envFrom:
- configMapRef:
name: payments-api-config
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: payments-api-hpa
namespace: platform
labels:
app.kubernetes.io/name: payments-api
app.kubernetes.io/component: api
app.kubernetes.io/part-of: payments-platform
app.kubernetes.io/managed-by: rank
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: payments-api
minReplicas: 2
maxReplicas: 6
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70

Key concepts

  • Single typed app spec drives every Kubernetes artifact instead of hand-editing multiple YAML files.
  • Shared metadata helper keeps labels and namespace consistent across Deployment, Service, Ingress, ConfigMap, and HPA.
  • Emit::manifest(...) emits multiple named files from one Rank program.
  • ConfigMap-backed container configuration keeps runtime settings alongside the workload definition.
  • Autoscaling policy as data makes min/max replicas and CPU targets part of the checked program.

Run it

rank examples/kubernetes-app --file-root out/kubernetes-app