Manifests
Kubernetes.Manifest applies one
Kubernetes object, written as a literal object in the same shape as
its YAML. Use it for everything
Deployment and
Job don’t model. That includes
Namespaces, ConfigMaps, Secrets, StatefulSets, DaemonSets, Ingresses,
RBAC, NetworkPolicies, and custom resources.
const config = yield* Kubernetes.Manifest("AppConfig", { cluster, manifest: { apiVersion: "v1", kind: "ConfigMap", metadata: { name: "app-config", namespace: "apps" }, data: { LOG_LEVEL: "info" }, },});The object is applied with server-side apply, re-applied whenever the manifest changes, and deleted on destroy.
Required fields
Section titled “Required fields”apiVersionandkindare required, as in YAML.metadata.nameis required. Objects that rely ongenerateNamearen’t supported, because Alchemy needs a fixed name to find the object again.metadata.namespaceis required for namespaced kinds. A Manifest doesn’t default todefault. Omit the namespace only for cluster-scoped kinds such asNamespace,ClusterRole, orCustomResourceDefinition.
Every other field (spec, data, stringData, type, rules, …)
is passed through untouched. The Kubernetes API server validates the
object when it’s applied, and a rejected object fails the deploy
with the server’s message.
Order objects with Outputs
Section titled “Order objects with Outputs”Each Manifest is its own resource, and resources deploy in parallel
unless one references another. To create an ordering, reference the
name, namespace, kind, apiVersion, or uid attribute of
another Manifest:
const ns = yield* Kubernetes.Manifest("Namespace", { cluster, manifest: { apiVersion: "v1", kind: "Namespace", metadata: { name: "apps" } },});
const quota = yield* Kubernetes.Manifest("Quota", { cluster, manifest: { apiVersion: "v1", kind: "ResourceQuota", metadata: { name: "apps-quota", namespace: ns.name }, spec: { hard: { pods: "50", "requests.cpu": "20" } }, },});ns.name resolves to "apps". It also tells Alchemy to create the
Namespace before the ResourceQuota, and to delete the ResourceQuota
before the Namespace on destroy.
Run a StatefulSet
Section titled “Run a StatefulSet”Sometimes you need full control of a workload’s containers, such as probes, volume mounts, sidecars, or Secret references. In that case, write the workload as a Manifest:
const redis = yield* Kubernetes.Manifest("Redis", { cluster, manifest: { apiVersion: "apps/v1", kind: "StatefulSet", metadata: { name: "redis", namespace: ns.name }, spec: { serviceName: "redis", replicas: 1, selector: { matchLabels: { app: "redis" } }, template: { metadata: { labels: { app: "redis" } }, spec: { containers: [ { name: "redis", image: "redis:7.4", ports: [{ containerPort: 6379 }], volumeMounts: [{ name: "data", mountPath: "/data" }], }, ], }, }, volumeClaimTemplates: [ { metadata: { name: "data" }, spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: "1Gi" } }, }, }, ], }, },});A Manifest applies exactly one object. The StatefulSet’s headless Service is a second Manifest.
Custom resources
Section titled “Custom resources”Kinds Alchemy doesn’t know are looked up through the cluster’s API discovery endpoint, so custom resources work without any registration. The CRD must be installed before the first custom resource is applied. When the CRD comes from the same Stack, reference one of its attributes from the custom resource. An annotation is a convenient place for it:
const crd = yield* Kubernetes.Manifest("WidgetCrd", { cluster, manifest: { apiVersion: "apiextensions.k8s.io/v1", kind: "CustomResourceDefinition", metadata: { name: "widgets.acme.io" }, spec: { group: "acme.io", scope: "Namespaced", names: { plural: "widgets", singular: "widget", kind: "Widget" }, versions: [ { name: "v1", served: true, storage: true, schema: { openAPIV3Schema: { type: "object", properties: { spec: { type: "object", properties: { size: { type: "integer" } } }, }, }, }, }, ], }, },});
const widget = yield* Kubernetes.Manifest("Widget", { cluster, manifest: { apiVersion: "acme.io/v1", kind: "Widget", metadata: { name: "w", namespace: "default", annotations: { "acme.io/crd": crd.name }, }, spec: { size: 3 }, },});The reference also orders destroy, so the Widget is deleted before its CRD. CRDs installed by an operator’s Helm chart come from a HelmChart. Reference one of its attributes the same way.
Updates and replacement
Section titled “Updates and replacement”An object’s identity is its cluster, apiVersion, kind, name,
and namespace:
- Any other field. Changing
spec,data, or labels re-applies the object in place. apiVersion,kind,name, ornamespace. Changing one causes a replacement. The new object is applied, then the old one is deleted.cluster. Changing it causes a replacement. See Move a resource to another cluster.
Server-side apply sends the full object and takes ownership of every field in it. Fields you remove from the manifest are removed from the object. Fields that Alchemy never set are left alone, whether controllers or other tools set them. See How objects are managed.
Attributes
Section titled “Attributes”nameis themetadata.nameof the object.namespaceis themetadata.namespace, orundefinedfor cluster-scoped kinds.kindis the object’s kind.apiVersionis the object’s API version.uidis the UID the API server assigned.refis a reference to the object (apiVersion,kind,name,namespace).connectionis the cluster connection.
Where next
Section titled “Where next”- Helm charts. Apply many objects from a chart as one resource.
- How objects are managed. Field ownership, drift, and deletion.
Manifestreference. Every prop and attribute.