@konfig.ts/k8s 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -130
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,154 +1,96 @@
|
|
|
1
1
|
# @konfig.ts/k8s
|
|
2
2
|
|
|
3
|
-
Kubernetes resource
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
and the `Dep.*` tracking propagates needs/provides through Effect
|
|
8
|
-
`Layer`s.
|
|
3
|
+
Kubernetes resource builders with branded references. Cross-resource links — an
|
|
4
|
+
env var to a Secret, a volume to a ConfigMap, Ingress TLS to a Secret — are
|
|
5
|
+
checked at the type level, so a workload pointing at a Secret nobody creates is
|
|
6
|
+
a compile error, not a failed `argocd sync`.
|
|
9
7
|
|
|
10
|
-
##
|
|
8
|
+
## Install
|
|
11
9
|
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const apiCreds = SecretRef.of("api-creds") // SecretRef<"api-creds">
|
|
16
|
-
const cfg = ConfigMapRef.of("oauth-templates") // ConfigMapRef<"oauth-templates">
|
|
17
|
-
const sa = ServiceAccountRef.of("api") // ServiceAccountRef<"api">
|
|
10
|
+
```bash
|
|
11
|
+
bun add @konfig.ts/k8s
|
|
18
12
|
```
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
enforcement points (env var `secretKeyRef` / `configMapKeyRef`, volume
|
|
22
|
-
secret, volume configMap, `imagePullSecret`, Ingress TLS) all accept the
|
|
23
|
-
branded type and reject raw strings.
|
|
24
|
-
|
|
25
|
-
## Identity constructors expose `.ref`
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
const apiSecret = Secret.make({ name: "api-creds", namespace: "prod", stringData: {...} });
|
|
29
|
-
// apiSecret.ref is a SecretRef<"api-creds">
|
|
30
|
-
```
|
|
14
|
+
## Usage
|
|
31
15
|
|
|
32
|
-
|
|
33
|
-
|
|
16
|
+
Compose a container and a web workload (Deployment + Service, plus an Ingress
|
|
17
|
+
if you add one):
|
|
34
18
|
|
|
35
19
|
```ts
|
|
36
|
-
|
|
20
|
+
import { Container, EnvVar, Port, Secret, Workload } from "@konfig.ts/k8s"
|
|
21
|
+
|
|
22
|
+
const apiCreds = Secret.make({
|
|
23
|
+
name: "api-creds",
|
|
24
|
+
namespace: "prod",
|
|
25
|
+
stringData: { url: "postgres://…" }
|
|
26
|
+
}) // apiCreds.ref is a SecretRef<"api-creds">
|
|
27
|
+
|
|
28
|
+
const api = Container.define({
|
|
29
|
+
name: "api",
|
|
30
|
+
image: "ghcr.io/example/api:1.0.0",
|
|
31
|
+
ports: [Port.make({ name: "http", containerPort: 8080 })],
|
|
32
|
+
env: [
|
|
33
|
+
// ref is branded — a raw string, or a Secret in the wrong namespace, won't compile
|
|
34
|
+
EnvVar.fromSecretForPod({ name: "DATABASE_URL", ref: apiCreds.ref, key: "url", podNamespace: "prod" }),
|
|
35
|
+
EnvVar.value({ name: "LOG_LEVEL", value: "info" })
|
|
36
|
+
],
|
|
37
|
+
readinessProbe: { httpGet: { path: "/healthz", port: Port.ref("http") } }
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const workload = Workload.web({
|
|
41
|
+
name: "api",
|
|
42
|
+
namespace: "prod",
|
|
43
|
+
deployment: { replicas: 2, containers: [api] },
|
|
44
|
+
service: { ports: [{ port: 80, targetPort: Port.ref("http") }] }
|
|
45
|
+
})
|
|
37
46
|
```
|
|
38
47
|
|
|
39
|
-
##
|
|
40
|
-
|
|
41
|
-
| Helper | Rejects raw string for |
|
|
42
|
-
| ------------------------------------------- | ---------------------- |
|
|
43
|
-
| `valueEnv(name, value)` | n/a |
|
|
44
|
-
| `secretEnv(name, {ref, key, optional?})` | `ref` |
|
|
45
|
-
| `configMapEnv(name, {ref, key, optional?})` | `ref` |
|
|
46
|
-
| `rawEnv({name, value?, valueFrom?})` | n/a (escape hatch) |
|
|
47
|
-
|
|
48
|
-
## Volume helpers
|
|
49
|
-
|
|
50
|
-
| Helper |
|
|
51
|
-
| ----------------------------------- |
|
|
52
|
-
| `volumeFromSecret(name, ref)` |
|
|
53
|
-
| `volumeFromConfigMap(name, ref)` |
|
|
54
|
-
| `emptyDirVolume(name, opts?)` |
|
|
55
|
-
| `pvcVolume(name, claimName, opts?)` |
|
|
56
|
-
|
|
57
|
-
## Constructors
|
|
58
|
-
|
|
59
|
-
Workload-tier (`Deployment.make`, `StatefulSet.make`, `Job.make`,
|
|
60
|
-
`CronJob.make`) accept typed containers, `Volume`s,
|
|
61
|
-
`imagePullSecrets: { name: SecretRef<N> }[]`, and
|
|
62
|
-
`serviceAccountName: ServiceAccountRef | string`.
|
|
48
|
+
## What's inside
|
|
63
49
|
|
|
64
|
-
|
|
65
|
-
`
|
|
50
|
+
**Branded refs** — `SecretRef`, `ConfigMapRef`, `ServiceAccountRef`, `PvcRef`.
|
|
51
|
+
Identity constructors (`Secret.make`, `ConfigMap.make`, …) expose a typed
|
|
52
|
+
`.ref`; the enforcement points (env `secretKeyRef`, volumes, `imagePullSecrets`,
|
|
53
|
+
Ingress TLS) take the brand and reject raw strings.
|
|
66
54
|
|
|
67
|
-
|
|
68
|
-
`
|
|
69
|
-
typed `.ref`.
|
|
55
|
+
**Env vars** — `EnvVar.value`, `EnvVar.fromSecretForPod`, `EnvVar.fromConfigMap`.
|
|
56
|
+
Duplicate names in one container's `env` are caught at compile time.
|
|
70
57
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
`Role.make`, `RoleBinding.make`) — simple constructors with no extra
|
|
74
|
-
dependencies.
|
|
58
|
+
**Ports** — `Port.make({ name, containerPort })` and `Port.ref(name)` brand the
|
|
59
|
+
port-name union, so a probe or Service targeting a typo'd port won't compile.
|
|
75
60
|
|
|
76
|
-
|
|
61
|
+
**Workloads** — `Workload.web` (Deployment + Service + optional Ingress) and
|
|
62
|
+
`Workload.cron` (CronJob + private ServiceAccount) cover the common shapes. The
|
|
63
|
+
lower-level `Deployment` / `StatefulSet` / `Job` / `CronJob` / `Service` /
|
|
64
|
+
`Ingress` and the identity / RBAC / policy constructors are there for bespoke
|
|
65
|
+
resources.
|
|
77
66
|
|
|
78
|
-
|
|
67
|
+
**Env contracts & secrets** — `Environment.bind` and `Secret.bind` turn a
|
|
68
|
+
[`@konfig.ts/env`](../env) contract into the Deployment env block plus a secret
|
|
69
|
+
backend's CRs ([sops](../sops), [sealed-secrets](../sealed-secrets),
|
|
70
|
+
[external-secrets](../external-secrets), or the built-in `NativeSecret`).
|
|
79
71
|
|
|
80
|
-
`Workload.web(
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
**Secret rotation** — `Workload.web({ reloader: "stakater" })` annotates for
|
|
73
|
+
[Stakater Reloader](https://github.com/stakater/Reloader) (rolls on in-cluster
|
|
74
|
+
change); `hashSecretValues` stamps a pod-spec hash that rolls on re-render.
|
|
83
75
|
|
|
84
|
-
##
|
|
76
|
+
## Internals
|
|
85
77
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
the secret material lives on the pod spec; re-rendering after a
|
|
91
|
-
rotation produces a new hash, so the Deployment's pod template
|
|
92
|
-
changes and Kubernetes rolls. Fast feedback, deterministic — but only
|
|
93
|
-
fires when konfig re-renders.
|
|
94
|
-
|
|
95
|
-
- **Runtime** (`Workload.web({ reloader: "stakater" })`). Emits
|
|
96
|
-
`reloader.stakater.com/auto: "true"` on the Deployment so
|
|
97
|
-
[Stakater Reloader](https://github.com/stakater/Reloader) watches
|
|
98
|
-
every referenced Secret/ConfigMap and patches the workload when the
|
|
99
|
-
in-cluster object changes. Pair with `ExternalSecrets` or a
|
|
100
|
-
controller that updates Secrets out-of-band.
|
|
101
|
-
|
|
102
|
-
Pick build-time hashes for stateless render → apply pipelines; pick
|
|
103
|
-
Reloader when secrets rotate independently of CI. They compose — a
|
|
104
|
-
config-managed Secret with `reloader: "stakater"` rolls on both
|
|
105
|
-
re-render and on out-of-band updates.
|
|
106
|
-
|
|
107
|
-
## Secret backends — `SecretBackend<N, K>`
|
|
108
|
-
|
|
109
|
-
`SecretBackend` is the contract Sops / SealedSecrets / ExternalSecrets
|
|
110
|
-
implement. `Secret.bind({ secret, backend, source? })` ties an env-bundle
|
|
111
|
-
secret to a backend emission. `backend.requiresSource` is `true` for
|
|
112
|
-
Sops and SealedSecrets (they need plaintext at render time) and `false`
|
|
113
|
-
for ExternalSecrets (it just emits an `ExternalSecret` CR pointing at a
|
|
114
|
-
remote store).
|
|
115
|
-
|
|
116
|
-
## k8s 1.30 types
|
|
117
|
-
|
|
118
|
-
Re-exported from `kubernetes-types@1.30.0` under
|
|
119
|
-
`src/.generated/k8s-types/`. We pin to the cluster minor and ship the
|
|
120
|
-
re-export rather than rolling our own OpenAPI codegen.
|
|
121
|
-
|
|
122
|
-
## Layout
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
src/
|
|
126
|
-
├── index.ts barrel
|
|
127
|
-
├── .generated/k8s-types/ thin re-export from kubernetes-types
|
|
128
|
-
├── refs.ts SecretRef, ConfigMapRef, ServiceAccountRef
|
|
129
|
-
├── env.ts secretEnv, configMapEnv, valueEnv, rawEnv
|
|
130
|
-
├── volume.ts volumeFromSecret, volumeFromConfigMap, ...
|
|
131
|
-
├── container.ts ContainerInput, PodSpecInput, imagePullSecret
|
|
132
|
-
├── identity.ts Namespace, ServiceAccount, ConfigMap, Secret
|
|
133
|
-
├── workload.ts Deployment, StatefulSet, Job, CronJob
|
|
134
|
-
├── network.ts Service, Ingress, ingressTLS
|
|
135
|
-
├── policy.ts PV, PVC, NetworkPolicy, RBAC
|
|
136
|
-
├── workloadHelpers.ts Workload.web, Workload.cron
|
|
137
|
-
└── backend.ts SecretBackend contract + Secret.bind
|
|
138
|
-
```
|
|
78
|
+
Built on `@konfig.ts/core`'s `Manifest<A>` carrier; refs and `Dep.*` needs
|
|
79
|
+
propagate through Effect `Layer`s. Raw Kubernetes types are re-exported as the
|
|
80
|
+
`K8s` namespace (pinned `kubernetes-types@1.30.0`). See
|
|
81
|
+
[`.docs/architecture.md`](../../.docs/architecture.md).
|
|
139
82
|
|
|
140
83
|
## Requirements
|
|
141
84
|
|
|
142
|
-
konfig.ts
|
|
143
|
-
|
|
144
|
-
|
|
85
|
+
konfig.ts is built on [Effect](https://effect.website/), currently in beta.
|
|
86
|
+
Until Effect ships a stable 4.x, install the exact beta konfig.ts is built
|
|
87
|
+
against:
|
|
145
88
|
|
|
146
|
-
- **`effect@4.0.0-beta.70`** — required.
|
|
147
|
-
- **`@effect/platform-node@4.0.0-beta.70`** — required only
|
|
148
|
-
(the Node filesystem/subprocess entrypoint); manifest-only
|
|
149
|
-
omit it.
|
|
89
|
+
- **`effect@4.0.0-beta.70`** — required by every package.
|
|
90
|
+
- **`@effect/platform-node@4.0.0-beta.70`** — required only when you call
|
|
91
|
+
`render()` (the Node filesystem/subprocess entrypoint); manifest-only
|
|
92
|
+
consumers can omit it (it is declared as an optional peer).
|
|
150
93
|
|
|
151
|
-
The
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
relax to a caret range once Effect reaches a stable 4.x.
|
|
94
|
+
The pin is exact on purpose: Effect's beta line makes breaking changes between
|
|
95
|
+
builds, so a looser range surfaces as `ERESOLVE` install conflicts. It relaxes
|
|
96
|
+
to a caret range once Effect reaches a stable 4.x.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konfig.ts/k8s",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Kubernetes resource builders (workloads, network, identity, policy, volume, env) for konfig.ts.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "David Stahl-Gruber",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"postpack": "node ../../scripts/prepack-exports.mjs restore"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@konfig.ts/core": "0.0.
|
|
54
|
-
"@konfig.ts/env": "0.0.
|
|
53
|
+
"@konfig.ts/core": "0.0.4",
|
|
54
|
+
"@konfig.ts/env": "0.0.4",
|
|
55
55
|
"kubernetes-types": "~1.30.0"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|