typekro 0.34.0 → 0.36.0
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/dist/.tsbuildinfo +1 -1
- package/dist/alchemy/clickhouse-schema/executor.d.ts +25 -0
- package/dist/alchemy/clickhouse-schema/executor.d.ts.map +1 -0
- package/dist/alchemy/clickhouse-schema/executor.js +144 -0
- package/dist/alchemy/clickhouse-schema/executor.js.map +1 -0
- package/dist/alchemy/clickhouse-schema/index.d.ts +12 -0
- package/dist/alchemy/clickhouse-schema/index.d.ts.map +1 -0
- package/dist/alchemy/clickhouse-schema/index.js +11 -0
- package/dist/alchemy/clickhouse-schema/index.js.map +1 -0
- package/dist/alchemy/clickhouse-schema/resource.d.ts +54 -0
- package/dist/alchemy/clickhouse-schema/resource.d.ts.map +1 -0
- package/dist/alchemy/clickhouse-schema/resource.js +158 -0
- package/dist/alchemy/clickhouse-schema/resource.js.map +1 -0
- package/dist/alchemy/clickhouse-schema/runner.d.ts +154 -0
- package/dist/alchemy/clickhouse-schema/runner.d.ts.map +1 -0
- package/dist/alchemy/clickhouse-schema/runner.js +613 -0
- package/dist/alchemy/clickhouse-schema/runner.js.map +1 -0
- package/dist/alchemy/clickhouse-schema/sql.d.ts +109 -0
- package/dist/alchemy/clickhouse-schema/sql.d.ts.map +1 -0
- package/dist/alchemy/clickhouse-schema/sql.js +368 -0
- package/dist/alchemy/clickhouse-schema/sql.js.map +1 -0
- package/dist/alchemy/clickhouse-schema/types.d.ts +394 -0
- package/dist/alchemy/clickhouse-schema/types.d.ts.map +1 -0
- package/dist/alchemy/clickhouse-schema/types.js +370 -0
- package/dist/alchemy/clickhouse-schema/types.js.map +1 -0
- package/dist/alchemy/index.d.ts +1 -0
- package/dist/alchemy/index.d.ts.map +1 -1
- package/dist/alchemy/index.js +2 -0
- package/dist/alchemy/index.js.map +1 -1
- package/dist/alchemy/kro-delete.d.ts +7 -0
- package/dist/alchemy/kro-delete.d.ts.map +1 -1
- package/dist/alchemy/kro-delete.js +28 -4
- package/dist/alchemy/kro-delete.js.map +1 -1
- package/dist/alchemy/resource-registration.d.ts +9 -2
- package/dist/alchemy/resource-registration.d.ts.map +1 -1
- package/dist/alchemy/resource-registration.js +117 -33
- package/dist/alchemy/resource-registration.js.map +1 -1
- package/dist/core/deployment/k8s-helpers.d.ts +77 -1
- package/dist/core/deployment/k8s-helpers.d.ts.map +1 -1
- package/dist/core/deployment/k8s-helpers.js +298 -17
- package/dist/core/deployment/k8s-helpers.js.map +1 -1
- package/dist/core/deployment/kro-artifact-binding-migration.d.ts +17 -6
- package/dist/core/deployment/kro-artifact-binding-migration.d.ts.map +1 -1
- package/dist/core/deployment/kro-artifact-binding-migration.js +16 -2
- package/dist/core/deployment/kro-artifact-binding-migration.js.map +1 -1
- package/dist/core/deployment/kro-namespace-teardown.d.ts +15 -1
- package/dist/core/deployment/kro-namespace-teardown.d.ts.map +1 -1
- package/dist/core/deployment/kro-namespace-teardown.js +34 -9
- package/dist/core/deployment/kro-namespace-teardown.js.map +1 -1
- package/dist/core/deployment/kro-readiness.d.ts +35 -0
- package/dist/core/deployment/kro-readiness.d.ts.map +1 -1
- package/dist/core/deployment/kro-readiness.js +201 -25
- package/dist/core/deployment/kro-readiness.js.map +1 -1
- package/dist/core/deployment/poll-timeout.d.ts +80 -2
- package/dist/core/deployment/poll-timeout.d.ts.map +1 -1
- package/dist/core/deployment/poll-timeout.js +164 -4
- package/dist/core/deployment/poll-timeout.js.map +1 -1
- package/dist/core/kubernetes/bun-http-library.d.ts +24 -0
- package/dist/core/kubernetes/bun-http-library.d.ts.map +1 -1
- package/dist/core/kubernetes/bun-http-library.js +146 -19
- package/dist/core/kubernetes/bun-http-library.js.map +1 -1
- package/dist/core/kubernetes/errors.d.ts +9 -0
- package/dist/core/kubernetes/errors.d.ts.map +1 -1
- package/dist/core/kubernetes/errors.js +7 -3
- package/dist/core/kubernetes/errors.js.map +1 -1
- package/dist/factories/clickhouse/compositions/clickhouse-cluster.d.ts +24 -2
- package/dist/factories/clickhouse/compositions/clickhouse-cluster.d.ts.map +1 -1
- package/dist/factories/clickhouse/compositions/clickhouse-cluster.js +24 -2
- package/dist/factories/clickhouse/compositions/clickhouse-cluster.js.map +1 -1
- package/dist/factories/clickhouse/resources/keeper.d.ts +61 -0
- package/dist/factories/clickhouse/resources/keeper.d.ts.map +1 -1
- package/dist/factories/clickhouse/resources/keeper.js +216 -2
- package/dist/factories/clickhouse/resources/keeper.js.map +1 -1
- package/dist/factories/clickhouse/resources/s3-backup.d.ts.map +1 -1
- package/dist/factories/clickhouse/resources/s3-backup.js +4 -3
- package/dist/factories/clickhouse/resources/s3-backup.js.map +1 -1
- package/dist/factories/clickhouse/types.d.ts +3 -2
- package/dist/factories/clickhouse/types.d.ts.map +1 -1
- package/dist/factories/clickhouse/types.js +28 -1
- package/dist/factories/clickhouse/types.js.map +1 -1
- package/dist/factories/clickhouse/utils/validation.d.ts +133 -10
- package/dist/factories/clickhouse/utils/validation.d.ts.map +1 -1
- package/dist/factories/clickhouse/utils/validation.js +165 -18
- package/dist/factories/clickhouse/utils/validation.js.map +1 -1
- package/dist/factories/clickstack/compositions/clickstack-bootstrap.d.ts +15 -1
- package/dist/factories/clickstack/compositions/clickstack-bootstrap.d.ts.map +1 -1
- package/dist/factories/clickstack/compositions/clickstack-bootstrap.js +15 -1
- package/dist/factories/clickstack/compositions/clickstack-bootstrap.js.map +1 -1
- package/dist/factories/rook/types.d.ts +16 -16
- package/package.json +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default {@link ClickHouseExecutor}: `@kubernetes/client-node`'s `Exec` over the
|
|
3
|
+
* Kubernetes API server's `pods/exec` subresource.
|
|
4
|
+
*
|
|
5
|
+
* WHY EXEC AND NOT A CLIENT CONNECTION. A converge runs wherever the alchemy runtime
|
|
6
|
+
* runs — a laptop, a CI runner — which in general has no network path to a ClickHouse
|
|
7
|
+
* pod. The alternatives are a port-forward (a second moving part, a local listening
|
|
8
|
+
* socket, and a tunnel that has to outlive every statement) or exposing the native port
|
|
9
|
+
* (a durable hole punched for a one-off migration). Exec needs neither: the statement
|
|
10
|
+
* travels over the same authenticated API-server connection everything else uses, the
|
|
11
|
+
* client runs next to the server, and the credentials stay inside the pod.
|
|
12
|
+
*
|
|
13
|
+
* RBAC: the identity running the converge needs `create` on `pods/exec` and `list` on
|
|
14
|
+
* `pods` in the target namespace.
|
|
15
|
+
*/
|
|
16
|
+
import type { KubeConfig } from '@kubernetes/client-node';
|
|
17
|
+
import type { ClickHouseExecCommand, ClickHouseExecResult, ClickHouseExecutor, ClickHousePodSummary } from './types.js';
|
|
18
|
+
/** `Exec` over the API server. Constructed per resource from the resolved KubeConfig. */
|
|
19
|
+
export declare class KubeExecClickHouseExecutor implements ClickHouseExecutor {
|
|
20
|
+
private readonly kubeConfig;
|
|
21
|
+
constructor(kubeConfig: KubeConfig);
|
|
22
|
+
listPods(namespace: string, podSelector: Readonly<Record<string, string>>, _abortSignal?: AbortSignal): Promise<readonly ClickHousePodSummary[]>;
|
|
23
|
+
exec(command: ClickHouseExecCommand, abortSignal?: AbortSignal): Promise<ClickHouseExecResult>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../../src/alchemy/clickhouse-schema/executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAY,MAAM,yBAAyB,CAAC;AAGpE,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAgCpB,yFAAyF;AACzF,qBAAa,0BAA2B,YAAW,kBAAkB;IACvD,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAE7C,QAAQ,CACZ,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC7C,YAAY,CAAC,EAAE,WAAW,GACzB,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC;IAkCrC,IAAI,CACR,OAAO,EAAE,qBAAqB,EAC9B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,oBAAoB,CAAC;CAyEjC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default {@link ClickHouseExecutor}: `@kubernetes/client-node`'s `Exec` over the
|
|
3
|
+
* Kubernetes API server's `pods/exec` subresource.
|
|
4
|
+
*
|
|
5
|
+
* WHY EXEC AND NOT A CLIENT CONNECTION. A converge runs wherever the alchemy runtime
|
|
6
|
+
* runs — a laptop, a CI runner — which in general has no network path to a ClickHouse
|
|
7
|
+
* pod. The alternatives are a port-forward (a second moving part, a local listening
|
|
8
|
+
* socket, and a tunnel that has to outlive every statement) or exposing the native port
|
|
9
|
+
* (a durable hole punched for a one-off migration). Exec needs neither: the statement
|
|
10
|
+
* travels over the same authenticated API-server connection everything else uses, the
|
|
11
|
+
* client runs next to the server, and the credentials stay inside the pod.
|
|
12
|
+
*
|
|
13
|
+
* RBAC: the identity running the converge needs `create` on `pods/exec` and `list` on
|
|
14
|
+
* `pods` in the target namespace.
|
|
15
|
+
*/
|
|
16
|
+
import { PassThrough, Readable as NodeReadable } from 'node:stream';
|
|
17
|
+
import { createBunCompatibleApiClient } from '../../core/kubernetes/bun-api-client.js';
|
|
18
|
+
import { getKubernetesClientNode } from '../../core/kubernetes/client-node-runtime.js';
|
|
19
|
+
/** Collect a stream into a string, capped so a runaway command cannot exhaust memory. */
|
|
20
|
+
const MAX_CAPTURED_BYTES = 64 * 1024;
|
|
21
|
+
function collector() {
|
|
22
|
+
const chunks = [];
|
|
23
|
+
let size = 0;
|
|
24
|
+
const stream = new PassThrough();
|
|
25
|
+
stream.on('data', (chunk) => {
|
|
26
|
+
if (size >= MAX_CAPTURED_BYTES)
|
|
27
|
+
return;
|
|
28
|
+
const slice = chunk.subarray(0, MAX_CAPTURED_BYTES - size);
|
|
29
|
+
chunks.push(slice);
|
|
30
|
+
size += slice.length;
|
|
31
|
+
});
|
|
32
|
+
return { stream, text: () => Buffer.concat(chunks).toString('utf8') };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Kubernetes reports a non-zero exit as a `Failure` status carrying an `ExitCode` cause.
|
|
36
|
+
* A `Failure` with no such cause (the connection died mid-command, for instance) is
|
|
37
|
+
* reported as exit 1 — a command failure, deliberately NOT a transient transport error,
|
|
38
|
+
* because the statement may well have run.
|
|
39
|
+
*/
|
|
40
|
+
function exitCodeFromStatus(status) {
|
|
41
|
+
if (!status)
|
|
42
|
+
return 1;
|
|
43
|
+
if (status.status === 'Success')
|
|
44
|
+
return 0;
|
|
45
|
+
const cause = status.details?.causes?.find((entry) => entry.reason === 'ExitCode');
|
|
46
|
+
const parsed = Number.parseInt(cause?.message ?? '', 10);
|
|
47
|
+
return Number.isNaN(parsed) ? 1 : parsed;
|
|
48
|
+
}
|
|
49
|
+
/** `Exec` over the API server. Constructed per resource from the resolved KubeConfig. */
|
|
50
|
+
export class KubeExecClickHouseExecutor {
|
|
51
|
+
kubeConfig;
|
|
52
|
+
constructor(kubeConfig) {
|
|
53
|
+
this.kubeConfig = kubeConfig;
|
|
54
|
+
}
|
|
55
|
+
async listPods(namespace, podSelector, _abortSignal) {
|
|
56
|
+
const clientNode = getKubernetesClientNode();
|
|
57
|
+
const coreApi = createBunCompatibleApiClient(this.kubeConfig, clientNode.CoreV1Api);
|
|
58
|
+
const labelSelector = Object.entries(podSelector)
|
|
59
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
60
|
+
.join(',');
|
|
61
|
+
const pods = await coreApi.listNamespacedPod({ namespace, labelSelector });
|
|
62
|
+
return pods.items.flatMap((pod) => {
|
|
63
|
+
const name = pod.metadata?.name;
|
|
64
|
+
if (!name)
|
|
65
|
+
return [];
|
|
66
|
+
// `ready`, `phase` and `terminating` are reported SEPARATELY rather than folded into
|
|
67
|
+
// one boolean: selection has to tell "not Ready yet, keep waiting" apart from "leaving
|
|
68
|
+
// or finished, stop waiting", and a pod that is Ready while terminating is the case
|
|
69
|
+
// where those two answers differ. Which of them disqualifies a pod is the runner's
|
|
70
|
+
// decision, and it differs by execution model.
|
|
71
|
+
const ready = (pod.status?.conditions ?? []).some((condition) => condition.type === 'Ready' && condition.status === 'True');
|
|
72
|
+
// The UID identifies the pod OBJECT, which is what separates "the replica I applied
|
|
73
|
+
// to" from "its same-named replacement" after a drain or a template change.
|
|
74
|
+
const uid = pod.metadata?.uid;
|
|
75
|
+
return [
|
|
76
|
+
{
|
|
77
|
+
name,
|
|
78
|
+
...(uid !== undefined ? { uid } : {}),
|
|
79
|
+
ready,
|
|
80
|
+
containers: (pod.spec?.containers ?? []).flatMap((c) => (c.name ? [c.name] : [])),
|
|
81
|
+
...(pod.status?.phase !== undefined ? { phase: pod.status.phase } : {}),
|
|
82
|
+
...(pod.metadata?.deletionTimestamp !== undefined ? { terminating: true } : {}),
|
|
83
|
+
},
|
|
84
|
+
];
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async exec(command, abortSignal) {
|
|
88
|
+
const clientNode = getKubernetesClientNode();
|
|
89
|
+
const exec = new clientNode.Exec(this.kubeConfig);
|
|
90
|
+
const stdout = collector();
|
|
91
|
+
const stderr = collector();
|
|
92
|
+
const stdin = NodeReadable.from([Buffer.from(command.stdin, 'utf8')]);
|
|
93
|
+
return await new Promise((resolve, reject) => {
|
|
94
|
+
let status;
|
|
95
|
+
let settled = false;
|
|
96
|
+
let socket;
|
|
97
|
+
const finish = (fn) => {
|
|
98
|
+
if (settled)
|
|
99
|
+
return;
|
|
100
|
+
settled = true;
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
abortSignal?.removeEventListener('abort', onAbort);
|
|
103
|
+
fn();
|
|
104
|
+
};
|
|
105
|
+
const fail = (error) => {
|
|
106
|
+
finish(() => {
|
|
107
|
+
try {
|
|
108
|
+
socket?.close();
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// The socket may already be gone; the original failure is what matters.
|
|
112
|
+
}
|
|
113
|
+
reject(error);
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
const onAbort = () => fail(new Error('ClickHouse exec aborted'));
|
|
117
|
+
const timer = setTimeout(() => fail(new Error(`ClickHouse exec timed out after ${command.timeoutMs}ms`)), command.timeoutMs);
|
|
118
|
+
abortSignal?.addEventListener('abort', onAbort, { once: true });
|
|
119
|
+
if (abortSignal?.aborted) {
|
|
120
|
+
onAbort();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
exec
|
|
124
|
+
.exec(command.namespace, command.podName, command.container, [...command.command], stdout.stream, stderr.stream, stdin, false, (received) => {
|
|
125
|
+
status = received;
|
|
126
|
+
})
|
|
127
|
+
.then((ws) => {
|
|
128
|
+
socket = ws;
|
|
129
|
+
// A websocket error before the command completes is a TRANSPORT failure and is
|
|
130
|
+
// what the retry loop exists for; a clean close carries the command's own status.
|
|
131
|
+
ws.on('error', (error) => fail(error));
|
|
132
|
+
ws.on('close', () => {
|
|
133
|
+
finish(() => resolve({
|
|
134
|
+
stdout: stdout.text(),
|
|
135
|
+
stderr: stderr.text(),
|
|
136
|
+
exitCode: exitCodeFromStatus(status),
|
|
137
|
+
}));
|
|
138
|
+
});
|
|
139
|
+
})
|
|
140
|
+
.catch((error) => fail(error instanceof Error ? error : new Error(String(error))));
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../src/alchemy/clickhouse-schema/executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClickHouse schema management as an alchemy v2 resource.
|
|
3
|
+
*
|
|
4
|
+
* @see docs/api/alchemy/clickhouse-schema.md
|
|
5
|
+
*/
|
|
6
|
+
export { KubeExecClickHouseExecutor } from './executor.js';
|
|
7
|
+
export { CLICKHOUSE_SCHEMA_RESOURCE_TYPE, ClickHouseSchema, type ClickHouseSchemaR, clickHouseSchema, clickHouseSchemaProvider, } from './resource.js';
|
|
8
|
+
export { applyClickHouseSchema, computeFingerprint, DEFAULT_BACKOFF_MS, DEFAULT_CLICKHOUSE_CONTAINER, DEFAULT_CLICKHOUSE_DATABASE, DEFAULT_CLICKHOUSE_PASSWORD_ENV, DEFAULT_CLICKHOUSE_PORT, DEFAULT_CLICKHOUSE_USER, DEFAULT_MAX_ATTEMPTS, DEFAULT_MAX_RECONCILE_PASSES, DEFAULT_STATEMENT_TIMEOUT_MS, DEFAULT_WAIT_FOR_POD_TIMEOUT_MS, deleteClickHouseSchema, needsApply, renderClickHouseCommand, runStatements, selectExecutionPods, } from './runner.js';
|
|
9
|
+
export type { ClickHouseSchemaRunContext, ClickHouseSchemaRuntimeDeps } from './runner.js';
|
|
10
|
+
export { type ClickHouseSqlToken, escapeClickHouseString, extractStatementSecrets, leadingKeyword, statementTargetsCluster, tokenizeClickHouseSql, validateOnClusterStatement, } from './sql.js';
|
|
11
|
+
export { type ClickHouseExecCommand, type ClickHouseExecResult, type ClickHouseExecutor, type ClickHousePodSummary, type ClickHouseSchemaAppliedPod, type ClickHouseSchemaClient, ClickHouseSchemaClientSchema, type ClickHouseSchemaConfig, type ClickHouseSchemaConfigInput, ClickHouseSchemaConfigSchema, ClickHouseSchemaError, type ClickHouseSchemaExecution, ClickHouseSchemaExecutionSchema, type ClickHouseSchemaProps, type ClickHouseSchemaResourceProps, type ClickHouseSchemaState, type ClickHouseSchemaTarget, ClickHouseSchemaTargetSchema, DEFAULT_EXECUTION, MAX_RETAINED_DETAIL_CHARS, parseClickHouseErrorCode, parseClickHouseExceptionName, redactClickHouseOutput, redactClickHouseText, } from './types.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/alchemy/clickhouse-schema/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,+BAA+B,EAC/B,gBAAgB,EAChB,KAAK,iBAAiB,EACtB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,4BAA4B,EAC5B,2BAA2B,EAC3B,+BAA+B,EAC/B,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,4BAA4B,EAC5B,4BAA4B,EAC5B,+BAA+B,EAC/B,sBAAsB,EACtB,UAAU,EACV,uBAAuB,EACvB,aAAa,EACb,mBAAmB,GACpB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EACL,KAAK,kBAAkB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,4BAA4B,EAC5B,qBAAqB,EACrB,KAAK,yBAAyB,EAC9B,+BAA+B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,4BAA4B,EAC5B,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClickHouse schema management as an alchemy v2 resource.
|
|
3
|
+
*
|
|
4
|
+
* @see docs/api/alchemy/clickhouse-schema.md
|
|
5
|
+
*/
|
|
6
|
+
export { KubeExecClickHouseExecutor } from './executor.js';
|
|
7
|
+
export { CLICKHOUSE_SCHEMA_RESOURCE_TYPE, ClickHouseSchema, clickHouseSchema, clickHouseSchemaProvider, } from './resource.js';
|
|
8
|
+
export { applyClickHouseSchema, computeFingerprint, DEFAULT_BACKOFF_MS, DEFAULT_CLICKHOUSE_CONTAINER, DEFAULT_CLICKHOUSE_DATABASE, DEFAULT_CLICKHOUSE_PASSWORD_ENV, DEFAULT_CLICKHOUSE_PORT, DEFAULT_CLICKHOUSE_USER, DEFAULT_MAX_ATTEMPTS, DEFAULT_MAX_RECONCILE_PASSES, DEFAULT_STATEMENT_TIMEOUT_MS, DEFAULT_WAIT_FOR_POD_TIMEOUT_MS, deleteClickHouseSchema, needsApply, renderClickHouseCommand, runStatements, selectExecutionPods, } from './runner.js';
|
|
9
|
+
export { escapeClickHouseString, extractStatementSecrets, leadingKeyword, statementTargetsCluster, tokenizeClickHouseSql, validateOnClusterStatement, } from './sql.js';
|
|
10
|
+
export { ClickHouseSchemaClientSchema, ClickHouseSchemaConfigSchema, ClickHouseSchemaError, ClickHouseSchemaExecutionSchema, ClickHouseSchemaTargetSchema, DEFAULT_EXECUTION, MAX_RETAINED_DETAIL_CHARS, parseClickHouseErrorCode, parseClickHouseExceptionName, redactClickHouseOutput, redactClickHouseText, } from './types.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/alchemy/clickhouse-schema/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,+BAA+B,EAC/B,gBAAgB,EAEhB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,4BAA4B,EAC5B,2BAA2B,EAC3B,+BAA+B,EAC/B,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,4BAA4B,EAC5B,4BAA4B,EAC5B,+BAA+B,EAC/B,sBAAsB,EACtB,UAAU,EACV,uBAAuB,EACvB,aAAa,EACb,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EAEL,sBAAsB,EACtB,uBAAuB,EACvB,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,UAAU,CAAC;AAClB,OAAO,EAOL,4BAA4B,EAG5B,4BAA4B,EAC5B,qBAAqB,EAErB,+BAA+B,EAK/B,4BAA4B,EAC5B,iBAAiB,EACjB,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `ClickHouseSchema` alchemy v2 resource.
|
|
3
|
+
*
|
|
4
|
+
* WHY AN ALCHEMY RESOURCE RATHER THAN A CronJob. TypeKro can already run ClickHouse DDL
|
|
5
|
+
* from inside the cluster — `clickStackStorage`'s retention CronJob does exactly that —
|
|
6
|
+
* and that is the right shape when the statements must wait for objects TypeKro does not
|
|
7
|
+
* own (the OTel tables appear only after the collector migrates). Schema ownership is the
|
|
8
|
+
* other case: the statements are the deployment's own DDL, they should run once per
|
|
9
|
+
* converge rather than on a timer, a failure should fail the deploy instead of showing up
|
|
10
|
+
* in a Job log, and the applied state should be diffable so an unchanged schema costs
|
|
11
|
+
* nothing. That is a converge-time, stateful, ordered operation — an alchemy resource.
|
|
12
|
+
*
|
|
13
|
+
* ORDERING. A `ClickHouseSchema` depends on the ClickHouse instance the same way any
|
|
14
|
+
* alchemy resource depends on another: pass the instance's `KroResource` output through
|
|
15
|
+
* its props. Alchemy then deploys the instance (and waits for TypeKro's readiness
|
|
16
|
+
* evaluation) before this resource's first exec. See `docs/api/alchemy/clickhouse-schema.md`.
|
|
17
|
+
*/
|
|
18
|
+
import * as ProviderMod from 'alchemy/Provider';
|
|
19
|
+
import type { Resource as ResourceT } from 'alchemy/Resource';
|
|
20
|
+
import * as ResourceMod from 'alchemy/Resource';
|
|
21
|
+
import { Effect } from 'effect';
|
|
22
|
+
import { type ClickHouseSchemaProps, type ClickHouseSchemaResourceProps, type ClickHouseSchemaState } from './types.js';
|
|
23
|
+
/** The alchemy resource type, following `KRO_RESOURCE_TYPE`'s `TypeKro.<Name>` convention. */
|
|
24
|
+
export declare const CLICKHOUSE_SCHEMA_RESOURCE_TYPE: "TypeKro.ClickHouseSchema";
|
|
25
|
+
export type ClickHouseSchemaR = ResourceT<typeof CLICKHOUSE_SCHEMA_RESOURCE_TYPE, ClickHouseSchemaResourceProps, ClickHouseSchemaState>;
|
|
26
|
+
/**
|
|
27
|
+
* The declarative resource. Prefer the {@link clickHouseSchema} factory, which validates
|
|
28
|
+
* props against the ArkType schema before they reach alchemy state.
|
|
29
|
+
*/
|
|
30
|
+
export declare const ClickHouseSchema: ResourceMod.ResourceClass<ClickHouseSchemaR>;
|
|
31
|
+
/**
|
|
32
|
+
* The provider `Layer` backing {@link ClickHouseSchema}. Merge it into the alchemy
|
|
33
|
+
* runtime's providers alongside `kroProvider`.
|
|
34
|
+
*/
|
|
35
|
+
export declare const clickHouseSchemaProvider: import("effect/Layer").Layer<ProviderMod.Provider<ClickHouseSchemaR>, never, never>;
|
|
36
|
+
/**
|
|
37
|
+
* Declare a ClickHouse schema, validating its configuration first.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* const schema = yield* clickHouseSchema('orders-schema', {
|
|
41
|
+
* target: { namespace: 'telemetry', podSelector: { 'clickhouse.altinity.com/chi': 'orders' } },
|
|
42
|
+
* statements: [
|
|
43
|
+
* 'CREATE DATABASE IF NOT EXISTS orders',
|
|
44
|
+
* 'CREATE TABLE IF NOT EXISTS orders.events (id UUID, at DateTime) ENGINE = MergeTree ORDER BY at',
|
|
45
|
+
* ],
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @param id - Stable alchemy resource id
|
|
50
|
+
* @param props - Validated against {@link ClickHouseSchemaConfigSchema}
|
|
51
|
+
* @throws ValidationError when the configuration is invalid
|
|
52
|
+
*/
|
|
53
|
+
export declare function clickHouseSchema(id: string, props: ClickHouseSchemaProps): Effect.Effect<ClickHouseSchemaR, never, ProviderMod.Provider<ClickHouseSchemaR>>;
|
|
54
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../../src/alchemy/clickhouse-schema/resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,IAAI,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAC;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAchC,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AAEpB,8FAA8F;AAC9F,eAAO,MAAM,+BAA+B,EAAG,0BAAmC,CAAC;AAEnF,MAAM,MAAM,iBAAiB,GAAG,SAAS,CACvC,OAAO,+BAA+B,EACtC,6BAA6B,EAC7B,qBAAqB,CACtB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,8CAE5B,CAAC;AAmCF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,qFA+DpC,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,oFAoBxE"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `ClickHouseSchema` alchemy v2 resource.
|
|
3
|
+
*
|
|
4
|
+
* WHY AN ALCHEMY RESOURCE RATHER THAN A CronJob. TypeKro can already run ClickHouse DDL
|
|
5
|
+
* from inside the cluster — `clickStackStorage`'s retention CronJob does exactly that —
|
|
6
|
+
* and that is the right shape when the statements must wait for objects TypeKro does not
|
|
7
|
+
* own (the OTel tables appear only after the collector migrates). Schema ownership is the
|
|
8
|
+
* other case: the statements are the deployment's own DDL, they should run once per
|
|
9
|
+
* converge rather than on a timer, a failure should fail the deploy instead of showing up
|
|
10
|
+
* in a Job log, and the applied state should be diffable so an unchanged schema costs
|
|
11
|
+
* nothing. That is a converge-time, stateful, ordered operation — an alchemy resource.
|
|
12
|
+
*
|
|
13
|
+
* ORDERING. A `ClickHouseSchema` depends on the ClickHouse instance the same way any
|
|
14
|
+
* alchemy resource depends on another: pass the instance's `KroResource` output through
|
|
15
|
+
* its props. Alchemy then deploys the instance (and waits for TypeKro's readiness
|
|
16
|
+
* evaluation) before this resource's first exec. See `docs/api/alchemy/clickhouse-schema.md`.
|
|
17
|
+
*/
|
|
18
|
+
import * as ProviderMod from 'alchemy/Provider';
|
|
19
|
+
import * as ResourceMod from 'alchemy/Resource';
|
|
20
|
+
import { type } from 'arktype';
|
|
21
|
+
import { Effect } from 'effect';
|
|
22
|
+
import { materializeSerializableKubeConfigOptions } from '../../core/deployment/shared-utilities.js';
|
|
23
|
+
import { ValidationError } from '../../core/errors.js';
|
|
24
|
+
import { clusterIdentity } from '../../core/kubernetes/api-capability.js';
|
|
25
|
+
import { createKubernetesClientProvider } from '../../core/kubernetes/client-provider.js';
|
|
26
|
+
import { getComponentLogger } from '../../core/logging/index.js';
|
|
27
|
+
import { ensureError } from '../../core/errors.js';
|
|
28
|
+
import { KubeExecClickHouseExecutor } from './executor.js';
|
|
29
|
+
import { applyClickHouseSchema, computeFingerprint, deleteClickHouseSchema, needsApply, } from './runner.js';
|
|
30
|
+
import { ClickHouseSchemaConfigSchema, } from './types.js';
|
|
31
|
+
/** The alchemy resource type, following `KRO_RESOURCE_TYPE`'s `TypeKro.<Name>` convention. */
|
|
32
|
+
export const CLICKHOUSE_SCHEMA_RESOURCE_TYPE = 'TypeKro.ClickHouseSchema';
|
|
33
|
+
/**
|
|
34
|
+
* The declarative resource. Prefer the {@link clickHouseSchema} factory, which validates
|
|
35
|
+
* props against the ArkType schema before they reach alchemy state.
|
|
36
|
+
*/
|
|
37
|
+
export const ClickHouseSchema = ResourceMod.Resource(CLICKHOUSE_SCHEMA_RESOURCE_TYPE);
|
|
38
|
+
/**
|
|
39
|
+
* Build the transport: the injected executor if present, else exec over the kube API.
|
|
40
|
+
*
|
|
41
|
+
* The cluster identity travels WITH the transport rather than being derived later,
|
|
42
|
+
* because they answer the same question — which API server this converge is talking to —
|
|
43
|
+
* and deriving it twice is how the two drift apart. `clusterIdentity` is the helper the
|
|
44
|
+
* per-cluster capability cache already keys on, reused rather than re-derived, so the two
|
|
45
|
+
* caches cannot disagree about what "the same cluster" means.
|
|
46
|
+
*
|
|
47
|
+
* An injected executor is still identified when a kubeConfig accompanies it: the caller
|
|
48
|
+
* supplying its own transport does not make the target cluster unknowable. Only an
|
|
49
|
+
* injected executor with no kubeConfig at all has no identity to record.
|
|
50
|
+
*/
|
|
51
|
+
function resolveTransport(props) {
|
|
52
|
+
const kubeConfig = props.executor && !props.kubeConfig
|
|
53
|
+
? undefined
|
|
54
|
+
: createKubernetesClientProvider(props.kubeConfig ? materializeSerializableKubeConfigOptions(props.kubeConfig) : undefined).getKubeConfig();
|
|
55
|
+
return {
|
|
56
|
+
executor: props.executor ?? new KubeExecClickHouseExecutor(kubeConfig),
|
|
57
|
+
clusterId: kubeConfig ? clusterIdentity(kubeConfig) : undefined,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The provider `Layer` backing {@link ClickHouseSchema}. Merge it into the alchemy
|
|
62
|
+
* runtime's providers alongside `kroProvider`.
|
|
63
|
+
*/
|
|
64
|
+
export const clickHouseSchemaProvider = ProviderMod.effect(ClickHouseSchema, Effect.succeed({
|
|
65
|
+
// A schema is applied IN PLACE. Nothing about it is identity-stable: re-pointing the
|
|
66
|
+
// resource at another server must re-apply the DDL there, never delete it here.
|
|
67
|
+
stables: [],
|
|
68
|
+
// Not discoverable cluster-wide: a schema leaves no object carrying this resource's
|
|
69
|
+
// identity, so there is nothing for `alchemy nuke` to enumerate. Same stance as
|
|
70
|
+
// `kroProvider`.
|
|
71
|
+
list: () => Effect.succeed([]),
|
|
72
|
+
// `id` is the author's own name for THIS schema — the thing that distinguishes
|
|
73
|
+
// `orders-schema` from `billing-schema` in an error. The provider TYPE is the same
|
|
74
|
+
// constant for every instance and says nothing, which is why it is not used here.
|
|
75
|
+
reconcile: Effect.fn(function* ({ id, news, output }) {
|
|
76
|
+
return yield* Effect.tryPromise({
|
|
77
|
+
try: async (abortSignal) => {
|
|
78
|
+
const logger = getComponentLogger('alchemy-clickhouse-schema');
|
|
79
|
+
const { executor, clusterId } = resolveTransport(news);
|
|
80
|
+
if (!needsApply(news, output, clusterId)) {
|
|
81
|
+
logger.debug('ClickHouse schema unchanged; verifying the server set', {
|
|
82
|
+
resourceId: id,
|
|
83
|
+
fingerprint: output?.fingerprint,
|
|
84
|
+
statementCount: news.statements.length,
|
|
85
|
+
clusterId,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
logger.info('Applying ClickHouse schema', {
|
|
90
|
+
resourceId: id,
|
|
91
|
+
namespace: news.target.namespace,
|
|
92
|
+
statementCount: news.statements.length,
|
|
93
|
+
fingerprint: computeFingerprint(news),
|
|
94
|
+
executionMode: news.execution.mode,
|
|
95
|
+
clusterId,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return await applyClickHouseSchema({ executor, config: news, resourceId: id, clusterId, abortSignal }, output);
|
|
99
|
+
},
|
|
100
|
+
catch: ensureError,
|
|
101
|
+
});
|
|
102
|
+
}),
|
|
103
|
+
delete: Effect.fn(function* ({ id, olds }) {
|
|
104
|
+
// `retain` (the default) must not even reach the cluster, so the missing-spec case
|
|
105
|
+
// below is a no-op rather than a guess: reconstructing an unknown `onDelete` from
|
|
106
|
+
// persisted output could only ever guess `retain`, which is what happens anyway.
|
|
107
|
+
if (!olds || olds.onDelete !== 'run')
|
|
108
|
+
return;
|
|
109
|
+
yield* Effect.tryPromise({
|
|
110
|
+
try: (abortSignal) => {
|
|
111
|
+
const { executor, clusterId } = resolveTransport(olds);
|
|
112
|
+
return deleteClickHouseSchema({
|
|
113
|
+
executor,
|
|
114
|
+
config: olds,
|
|
115
|
+
resourceId: id,
|
|
116
|
+
clusterId,
|
|
117
|
+
abortSignal,
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
catch: ensureError,
|
|
121
|
+
});
|
|
122
|
+
}),
|
|
123
|
+
}));
|
|
124
|
+
/**
|
|
125
|
+
* Declare a ClickHouse schema, validating its configuration first.
|
|
126
|
+
*
|
|
127
|
+
* ```ts
|
|
128
|
+
* const schema = yield* clickHouseSchema('orders-schema', {
|
|
129
|
+
* target: { namespace: 'telemetry', podSelector: { 'clickhouse.altinity.com/chi': 'orders' } },
|
|
130
|
+
* statements: [
|
|
131
|
+
* 'CREATE DATABASE IF NOT EXISTS orders',
|
|
132
|
+
* 'CREATE TABLE IF NOT EXISTS orders.events (id UUID, at DateTime) ENGINE = MergeTree ORDER BY at',
|
|
133
|
+
* ],
|
|
134
|
+
* });
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* @param id - Stable alchemy resource id
|
|
138
|
+
* @param props - Validated against {@link ClickHouseSchemaConfigSchema}
|
|
139
|
+
* @throws ValidationError when the configuration is invalid
|
|
140
|
+
*/
|
|
141
|
+
export function clickHouseSchema(id, props) {
|
|
142
|
+
// `kubeConfig`/`executor` are not part of the ArkType schema (see `types.ts`), so they
|
|
143
|
+
// are split off before validation and re-attached to the validated config afterwards.
|
|
144
|
+
const { kubeConfig, executor, readyBarrier, ...configurable } = props;
|
|
145
|
+
const validated = ClickHouseSchemaConfigSchema(configurable);
|
|
146
|
+
if (validated instanceof type.errors) {
|
|
147
|
+
throw new ValidationError(`Invalid ClickHouseSchema configuration for '${id}': ${validated.summary}`, 'ClickHouseSchema', id);
|
|
148
|
+
}
|
|
149
|
+
return ClickHouseSchema(id, {
|
|
150
|
+
...validated,
|
|
151
|
+
...(kubeConfig ? { kubeConfig } : {}),
|
|
152
|
+
...(executor ? { executor } : {}),
|
|
153
|
+
// Passed through unvalidated and unresolved: this is an alchemy `Output` at
|
|
154
|
+
// declaration time, and alchemy resolves it before reconcile.
|
|
155
|
+
...(readyBarrier !== undefined ? { readyBarrier } : {}),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../../../src/alchemy/clickhouse-schema/resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convergence logic for the `ClickHouseSchema` alchemy resource: fingerprinting,
|
|
3
|
+
* `clickhouse-client` command rendering, Ready-pod selection and statement execution.
|
|
4
|
+
*
|
|
5
|
+
* Kept free of alchemy and of `@kubernetes/client-node` so it can be exercised whole
|
|
6
|
+
* against an injected {@link ClickHouseExecutor}.
|
|
7
|
+
*/
|
|
8
|
+
import { type ClickHouseExecutor, type ClickHousePodSummary, type ClickHouseSchemaConfig, type ClickHouseSchemaState } from './types.js';
|
|
9
|
+
/** The Altinity CHI server container name (see `clickHouseInstallation`'s pod template). */
|
|
10
|
+
export declare const DEFAULT_CLICKHOUSE_CONTAINER = "clickhouse";
|
|
11
|
+
/** ClickHouse's native protocol port, which `clickhouse-client` speaks. */
|
|
12
|
+
export declare const DEFAULT_CLICKHOUSE_PORT = 9000;
|
|
13
|
+
export declare const DEFAULT_CLICKHOUSE_USER = "default";
|
|
14
|
+
export declare const DEFAULT_CLICKHOUSE_DATABASE = "default";
|
|
15
|
+
/**
|
|
16
|
+
* Default environment variable read for the password INSIDE the container.
|
|
17
|
+
*
|
|
18
|
+
* This is the convention every in-repo ClickHouse workload already uses: the ClickStack
|
|
19
|
+
* retention CronJob reads `CLICKHOUSE_PASSWORD`, and `clickHouseS3BackupCronJob` sets
|
|
20
|
+
* that same variable from the credentials Secret. `clickhouse-client` also honours
|
|
21
|
+
* `CLICKHOUSE_PASSWORD` natively, but this resource does NOT depend on that: it passes
|
|
22
|
+
* `--password "${VAR:-}"` explicitly through `sh -c`, which behaves identically for a
|
|
23
|
+
* custom `passwordEnv` and on images whose client build predates the native support.
|
|
24
|
+
*/
|
|
25
|
+
export declare const DEFAULT_CLICKHOUSE_PASSWORD_ENV = "CLICKHOUSE_PASSWORD";
|
|
26
|
+
export declare const DEFAULT_WAIT_FOR_POD_TIMEOUT_MS = 120000;
|
|
27
|
+
export declare const DEFAULT_STATEMENT_TIMEOUT_MS = 300000;
|
|
28
|
+
export declare const DEFAULT_MAX_ATTEMPTS = 3;
|
|
29
|
+
export declare const DEFAULT_BACKOFF_MS = 1000;
|
|
30
|
+
/**
|
|
31
|
+
* How many times a `fanout` apply re-lists and applies to newly appeared pods before it
|
|
32
|
+
* gives up and fails. Three covers the ordinary races — a scale-out or a replacement
|
|
33
|
+
* landing mid-apply — without letting a cluster that is genuinely churning stretch one
|
|
34
|
+
* converge indefinitely.
|
|
35
|
+
*/
|
|
36
|
+
export declare const DEFAULT_MAX_RECONCILE_PASSES = 3;
|
|
37
|
+
/** Injected clock and sleep, so the wait/timeout and backoff paths are testable. */
|
|
38
|
+
export interface ClickHouseSchemaRuntimeDeps {
|
|
39
|
+
readonly now: () => number;
|
|
40
|
+
readonly sleep: (ms: number) => Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export declare const defaultRuntimeDeps: ClickHouseSchemaRuntimeDeps;
|
|
43
|
+
export declare function resolveDatabase(config: ClickHouseSchemaConfig): string;
|
|
44
|
+
export declare function resolveContainer(config: ClickHouseSchemaConfig): string;
|
|
45
|
+
/**
|
|
46
|
+
* The `sh -c` command run inside the server container.
|
|
47
|
+
*
|
|
48
|
+
* The password is expanded from the container's OWN environment (`"${VAR:-}"`, a POSIX
|
|
49
|
+
* default expansion so an unset variable means the empty password the stock Altinity
|
|
50
|
+
* `default` user has) and is therefore never in props, never in alchemy state, never in
|
|
51
|
+
* this process's memory, and never echoed. `exec` replaces the shell so the client is
|
|
52
|
+
* the container's direct child and receives the statement on stdin unbuffered.
|
|
53
|
+
*
|
|
54
|
+
* `--host 127.0.0.1`: the exec already landed inside a server pod, so the connection
|
|
55
|
+
* never leaves it. There is no port-forward and no network path from the runner.
|
|
56
|
+
*/
|
|
57
|
+
export declare function renderClickHouseCommand(config: ClickHouseSchemaConfig): readonly string[];
|
|
58
|
+
/**
|
|
59
|
+
* sha256 over the ordered statements, the settings, the client configuration and the
|
|
60
|
+
* execution model.
|
|
61
|
+
*
|
|
62
|
+
* The TARGET is deliberately NOT part of the fingerprint — it describes where to reach
|
|
63
|
+
* the server, not what is applied — so {@link needsApply} compares it separately and a
|
|
64
|
+
* re-pointed resource re-applies even though its DDL is byte-identical.
|
|
65
|
+
*
|
|
66
|
+
* The EXECUTION MODEL is part of it, because switching from `onCluster` to `fanout`
|
|
67
|
+
* changes which servers the identical statements reached, and that is a change to what is
|
|
68
|
+
* applied even though the SQL is untouched.
|
|
69
|
+
*/
|
|
70
|
+
export declare function computeFingerprint(config: ClickHouseSchemaConfig): string;
|
|
71
|
+
/**
|
|
72
|
+
* Everything one converge needs, in one place.
|
|
73
|
+
*
|
|
74
|
+
* An options object rather than a parameter list: the execution model gives the runner
|
|
75
|
+
* several independent knobs, and `clusterId` is not derivable here at all — the runner is
|
|
76
|
+
* deliberately free of `@kubernetes/client-node`, so the identity of the cluster it is
|
|
77
|
+
* talking to has to arrive from the provider that built the transport.
|
|
78
|
+
*/
|
|
79
|
+
export interface ClickHouseSchemaRunContext {
|
|
80
|
+
readonly executor: ClickHouseExecutor;
|
|
81
|
+
readonly config: ClickHouseSchemaConfig;
|
|
82
|
+
/** The alchemy resource `id` — what every error and log line is attributed to. */
|
|
83
|
+
readonly resourceId: string;
|
|
84
|
+
/** Credential-free cluster identity; see {@link ClickHouseSchemaState.clusterId}. */
|
|
85
|
+
readonly clusterId?: string | undefined;
|
|
86
|
+
readonly deps?: ClickHouseSchemaRuntimeDeps | undefined;
|
|
87
|
+
readonly abortSignal?: AbortSignal | undefined;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Whether a converge must (re-)run the statements against this target.
|
|
91
|
+
*
|
|
92
|
+
* Three things are compared, and the third is the one that is easy to forget: the
|
|
93
|
+
* fingerprint (what is applied), the target strings (where, within a cluster), and the
|
|
94
|
+
* CLUSTER ITSELF. Namespace, selector and container are just strings — `telemetry` +
|
|
95
|
+
* `chi=orders` names a pod in staging exactly as well as it names one in production — so
|
|
96
|
+
* without the cluster identity, re-pointing a resource at a second cluster matches the
|
|
97
|
+
* recorded target, matches the fingerprint, and silently applies nothing there.
|
|
98
|
+
*
|
|
99
|
+
* The live pod SET is deliberately not compared here: it needs an API call, so
|
|
100
|
+
* {@link applyClickHouseSchema} checks it separately and only under `fanout`, by NAME AND
|
|
101
|
+
* UID ({@link samePodIdentitySet}) so a same-name replacement is not invisible.
|
|
102
|
+
*/
|
|
103
|
+
export declare function needsApply(config: ClickHouseSchemaConfig, previous: ClickHouseSchemaState | undefined, clusterId?: string): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Poll until the server pods the execution model needs are Ready, or the budget runs out.
|
|
106
|
+
*
|
|
107
|
+
* A ClickHouse server accepts connections only once it is Ready, and a CHI rollout has a
|
|
108
|
+
* window where pods exist but are still replaying logs — exec'ing then produces a
|
|
109
|
+
* connection-refused that looks like a SQL failure. Waiting for readiness first is what
|
|
110
|
+
* makes "ordered after the instance is ready" true in practice as well as in the
|
|
111
|
+
* dependency graph.
|
|
112
|
+
*
|
|
113
|
+
* WHICH pods have to be Ready is the execution model's whole difference: `fanout` reaches
|
|
114
|
+
* every server itself and so needs all of them ({@link selectFanoutPods}); `onCluster`
|
|
115
|
+
* hands the statements to Keeper and so needs exactly one ({@link selectInitiatorPod}).
|
|
116
|
+
*/
|
|
117
|
+
export declare function selectExecutionPods(context: ClickHouseSchemaRunContext): Promise<readonly ClickHousePodSummary[]>;
|
|
118
|
+
/**
|
|
119
|
+
* Run an ordered statement list against every selected pod, stopping at the first failure.
|
|
120
|
+
*
|
|
121
|
+
* Per POD, not per statement: each server receives the whole list in order, because the
|
|
122
|
+
* order is what makes the list meaningful (a table cannot be created before its database)
|
|
123
|
+
* and interleaving across pods would only make a partial failure harder to read.
|
|
124
|
+
*/
|
|
125
|
+
export declare function runStatements(context: ClickHouseSchemaRunContext, statements: readonly string[]): Promise<{
|
|
126
|
+
readonly podNames: readonly string[];
|
|
127
|
+
readonly pods: readonly ClickHousePodSummary[];
|
|
128
|
+
}>;
|
|
129
|
+
/**
|
|
130
|
+
* Converge the schema: no-op when nothing changed, otherwise re-run EVERY statement.
|
|
131
|
+
*
|
|
132
|
+
* There is no partial application. A fingerprint change re-runs the whole ordered list,
|
|
133
|
+
* which is only correct because every statement is required to be idempotent — and it is
|
|
134
|
+
* also what makes a failed converge recoverable: the fingerprint is recorded only after
|
|
135
|
+
* the last statement succeeds, so a run that dies at statement 7 re-runs 0..6 next time.
|
|
136
|
+
*
|
|
137
|
+
* Under `fanout` the POD SET is part of what "nothing changed" means, and the set is
|
|
138
|
+
* compared by NAME AND UID. A statement list applied to two replicas is not applied to the
|
|
139
|
+
* third one that a scale-out added, nor to the replacement a drain put back under the same
|
|
140
|
+
* name with an empty disk, and the fingerprint cannot see either — so an otherwise-unchanged
|
|
141
|
+
* converge still lists pods and re-applies when the set moved. That listing is one API call
|
|
142
|
+
* and no exec, so an unchanged, unchanged-topology converge stays free. When it does apply,
|
|
143
|
+
* it applies until the live set is COVERED; see {@link applyFanoutUntilCovered}.
|
|
144
|
+
*
|
|
145
|
+
* `onCluster` has no coverage to reconcile: one execution hands the DDL to Keeper's queue,
|
|
146
|
+
* which reaches the pods this converge never looked at, including ones that appear later.
|
|
147
|
+
*/
|
|
148
|
+
export declare function applyClickHouseSchema(context: ClickHouseSchemaRunContext, previous: ClickHouseSchemaState | undefined): Promise<ClickHouseSchemaState>;
|
|
149
|
+
/**
|
|
150
|
+
* Teardown. `retain` touches nothing at all — not even the cluster — so a destroyed
|
|
151
|
+
* stack leaves the data and the schema exactly as they were.
|
|
152
|
+
*/
|
|
153
|
+
export declare function deleteClickHouseSchema(context: ClickHouseSchemaRunContext): Promise<void>;
|
|
154
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/alchemy/clickhouse-schema/runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAEzB,KAAK,sBAAsB,EAE3B,KAAK,qBAAqB,EAI3B,MAAM,YAAY,CAAC;AAEpB,4FAA4F;AAC5F,eAAO,MAAM,4BAA4B,eAAe,CAAC;AAEzD,2EAA2E;AAC3E,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,eAAO,MAAM,uBAAuB,YAAY,CAAC;AACjD,eAAO,MAAM,2BAA2B,YAAY,CAAC;AAErD;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,wBAAwB,CAAC;AAErE,eAAO,MAAM,+BAA+B,SAAU,CAAC;AACvD,eAAO,MAAM,4BAA4B,SAAU,CAAC;AACpD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAG9C,oFAAoF;AACpF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,eAAO,MAAM,kBAAkB,EAAE,2BAMhC,CAAC;AAOF,wBAAgB,eAAe,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAEtE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAEvE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,sBAAsB,GAAG,SAAS,MAAM,EAAE,CAkBzF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAezE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;IACxC,kFAAkF;IAClF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,IAAI,CAAC,EAAE,2BAA2B,GAAG,SAAS,CAAC;IACxD,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAChD;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,sBAAsB,EAC9B,QAAQ,EAAE,qBAAqB,GAAG,SAAS,EAC3C,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAgBT;AA+MD;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAI1C;AA2ED;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,0BAA0B,EACnC,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,OAAO,CAAC;IACT,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,SAAS,oBAAoB,EAAE,CAAC;CAChD,CAAC,CAMD;AA4MD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,0BAA0B,EACnC,QAAQ,EAAE,qBAAqB,GAAG,SAAS,GAC1C,OAAO,CAAC,qBAAqB,CAAC,CAyBhC;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/F"}
|