@konfig.ts/k8s 0.0.1
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/LICENSE +21 -0
- package/README.md +154 -0
- package/dist/.generated/k8s-types/index.d.ts +6 -0
- package/dist/backend.d.ts +26 -0
- package/dist/container.d.ts +190 -0
- package/dist/env.d.ts +110 -0
- package/dist/environmentBind.d.ts +133 -0
- package/dist/identity.d.ts +58 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.mjs +1008 -0
- package/dist/index.mjs.map +1 -0
- package/dist/nativeSecret.d.ts +9 -0
- package/dist/network.d.ts +99 -0
- package/dist/podHash.d.ts +12 -0
- package/dist/podSet.d.ts +36 -0
- package/dist/policy.d.ts +150 -0
- package/dist/ports.d.ts +101 -0
- package/dist/refs.d.ts +26 -0
- package/dist/secretBind.d.ts +36 -0
- package/dist/selector.d.ts +30 -0
- package/dist/volume.d.ts +108 -0
- package/dist/workload.d.ts +117 -0
- package/dist/workloadHelpers.d.ts +97 -0
- package/package.json +69 -0
package/dist/ports.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branded port name — a string carrying the literal `N` in its type.
|
|
3
|
+
* Constructed by `Port.make({ name, containerPort })` and `Port.ref(name)`.
|
|
4
|
+
* The brand lets probes and Service `targetPort` constrain their
|
|
5
|
+
* `port` field to a member of the container's declared port-name union
|
|
6
|
+
* rather than `string`.
|
|
7
|
+
*/
|
|
8
|
+
declare const PortNameBrand: unique symbol;
|
|
9
|
+
export type PortName<N extends string> = string & {
|
|
10
|
+
readonly [PortNameBrand]: N;
|
|
11
|
+
};
|
|
12
|
+
export type ContainerProtocol = "TCP" | "UDP" | "SCTP";
|
|
13
|
+
export interface ContainerPort<N extends string = string> {
|
|
14
|
+
readonly containerPort: number;
|
|
15
|
+
readonly name?: PortName<N>;
|
|
16
|
+
readonly protocol?: ContainerProtocol;
|
|
17
|
+
readonly hostPort?: number;
|
|
18
|
+
readonly hostIP?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface PortInput<N extends string> {
|
|
21
|
+
readonly name: N;
|
|
22
|
+
readonly containerPort: number;
|
|
23
|
+
readonly protocol?: ContainerProtocol;
|
|
24
|
+
readonly hostPort?: number;
|
|
25
|
+
readonly hostIP?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* `Port` value namespace.
|
|
29
|
+
*
|
|
30
|
+
* ports: [Port.make({ name: "http", containerPort: 8080 })],
|
|
31
|
+
* readinessProbe: { httpGet: { port: Port.ref("http") } },
|
|
32
|
+
*
|
|
33
|
+
* - `Port.make(input)` constructs a named container port; the literal
|
|
34
|
+
* `name` is captured in the returned `ContainerPort<N>` brand so
|
|
35
|
+
* `Container` can infer the port-name union and constrain
|
|
36
|
+
* cross-references (probes, Service.targetPort).
|
|
37
|
+
* - `Port.ref(name)` returns the brand alone, for probe targets and
|
|
38
|
+
* `targetPort` references that need to name an existing declared port.
|
|
39
|
+
*/
|
|
40
|
+
export declare const Port: {
|
|
41
|
+
make: <const N extends string>(input: PortInput<N>) => ContainerPort<N>;
|
|
42
|
+
ref: <const N extends string>(name: N) => PortName<N>;
|
|
43
|
+
};
|
|
44
|
+
export interface HttpHeader {
|
|
45
|
+
readonly name: string;
|
|
46
|
+
readonly value: string;
|
|
47
|
+
}
|
|
48
|
+
export interface HttpGetAction<Ports extends string> {
|
|
49
|
+
readonly path?: string;
|
|
50
|
+
readonly port: number | PortName<Ports>;
|
|
51
|
+
readonly host?: string;
|
|
52
|
+
readonly scheme?: "HTTP" | "HTTPS";
|
|
53
|
+
readonly httpHeaders?: ReadonlyArray<HttpHeader>;
|
|
54
|
+
}
|
|
55
|
+
export interface TcpSocketAction<Ports extends string> {
|
|
56
|
+
readonly port: number | PortName<Ports>;
|
|
57
|
+
readonly host?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface GrpcAction<Ports extends string> {
|
|
60
|
+
readonly port: number | PortName<Ports>;
|
|
61
|
+
readonly service?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ExecAction {
|
|
64
|
+
readonly command: ReadonlyArray<string>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Probe target — `port` references on httpGet/tcpSocket/grpc are
|
|
68
|
+
* constrained to `Ports`, the union of names declared on the owning
|
|
69
|
+
* container. A bare number is always accepted; only the named variant
|
|
70
|
+
* is checked.
|
|
71
|
+
*/
|
|
72
|
+
export interface ProbeTarget<Ports extends string> {
|
|
73
|
+
readonly httpGet?: HttpGetAction<Ports>;
|
|
74
|
+
readonly tcpSocket?: TcpSocketAction<Ports>;
|
|
75
|
+
readonly grpc?: GrpcAction<Ports>;
|
|
76
|
+
readonly exec?: ExecAction;
|
|
77
|
+
readonly initialDelaySeconds?: number;
|
|
78
|
+
readonly periodSeconds?: number;
|
|
79
|
+
readonly timeoutSeconds?: number;
|
|
80
|
+
readonly successThreshold?: number;
|
|
81
|
+
readonly failureThreshold?: number;
|
|
82
|
+
readonly terminationGracePeriodSeconds?: number;
|
|
83
|
+
}
|
|
84
|
+
export type NamesOf<P extends ReadonlyArray<unknown>> = {
|
|
85
|
+
readonly [K in keyof P]: P[K] extends ContainerPort<infer N> ? N : never;
|
|
86
|
+
}[number];
|
|
87
|
+
/**
|
|
88
|
+
* Service-port input bound to a container's port-name union. `targetPort`
|
|
89
|
+
* accepts a bare number or a `PortName<Ports>`. The `Ports` parameter is
|
|
90
|
+
* locked by `forContainer` on `Service.fromContainer`; use `Port.ref(name)`
|
|
91
|
+
* to reference declared ports.
|
|
92
|
+
*/
|
|
93
|
+
export interface ServicePortSpec<Ports extends string> {
|
|
94
|
+
readonly name?: string;
|
|
95
|
+
readonly port: number;
|
|
96
|
+
readonly targetPort: number | PortName<Ports>;
|
|
97
|
+
readonly protocol?: ContainerProtocol;
|
|
98
|
+
readonly appProtocol?: string;
|
|
99
|
+
readonly nodePort?: number;
|
|
100
|
+
}
|
|
101
|
+
export {};
|
package/dist/refs.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ConfigMapRef as CMRef, type PvcRef as PRef, type ServiceAccountRef as SARef, type SecretRef as SRef } from "@konfig.ts/core";
|
|
2
|
+
export type SecretRef<N extends string = string, K extends string = string, Ns extends string = string> = SRef<N, K, Ns>;
|
|
3
|
+
export type ConfigMapRef<N extends string = string, K extends string = string> = CMRef<N, K>;
|
|
4
|
+
export type ServiceAccountRef<N extends string = string> = SARef<N>;
|
|
5
|
+
export type PvcRef<N extends string = string> = PRef<N>;
|
|
6
|
+
export type { ConfigMapRefKeys, ConfigMapRefName, PvcRefName, SecretRefKeys, SecretRefName, } from "@konfig.ts/core";
|
|
7
|
+
export type { SecretRefNamespace } from "@konfig.ts/core";
|
|
8
|
+
export declare const SecretRef: {
|
|
9
|
+
of: <N extends string, K extends string = string, Ns extends string = string>(name: N) => SecretRef<N, K, Ns>;
|
|
10
|
+
/**
|
|
11
|
+
* Escape hatch — widen a typed ref's namespace slot to `any`, making
|
|
12
|
+
* it usable across any pod context. Use sparingly (legitimate
|
|
13
|
+
* cross-namespace cases: ExternalSecret reflection, in-cluster
|
|
14
|
+
* shared infra). The cast is grep-able so PR reviewers see opt-ins.
|
|
15
|
+
*/
|
|
16
|
+
unsafeReNamespace: <N extends string, K extends string>(ref: SecretRef<N, K, any>) => SecretRef<N, K, any>;
|
|
17
|
+
};
|
|
18
|
+
export declare const ConfigMapRef: {
|
|
19
|
+
of: <N extends string, K extends string = string>(name: N) => ConfigMapRef<N, K>;
|
|
20
|
+
};
|
|
21
|
+
export declare const ServiceAccountRef: {
|
|
22
|
+
of: <N extends string>(name: N) => ServiceAccountRef<N>;
|
|
23
|
+
};
|
|
24
|
+
export declare const PvcRef: {
|
|
25
|
+
of: <N extends string>(name: N) => PvcRef<N>;
|
|
26
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Dep, type Manifest, RenderError, type SecretRef } from "@konfig.ts/core";
|
|
2
|
+
import type { SecretEntry, SecretSource } from "@konfig.ts/env";
|
|
3
|
+
import { type Context, type Layer } from "effect";
|
|
4
|
+
import type { SecretBackend } from "./backend";
|
|
5
|
+
import { EnvVar } from "./env";
|
|
6
|
+
export interface DeclaredSecret<N extends string, K extends string, Ns extends string = string> {
|
|
7
|
+
readonly ref: SecretRef<N, K, Ns>;
|
|
8
|
+
readonly name: N;
|
|
9
|
+
readonly namespace: Ns;
|
|
10
|
+
readonly keys: ReadonlyArray<K>;
|
|
11
|
+
readonly envVars: ReadonlyArray<EnvVar>;
|
|
12
|
+
readonly manifest?: Manifest.Manifest<unknown>;
|
|
13
|
+
readonly refLayer: Layer.Layer<Dep.Provide<"Secret", N>>;
|
|
14
|
+
readonly values?: Context.Service<Dep.Need<"SecretValues", N>, Dep.SecretValuesRecord<K>>;
|
|
15
|
+
readonly layer?: Layer.Layer<Dep.Provide<"SecretValues", N>, RenderError, Manifest.RenderServices>;
|
|
16
|
+
}
|
|
17
|
+
export interface BindSecretInput<N extends string, K extends string, E extends Readonly<Record<K, string>>, Ns extends string = string> {
|
|
18
|
+
readonly secret: SecretEntry<N, K, E>;
|
|
19
|
+
readonly backend?: SecretBackend<N, K>;
|
|
20
|
+
readonly source?: SecretSource<K, Manifest.RenderServices>;
|
|
21
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
22
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
23
|
+
/**
|
|
24
|
+
* Override the contract's baked-in `namespace` for this bind. Useful
|
|
25
|
+
* when the same `Secret` declaration is consumed across multiple
|
|
26
|
+
* k8s namespaces (e.g. prod / staging / local of the same workload)
|
|
27
|
+
* — the runtime read is namespace-independent, but each binding emits
|
|
28
|
+
* its manifest into a different namespace.
|
|
29
|
+
*
|
|
30
|
+
* When passed as a string literal (via `Environment.bind`'s
|
|
31
|
+
* `const namespace`), the literal flows into the ref's brand so
|
|
32
|
+
* `secretEnvForPod` can enforce cross-namespace coherence.
|
|
33
|
+
*/
|
|
34
|
+
readonly namespace?: Ns;
|
|
35
|
+
}
|
|
36
|
+
export declare const bindSecret: <N extends string, K extends string, E extends Readonly<Record<K, string>>, const Ns extends string = string>(input: BindSecretInput<N, K, E, Ns>) => DeclaredSecret<N, K, Ns>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selector — one source of truth for "this pod set."
|
|
3
|
+
*
|
|
4
|
+
* `Selector<L>` wraps a literal label record and brands it. Resources
|
|
5
|
+
* that *select* pods — Deployment.selector, Service.selector,
|
|
6
|
+
* NetworkPolicy.podSelector, HPA target — take the bundle directly via
|
|
7
|
+
* `Deployment.fromPodSet` / `Service.fromPodSet` / `NetworkPolicy.fromPodSet`
|
|
8
|
+
* so a drift across three resources (the classic "service has no
|
|
9
|
+
* endpoints / netpol denies everything" bug) becomes a compile error.
|
|
10
|
+
*
|
|
11
|
+
* `Workload.web` already keeps these labels coherent internally; the
|
|
12
|
+
* Selector is the win when users go off-script: StatefulSet, DaemonSet,
|
|
13
|
+
* cross-namespace selectors, or wiring a NetworkPolicy from app A to
|
|
14
|
+
* peer pods B.
|
|
15
|
+
*/
|
|
16
|
+
declare const SelectorBrand: unique symbol;
|
|
17
|
+
export interface Selector<L extends Readonly<Record<string, string>>> {
|
|
18
|
+
readonly [SelectorBrand]: L;
|
|
19
|
+
readonly labels: L;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* `Selector` value namespace.
|
|
23
|
+
*
|
|
24
|
+
* const apiPods = Selector.make({ app: "api", tier: "web" });
|
|
25
|
+
*/
|
|
26
|
+
export declare const Selector: {
|
|
27
|
+
make: <const L extends Readonly<Record<string, string>>>(labels: L) => Selector<L>;
|
|
28
|
+
};
|
|
29
|
+
export type SelectorLabels<S> = S extends Selector<infer L> ? L : never;
|
|
30
|
+
export {};
|
package/dist/volume.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { ConfigMapRef, PvcRef, SecretRef } from "@konfig.ts/core";
|
|
2
|
+
/**
|
|
3
|
+
* Branded volume name — a string carrying the literal `N` in its
|
|
4
|
+
* phantom. Constructed by the volume factories
|
|
5
|
+
* (`Volume.empty`, `Volume.fromSecret`, …) and `Volume.mountRef`. Lets
|
|
6
|
+
* `VolumeMount<Mounts>` and `Pod` constrain a container's
|
|
7
|
+
* `volumeMounts[i].name` to volumes that the pod actually declares.
|
|
8
|
+
*/
|
|
9
|
+
declare const VolumeNameBrand: unique symbol;
|
|
10
|
+
export type VolumeName<N extends string> = string & {
|
|
11
|
+
readonly [VolumeNameBrand]: N;
|
|
12
|
+
};
|
|
13
|
+
export interface Volume<N extends string = string> {
|
|
14
|
+
readonly name: VolumeName<N>;
|
|
15
|
+
readonly secret?: {
|
|
16
|
+
readonly secretName: string;
|
|
17
|
+
readonly optional?: boolean;
|
|
18
|
+
readonly defaultMode?: number;
|
|
19
|
+
};
|
|
20
|
+
readonly configMap?: {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly optional?: boolean;
|
|
23
|
+
readonly defaultMode?: number;
|
|
24
|
+
readonly items?: ReadonlyArray<{
|
|
25
|
+
readonly key: string;
|
|
26
|
+
readonly path: string;
|
|
27
|
+
}>;
|
|
28
|
+
};
|
|
29
|
+
readonly emptyDir?: {
|
|
30
|
+
readonly medium?: string;
|
|
31
|
+
readonly sizeLimit?: string;
|
|
32
|
+
};
|
|
33
|
+
readonly persistentVolumeClaim?: {
|
|
34
|
+
readonly claimName: PvcRef<string>;
|
|
35
|
+
readonly readOnly?: boolean;
|
|
36
|
+
};
|
|
37
|
+
readonly hostPath?: {
|
|
38
|
+
readonly path: string;
|
|
39
|
+
readonly type?: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export interface EmptyVolumeInput<N extends string> {
|
|
43
|
+
readonly name: N;
|
|
44
|
+
readonly medium?: string;
|
|
45
|
+
readonly sizeLimit?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface VolumeFromSecretInput<N extends string> {
|
|
48
|
+
readonly name: N;
|
|
49
|
+
readonly ref: SecretRef<string>;
|
|
50
|
+
readonly optional?: boolean;
|
|
51
|
+
readonly defaultMode?: number;
|
|
52
|
+
}
|
|
53
|
+
export interface VolumeFromConfigMapInput<N extends string> {
|
|
54
|
+
readonly name: N;
|
|
55
|
+
readonly ref: ConfigMapRef<string>;
|
|
56
|
+
readonly optional?: boolean;
|
|
57
|
+
readonly defaultMode?: number;
|
|
58
|
+
readonly items?: ReadonlyArray<{
|
|
59
|
+
readonly key: string;
|
|
60
|
+
readonly path: string;
|
|
61
|
+
}>;
|
|
62
|
+
}
|
|
63
|
+
export interface VolumeFromPvcInput<N extends string, PvcN extends string> {
|
|
64
|
+
readonly name: N;
|
|
65
|
+
readonly claim: PvcRef<PvcN>;
|
|
66
|
+
readonly readOnly?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* `Volume` value namespace.
|
|
70
|
+
*
|
|
71
|
+
* volumes: [
|
|
72
|
+
* Volume.empty({ name: "config" }),
|
|
73
|
+
* Volume.fromSecret({ name: "tls", ref: tlsSecret.ref }),
|
|
74
|
+
* Volume.fromConfigMap({ name: "settings", ref: cfg.ref }),
|
|
75
|
+
* Volume.fromPvc({ name: "data", claim: pvc.ref }),
|
|
76
|
+
* ],
|
|
77
|
+
* volumeMounts: [
|
|
78
|
+
* { name: Volume.mountRef("config"), mountPath: "/etc/conf" },
|
|
79
|
+
* ],
|
|
80
|
+
*
|
|
81
|
+
* All four constructors capture the literal `name` in the returned
|
|
82
|
+
* `Volume<N>` brand; `Pod` infers the union and constrains each
|
|
83
|
+
* container's `volumeMounts[i].name` to it.
|
|
84
|
+
*/
|
|
85
|
+
export declare const Volume: {
|
|
86
|
+
empty: <const N extends string>(input: EmptyVolumeInput<N>) => Volume<N>;
|
|
87
|
+
fromSecret: <const N extends string>(input: VolumeFromSecretInput<N>) => Volume<N>;
|
|
88
|
+
fromConfigMap: <const N extends string>(input: VolumeFromConfigMapInput<N>) => Volume<N>;
|
|
89
|
+
fromPvc: <const N extends string, const PvcN extends string>(input: VolumeFromPvcInput<N, PvcN>) => Volume<N>;
|
|
90
|
+
mountRef: <const N extends string>(name: N) => VolumeName<N>;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Typed container volumeMount. `Mounts` is the union of volume names
|
|
94
|
+
* declared on the surrounding pod; `name` must reference one of them.
|
|
95
|
+
* Bare-number variants don't apply (mounts are always by name).
|
|
96
|
+
*/
|
|
97
|
+
export interface VolumeMount<Mounts extends string = string> {
|
|
98
|
+
readonly name: VolumeName<Mounts>;
|
|
99
|
+
readonly mountPath: string;
|
|
100
|
+
readonly readOnly?: boolean;
|
|
101
|
+
readonly subPath?: string;
|
|
102
|
+
readonly subPathExpr?: string;
|
|
103
|
+
readonly mountPropagation?: string;
|
|
104
|
+
}
|
|
105
|
+
export type VolumeNamesOf<V extends ReadonlyArray<Volume<string>>> = {
|
|
106
|
+
readonly [I in keyof V]: V[I] extends Volume<infer N> ? N : never;
|
|
107
|
+
}[number];
|
|
108
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Manifest } from "@konfig.ts/core";
|
|
2
|
+
import type { CronJob as K8sCronJob, Deployment as K8sDeployment, Job as K8sJob, StatefulSet as K8sStatefulSet } from "./.generated/k8s-types";
|
|
3
|
+
import type { PodSpecInput } from "./container";
|
|
4
|
+
import type { Selector } from "./selector";
|
|
5
|
+
interface WorkloadMeta {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly namespace: string;
|
|
8
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
9
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
10
|
+
}
|
|
11
|
+
interface SelectorAndTemplate {
|
|
12
|
+
readonly selector: {
|
|
13
|
+
readonly matchLabels: Readonly<Record<string, string>>;
|
|
14
|
+
};
|
|
15
|
+
readonly template: {
|
|
16
|
+
readonly metadata?: {
|
|
17
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
18
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
19
|
+
};
|
|
20
|
+
readonly spec: PodSpecInput;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface DeploymentInput extends WorkloadMeta, SelectorAndTemplate {
|
|
24
|
+
readonly replicas?: number;
|
|
25
|
+
readonly strategy?: unknown;
|
|
26
|
+
readonly revisionHistoryLimit?: number;
|
|
27
|
+
readonly progressDeadlineSeconds?: number;
|
|
28
|
+
readonly minReadySeconds?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Deployment built from a `Selector`. The selector's labels become both
|
|
32
|
+
* the Deployment's `spec.selector.matchLabels` and the pod template's
|
|
33
|
+
* `metadata.labels` — coherent by construction. Drift between selector
|
|
34
|
+
* and template (the classic "service has no endpoints" footgun) is
|
|
35
|
+
* structurally impossible once both consume the same `podSet`.
|
|
36
|
+
*/
|
|
37
|
+
export interface DeploymentFromPodSetInput<L extends Readonly<Record<string, string>>> {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly namespace: string;
|
|
40
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
41
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
42
|
+
readonly podSet: Selector<L>;
|
|
43
|
+
readonly replicas?: number;
|
|
44
|
+
readonly template: {
|
|
45
|
+
readonly metadata?: {
|
|
46
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
47
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
48
|
+
};
|
|
49
|
+
readonly spec: PodSpecInput;
|
|
50
|
+
};
|
|
51
|
+
readonly strategy?: unknown;
|
|
52
|
+
readonly revisionHistoryLimit?: number;
|
|
53
|
+
readonly progressDeadlineSeconds?: number;
|
|
54
|
+
readonly minReadySeconds?: number;
|
|
55
|
+
}
|
|
56
|
+
export declare const Deployment: {
|
|
57
|
+
make: (input: DeploymentInput) => Manifest.Manifest<K8sDeployment>;
|
|
58
|
+
fromPodSet: <L extends Readonly<Record<string, string>>>(input: DeploymentFromPodSetInput<L>) => Manifest.Manifest<K8sDeployment>;
|
|
59
|
+
};
|
|
60
|
+
export interface StatefulSetInput extends WorkloadMeta, SelectorAndTemplate {
|
|
61
|
+
readonly replicas?: number;
|
|
62
|
+
readonly serviceName: string;
|
|
63
|
+
readonly volumeClaimTemplates?: ReadonlyArray<unknown>;
|
|
64
|
+
readonly podManagementPolicy?: string;
|
|
65
|
+
readonly updateStrategy?: unknown;
|
|
66
|
+
}
|
|
67
|
+
export declare const StatefulSet: {
|
|
68
|
+
make: (input: StatefulSetInput) => Manifest.Manifest<K8sStatefulSet>;
|
|
69
|
+
};
|
|
70
|
+
export interface JobInput extends WorkloadMeta {
|
|
71
|
+
readonly parallelism?: number;
|
|
72
|
+
readonly completions?: number;
|
|
73
|
+
readonly backoffLimit?: number;
|
|
74
|
+
readonly activeDeadlineSeconds?: number;
|
|
75
|
+
readonly ttlSecondsAfterFinished?: number;
|
|
76
|
+
readonly suspend?: boolean;
|
|
77
|
+
readonly template: {
|
|
78
|
+
readonly metadata?: {
|
|
79
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
80
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
81
|
+
};
|
|
82
|
+
readonly spec: PodSpecInput;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export declare const Job: {
|
|
86
|
+
make: (input: JobInput) => Manifest.Manifest<K8sJob>;
|
|
87
|
+
};
|
|
88
|
+
export interface CronJobInput extends WorkloadMeta {
|
|
89
|
+
readonly schedule: string;
|
|
90
|
+
readonly concurrencyPolicy?: string;
|
|
91
|
+
readonly successfulJobsHistoryLimit?: number;
|
|
92
|
+
readonly failedJobsHistoryLimit?: number;
|
|
93
|
+
readonly startingDeadlineSeconds?: number;
|
|
94
|
+
readonly suspend?: boolean;
|
|
95
|
+
readonly jobTemplate: {
|
|
96
|
+
readonly metadata?: {
|
|
97
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
98
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
99
|
+
};
|
|
100
|
+
readonly spec: {
|
|
101
|
+
readonly template: {
|
|
102
|
+
readonly metadata?: {
|
|
103
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
104
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
105
|
+
};
|
|
106
|
+
readonly spec: PodSpecInput;
|
|
107
|
+
};
|
|
108
|
+
readonly backoffLimit?: number;
|
|
109
|
+
readonly activeDeadlineSeconds?: number;
|
|
110
|
+
readonly ttlSecondsAfterFinished?: number;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export declare const CronJob: {
|
|
115
|
+
make: (input: CronJobInput) => Manifest.Manifest<K8sCronJob>;
|
|
116
|
+
};
|
|
117
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Manifest, SecretRef } from "@konfig.ts/core";
|
|
2
|
+
import type { CronJob as K8sCronJob, Deployment as K8sDeployment, Ingress as K8sIngress, IngressRule as K8sIngressRule, Service as K8sService, ServiceAccount as K8sServiceAccount } from "./.generated/k8s-types";
|
|
3
|
+
import type { ContainerInput, ContainerSpec } from "./container";
|
|
4
|
+
import { type IngressTLSInput } from "./network";
|
|
5
|
+
import type { ServicePortSpec } from "./ports";
|
|
6
|
+
import type { Volume } from "./volume";
|
|
7
|
+
/**
|
|
8
|
+
* Union of port names declared by every `ContainerSpec` in `Cs`. Raw
|
|
9
|
+
* `ContainerInput` entries (no `Container.define`) contribute `never`,
|
|
10
|
+
* so untyped containers don't widen the result — they just don't add
|
|
11
|
+
* any named-port options to the Service. With every container untyped,
|
|
12
|
+
* the union collapses to `never` and `targetPort` is effectively
|
|
13
|
+
* `number`-only, matching Kubernetes' behaviour when no port is named.
|
|
14
|
+
*/
|
|
15
|
+
type _PortNamesOfContainers<Cs extends ReadonlyArray<ContainerInput>> = {
|
|
16
|
+
readonly [K in keyof Cs]: Cs[K] extends ContainerSpec<infer P, string> ? P : never;
|
|
17
|
+
}[number];
|
|
18
|
+
/**
|
|
19
|
+
* Reloader integration shorthand. Two complementary rotation models:
|
|
20
|
+
*
|
|
21
|
+
* - *Build-time* rotation (re-render → new hash → rolling update). konfig
|
|
22
|
+
* does NOT stamp this automatically; it ships `hashSecretValues` as a
|
|
23
|
+
* helper you attach yourself — feed it your resolved secret values and
|
|
24
|
+
* place the digest into `deployment.podAnnotations`, so a value change
|
|
25
|
+
* flips the pod template and triggers a rollout.
|
|
26
|
+
* - *Runtime* rotation via Stakater's Reloader (operator watches mounted
|
|
27
|
+
* Secrets/ConfigMaps and patches the workload to restart pods on change),
|
|
28
|
+
* selected by the option below.
|
|
29
|
+
*
|
|
30
|
+
* Pick:
|
|
31
|
+
*
|
|
32
|
+
* - `"off"` (default) — no Reloader annotation. Pair with a build-time
|
|
33
|
+
* `hashSecretValues` annotation if you accept the redeploy-on-edit model.
|
|
34
|
+
* - `"stakater"` — emit `reloader.stakater.com/auto: "true"`. Reloader
|
|
35
|
+
* watches every Secret/ConfigMap referenced by the pod spec.
|
|
36
|
+
* - `"stakater-strict"` — emit `reloader.stakater.com/auto: "true"`
|
|
37
|
+
* plus `reloader.stakater.com/match: "true"`, restricting watch
|
|
38
|
+
* to objects with the matching annotation set on them.
|
|
39
|
+
* - `{ secrets, configMaps }` — explicit per-resource lists, emitted
|
|
40
|
+
* as `secret.reloader.stakater.com/reload` /
|
|
41
|
+
* `configmap.reloader.stakater.com/reload` (comma-joined).
|
|
42
|
+
*
|
|
43
|
+
* See packages/k8s/README.md for the trade-off between build-time
|
|
44
|
+
* hashes and runtime reloader.
|
|
45
|
+
*/
|
|
46
|
+
export type ReloaderOption = "off" | "stakater" | "stakater-strict" | {
|
|
47
|
+
readonly secrets?: ReadonlyArray<string>;
|
|
48
|
+
readonly configMaps?: ReadonlyArray<string>;
|
|
49
|
+
};
|
|
50
|
+
interface WebInput<Cs extends ReadonlyArray<ContainerInput>> {
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly namespace: string;
|
|
53
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
54
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
55
|
+
/** Pod-restart-on-rotation integration. See `ReloaderOption`. */
|
|
56
|
+
readonly reloader?: ReloaderOption;
|
|
57
|
+
readonly deployment: {
|
|
58
|
+
readonly replicas?: number;
|
|
59
|
+
readonly containers: Cs;
|
|
60
|
+
readonly volumes?: ReadonlyArray<Volume>;
|
|
61
|
+
readonly imagePullSecrets?: ReadonlyArray<{
|
|
62
|
+
readonly name: SecretRef<string>;
|
|
63
|
+
}>;
|
|
64
|
+
readonly serviceAccountName?: string;
|
|
65
|
+
readonly podLabels?: Readonly<Record<string, string>>;
|
|
66
|
+
readonly podAnnotations?: Readonly<Record<string, string>>;
|
|
67
|
+
};
|
|
68
|
+
readonly service: {
|
|
69
|
+
readonly ports: ReadonlyArray<ServicePortSpec<NoInfer<_PortNamesOfContainers<Cs>>>>;
|
|
70
|
+
readonly type?: "ClusterIP" | "NodePort" | "LoadBalancer";
|
|
71
|
+
};
|
|
72
|
+
readonly ingress?: {
|
|
73
|
+
readonly ingressClassName?: string;
|
|
74
|
+
readonly rules?: ReadonlyArray<K8sIngressRule>;
|
|
75
|
+
readonly tls?: ReadonlyArray<IngressTLSInput>;
|
|
76
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export declare const web: <const Cs extends ReadonlyArray<ContainerInput>>(input: WebInput<Cs>) => Manifest.Manifest<readonly [K8sDeployment, K8sService] | readonly [K8sDeployment, K8sService, K8sIngress]>;
|
|
80
|
+
interface CronInput {
|
|
81
|
+
readonly name: string;
|
|
82
|
+
readonly namespace: string;
|
|
83
|
+
readonly schedule: string;
|
|
84
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
85
|
+
readonly annotations?: Readonly<Record<string, string>>;
|
|
86
|
+
readonly concurrencyPolicy?: "Allow" | "Forbid" | "Replace";
|
|
87
|
+
readonly successfulJobsHistoryLimit?: number;
|
|
88
|
+
readonly failedJobsHistoryLimit?: number;
|
|
89
|
+
readonly containers: ReadonlyArray<ContainerInput>;
|
|
90
|
+
readonly volumes?: ReadonlyArray<Volume>;
|
|
91
|
+
readonly imagePullSecrets?: ReadonlyArray<{
|
|
92
|
+
readonly name: SecretRef<string>;
|
|
93
|
+
}>;
|
|
94
|
+
readonly restartPolicy?: "OnFailure" | "Never";
|
|
95
|
+
}
|
|
96
|
+
export declare const cron: (input: CronInput) => Manifest.Manifest<readonly [K8sServiceAccount, K8sCronJob]>;
|
|
97
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@konfig.ts/k8s",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Kubernetes resource builders (workloads, network, identity, policy, volume, env) for konfig.ts.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "David Stahl-Gruber",
|
|
7
|
+
"homepage": "https://github.com/DwieDave/konfig.ts#readme",
|
|
8
|
+
"bugs": "https://github.com/DwieDave/konfig.ts/issues",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/DwieDave/konfig.ts.git",
|
|
12
|
+
"directory": "packages/k8s"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"kubernetes",
|
|
16
|
+
"k8s",
|
|
17
|
+
"config",
|
|
18
|
+
"effect",
|
|
19
|
+
"typescript"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"main": "./dist/index.mjs",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.mjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=20"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsdown ./src/index.ts --format=esm --target=node20 --sourcemap --clean --no-dts && tsc -p tsconfig.build.json",
|
|
44
|
+
"check": "tsc -p tsconfig.check.json --noEmit",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"lint": "oxlint",
|
|
47
|
+
"lint:apply": "oxlint --fix",
|
|
48
|
+
"prepublishOnly": "bun run build",
|
|
49
|
+
"prepack": "node ../../scripts/prepack-exports.mjs strip",
|
|
50
|
+
"postpack": "node ../../scripts/prepack-exports.mjs restore"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@konfig.ts/core": "0.0.1",
|
|
54
|
+
"@konfig.ts/env": "0.0.1",
|
|
55
|
+
"kubernetes-types": "~1.30.0"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"effect": "4.0.0-beta.70"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@effect/platform-node": "catalog:",
|
|
62
|
+
"@effect/vitest": "catalog:",
|
|
63
|
+
"@types/bun": "catalog:",
|
|
64
|
+
"effect": "catalog:",
|
|
65
|
+
"fast-check": "catalog:",
|
|
66
|
+
"typescript": "catalog:",
|
|
67
|
+
"vitest": "catalog:"
|
|
68
|
+
}
|
|
69
|
+
}
|