@camunda/ccma-shared-types 5.0.0 → 7.0.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/_tests/approvals/dns-subdomain.spec.d.ts +1 -0
- package/dist/_tests/approvals/expected-value.spec.d.ts +1 -0
- package/dist/_tests/charts/chart-query.spec.d.ts +1 -0
- package/dist/_tests/constants/customer-hardware-sizing.const.spec.d.ts +1 -0
- package/dist/_tests/dashboards/definition.spec.d.ts +1 -0
- package/dist/_tests/dtos/ClusterPlanWhiteList.spec.d.ts +1 -0
- package/dist/_tests/utils/elasticsearchPlanPath.utils.spec.d.ts +1 -0
- package/dist/_tests/utils/upgradeGateReason.utils.spec.d.ts +1 -0
- package/dist/approvals/dns-subdomain.d.ts +12 -0
- package/dist/approvals/dns-subdomain.js +16 -0
- package/dist/approvals/dns-subdomain.js.map +7 -0
- package/dist/approvals/expected-value.d.ts +33 -0
- package/dist/approvals/expected-value.js +26 -0
- package/dist/approvals/expected-value.js.map +7 -0
- package/dist/approvals/policy.js +1 -1
- package/dist/approvals/policy.js.map +2 -2
- package/dist/approvals/use-cases.const.d.ts +96 -0
- package/dist/approvals/use-cases.const.js +59 -2
- package/dist/approvals/use-cases.const.js.map +2 -2
- package/dist/charts/chart-metrics.d.ts +35 -0
- package/dist/charts/chart-metrics.js +188 -0
- package/dist/charts/chart-metrics.js.map +7 -0
- package/dist/charts/chart-query.d.ts +55 -0
- package/dist/charts/chart-query.js +376 -0
- package/dist/charts/chart-query.js.map +7 -0
- package/dist/charts/chart-spec.d.ts +103 -0
- package/dist/charts/chart-spec.js +79 -0
- package/dist/charts/chart-spec.js.map +7 -0
- package/dist/charts/index.d.ts +3 -0
- package/dist/charts/index.js +59 -0
- package/dist/charts/index.js.map +7 -0
- package/dist/constants/customer-hardware-sizing.const.d.ts +124 -0
- package/dist/constants/customer-hardware-sizing.const.js +266 -0
- package/dist/constants/customer-hardware-sizing.const.js.map +7 -0
- package/dist/constants/version-gates.const.d.ts +12 -0
- package/dist/constants/version-gates.const.js +2 -0
- package/dist/constants/version-gates.const.js.map +2 -2
- package/dist/dashboards/definition.d.ts +86 -0
- package/dist/dashboards/definition.js +137 -0
- package/dist/dashboards/definition.js.map +7 -0
- package/dist/dtos/Annotation.dto.d.ts +1 -1
- package/dist/dtos/Annotation.dto.js +10 -2
- package/dist/dtos/Annotation.dto.js.map +2 -2
- package/dist/dtos/ApprovalJob.dto.d.ts +33 -3
- package/dist/dtos/ApprovalJob.dto.js.map +2 -2
- package/dist/dtos/ClusterPlanTypeChange.dto.d.ts +23 -0
- package/dist/dtos/ClusterPlanTypeChange.dto.js +1 -0
- package/dist/dtos/ClusterPlanTypeChange.dto.js.map +7 -0
- package/dist/dtos/CommonCluster.dto.d.ts +5 -0
- package/dist/dtos/ControlV2.dto.d.ts +17 -0
- package/dist/dtos/Dashboard.dto.d.ts +336 -0
- package/dist/dtos/Dashboard.dto.js +70 -0
- package/dist/dtos/Dashboard.dto.js.map +7 -0
- package/dist/dtos/EarlyAccessFeature.dto.d.ts +1 -0
- package/dist/dtos/FeatureFlags.dto.d.ts +96 -54
- package/dist/dtos/Hdp.dto.d.ts +5 -88
- package/dist/dtos/Organization.dto.d.ts +21 -0
- package/dist/dtos/Organization.dto.js.map +1 -1
- package/dist/dtos/SalesPlan.dto.d.ts +18 -0
- package/dist/dtos/SalesPlan.dto.js +18 -0
- package/dist/dtos/SalesPlan.dto.js.map +2 -2
- package/dist/dtos/UpgradeReadiness.dto.d.ts +32 -0
- package/dist/dtos/UpgradeReadiness.dto.js +1 -0
- package/dist/dtos/UpgradeReadiness.dto.js.map +7 -0
- package/dist/enums/SystemAuditEntity.enum.d.ts +5 -2
- package/dist/enums/SystemAuditEntity.enum.js +3 -0
- package/dist/enums/SystemAuditEntity.enum.js.map +2 -2
- package/dist/index.d.ts +18 -5
- package/dist/index.js +192 -1
- package/dist/index.js.map +2 -2
- package/dist/permissions/accounts.permissions.d.ts +12 -0
- package/dist/permissions/accounts.permissions.js +11 -0
- package/dist/permissions/accounts.permissions.js.map +2 -2
- package/dist/permissions/combo.js +5 -0
- package/dist/permissions/combo.js.map +2 -2
- package/dist/search/compiler.d.ts +20 -0
- package/dist/search/compiler.js +48 -11
- package/dist/search/compiler.js.map +2 -2
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js.map +2 -2
- package/dist/search/organization-search-fields.d.ts +5 -2
- package/dist/search/organization-search-fields.js +9 -1
- package/dist/search/organization-search-fields.js.map +2 -2
- package/dist/search/parser.d.ts +3 -0
- package/dist/search/parser.js +3 -1
- package/dist/search/parser.js.map +2 -2
- package/dist/types/ClusterBulkOperation.type.d.ts +14 -0
- package/dist/types/ClusterPlanFields.type.js +74 -74
- package/dist/types/ClusterPlanFields.type.js.map +2 -2
- package/dist/utils/elasticsearchPlanPath.utils.d.ts +20 -0
- package/dist/utils/elasticsearchPlanPath.utils.js +32 -0
- package/dist/utils/elasticsearchPlanPath.utils.js.map +7 -0
- package/dist/utils/qa-user.utils.d.ts +14 -0
- package/dist/utils/qa-user.utils.js +14 -0
- package/dist/utils/qa-user.utils.js.map +7 -0
- package/dist/utils/qa-user.utils.spec.d.ts +1 -0
- package/dist/utils/upgradeGateReason.utils.d.ts +8 -0
- package/dist/utils/upgradeGateReason.utils.js +23 -0
- package/dist/utils/upgradeGateReason.utils.js.map +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { canonicalPlan } from "../utils/elasticsearchPlanPath.utils.js";
|
|
2
|
+
const isCustomerHardwareSizingStage = (stage) => stage === "dev";
|
|
3
|
+
const CUSTOMER_HARDWARE_SIZING_MAX_PLANS_PER_REQUESTER = 10;
|
|
4
|
+
const CUSTOMER_HARDWARE_SIZING_NAME_MIN_LENGTH = 3;
|
|
5
|
+
const CUSTOMER_HARDWARE_SIZING_NAME_MAX_LENGTH = 100;
|
|
6
|
+
const CUSTOMER_HARDWARE_SIZING_MAX_PLAN_JSON_LENGTH = 5e4;
|
|
7
|
+
const CUSTOMER_HARDWARE_SIZING_RESOURCE_CAPS = {
|
|
8
|
+
broker: { cpu: 160, memoryGi: 500, storageGi: 5e3 },
|
|
9
|
+
elasticsearch: { cpu: 300, memoryGi: 1e3, storageGi: 3e4 },
|
|
10
|
+
other: { cpu: 60, memoryGi: 300, storageGi: 5e3 }
|
|
11
|
+
};
|
|
12
|
+
const CUSTOMER_HARDWARE_SIZING_COUNT_CAPS = {
|
|
13
|
+
// 7
|
|
14
|
+
clusterSize: 70,
|
|
15
|
+
// 50
|
|
16
|
+
partitionsCount: 500,
|
|
17
|
+
// 3
|
|
18
|
+
replicationFactor: 30,
|
|
19
|
+
// 5
|
|
20
|
+
nodesCount: 50,
|
|
21
|
+
// `replicas` of any component. 3.
|
|
22
|
+
replicas: 30
|
|
23
|
+
};
|
|
24
|
+
const CUSTOMER_HARDWARE_SIZING_MAX_AUTO_RESIZE_INCREASE_GI = 180;
|
|
25
|
+
const CUSTOMER_HARDWARE_SIZING_BROKER_TOTAL_CAPS = {
|
|
26
|
+
cpu: 840,
|
|
27
|
+
memoryGi: 2500,
|
|
28
|
+
storageGi: 25e3
|
|
29
|
+
};
|
|
30
|
+
const CUSTOMER_HARDWARE_SIZING_ELASTICSEARCH_TOTAL_CAPS = {
|
|
31
|
+
cpu: 900,
|
|
32
|
+
memoryGi: 3e3,
|
|
33
|
+
storageGi: 1e5
|
|
34
|
+
};
|
|
35
|
+
const CUSTOMER_HARDWARE_SIZING_REPLICA_SET_TOTAL_CAPS = {
|
|
36
|
+
cpu: 120,
|
|
37
|
+
memoryGi: 600,
|
|
38
|
+
storageGi: 5e4
|
|
39
|
+
};
|
|
40
|
+
const GIBIBYTE = 1024 ** 3;
|
|
41
|
+
const QUANTITY_MULTIPLIERS = {
|
|
42
|
+
"": 1,
|
|
43
|
+
m: 1e-3,
|
|
44
|
+
k: 1e3,
|
|
45
|
+
M: 1e6,
|
|
46
|
+
G: 1e9,
|
|
47
|
+
T: 1e12,
|
|
48
|
+
P: 1e15,
|
|
49
|
+
Ki: 1024,
|
|
50
|
+
Mi: 1024 ** 2,
|
|
51
|
+
Gi: 1024 ** 3,
|
|
52
|
+
Ti: 1024 ** 4,
|
|
53
|
+
Pi: 1024 ** 5
|
|
54
|
+
};
|
|
55
|
+
const QUANTITY_PATTERN = /^([0-9]+(?:\.[0-9]+)?)(m|k|M|G|T|P|Ki|Mi|Gi|Ti|Pi)?$/u;
|
|
56
|
+
function parseKubernetesQuantity(value) {
|
|
57
|
+
if (typeof value === "number") {
|
|
58
|
+
return Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
59
|
+
}
|
|
60
|
+
if (typeof value !== "string") {
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
const match = QUANTITY_PATTERN.exec(value);
|
|
64
|
+
if (!match) {
|
|
65
|
+
return void 0;
|
|
66
|
+
}
|
|
67
|
+
return Number(match[1]) * (QUANTITY_MULTIPLIERS[match[2] ?? ""] ?? 1);
|
|
68
|
+
}
|
|
69
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
70
|
+
function componentOf(path) {
|
|
71
|
+
if (path[0] === "zeebe" && path[1] === "broker") {
|
|
72
|
+
return "broker";
|
|
73
|
+
}
|
|
74
|
+
if (path[0] === "elasticsearch") {
|
|
75
|
+
return "elasticsearch";
|
|
76
|
+
}
|
|
77
|
+
return "other";
|
|
78
|
+
}
|
|
79
|
+
function checkNumberCap(violations, path, value, cap) {
|
|
80
|
+
const name = path.join(".");
|
|
81
|
+
const parsed = parseKubernetesQuantity(value);
|
|
82
|
+
if (parsed === void 0) {
|
|
83
|
+
violations.push(`${name} is not a number that can be checked`);
|
|
84
|
+
} else if (parsed > cap) {
|
|
85
|
+
violations.push(`${name} is ${parsed}, and the most is ${cap}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function checkResource(violations, path, value, kind) {
|
|
89
|
+
const name = path.join(".");
|
|
90
|
+
const caps = CUSTOMER_HARDWARE_SIZING_RESOURCE_CAPS[componentOf(path)];
|
|
91
|
+
const parsed = parseKubernetesQuantity(value);
|
|
92
|
+
if (parsed === void 0) {
|
|
93
|
+
violations.push(`${name} is not a quantity that can be checked`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (kind === "cpu") {
|
|
97
|
+
if (parsed > caps.cpu) {
|
|
98
|
+
violations.push(`${name} is ${parsed} CPU, and the most is ${caps.cpu}`);
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const cap = kind === "memory" ? caps.memoryGi : caps.storageGi;
|
|
103
|
+
if (parsed / GIBIBYTE > cap) {
|
|
104
|
+
violations.push(`${name} is over ${cap} GiB`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function walk(violations, node, path) {
|
|
108
|
+
if (!isRecord(node)) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
for (const [key, value] of Object.entries(node)) {
|
|
112
|
+
if (value === null || value === void 0) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const next = [...path, key];
|
|
116
|
+
const parent = path.at(-1);
|
|
117
|
+
const grandParent = path.at(-2);
|
|
118
|
+
if (Object.hasOwn(CUSTOMER_HARDWARE_SIZING_COUNT_CAPS, key) && !isRecord(value)) {
|
|
119
|
+
checkNumberCap(
|
|
120
|
+
violations,
|
|
121
|
+
next,
|
|
122
|
+
value,
|
|
123
|
+
CUSTOMER_HARDWARE_SIZING_COUNT_CAPS[key]
|
|
124
|
+
);
|
|
125
|
+
} else if ((parent === "limits" || parent === "requests") && grandParent === "resources" && (key === "cpu" || key === "memory" || key === "storage")) {
|
|
126
|
+
checkResource(violations, next, value, key);
|
|
127
|
+
} else if ((parent === "limits" || parent === "requests") && grandParent === "resources") {
|
|
128
|
+
violations.push(`${next.join(".")} is not a resource that has a cap`);
|
|
129
|
+
} else if (key === "persistentStorageCapacity") {
|
|
130
|
+
checkResource(violations, next, value, "storage");
|
|
131
|
+
} else if (parent === "autoResizing" && key === "increase") {
|
|
132
|
+
const parsed = parseKubernetesQuantity(value);
|
|
133
|
+
if (parsed === void 0 || parsed / GIBIBYTE > CUSTOMER_HARDWARE_SIZING_MAX_AUTO_RESIZE_INCREASE_GI) {
|
|
134
|
+
violations.push(
|
|
135
|
+
`${next.join(".")} is not a quantity of at most ${CUSTOMER_HARDWARE_SIZING_MAX_AUTO_RESIZE_INCREASE_GI} GiB`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
walk(violations, value, next);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const larger = (block, key) => {
|
|
144
|
+
const values = [block?.limits, block?.requests].map(
|
|
145
|
+
(entry) => isRecord(entry) ? parseKubernetesQuantity(entry[key]) : void 0
|
|
146
|
+
);
|
|
147
|
+
const known = values.filter((value) => value !== void 0);
|
|
148
|
+
return known.length > 0 ? Math.max(...known) : void 0;
|
|
149
|
+
};
|
|
150
|
+
function checkSetTotals(violations, who, count, parts, caps) {
|
|
151
|
+
const nodes = parseKubernetesQuantity(count);
|
|
152
|
+
if (nodes === void 0) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const resources = isRecord(parts.resources) ? parts.resources : void 0;
|
|
156
|
+
const storageResources = isRecord(parts.storageResources) ? parts.storageResources : void 0;
|
|
157
|
+
const cpu = larger(resources, "cpu");
|
|
158
|
+
if (cpu !== void 0 && cpu * nodes > caps.cpu) {
|
|
159
|
+
violations.push(`${who} together ask for more than ${caps.cpu} CPU`);
|
|
160
|
+
}
|
|
161
|
+
const memory = larger(resources, "memory");
|
|
162
|
+
if (memory !== void 0 && memory * nodes / GIBIBYTE > caps.memoryGi) {
|
|
163
|
+
violations.push(
|
|
164
|
+
`${who} together ask for more than ${caps.memoryGi} GiB of memory`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const disks = [
|
|
168
|
+
larger(storageResources, "storage"),
|
|
169
|
+
parseKubernetesQuantity(parts.legacyStorage)
|
|
170
|
+
].filter((value) => value !== void 0);
|
|
171
|
+
if (disks.length > 0 && Math.max(...disks) * nodes / GIBIBYTE > caps.storageGi) {
|
|
172
|
+
violations.push(
|
|
173
|
+
`${who} together ask for more than ${caps.storageGi} GiB of storage`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function checkReplicaSets(violations, node, path) {
|
|
178
|
+
if (!isRecord(node)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (node.replicas !== void 0 && node.replicas !== null && !isRecord(node.replicas)) {
|
|
182
|
+
const backend = isRecord(node.backend) ? node.backend : void 0;
|
|
183
|
+
const podResources = node.resources ?? backend?.resources;
|
|
184
|
+
checkSetTotals(
|
|
185
|
+
violations,
|
|
186
|
+
`the replicas of ${path.join(".") || "the plan"}`,
|
|
187
|
+
node.replicas,
|
|
188
|
+
{
|
|
189
|
+
resources: podResources,
|
|
190
|
+
// The same block: a pod can ask for storage in its resources too.
|
|
191
|
+
storageResources: podResources,
|
|
192
|
+
legacyStorage: void 0
|
|
193
|
+
},
|
|
194
|
+
CUSTOMER_HARDWARE_SIZING_REPLICA_SET_TOTAL_CAPS
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
for (const [key, value] of Object.entries(node)) {
|
|
198
|
+
checkReplicaSets(violations, value, [...path, key]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function checkNodeSetTotals(violations, plan) {
|
|
202
|
+
const zeebe = isRecord(plan.zeebe) ? plan.zeebe : void 0;
|
|
203
|
+
const broker = isRecord(zeebe?.broker) ? zeebe.broker : void 0;
|
|
204
|
+
if (broker) {
|
|
205
|
+
const storage = isRecord(broker.storage) ? broker.storage : void 0;
|
|
206
|
+
checkSetTotals(
|
|
207
|
+
violations,
|
|
208
|
+
"the brokers",
|
|
209
|
+
broker.clusterSize,
|
|
210
|
+
{
|
|
211
|
+
resources: broker.resources,
|
|
212
|
+
storageResources: storage?.resources,
|
|
213
|
+
legacyStorage: broker.persistentStorageCapacity
|
|
214
|
+
},
|
|
215
|
+
CUSTOMER_HARDWARE_SIZING_BROKER_TOTAL_CAPS
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
checkReplicaSets(violations, plan, []);
|
|
219
|
+
const elasticsearch = isRecord(plan.elasticsearch) ? plan.elasticsearch : void 0;
|
|
220
|
+
if (elasticsearch) {
|
|
221
|
+
const config = isRecord(elasticsearch.config) ? elasticsearch.config : void 0;
|
|
222
|
+
const storage = isRecord(config?.storage) ? config.storage : void 0;
|
|
223
|
+
checkSetTotals(
|
|
224
|
+
violations,
|
|
225
|
+
"the Elasticsearch nodes",
|
|
226
|
+
config?.nodesCount,
|
|
227
|
+
{
|
|
228
|
+
resources: elasticsearch.resources,
|
|
229
|
+
storageResources: storage?.resources,
|
|
230
|
+
legacyStorage: elasticsearch.persistentStorageCapacity
|
|
231
|
+
},
|
|
232
|
+
CUSTOMER_HARDWARE_SIZING_ELASTICSEARCH_TOTAL_CAPS
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function checkCustomerHardwareSizing(plan) {
|
|
237
|
+
if (!isRecord(plan)) {
|
|
238
|
+
return ["the plan is not an object"];
|
|
239
|
+
}
|
|
240
|
+
if (JSON.stringify(plan).length > CUSTOMER_HARDWARE_SIZING_MAX_PLAN_JSON_LENGTH) {
|
|
241
|
+
return [
|
|
242
|
+
`the plan is longer than ${CUSTOMER_HARDWARE_SIZING_MAX_PLAN_JSON_LENGTH} characters`
|
|
243
|
+
];
|
|
244
|
+
}
|
|
245
|
+
const canonical = canonicalPlan(plan);
|
|
246
|
+
const violations = [];
|
|
247
|
+
walk(violations, canonical, []);
|
|
248
|
+
checkNodeSetTotals(violations, canonical);
|
|
249
|
+
return violations;
|
|
250
|
+
}
|
|
251
|
+
export {
|
|
252
|
+
CUSTOMER_HARDWARE_SIZING_BROKER_TOTAL_CAPS,
|
|
253
|
+
CUSTOMER_HARDWARE_SIZING_COUNT_CAPS,
|
|
254
|
+
CUSTOMER_HARDWARE_SIZING_ELASTICSEARCH_TOTAL_CAPS,
|
|
255
|
+
CUSTOMER_HARDWARE_SIZING_MAX_AUTO_RESIZE_INCREASE_GI,
|
|
256
|
+
CUSTOMER_HARDWARE_SIZING_MAX_PLANS_PER_REQUESTER,
|
|
257
|
+
CUSTOMER_HARDWARE_SIZING_MAX_PLAN_JSON_LENGTH,
|
|
258
|
+
CUSTOMER_HARDWARE_SIZING_NAME_MAX_LENGTH,
|
|
259
|
+
CUSTOMER_HARDWARE_SIZING_NAME_MIN_LENGTH,
|
|
260
|
+
CUSTOMER_HARDWARE_SIZING_REPLICA_SET_TOTAL_CAPS,
|
|
261
|
+
CUSTOMER_HARDWARE_SIZING_RESOURCE_CAPS,
|
|
262
|
+
checkCustomerHardwareSizing,
|
|
263
|
+
isCustomerHardwareSizingStage,
|
|
264
|
+
parseKubernetesQuantity
|
|
265
|
+
};
|
|
266
|
+
//# sourceMappingURL=customer-hardware-sizing.const.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/constants/customer-hardware-sizing.const.ts"],
|
|
4
|
+
"sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\nimport { canonicalPlan } from '../utils/elasticsearchPlanPath.utils.js'\n\n/**\n * Customer hardware sizing: a cluster plan that a Camundi creates for an\n * organization of their own, with no admin permission. The approval use cases\n * `create-customer-hardware-sizing` and `remove-customer-hardware-sizing` run\n * on dev only, and the limits in this file keep one plan from asking for a\n * machine park.\n *\n * Every cap below is 10 times the largest value in the cluster plans of prod\n * (dump of 2026-10-01, 27 plans). The cap for storage is 10 times the largest\n * value of a plan that is NOT a load test: `Cisco XXL Year 1` and\n * `G3-M (adapted for Cisco load test)` ask for 10 TB on purpose, and a cap\n * from them would be 100 TB.\n */\n\n/** The stage where customer hardware sizing runs. */\nexport const isCustomerHardwareSizingStage = (stage: string): boolean =>\n stage === 'dev'\n\n/**\n * The most plans that one Camundi can own through customer hardware sizing.\n *\n * The count is the number of plans the Camundi created. A plan that the\n * Camundi deleted does not count.\n */\nexport const CUSTOMER_HARDWARE_SIZING_MAX_PLANS_PER_REQUESTER = 10\n\n/** The shortest and longest name of a customer hardware sizing plan. */\nexport const CUSTOMER_HARDWARE_SIZING_NAME_MIN_LENGTH = 3\nexport const CUSTOMER_HARDWARE_SIZING_NAME_MAX_LENGTH = 100\n\n/**\n * The longest plan, as JSON text. The largest plan of prod is about 6000\n * characters. The cap stops a request that carries a huge object.\n */\nexport const CUSTOMER_HARDWARE_SIZING_MAX_PLAN_JSON_LENGTH = 50_000\n\n/** One cap for a resource of one kind of component. `gi` is gibibytes. */\ninterface ResourceCaps {\n cpu: number\n memoryGi: number\n storageGi: number\n}\n\n/**\n * Caps for the requests and the limits of one component.\n *\n * `other` is every component that is not the Zeebe broker or Elasticsearch:\n * Operate, Tasklist, Optimize, the gateways, Identity and the others. The\n * largest of them in prod is Operate, with 6 CPU and 30 GiB.\n *\n * Prod maximum, before the factor of 10:\n * - broker: 16 CPU, 50 GiB, 500 GiB of storage (no load test)\n * - Elasticsearch: 30 CPU, 100 GiB, 3000 GiB of storage (no load test)\n * - other: 6 CPU, 30 GiB, no storage\n */\nexport const CUSTOMER_HARDWARE_SIZING_RESOURCE_CAPS = {\n broker: { cpu: 160, memoryGi: 500, storageGi: 5000 },\n elasticsearch: { cpu: 300, memoryGi: 1000, storageGi: 30_000 },\n other: { cpu: 60, memoryGi: 300, storageGi: 5000 },\n} as const satisfies Record<string, ResourceCaps>\n\n/** Caps for the count fields of a plan. Prod maximum is in the comments. */\nexport const CUSTOMER_HARDWARE_SIZING_COUNT_CAPS = {\n // 7\n clusterSize: 70,\n // 50\n partitionsCount: 500,\n // 3\n replicationFactor: 30,\n // 5\n nodesCount: 50,\n // `replicas` of any component. 3.\n replicas: 30,\n} as const\n\n/** `zeebe.broker.storage.autoResizing.increase`. Prod maximum is 18 GiB. */\nexport const CUSTOMER_HARDWARE_SIZING_MAX_AUTO_RESIZE_INCREASE_GI = 180\n\n/**\n * The caps over the whole broker set, which the caps for one broker do not\n * give. A plan with 70 brokers of 160 CPU passes every cap above, and it asks\n * for 11200 CPU. The prod maximum is 84 CPU (7 brokers, `Cisco XXL Year 1`),\n * 250 GiB of memory (5 brokers, `GLS HA`) and 2500 GiB of storage (5 brokers,\n * `GLS HA`, no load test).\n */\nexport const CUSTOMER_HARDWARE_SIZING_BROKER_TOTAL_CAPS = {\n cpu: 840,\n memoryGi: 2500,\n storageGi: 25_000,\n} as const\n\n/**\n * The same, for the whole Elasticsearch node set. Ten times the prod maximum:\n * 90 CPU and 300 GiB of memory (3 nodes, `GLS HA`), and 10000 GiB of storage\n * (5 nodes, `CCL G3L HA`). The storage of one node is a claim of one volume,\n * so the total is the claim times the number of nodes.\n */\nexport const CUSTOMER_HARDWARE_SIZING_ELASTICSEARCH_TOTAL_CAPS = {\n cpu: 900,\n memoryGi: 3000,\n storageGi: 100_000,\n} as const\n\n/**\n * The same, for the pods of one component that has `replicas`, such as\n * Operate or the gateway. Ten times the prod maximum: 12 CPU (the gateway, 3\n * replicas of 4 CPU) and 60 GiB of memory (Operate, 2 replicas of 30 GiB). No\n * component of prod has storage in its pod resources, so the storage total is\n * the cap for one pod, 10 times over. The check counts a storage request of a\n * pod in `resources` against it.\n */\nexport const CUSTOMER_HARDWARE_SIZING_REPLICA_SET_TOTAL_CAPS = {\n cpu: 120,\n memoryGi: 600,\n storageGi: 50_000,\n} as const\n\nconst GIBIBYTE = 1024 ** 3\n\nconst QUANTITY_MULTIPLIERS: Record<string, number> = {\n '': 1,\n m: 1e-3,\n k: 1e3,\n M: 1e6,\n G: 1e9,\n T: 1e12,\n P: 1e15,\n Ki: 1024,\n Mi: 1024 ** 2,\n Gi: 1024 ** 3,\n Ti: 1024 ** 4,\n Pi: 1024 ** 5,\n}\n\nconst QUANTITY_PATTERN = /^([0-9]+(?:\\.[0-9]+)?)(m|k|M|G|T|P|Ki|Mi|Gi|Ti|Pi)?$/u\n\n/**\n * Reads a Kubernetes quantity such as `500m`, `2`, `4Gi` or `1G`, and returns\n * its value in base units: cores for a CPU, bytes for memory and storage.\n *\n * Returns `undefined` for a text that this function cannot read, so the\n * caller can refuse it. The exponent form (`1e3`) is not read on purpose.\n */\nexport function parseKubernetesQuantity(value: unknown): number | undefined {\n if (typeof value === 'number') {\n return Number.isFinite(value) && value >= 0 ? value : undefined\n }\n if (typeof value !== 'string') {\n return undefined\n }\n const match = QUANTITY_PATTERN.exec(value)\n if (!match) {\n return undefined\n }\n return Number(match[1]) * (QUANTITY_MULTIPLIERS[match[2] ?? ''] ?? 1)\n}\n\ntype ComponentKind = keyof typeof CUSTOMER_HARDWARE_SIZING_RESOURCE_CAPS\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\nfunction componentOf(path: string[]): ComponentKind {\n if (path[0] === 'zeebe' && path[1] === 'broker') {\n return 'broker'\n }\n if (path[0] === 'elasticsearch') {\n return 'elasticsearch'\n }\n return 'other'\n}\n\nfunction checkNumberCap(\n violations: string[],\n path: string[],\n value: unknown,\n cap: number,\n): void {\n const name = path.join('.')\n const parsed = parseKubernetesQuantity(value)\n if (parsed === undefined) {\n violations.push(`${name} is not a number that can be checked`)\n } else if (parsed > cap) {\n violations.push(`${name} is ${parsed}, and the most is ${cap}`)\n }\n}\n\nfunction checkResource(\n violations: string[],\n path: string[],\n value: unknown,\n kind: 'cpu' | 'memory' | 'storage',\n): void {\n const name = path.join('.')\n const caps = CUSTOMER_HARDWARE_SIZING_RESOURCE_CAPS[componentOf(path)]\n const parsed = parseKubernetesQuantity(value)\n if (parsed === undefined) {\n violations.push(`${name} is not a quantity that can be checked`)\n return\n }\n if (kind === 'cpu') {\n if (parsed > caps.cpu) {\n violations.push(`${name} is ${parsed} CPU, and the most is ${caps.cpu}`)\n }\n return\n }\n const cap = kind === 'memory' ? caps.memoryGi : caps.storageGi\n if (parsed / GIBIBYTE > cap) {\n violations.push(`${name} is over ${cap} GiB`)\n }\n}\n\nfunction walk(violations: string[], node: unknown, path: string[]): void {\n if (!isRecord(node)) {\n return\n }\n for (const [key, value] of Object.entries(node)) {\n // The shape check of console treats `null` like an absent key, so this\n // check does too. A `null` has nothing to cap.\n if (value === null || value === undefined) {\n continue\n }\n const next = [...path, key]\n const parent = path.at(-1)\n const grandParent = path.at(-2)\n if (\n Object.hasOwn(CUSTOMER_HARDWARE_SIZING_COUNT_CAPS, key) &&\n !isRecord(value)\n ) {\n checkNumberCap(\n violations,\n next,\n value,\n CUSTOMER_HARDWARE_SIZING_COUNT_CAPS[\n key as keyof typeof CUSTOMER_HARDWARE_SIZING_COUNT_CAPS\n ],\n )\n } else if (\n (parent === 'limits' || parent === 'requests') &&\n grandParent === 'resources' &&\n (key === 'cpu' || key === 'memory' || key === 'storage')\n ) {\n checkResource(violations, next, value, key)\n } else if (\n (parent === 'limits' || parent === 'requests') &&\n grandParent === 'resources'\n ) {\n // Any other resource name (`ephemeral-storage`, a GPU) has no cap.\n violations.push(`${next.join('.')} is not a resource that has a cap`)\n } else if (key === 'persistentStorageCapacity') {\n // The legacy size of the volume. The operator can still read it.\n checkResource(violations, next, value, 'storage')\n } else if (parent === 'autoResizing' && key === 'increase') {\n // Only the step is capped. The disk itself stops at\n // `storage.resources.limits.storage`: `Plan.type.ts` calls it \"the upper\n // limit the pvc autoscaler can scale up to\", and the operator hands it\n // to the autoscaler as the storage limit of the PVC. The caps for that\n // field, for one node and for all nodes together, bound the size that a\n // disk can reach. `threshold` has no effect on that size.\n const parsed = parseKubernetesQuantity(value)\n if (\n parsed === undefined ||\n parsed / GIBIBYTE > CUSTOMER_HARDWARE_SIZING_MAX_AUTO_RESIZE_INCREASE_GI\n ) {\n violations.push(\n `${next.join('.')} is not a quantity of at most ${CUSTOMER_HARDWARE_SIZING_MAX_AUTO_RESIZE_INCREASE_GI} GiB`,\n )\n }\n } else {\n walk(violations, value, next)\n }\n }\n}\n\nconst larger = (\n block: Record<string, unknown> | undefined,\n key: string,\n): number | undefined => {\n const values = [block?.limits, block?.requests].map((entry) =>\n isRecord(entry) ? parseKubernetesQuantity(entry[key]) : undefined,\n )\n const known = values.filter((value) => value !== undefined)\n return known.length > 0 ? Math.max(...known) : undefined\n}\n\n/**\n * Checks the whole node set of one component against its totals. The caps for\n * one node do not bound `count` nodes together.\n *\n * Kubernetes schedules on `requests`, and a plan can set only `requests`. The\n * total reads the larger of the two, so that neither one skips it. The legacy\n * `persistentStorageCapacity` counts as storage too.\n */\nfunction checkSetTotals(\n violations: string[],\n who: string,\n count: unknown,\n parts: {\n resources: unknown\n storageResources: unknown\n legacyStorage: unknown\n },\n caps: { cpu: number; memoryGi: number; storageGi: number },\n): void {\n const nodes = parseKubernetesQuantity(count)\n if (nodes === undefined) {\n return\n }\n const resources = isRecord(parts.resources) ? parts.resources : undefined\n const storageResources = isRecord(parts.storageResources)\n ? parts.storageResources\n : undefined\n const cpu = larger(resources, 'cpu')\n if (cpu !== undefined && cpu * nodes > caps.cpu) {\n violations.push(`${who} together ask for more than ${caps.cpu} CPU`)\n }\n const memory = larger(resources, 'memory')\n if (memory !== undefined && (memory * nodes) / GIBIBYTE > caps.memoryGi) {\n violations.push(\n `${who} together ask for more than ${caps.memoryGi} GiB of memory`,\n )\n }\n const disks = [\n larger(storageResources, 'storage'),\n parseKubernetesQuantity(parts.legacyStorage),\n ].filter((value) => value !== undefined)\n if (\n disks.length > 0 &&\n (Math.max(...disks) * nodes) / GIBIBYTE > caps.storageGi\n ) {\n violations.push(\n `${who} together ask for more than ${caps.storageGi} GiB of storage`,\n )\n }\n}\n\n/**\n * Walks the plan for every component that has `replicas`, and checks its pods\n * together. The pod resources are in `resources`, or in `backend.resources`.\n * The brokers (`clusterSize`) and Elasticsearch (`nodesCount`) have their own\n * count and their own totals.\n */\nfunction checkReplicaSets(\n violations: string[],\n node: unknown,\n path: string[],\n): void {\n if (!isRecord(node)) {\n return\n }\n if (\n node.replicas !== undefined &&\n node.replicas !== null &&\n !isRecord(node.replicas)\n ) {\n const backend = isRecord(node.backend) ? node.backend : undefined\n const podResources = node.resources ?? backend?.resources\n checkSetTotals(\n violations,\n `the replicas of ${path.join('.') || 'the plan'}`,\n node.replicas,\n {\n resources: podResources,\n // The same block: a pod can ask for storage in its resources too.\n storageResources: podResources,\n legacyStorage: undefined,\n },\n CUSTOMER_HARDWARE_SIZING_REPLICA_SET_TOTAL_CAPS,\n )\n }\n for (const [key, value] of Object.entries(node)) {\n checkReplicaSets(violations, value, [...path, key])\n }\n}\n\nfunction checkNodeSetTotals(\n violations: string[],\n plan: Record<string, unknown>,\n): void {\n const zeebe = isRecord(plan.zeebe) ? plan.zeebe : undefined\n const broker = isRecord(zeebe?.broker) ? zeebe.broker : undefined\n if (broker) {\n const storage = isRecord(broker.storage) ? broker.storage : undefined\n checkSetTotals(\n violations,\n 'the brokers',\n broker.clusterSize,\n {\n resources: broker.resources,\n storageResources: storage?.resources,\n legacyStorage: broker.persistentStorageCapacity,\n },\n CUSTOMER_HARDWARE_SIZING_BROKER_TOTAL_CAPS,\n )\n }\n checkReplicaSets(violations, plan, [])\n const elasticsearch = isRecord(plan.elasticsearch)\n ? plan.elasticsearch\n : undefined\n if (elasticsearch) {\n const config = isRecord(elasticsearch.config)\n ? elasticsearch.config\n : undefined\n const storage = isRecord(config?.storage) ? config.storage : undefined\n checkSetTotals(\n violations,\n 'the Elasticsearch nodes',\n config?.nodesCount,\n {\n resources: elasticsearch.resources,\n storageResources: storage?.resources,\n legacyStorage: elasticsearch.persistentStorageCapacity,\n },\n CUSTOMER_HARDWARE_SIZING_ELASTICSEARCH_TOTAL_CAPS,\n )\n }\n}\n\n/**\n * Checks a cluster plan against the caps of customer hardware sizing.\n *\n * Returns the list of violations, and an empty list when the plan is within\n * the caps. It checks the size only. The shape of the plan is the job of the\n * plan validator of console-backend.\n *\n * A value that this function cannot read is a violation too: a cap that the\n * plan can skip with a strange spelling is no cap.\n */\nexport function checkCustomerHardwareSizing(plan: unknown): string[] {\n if (!isRecord(plan)) {\n return ['the plan is not an object']\n }\n if (\n JSON.stringify(plan).length > CUSTOMER_HARDWARE_SIZING_MAX_PLAN_JSON_LENGTH\n ) {\n return [\n `the plan is longer than ${CUSTOMER_HARDWARE_SIZING_MAX_PLAN_JSON_LENGTH} characters`,\n ]\n }\n // `operate.elasticsearch` is the old place of the Elasticsearch block. The\n // caps for Elasticsearch must read it as one place, so the plan is moved\n // to the canonical form first.\n const canonical = canonicalPlan(plan)\n const violations: string[] = []\n walk(violations, canonical, [])\n checkNodeSetTotals(violations, canonical)\n return violations\n}\n"],
|
|
5
|
+
"mappings": "AAMA,SAAS,qBAAqB;AAiBvB,MAAM,gCAAgC,CAAC,UAC5C,UAAU;AAQL,MAAM,mDAAmD;AAGzD,MAAM,2CAA2C;AACjD,MAAM,2CAA2C;AAMjD,MAAM,gDAAgD;AAqBtD,MAAM,yCAAyC;AAAA,EACpD,QAAQ,EAAE,KAAK,KAAK,UAAU,KAAK,WAAW,IAAK;AAAA,EACnD,eAAe,EAAE,KAAK,KAAK,UAAU,KAAM,WAAW,IAAO;AAAA,EAC7D,OAAO,EAAE,KAAK,IAAI,UAAU,KAAK,WAAW,IAAK;AACnD;AAGO,MAAM,sCAAsC;AAAA;AAAA,EAEjD,aAAa;AAAA;AAAA,EAEb,iBAAiB;AAAA;AAAA,EAEjB,mBAAmB;AAAA;AAAA,EAEnB,YAAY;AAAA;AAAA,EAEZ,UAAU;AACZ;AAGO,MAAM,uDAAuD;AAS7D,MAAM,6CAA6C;AAAA,EACxD,KAAK;AAAA,EACL,UAAU;AAAA,EACV,WAAW;AACb;AAQO,MAAM,oDAAoD;AAAA,EAC/D,KAAK;AAAA,EACL,UAAU;AAAA,EACV,WAAW;AACb;AAUO,MAAM,kDAAkD;AAAA,EAC7D,KAAK;AAAA,EACL,UAAU;AAAA,EACV,WAAW;AACb;AAEA,MAAM,WAAW,QAAQ;AAEzB,MAAM,uBAA+C;AAAA,EACnD,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,IAAI;AAAA,EACJ,IAAI,QAAQ;AAAA,EACZ,IAAI,QAAQ;AAAA,EACZ,IAAI,QAAQ;AAAA,EACZ,IAAI,QAAQ;AACd;AAEA,MAAM,mBAAmB;AASlB,SAAS,wBAAwB,OAAoC;AAC1E,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,OAAO,SAAS,KAAK,KAAK,SAAS,IAAI,QAAQ;AAAA,EACxD;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,iBAAiB,KAAK,KAAK;AACzC,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AACA,SAAO,OAAO,MAAM,CAAC,CAAC,KAAK,qBAAqB,MAAM,CAAC,KAAK,EAAE,KAAK;AACrE;AAIA,MAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAErE,SAAS,YAAY,MAA+B;AAClD,MAAI,KAAK,CAAC,MAAM,WAAW,KAAK,CAAC,MAAM,UAAU;AAC/C,WAAO;AAAA,EACT;AACA,MAAI,KAAK,CAAC,MAAM,iBAAiB;AAC/B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,eACP,YACA,MACA,OACA,KACM;AACN,QAAM,OAAO,KAAK,KAAK,GAAG;AAC1B,QAAM,SAAS,wBAAwB,KAAK;AAC5C,MAAI,WAAW,QAAW;AACxB,eAAW,KAAK,GAAG,IAAI,sCAAsC;AAAA,EAC/D,WAAW,SAAS,KAAK;AACvB,eAAW,KAAK,GAAG,IAAI,OAAO,MAAM,qBAAqB,GAAG,EAAE;AAAA,EAChE;AACF;AAEA,SAAS,cACP,YACA,MACA,OACA,MACM;AACN,QAAM,OAAO,KAAK,KAAK,GAAG;AAC1B,QAAM,OAAO,uCAAuC,YAAY,IAAI,CAAC;AACrE,QAAM,SAAS,wBAAwB,KAAK;AAC5C,MAAI,WAAW,QAAW;AACxB,eAAW,KAAK,GAAG,IAAI,wCAAwC;AAC/D;AAAA,EACF;AACA,MAAI,SAAS,OAAO;AAClB,QAAI,SAAS,KAAK,KAAK;AACrB,iBAAW,KAAK,GAAG,IAAI,OAAO,MAAM,yBAAyB,KAAK,GAAG,EAAE;AAAA,IACzE;AACA;AAAA,EACF;AACA,QAAM,MAAM,SAAS,WAAW,KAAK,WAAW,KAAK;AACrD,MAAI,SAAS,WAAW,KAAK;AAC3B,eAAW,KAAK,GAAG,IAAI,YAAY,GAAG,MAAM;AAAA,EAC9C;AACF;AAEA,SAAS,KAAK,YAAsB,MAAe,MAAsB;AACvE,MAAI,CAAC,SAAS,IAAI,GAAG;AACnB;AAAA,EACF;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAG/C,QAAI,UAAU,QAAQ,UAAU,QAAW;AACzC;AAAA,IACF;AACA,UAAM,OAAO,CAAC,GAAG,MAAM,GAAG;AAC1B,UAAM,SAAS,KAAK,GAAG,EAAE;AACzB,UAAM,cAAc,KAAK,GAAG,EAAE;AAC9B,QACE,OAAO,OAAO,qCAAqC,GAAG,KACtD,CAAC,SAAS,KAAK,GACf;AACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA,oCACE,GACF;AAAA,MACF;AAAA,IACF,YACG,WAAW,YAAY,WAAW,eACnC,gBAAgB,gBACf,QAAQ,SAAS,QAAQ,YAAY,QAAQ,YAC9C;AACA,oBAAc,YAAY,MAAM,OAAO,GAAG;AAAA,IAC5C,YACG,WAAW,YAAY,WAAW,eACnC,gBAAgB,aAChB;AAEA,iBAAW,KAAK,GAAG,KAAK,KAAK,GAAG,CAAC,mCAAmC;AAAA,IACtE,WAAW,QAAQ,6BAA6B;AAE9C,oBAAc,YAAY,MAAM,OAAO,SAAS;AAAA,IAClD,WAAW,WAAW,kBAAkB,QAAQ,YAAY;AAO1D,YAAM,SAAS,wBAAwB,KAAK;AAC5C,UACE,WAAW,UACX,SAAS,WAAW,sDACpB;AACA,mBAAW;AAAA,UACT,GAAG,KAAK,KAAK,GAAG,CAAC,iCAAiC,oDAAoD;AAAA,QACxG;AAAA,MACF;AAAA,IACF,OAAO;AACL,WAAK,YAAY,OAAO,IAAI;AAAA,IAC9B;AAAA,EACF;AACF;AAEA,MAAM,SAAS,CACb,OACA,QACuB;AACvB,QAAM,SAAS,CAAC,OAAO,QAAQ,OAAO,QAAQ,EAAE;AAAA,IAAI,CAAC,UACnD,SAAS,KAAK,IAAI,wBAAwB,MAAM,GAAG,CAAC,IAAI;AAAA,EAC1D;AACA,QAAM,QAAQ,OAAO,OAAO,CAAC,UAAU,UAAU,MAAS;AAC1D,SAAO,MAAM,SAAS,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI;AACjD;AAUA,SAAS,eACP,YACA,KACA,OACA,OAKA,MACM;AACN,QAAM,QAAQ,wBAAwB,KAAK;AAC3C,MAAI,UAAU,QAAW;AACvB;AAAA,EACF;AACA,QAAM,YAAY,SAAS,MAAM,SAAS,IAAI,MAAM,YAAY;AAChE,QAAM,mBAAmB,SAAS,MAAM,gBAAgB,IACpD,MAAM,mBACN;AACJ,QAAM,MAAM,OAAO,WAAW,KAAK;AACnC,MAAI,QAAQ,UAAa,MAAM,QAAQ,KAAK,KAAK;AAC/C,eAAW,KAAK,GAAG,GAAG,+BAA+B,KAAK,GAAG,MAAM;AAAA,EACrE;AACA,QAAM,SAAS,OAAO,WAAW,QAAQ;AACzC,MAAI,WAAW,UAAc,SAAS,QAAS,WAAW,KAAK,UAAU;AACvE,eAAW;AAAA,MACT,GAAG,GAAG,+BAA+B,KAAK,QAAQ;AAAA,IACpD;AAAA,EACF;AACA,QAAM,QAAQ;AAAA,IACZ,OAAO,kBAAkB,SAAS;AAAA,IAClC,wBAAwB,MAAM,aAAa;AAAA,EAC7C,EAAE,OAAO,CAAC,UAAU,UAAU,MAAS;AACvC,MACE,MAAM,SAAS,KACd,KAAK,IAAI,GAAG,KAAK,IAAI,QAAS,WAAW,KAAK,WAC/C;AACA,eAAW;AAAA,MACT,GAAG,GAAG,+BAA+B,KAAK,SAAS;AAAA,IACrD;AAAA,EACF;AACF;AAQA,SAAS,iBACP,YACA,MACA,MACM;AACN,MAAI,CAAC,SAAS,IAAI,GAAG;AACnB;AAAA,EACF;AACA,MACE,KAAK,aAAa,UAClB,KAAK,aAAa,QAClB,CAAC,SAAS,KAAK,QAAQ,GACvB;AACA,UAAM,UAAU,SAAS,KAAK,OAAO,IAAI,KAAK,UAAU;AACxD,UAAM,eAAe,KAAK,aAAa,SAAS;AAChD;AAAA,MACE;AAAA,MACA,mBAAmB,KAAK,KAAK,GAAG,KAAK,UAAU;AAAA,MAC/C,KAAK;AAAA,MACL;AAAA,QACE,WAAW;AAAA;AAAA,QAEX,kBAAkB;AAAA,QAClB,eAAe;AAAA,MACjB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,qBAAiB,YAAY,OAAO,CAAC,GAAG,MAAM,GAAG,CAAC;AAAA,EACpD;AACF;AAEA,SAAS,mBACP,YACA,MACM;AACN,QAAM,QAAQ,SAAS,KAAK,KAAK,IAAI,KAAK,QAAQ;AAClD,QAAM,SAAS,SAAS,OAAO,MAAM,IAAI,MAAM,SAAS;AACxD,MAAI,QAAQ;AACV,UAAM,UAAU,SAAS,OAAO,OAAO,IAAI,OAAO,UAAU;AAC5D;AAAA,MACE;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP;AAAA,QACE,WAAW,OAAO;AAAA,QAClB,kBAAkB,SAAS;AAAA,QAC3B,eAAe,OAAO;AAAA,MACxB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,mBAAiB,YAAY,MAAM,CAAC,CAAC;AACrC,QAAM,gBAAgB,SAAS,KAAK,aAAa,IAC7C,KAAK,gBACL;AACJ,MAAI,eAAe;AACjB,UAAM,SAAS,SAAS,cAAc,MAAM,IACxC,cAAc,SACd;AACJ,UAAM,UAAU,SAAS,QAAQ,OAAO,IAAI,OAAO,UAAU;AAC7D;AAAA,MACE;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,QACE,WAAW,cAAc;AAAA,QACzB,kBAAkB,SAAS;AAAA,QAC3B,eAAe,cAAc;AAAA,MAC/B;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAYO,SAAS,4BAA4B,MAAyB;AACnE,MAAI,CAAC,SAAS,IAAI,GAAG;AACnB,WAAO,CAAC,2BAA2B;AAAA,EACrC;AACA,MACE,KAAK,UAAU,IAAI,EAAE,SAAS,+CAC9B;AACA,WAAO;AAAA,MACL,2BAA2B,6CAA6C;AAAA,IAC1E;AAAA,EACF;AAIA,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,aAAuB,CAAC;AAC9B,OAAK,YAAY,WAAW,CAAC,CAAC;AAC9B,qBAAmB,YAAY,SAAS;AACxC,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -370,3 +370,15 @@ export declare const OPTIONAL_TELEMETRY_MIN_VERSION = "8.10.0-alpha1";
|
|
|
370
370
|
* the two values can move apart.
|
|
371
371
|
*/
|
|
372
372
|
export declare const RESUME_ALPHA_MIN_VERSION = "8.10.0-alpha5";
|
|
373
|
+
/**
|
|
374
|
+
* The cluster serves the public upgrade status endpoint
|
|
375
|
+
* `cluster/v2/status/upgrade` (PDP-3067, camunda/camunda#61876). From this
|
|
376
|
+
* floor the upgrade gate of console-backend asks the cluster if it finished
|
|
377
|
+
* the migrations of its last upgrade. Under it, the 24-hour grace period
|
|
378
|
+
* decides.
|
|
379
|
+
*
|
|
380
|
+
* Spelled `8.10.0`, which sorts over all `8.10.0-*` prereleases. The endpoint
|
|
381
|
+
* lands in the first release of the 8.10 line. The alphas do not have it, and
|
|
382
|
+
* they stay on the grace period.
|
|
383
|
+
*/
|
|
384
|
+
export declare const UPGRADE_READINESS_MIN_VERSION = "8.10.0";
|
|
@@ -52,6 +52,7 @@ const APP_INTEGRATIONS_89_STREAM_MINOR = "8.9.0";
|
|
|
52
52
|
const CLUSTER_SECRETS_LABEL_MIN_VERSION = CONNECTOR_SECRET_SYNC_MIN_VERSION;
|
|
53
53
|
const OPTIONAL_TELEMETRY_MIN_VERSION = "8.10.0-alpha1";
|
|
54
54
|
const RESUME_ALPHA_MIN_VERSION = "8.10.0-alpha5";
|
|
55
|
+
const UPGRADE_READINESS_MIN_VERSION = "8.10.0";
|
|
55
56
|
export {
|
|
56
57
|
ADMIN_APP_RENAME_MIN_VERSION,
|
|
57
58
|
APP_INTEGRATIONS_89_STREAM_CONNECTOR_BRIDGE_MIN_VERSION,
|
|
@@ -106,6 +107,7 @@ export {
|
|
|
106
107
|
TASKLIST_V2_SETTING_MAX_VERSION,
|
|
107
108
|
TASKLIST_V2_UPGRADE_MIN_VERSION,
|
|
108
109
|
TASKLIST_V2_UPGRADE_TARGET_MIN_VERSION,
|
|
110
|
+
UPGRADE_READINESS_MIN_VERSION,
|
|
109
111
|
ZEEBE_REST_URL_MIN_VERSION
|
|
110
112
|
};
|
|
111
113
|
//# sourceMappingURL=version-gates.const.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/constants/version-gates.const.ts"],
|
|
4
|
-
"sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\n/**\n * Central list of the cluster version floors that gate features.\n *\n * Every `checkVersion({ versionToCompareTo })` / `checkClusterVersion()` call in\n * the apps and in `packages/c4` reads its floor from here. Put a new floor here\n * before you use it. Do not write a version literal at the call site \u2014 the same\n * feature is usually gated in more than one place (a c4 tile, a console-backend\n * guard, a CRD spec), and two literals drift. Incident #9703 was two copies of\n * one floor that disagreed.\n *\n * `packages/c3` keeps its own copy of the version helpers and is out of scope:\n * it is a public package that this repo must not change.\n *\n * The values compare with semver, so the spelling of a floor decides which\n * prerelease cohort it covers:\n * - `8.10.0-0` and `8.10.0-alpha0` sort below every `8.10.0-alphaN`.\n * - `8.10.0-alpha1` sorts above `alpha0` but below `alpha2`.\n * - `8.10.0` sorts above ALL `8.10.0-*` prereleases.\n *\n * Two constants in this file can hold the same value. Then they gate different\n * features and can move apart. When a name is an alias of another constant, the\n * two answer the same question and must never drift.\n *\n * A call site compares a floor against the zeebe version of the generation,\n * unless the comment of the constant names a different field. Name that field\n * in the comment when you add a floor for one, because the same number means a\n * different thing for `connectorBridge` or for a generation name.\n */\n\n// ---------------------------------------------------------------------------\n// Release-line floors\n//\n// Used where the code asks \"is this generation on the 8.x line?\" and no single\n// feature owns the answer. Both spell the line start as `-0`, so every\n// prerelease of that line is included.\n// ---------------------------------------------------------------------------\n\n/** Start of the 8.9 line, prereleases included. */\nexport const LINE_8_9_MIN_VERSION = '8.9.0-0'\n\n/** Start of the 8.10 line, prereleases included. */\nexport const LINE_8_10_MIN_VERSION = '8.10.0-0'\n\n// ---------------------------------------------------------------------------\n// Backups\n// ---------------------------------------------------------------------------\n\n/** Hot backups are available on the cluster (c4 hot-backups tab). */\nexport const HOT_BACKUP_MIN_VERSION = '8.2.4'\n\n// ---------------------------------------------------------------------------\n// Identity, authorizations and roles\n// ---------------------------------------------------------------------------\n\n/**\n * The \"enforce groups\" toggle is offered (c4 settings tile). The tile also\n * disappears again on 8.8 clusters that run Tasklist V2 \u2014 see\n * `ENFORCE_GROUPS_TASKLIST_V2_HIDDEN_MIN_VERSION`.\n */\nexport const ENFORCE_GROUPS_TOGGLE_MIN_VERSION = '8.5.0-alpha2'\n\n/** Resource-based authorizations exist. Replaced by identity authorizations. */\nexport const RESOURCE_BASED_AUTHORIZATIONS_MIN_VERSION = '8.6.0'\n\n/**\n * Identity-based authorizations replace the resource-based ones. Read as an\n * upper bound by the c4 settings tile: below this the cluster still shows the\n * resource-based section.\n */\nexport const IDENTITY_AUTHORIZATION_MIN_VERSION = '8.8.0-alpha1'\n\n/**\n * Upper bound of the \"your upgrade turns on identity authorizations\" notice in\n * the c4 overview tab. The notice is for 8.8-line targets only, so an 8.9\n * target does not show it.\n */\nexport const IDENTITY_AUTHORIZATION_NOTICE_MAX_VERSION = '8.9.0-alpha1'\n\n/**\n * The cluster uses the 8.8 role model, so the legacy per-cluster roles are\n * gone (c4 user-details and management widgets).\n */\nexport const NEW_CLUSTER_ROLES_MIN_VERSION = '8.8.0-alpha4'\n\n/**\n * The authorizations toggle is offered while creating a cluster (c4 create\n * cluster wizard).\n */\nexport const AUTHORIZATION_TOGGLE_MIN_VERSION = '8.8.0-alpha4'\n\n/**\n * Identity manages users, groups and tenants. The cluster shows the Identity\n * screens (c4 cluster details, user details, group details, management widget,\n * console-frontend and console-sm-frontend), and the CRD spec sends the\n * internal client IDs to Identity\n * (`K8sSpecUtils.clusterHasEnabledIdentityIntegration`).\n *\n * console-backend used `8.8.0-alpha4` until the gates moved here. That floor\n * was one alpha under the frontend. As a result, an 8.8.0-alpha4 cluster got\n * its `internalClientIDs` sent to an Identity that the UI did not show. There\n * is one floor now, on the version the UI has always used.\n */\nexport const IDENTITY_MANAGEMENT_MIN_VERSION = '8.8.0-alpha5'\n\n/** The multi-tenancy toggle is offered (c4 settings tile). */\nexport const MULTI_TENANCY_TOGGLE_MIN_VERSION = '8.8.0-alpha7'\n\n/** Business-ID uniqueness toggle is offered (c4 settings tile). */\nexport const BUSINESS_ID_UNIQUENESS_MIN_VERSION = '8.9.0-alpha5'\n\n// ---------------------------------------------------------------------------\n// Tasklist\n// ---------------------------------------------------------------------------\n\n/**\n * The current generation is good enough to upgrade to Tasklist V2 (c4 overview\n * tab).\n */\nexport const TASKLIST_V2_UPGRADE_MIN_VERSION = '8.7.0-alpha4'\n\n/**\n * The upgrade target carries Tasklist V2 (c4 overview tab). Pairs with\n * `LINE_8_10_MIN_VERSION` as the upper bound, because from 8.10 on the setting\n * is gone.\n */\nexport const TASKLIST_V2_UPGRADE_TARGET_MIN_VERSION = '8.8.0-alpha1'\n\n/**\n * A new cluster gets `tasklistV2Enabled` by default (console-backend cluster\n * creation). Reads the generation's tasklist version when it has one, else\n * zeebe.\n */\nexport const TASKLIST_V2_DEFAULT_ON_MIN_VERSION = '8.8.0'\n\n/**\n * From this version, the c4 settings tile hides the \"enforce groups\" toggle on\n * a cluster that runs Tasklist V2. The tile reads the constant with `>=`, and\n * it also needs `tasklistV2Enabled` to be true.\n *\n * The name says MIN because of that `>=`. The other MAX names in this file mark\n * the version where a gate stops, and their call sites read them with `<`.\n */\nexport const ENFORCE_GROUPS_TASKLIST_V2_HIDDEN_MIN_VERSION = '8.8.0'\n\n/** The Tasklist upgrade banner is offered (c4 settings tile). */\nexport const TASKLIST_UPGRADE_BANNER_MIN_VERSION = '8.8.0-alpha2'\n\n/**\n * The Tasklist mode toggle is offered while creating a cluster (c4 create\n * cluster wizard). Upper bound is `LINE_8_10_MIN_VERSION`.\n */\nexport const TASKLIST_MODE_TOGGLE_MIN_VERSION = '8.8.0-alpha8'\n\n/**\n * Minor-version pair that turns Tasklist V2 on during a generation update\n * (console-backend). Both are read with `VersionCheckOperator.SameMinor`: an\n * 8.7 cluster that moves to an 8.8 generation gets the setting.\n */\nexport const TASKLIST_V2_AUTO_ENABLE_FROM_MINOR = '8.7.0'\nexport const TASKLIST_V2_AUTO_ENABLE_TO_MINOR = '8.8.0'\n\n/**\n * The version where the Tasklist V2 flag stops to mean anything, because there\n * is no Tasklist V1 left to switch away from. Two readers use it. The CRD spec\n * stops to emit the Tasklist V2 mode environment variable\n * (`K8sSpecUtils.shouldSkipTasklistV2ModeEnvVar`). The accounts-frontend\n * support rows stop to show the flag.\n *\n * Spelled `8.10.0` and NOT `LINE_8_10_MIN_VERSION` (`8.10.0-0`). Cluster\n * creation sets the flag on every generation from\n * `TASKLIST_V2_DEFAULT_ON_MIN_VERSION` on. An 8.10 prerelease thus carries a\n * live value, and it must stay under this bound.\n *\n * c4 bounds its own Tasklist V2 checks with `LINE_8_10_MIN_VERSION` instead.\n * That constant answers a different question: can Console still offer the\n * Tasklist upgrade to this cluster. It correctly leaves out the 8.10\n * prereleases.\n */\nexport const TASKLIST_V2_SETTING_MAX_VERSION = '8.10.0'\n\n// ---------------------------------------------------------------------------\n// Connectors\n// ---------------------------------------------------------------------------\n\n/** The internal connectors-management page exists (c4 overview tab link). */\nexport const CONNECTORS_MANAGEMENT_LINK_MIN_VERSION = '8.7.0-alpha1'\n\n/**\n * The applications list offers \"Manage\" for connectors (c4 overview tab).\n * Matches what console-frontend has always done.\n */\nexport const INTERNAL_CONNECTORS_MANAGEMENT_MIN_VERSION = '8.8.0-alpha1'\n\n/**\n * Connector management shows the outbound connectors, the per-connector metrics\n * and the restart button. Under this floor the pages show the inbound-only\n * table.\n *\n * CAUTION: the two readers compare this floor against different version fields.\n * The c4 connector-management widgets compare\n * `generation.versions.connectorBridge`\n * (`useConnectorManagementRuntime.checkClusterVersion`). The\n * console-sm-frontend pages compare the generation name of the cluster\n * (`page.getData().generation`). Both sites had the same literal before the\n * gates moved here, so this constant keeps that. Do not read it as one\n * question until the two readers compare the same field.\n */\nexport const CONNECTOR_MANAGEMENT_METRICS_MIN_VERSION = '8.10.0-alpha3'\n\n/**\n * The secrets filter mode setting is offered on the 8.6 line (zeebe). Shared by\n * isSecretsFilterModeConfigurable (c4 settings tile + console-backend update\n * guard).\n */\nexport const SECRETS_FILTER_MODE_8_6_MIN_VERSION = '8.6.28'\n\n/** Same setting, floor for the 8.7 line. */\nexport const SECRETS_FILTER_MODE_8_7_MIN_VERSION = '8.7.25'\n\n/** Same setting, floor for the 8.8 line. */\nexport const SECRETS_FILTER_MODE_8_8_MIN_VERSION = '8.8.19'\n\n/**\n * Same setting, floor for the 8.9 line. The env var works from 8.9.7, but the\n * default stays DISABLED until 8.9.10. The floor waits for that default\n * change, so the toggle never shows an unset value with an unknown default.\n */\nexport const SECRETS_FILTER_MODE_8_9_MIN_VERSION = '8.9.10'\n\n/**\n * Same setting, floor for the 8.10 line and every line above it.\n *\n * This is the last floor: isSecretsFilterModeConfigurable applies it to all\n * versions above the 8.9 line, so 8.11 and later need no floor of their own.\n *\n * Two different reasons keep the versions below it out, and the second one is\n * why this floor is alpha6 and not alpha5:\n *\n * - alpha1 to alpha4 shipped the DISABLED default. Same reason as 8.9.7-8.9.9.\n * - alpha5 shipped the STRICT default, but not the Connectors change that this\n * setting needs. SaaS runs Connectors with legacy.mode=FALLBACK from the 8.10\n * line on, and the Connectors runtime of alpha5 refuses to start unless\n * FALLBACK is paired with STRICT\n * (ConnectorsAutoConfiguration.checkLegacyFallbackSecretFilter). A customer\n * who selects LAX or DISABLED on alpha5 crashes their own Connectors runtime\n * at startup, so the toggle must stay hidden there.\n *\n * Connectors removed that check after the alpha5 release (2026-09-03). The\n * removal is in the SNAPSHOT images and lands in the releases from alpha6 on.\n * SNAPSHOT clusters run the newest images, so they get the toggle.\n */\nexport const SECRETS_FILTER_MODE_8_10_MIN_VERSION = '8.10.0-alpha6'\n\n// ---------------------------------------------------------------------------\n// APIs and cluster URLs\n// ---------------------------------------------------------------------------\n\n/** The client details page can transform the cluster URL string (c4). */\nexport const REST_API_URL_TRANSFORM_MIN_VERSION = '8.6.0'\n\n/** The cluster publishes a Zeebe REST URL, so the client details page uses it. */\nexport const ZEEBE_REST_URL_MIN_VERSION = '8.8.0-alpha1'\n\n/** The cluster serves the v2 REST API under `<clusterUrl>/v2` (c4). */\nexport const REST_API_V2_MIN_VERSION = '8.10.0-alpha1'\n\n/** The Swagger toggle and the Swagger link are offered (c4). */\nexport const SWAGGER_MIN_VERSION = '8.8.0-alpha8'\n\n/**\n * The Swagger URL moves to its 8.9 shape (c4 `buildClusterSwaggerUrl`).\n * Spelled `8.9.0`, so 8.9 prereleases keep the older URL.\n */\nexport const SWAGGER_URL_8_9_MIN_VERSION = '8.9.0'\n\n/** The MCP toggle and the MCP client tab are offered (c4). */\nexport const MCP_MIN_VERSION = '8.9.0-alpha4'\n\n// ---------------------------------------------------------------------------\n// Apps, tabs and dashboards\n// ---------------------------------------------------------------------------\n\n/**\n * Identity is called \"Admin\" from here on (c4 navigation and overview tab,\n * console-sm-frontend). The URL and the permission still say `identity`.\n */\nexport const ADMIN_APP_RENAME_MIN_VERSION = '8.9.0-alpha5'\n\n/** The job dashboard is offered (c4 overview tab and console-sm-frontend). */\nexport const JOB_DASHBOARD_MIN_VERSION = '8.9.0-alpha5'\n\n/** The cluster serves the statistics config endpoint (c4 cluster details). */\nexport const CLUSTER_STATISTICS_CONFIG_MIN_VERSION = '8.9.0-alpha5'\n\n/** The audit log tab is offered (c4 cluster details). */\nexport const AUDIT_LOG_TAB_MIN_VERSION = '8.9.0-alpha5'\n\n// ---------------------------------------------------------------------------\n// Cluster spec and provisioning\n// ---------------------------------------------------------------------------\n\n/** Only 8.7 and newer generations are offered to AWS-only orgs (console-backend). */\nexport const AWS_ONLY_GENERATION_MIN_VERSION = '8.7.0'\n\n/**\n * The gateway spec takes its 8.9 shape (console-backend\n * `K8sSpecUtils.getGatewaySpec`). Spelled `-alpha0`, so every 8.9 prerelease\n * is included.\n */\nexport const GATEWAY_SPEC_8_9_MIN_VERSION = '8.9.0-alpha0'\n\n/**\n * The operator wires the session idle-timeout config through to Operate,\n * Tasklist and the unified app (camunda-security-library ADR-0042,\n * camunda-operator#5863). This floor matches the `SessionConfigSupported` gate\n * of the operator. The CRD spec and the c4 settings tile both read it, which is\n * why it lives here.\n */\nexport const SESSION_CONFIG_MIN_VERSION = '8.10.0-alpha5'\n\n/** A new cluster gets Optimize turned on by default (console-backend). */\nexport const OPTIMIZE_DEFAULT_ON_MIN_VERSION = '8.10.0-alpha5'\n\n// ---------------------------------------------------------------------------\n// Secrets, BYOIDP, Optimize and App Integrations\n//\n// Moved here from misc.const.ts. Same names, same values.\n// ---------------------------------------------------------------------------\n/**\n * Minimum cluster (zeebe) version for the Optimize data-filters feature. Shared by\n * the c4 settings tile, the new-cluster default, and CRD spec generation so the gate\n * stays consistent. Stricter than the operator's own gate (Camunda >= 8.9.0-alpha1,\n * where the exporter starts honoring the filters) \u2014 this is the product rollout gate.\n */\nexport const OPTIMIZE_FILTERING_MIN_VERSION = '8.10.0-alpha1'\n\n/**\n * Minimum cluster (zeebe) version at which the operator understands the new\n * cluster-secret CRDs (camunda-operator#5760) \u2014 SecretSource and SecretSync exist and\n * reconcile. Governs *provisioning the CRs*, nothing about which store they point at:\n * SecretSource provisioning (K8sClusterController.ensureSecretSource) and BYOIDP's\n * oidcProviders emission, which needs a SecretSource to deliver its clientSecretRef\n * (ByoidpSecretDeliveryController.deliverSecret refuses without one).\n *\n * BYOIDP is live up to prod, so this floor must stay at the generation BYOIDP\n * shipped on. Raising it would strand BYOIDP on every alpha below the new value.\n */\nexport const SECRET_SOURCE_MIN_VERSION = '8.10.0-alpha3'\n\n/**\n * Minimum cluster (zeebe) version for BYOIDP (external identity provider, #3190).\n * Shared by the c4 settings tile gate and the console-backend update guard so both\n * stay in lockstep.\n *\n * Aliased to SECRET_SOURCE_MIN_VERSION rather than repeating the literal: BYOIDP\n * needs the SecretSource/SecretSync CRDs to exist and nothing more (it delivers its\n * clientSecret through the SecretSource), so the two answer the same question and a\n * second literal would only let them drift. It is NOT CONNECTOR_SECRET_SYNC_MIN_VERSION\n * \u2014 that one is higher, and gating BYOIDP on it would strand BYOIDP on every alpha,\n * where it currently ships to prod.\n */\nexport const BYOIDP_MIN_VERSION = SECRET_SOURCE_MIN_VERSION\n\n/**\n * Minimum cluster (zeebe) version at which a cluster's *connector and internal*\n * secrets live in the new per-stage secret manager rather than the legacy store.\n *\n * Higher than SECRET_SOURCE_MIN_VERSION on purpose: a cluster can have a\n * SecretSource (so BYOIDP works) while its connector secrets are still in the legacy\n * store and it gets no connector SecretSync. The one thing that must never drift is\n * the pair inside THIS concern: store routing\n * (ConnectorSecretsController.resolveSecretManagerTarget), the connector SecretSync\n * and its projection into the broker's file store (K8sSpecUtils.getSecretStoresSpec),\n * the pre-8.10-backup mirror check, and the upgrade-copy crossing check\n * (GenerationController.crossesInto810) all read THIS constant. #9703 was those\n * disagreeing \u2014 where Console writes a secret must equal where the cluster reads it.\n *\n * Was the 8.10 GA line (`8.10.0`) until the connector-secret changes landed on\n * `8.10.0-alpha5` (2026-09-02). The floor moved down to that alpha because the\n * ordering requirement below was already met at that time: no\n * `8.10.0-alpha5` cluster existed yet, on any stage. So no cluster's secrets\n * were left in the legacy store under the new floor.\n *\n * Why alphas normally stay out: routing is version-based with no persisted \"which\n * store holds this cluster's secrets\" marker, so a cluster above the floor while the\n * feature gate is off has its secrets in the legacy store, and enabling the gate\n * later reroutes it to a store nobody ever populated. The copy that would fix that\n * (GenerationController.crossesInto810 -> copyConnectorSecretsToNewManager) only\n * fires on a generation CHANGE, and alpha generations have no upgrade chain at all\n * (`upgradeableFrom = []`, ReleaseTrainController.handleAlpha) \u2014 an alpha cluster is\n * created on its generation and stays there. So an alpha cohort above this floor\n * could never be migrated; the ~200 8.10.0-alpha4 clusters on prod are exactly that\n * cohort, and they stay below THIS floor (`alpha4 < alpha5`), so they are unaffected.\n *\n * The remaining ordering requirement is that a stage's gate be enabled before the\n * first at-floor cluster exists on it \u2014 which for SNAPSHOT-tagged clusters (at-floor\n * by design, since they run the newest operator) still has to be respected by hand. A\n * future move to `8.10.0-alpha6` or higher must check the same thing this move did:\n * no cluster already sitting at that version on any stage.\n */\nexport const CONNECTOR_SECRET_SYNC_MIN_VERSION = '8.10.0-alpha5'\n\n/**\n * Version floors for App Integrations (#8536). The fix landed as patches on\n * the 8.9 stream (zeebe >=8.9.14 AND connectorBridge >=8.9.7), everything\n * from 8.10.0-alpha4 on already carries both fixes. Shared by\n * isAppIntegrationsVersionSupported (c4 settings tile + console-backend\n * update guard) so the gate logic and its error message stay in lockstep.\n */\nexport const APP_INTEGRATIONS_MIN_VERSION = '8.10.0-alpha4'\nexport const APP_INTEGRATIONS_89_STREAM_ZEEBE_MIN_VERSION = '8.9.14'\nexport const APP_INTEGRATIONS_89_STREAM_CONNECTOR_BRIDGE_MIN_VERSION = '8.9.7'\n\n/**\n * Minor selector for the 8.9 stream, read with `VersionCheckOperator.SameMinor`\n * by `isAppIntegrationsVersionSupported`. It picks the stream, it is not a\n * floor: the floors are the two constants above.\n */\nexport const APP_INTEGRATIONS_89_STREAM_MINOR = '8.9.0'\n\n/**\n * The cluster details header calls the secrets \"cluster secrets\" instead of\n * the legacy name (c4).\n *\n * Aliased to CONNECTOR_SECRET_SYNC_MIN_VERSION rather than repeating the\n * literal: the label must follow the store the cluster actually uses, so the\n * two answer the same question.\n */\nexport const CLUSTER_SECRETS_LABEL_MIN_VERSION =\n CONNECTOR_SECRET_SYNC_MIN_VERSION\n\n/**\n * Minimum cluster (zeebe) version that runs the analytics exporter, thus the\n * floor for optional telemetry.\n *\n * The number comes from the operator: `AnalyticsExporterSupported` in\n * `pkg/features/features.go` reads `>= 8.10.0-alpha1`, and below it\n * `zeebe_statefulset.go` adds no exporter env vars at all. A cluster under the\n * floor sends no analytics telemetry, OPTIONAL or CONTRACTUAL, thus the row\n * has no state to show and the toggle has nothing to switch.\n */\nexport const OPTIONAL_TELEMETRY_MIN_VERSION = '8.10.0-alpha1'\n\n// ---------------------------------------------------------------------------\n// Suspend and resume\n// ---------------------------------------------------------------------------\n\n/**\n * Lowest ALPHA generation that a suspended cluster can resume from. A cluster\n * on an alpha under this floor stays suspended forever.\n * `isGenerationResumable` reads this floor. The c4 resume actions and the\n * console-backend wake-up guards share that function.\n *\n * The floor applies to alphas only. A release generation under it, 8.9 or\n * 8.8 for example, resumes as before. Those lines have an upgrade chain, so a\n * customer can move off them. An alpha has no upgrade chain\n * (`upgradeableFrom = []`, `ReleaseTrainController.handleAlpha`). A cluster\n * starts on its alpha generation and stays there, so the only way off an old\n * alpha is a new cluster. A bulk operation suspends that cohort, and this floor\n * then refuses the wake-up.\n *\n * Spelled `8.10.0-alpha5` and not `-0`, because the cohort to retire is every\n * alpha under alpha5. It holds the same value as\n * `CONNECTOR_SECRET_SYNC_MIN_VERSION` today. The alphas under that floor keep\n * their connector secrets in the legacy store, and no migration can move them.\n * It stays a constant of its own, because it answers a different question and\n * the two values can move apart.\n */\nexport const RESUME_ALPHA_MIN_VERSION = '8.10.0-alpha5'\n"],
|
|
5
|
-
"mappings": "AA4CO,MAAM,uBAAuB;AAG7B,MAAM,wBAAwB;AAO9B,MAAM,yBAAyB;AAW/B,MAAM,oCAAoC;AAG1C,MAAM,4CAA4C;AAOlD,MAAM,qCAAqC;AAO3C,MAAM,4CAA4C;AAMlD,MAAM,gCAAgC;AAMtC,MAAM,mCAAmC;AAczC,MAAM,kCAAkC;AAGxC,MAAM,mCAAmC;AAGzC,MAAM,qCAAqC;AAU3C,MAAM,kCAAkC;AAOxC,MAAM,yCAAyC;AAO/C,MAAM,qCAAqC;AAU3C,MAAM,gDAAgD;AAGtD,MAAM,sCAAsC;AAM5C,MAAM,mCAAmC;AAOzC,MAAM,qCAAqC;AAC3C,MAAM,mCAAmC;AAmBzC,MAAM,kCAAkC;AAOxC,MAAM,yCAAyC;AAM/C,MAAM,6CAA6C;AAgBnD,MAAM,2CAA2C;AAOjD,MAAM,sCAAsC;AAG5C,MAAM,sCAAsC;AAG5C,MAAM,sCAAsC;AAO5C,MAAM,sCAAsC;AAwB5C,MAAM,uCAAuC;AAO7C,MAAM,qCAAqC;AAG3C,MAAM,6BAA6B;AAGnC,MAAM,0BAA0B;AAGhC,MAAM,sBAAsB;AAM5B,MAAM,8BAA8B;AAGpC,MAAM,kBAAkB;AAUxB,MAAM,+BAA+B;AAGrC,MAAM,4BAA4B;AAGlC,MAAM,wCAAwC;AAG9C,MAAM,4BAA4B;AAOlC,MAAM,kCAAkC;AAOxC,MAAM,+BAA+B;AASrC,MAAM,6BAA6B;AAGnC,MAAM,kCAAkC;AAaxC,MAAM,iCAAiC;AAavC,MAAM,4BAA4B;AAclC,MAAM,qBAAqB;AAuC3B,MAAM,oCAAoC;AAS1C,MAAM,+BAA+B;AACrC,MAAM,+CAA+C;AACrD,MAAM,0DAA0D;AAOhE,MAAM,mCAAmC;AAUzC,MAAM,oCACX;AAYK,MAAM,iCAAiC;AA2BvC,MAAM,2BAA2B;",
|
|
4
|
+
"sourcesContent": ["/*\n * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH\n * under one or more contributor license agreements. Licensed under a commercial license.\n * You may not use this file except in compliance with the commercial license.\n */\n\n/**\n * Central list of the cluster version floors that gate features.\n *\n * Every `checkVersion({ versionToCompareTo })` / `checkClusterVersion()` call in\n * the apps and in `packages/c4` reads its floor from here. Put a new floor here\n * before you use it. Do not write a version literal at the call site \u2014 the same\n * feature is usually gated in more than one place (a c4 tile, a console-backend\n * guard, a CRD spec), and two literals drift. Incident #9703 was two copies of\n * one floor that disagreed.\n *\n * `packages/c3` keeps its own copy of the version helpers and is out of scope:\n * it is a public package that this repo must not change.\n *\n * The values compare with semver, so the spelling of a floor decides which\n * prerelease cohort it covers:\n * - `8.10.0-0` and `8.10.0-alpha0` sort below every `8.10.0-alphaN`.\n * - `8.10.0-alpha1` sorts above `alpha0` but below `alpha2`.\n * - `8.10.0` sorts above ALL `8.10.0-*` prereleases.\n *\n * Two constants in this file can hold the same value. Then they gate different\n * features and can move apart. When a name is an alias of another constant, the\n * two answer the same question and must never drift.\n *\n * A call site compares a floor against the zeebe version of the generation,\n * unless the comment of the constant names a different field. Name that field\n * in the comment when you add a floor for one, because the same number means a\n * different thing for `connectorBridge` or for a generation name.\n */\n\n// ---------------------------------------------------------------------------\n// Release-line floors\n//\n// Used where the code asks \"is this generation on the 8.x line?\" and no single\n// feature owns the answer. Both spell the line start as `-0`, so every\n// prerelease of that line is included.\n// ---------------------------------------------------------------------------\n\n/** Start of the 8.9 line, prereleases included. */\nexport const LINE_8_9_MIN_VERSION = '8.9.0-0'\n\n/** Start of the 8.10 line, prereleases included. */\nexport const LINE_8_10_MIN_VERSION = '8.10.0-0'\n\n// ---------------------------------------------------------------------------\n// Backups\n// ---------------------------------------------------------------------------\n\n/** Hot backups are available on the cluster (c4 hot-backups tab). */\nexport const HOT_BACKUP_MIN_VERSION = '8.2.4'\n\n// ---------------------------------------------------------------------------\n// Identity, authorizations and roles\n// ---------------------------------------------------------------------------\n\n/**\n * The \"enforce groups\" toggle is offered (c4 settings tile). The tile also\n * disappears again on 8.8 clusters that run Tasklist V2 \u2014 see\n * `ENFORCE_GROUPS_TASKLIST_V2_HIDDEN_MIN_VERSION`.\n */\nexport const ENFORCE_GROUPS_TOGGLE_MIN_VERSION = '8.5.0-alpha2'\n\n/** Resource-based authorizations exist. Replaced by identity authorizations. */\nexport const RESOURCE_BASED_AUTHORIZATIONS_MIN_VERSION = '8.6.0'\n\n/**\n * Identity-based authorizations replace the resource-based ones. Read as an\n * upper bound by the c4 settings tile: below this the cluster still shows the\n * resource-based section.\n */\nexport const IDENTITY_AUTHORIZATION_MIN_VERSION = '8.8.0-alpha1'\n\n/**\n * Upper bound of the \"your upgrade turns on identity authorizations\" notice in\n * the c4 overview tab. The notice is for 8.8-line targets only, so an 8.9\n * target does not show it.\n */\nexport const IDENTITY_AUTHORIZATION_NOTICE_MAX_VERSION = '8.9.0-alpha1'\n\n/**\n * The cluster uses the 8.8 role model, so the legacy per-cluster roles are\n * gone (c4 user-details and management widgets).\n */\nexport const NEW_CLUSTER_ROLES_MIN_VERSION = '8.8.0-alpha4'\n\n/**\n * The authorizations toggle is offered while creating a cluster (c4 create\n * cluster wizard).\n */\nexport const AUTHORIZATION_TOGGLE_MIN_VERSION = '8.8.0-alpha4'\n\n/**\n * Identity manages users, groups and tenants. The cluster shows the Identity\n * screens (c4 cluster details, user details, group details, management widget,\n * console-frontend and console-sm-frontend), and the CRD spec sends the\n * internal client IDs to Identity\n * (`K8sSpecUtils.clusterHasEnabledIdentityIntegration`).\n *\n * console-backend used `8.8.0-alpha4` until the gates moved here. That floor\n * was one alpha under the frontend. As a result, an 8.8.0-alpha4 cluster got\n * its `internalClientIDs` sent to an Identity that the UI did not show. There\n * is one floor now, on the version the UI has always used.\n */\nexport const IDENTITY_MANAGEMENT_MIN_VERSION = '8.8.0-alpha5'\n\n/** The multi-tenancy toggle is offered (c4 settings tile). */\nexport const MULTI_TENANCY_TOGGLE_MIN_VERSION = '8.8.0-alpha7'\n\n/** Business-ID uniqueness toggle is offered (c4 settings tile). */\nexport const BUSINESS_ID_UNIQUENESS_MIN_VERSION = '8.9.0-alpha5'\n\n// ---------------------------------------------------------------------------\n// Tasklist\n// ---------------------------------------------------------------------------\n\n/**\n * The current generation is good enough to upgrade to Tasklist V2 (c4 overview\n * tab).\n */\nexport const TASKLIST_V2_UPGRADE_MIN_VERSION = '8.7.0-alpha4'\n\n/**\n * The upgrade target carries Tasklist V2 (c4 overview tab). Pairs with\n * `LINE_8_10_MIN_VERSION` as the upper bound, because from 8.10 on the setting\n * is gone.\n */\nexport const TASKLIST_V2_UPGRADE_TARGET_MIN_VERSION = '8.8.0-alpha1'\n\n/**\n * A new cluster gets `tasklistV2Enabled` by default (console-backend cluster\n * creation). Reads the generation's tasklist version when it has one, else\n * zeebe.\n */\nexport const TASKLIST_V2_DEFAULT_ON_MIN_VERSION = '8.8.0'\n\n/**\n * From this version, the c4 settings tile hides the \"enforce groups\" toggle on\n * a cluster that runs Tasklist V2. The tile reads the constant with `>=`, and\n * it also needs `tasklistV2Enabled` to be true.\n *\n * The name says MIN because of that `>=`. The other MAX names in this file mark\n * the version where a gate stops, and their call sites read them with `<`.\n */\nexport const ENFORCE_GROUPS_TASKLIST_V2_HIDDEN_MIN_VERSION = '8.8.0'\n\n/** The Tasklist upgrade banner is offered (c4 settings tile). */\nexport const TASKLIST_UPGRADE_BANNER_MIN_VERSION = '8.8.0-alpha2'\n\n/**\n * The Tasklist mode toggle is offered while creating a cluster (c4 create\n * cluster wizard). Upper bound is `LINE_8_10_MIN_VERSION`.\n */\nexport const TASKLIST_MODE_TOGGLE_MIN_VERSION = '8.8.0-alpha8'\n\n/**\n * Minor-version pair that turns Tasklist V2 on during a generation update\n * (console-backend). Both are read with `VersionCheckOperator.SameMinor`: an\n * 8.7 cluster that moves to an 8.8 generation gets the setting.\n */\nexport const TASKLIST_V2_AUTO_ENABLE_FROM_MINOR = '8.7.0'\nexport const TASKLIST_V2_AUTO_ENABLE_TO_MINOR = '8.8.0'\n\n/**\n * The version where the Tasklist V2 flag stops to mean anything, because there\n * is no Tasklist V1 left to switch away from. Two readers use it. The CRD spec\n * stops to emit the Tasklist V2 mode environment variable\n * (`K8sSpecUtils.shouldSkipTasklistV2ModeEnvVar`). The accounts-frontend\n * support rows stop to show the flag.\n *\n * Spelled `8.10.0` and NOT `LINE_8_10_MIN_VERSION` (`8.10.0-0`). Cluster\n * creation sets the flag on every generation from\n * `TASKLIST_V2_DEFAULT_ON_MIN_VERSION` on. An 8.10 prerelease thus carries a\n * live value, and it must stay under this bound.\n *\n * c4 bounds its own Tasklist V2 checks with `LINE_8_10_MIN_VERSION` instead.\n * That constant answers a different question: can Console still offer the\n * Tasklist upgrade to this cluster. It correctly leaves out the 8.10\n * prereleases.\n */\nexport const TASKLIST_V2_SETTING_MAX_VERSION = '8.10.0'\n\n// ---------------------------------------------------------------------------\n// Connectors\n// ---------------------------------------------------------------------------\n\n/** The internal connectors-management page exists (c4 overview tab link). */\nexport const CONNECTORS_MANAGEMENT_LINK_MIN_VERSION = '8.7.0-alpha1'\n\n/**\n * The applications list offers \"Manage\" for connectors (c4 overview tab).\n * Matches what console-frontend has always done.\n */\nexport const INTERNAL_CONNECTORS_MANAGEMENT_MIN_VERSION = '8.8.0-alpha1'\n\n/**\n * Connector management shows the outbound connectors, the per-connector metrics\n * and the restart button. Under this floor the pages show the inbound-only\n * table.\n *\n * CAUTION: the two readers compare this floor against different version fields.\n * The c4 connector-management widgets compare\n * `generation.versions.connectorBridge`\n * (`useConnectorManagementRuntime.checkClusterVersion`). The\n * console-sm-frontend pages compare the generation name of the cluster\n * (`page.getData().generation`). Both sites had the same literal before the\n * gates moved here, so this constant keeps that. Do not read it as one\n * question until the two readers compare the same field.\n */\nexport const CONNECTOR_MANAGEMENT_METRICS_MIN_VERSION = '8.10.0-alpha3'\n\n/**\n * The secrets filter mode setting is offered on the 8.6 line (zeebe). Shared by\n * isSecretsFilterModeConfigurable (c4 settings tile + console-backend update\n * guard).\n */\nexport const SECRETS_FILTER_MODE_8_6_MIN_VERSION = '8.6.28'\n\n/** Same setting, floor for the 8.7 line. */\nexport const SECRETS_FILTER_MODE_8_7_MIN_VERSION = '8.7.25'\n\n/** Same setting, floor for the 8.8 line. */\nexport const SECRETS_FILTER_MODE_8_8_MIN_VERSION = '8.8.19'\n\n/**\n * Same setting, floor for the 8.9 line. The env var works from 8.9.7, but the\n * default stays DISABLED until 8.9.10. The floor waits for that default\n * change, so the toggle never shows an unset value with an unknown default.\n */\nexport const SECRETS_FILTER_MODE_8_9_MIN_VERSION = '8.9.10'\n\n/**\n * Same setting, floor for the 8.10 line and every line above it.\n *\n * This is the last floor: isSecretsFilterModeConfigurable applies it to all\n * versions above the 8.9 line, so 8.11 and later need no floor of their own.\n *\n * Two different reasons keep the versions below it out, and the second one is\n * why this floor is alpha6 and not alpha5:\n *\n * - alpha1 to alpha4 shipped the DISABLED default. Same reason as 8.9.7-8.9.9.\n * - alpha5 shipped the STRICT default, but not the Connectors change that this\n * setting needs. SaaS runs Connectors with legacy.mode=FALLBACK from the 8.10\n * line on, and the Connectors runtime of alpha5 refuses to start unless\n * FALLBACK is paired with STRICT\n * (ConnectorsAutoConfiguration.checkLegacyFallbackSecretFilter). A customer\n * who selects LAX or DISABLED on alpha5 crashes their own Connectors runtime\n * at startup, so the toggle must stay hidden there.\n *\n * Connectors removed that check after the alpha5 release (2026-09-03). The\n * removal is in the SNAPSHOT images and lands in the releases from alpha6 on.\n * SNAPSHOT clusters run the newest images, so they get the toggle.\n */\nexport const SECRETS_FILTER_MODE_8_10_MIN_VERSION = '8.10.0-alpha6'\n\n// ---------------------------------------------------------------------------\n// APIs and cluster URLs\n// ---------------------------------------------------------------------------\n\n/** The client details page can transform the cluster URL string (c4). */\nexport const REST_API_URL_TRANSFORM_MIN_VERSION = '8.6.0'\n\n/** The cluster publishes a Zeebe REST URL, so the client details page uses it. */\nexport const ZEEBE_REST_URL_MIN_VERSION = '8.8.0-alpha1'\n\n/** The cluster serves the v2 REST API under `<clusterUrl>/v2` (c4). */\nexport const REST_API_V2_MIN_VERSION = '8.10.0-alpha1'\n\n/** The Swagger toggle and the Swagger link are offered (c4). */\nexport const SWAGGER_MIN_VERSION = '8.8.0-alpha8'\n\n/**\n * The Swagger URL moves to its 8.9 shape (c4 `buildClusterSwaggerUrl`).\n * Spelled `8.9.0`, so 8.9 prereleases keep the older URL.\n */\nexport const SWAGGER_URL_8_9_MIN_VERSION = '8.9.0'\n\n/** The MCP toggle and the MCP client tab are offered (c4). */\nexport const MCP_MIN_VERSION = '8.9.0-alpha4'\n\n// ---------------------------------------------------------------------------\n// Apps, tabs and dashboards\n// ---------------------------------------------------------------------------\n\n/**\n * Identity is called \"Admin\" from here on (c4 navigation and overview tab,\n * console-sm-frontend). The URL and the permission still say `identity`.\n */\nexport const ADMIN_APP_RENAME_MIN_VERSION = '8.9.0-alpha5'\n\n/** The job dashboard is offered (c4 overview tab and console-sm-frontend). */\nexport const JOB_DASHBOARD_MIN_VERSION = '8.9.0-alpha5'\n\n/** The cluster serves the statistics config endpoint (c4 cluster details). */\nexport const CLUSTER_STATISTICS_CONFIG_MIN_VERSION = '8.9.0-alpha5'\n\n/** The audit log tab is offered (c4 cluster details). */\nexport const AUDIT_LOG_TAB_MIN_VERSION = '8.9.0-alpha5'\n\n// ---------------------------------------------------------------------------\n// Cluster spec and provisioning\n// ---------------------------------------------------------------------------\n\n/** Only 8.7 and newer generations are offered to AWS-only orgs (console-backend). */\nexport const AWS_ONLY_GENERATION_MIN_VERSION = '8.7.0'\n\n/**\n * The gateway spec takes its 8.9 shape (console-backend\n * `K8sSpecUtils.getGatewaySpec`). Spelled `-alpha0`, so every 8.9 prerelease\n * is included.\n */\nexport const GATEWAY_SPEC_8_9_MIN_VERSION = '8.9.0-alpha0'\n\n/**\n * The operator wires the session idle-timeout config through to Operate,\n * Tasklist and the unified app (camunda-security-library ADR-0042,\n * camunda-operator#5863). This floor matches the `SessionConfigSupported` gate\n * of the operator. The CRD spec and the c4 settings tile both read it, which is\n * why it lives here.\n */\nexport const SESSION_CONFIG_MIN_VERSION = '8.10.0-alpha5'\n\n/** A new cluster gets Optimize turned on by default (console-backend). */\nexport const OPTIMIZE_DEFAULT_ON_MIN_VERSION = '8.10.0-alpha5'\n\n// ---------------------------------------------------------------------------\n// Secrets, BYOIDP, Optimize and App Integrations\n//\n// Moved here from misc.const.ts. Same names, same values.\n// ---------------------------------------------------------------------------\n/**\n * Minimum cluster (zeebe) version for the Optimize data-filters feature. Shared by\n * the c4 settings tile, the new-cluster default, and CRD spec generation so the gate\n * stays consistent. Stricter than the operator's own gate (Camunda >= 8.9.0-alpha1,\n * where the exporter starts honoring the filters) \u2014 this is the product rollout gate.\n */\nexport const OPTIMIZE_FILTERING_MIN_VERSION = '8.10.0-alpha1'\n\n/**\n * Minimum cluster (zeebe) version at which the operator understands the new\n * cluster-secret CRDs (camunda-operator#5760) \u2014 SecretSource and SecretSync exist and\n * reconcile. Governs *provisioning the CRs*, nothing about which store they point at:\n * SecretSource provisioning (K8sClusterController.ensureSecretSource) and BYOIDP's\n * oidcProviders emission, which needs a SecretSource to deliver its clientSecretRef\n * (ByoidpSecretDeliveryController.deliverSecret refuses without one).\n *\n * BYOIDP is live up to prod, so this floor must stay at the generation BYOIDP\n * shipped on. Raising it would strand BYOIDP on every alpha below the new value.\n */\nexport const SECRET_SOURCE_MIN_VERSION = '8.10.0-alpha3'\n\n/**\n * Minimum cluster (zeebe) version for BYOIDP (external identity provider, #3190).\n * Shared by the c4 settings tile gate and the console-backend update guard so both\n * stay in lockstep.\n *\n * Aliased to SECRET_SOURCE_MIN_VERSION rather than repeating the literal: BYOIDP\n * needs the SecretSource/SecretSync CRDs to exist and nothing more (it delivers its\n * clientSecret through the SecretSource), so the two answer the same question and a\n * second literal would only let them drift. It is NOT CONNECTOR_SECRET_SYNC_MIN_VERSION\n * \u2014 that one is higher, and gating BYOIDP on it would strand BYOIDP on every alpha,\n * where it currently ships to prod.\n */\nexport const BYOIDP_MIN_VERSION = SECRET_SOURCE_MIN_VERSION\n\n/**\n * Minimum cluster (zeebe) version at which a cluster's *connector and internal*\n * secrets live in the new per-stage secret manager rather than the legacy store.\n *\n * Higher than SECRET_SOURCE_MIN_VERSION on purpose: a cluster can have a\n * SecretSource (so BYOIDP works) while its connector secrets are still in the legacy\n * store and it gets no connector SecretSync. The one thing that must never drift is\n * the pair inside THIS concern: store routing\n * (ConnectorSecretsController.resolveSecretManagerTarget), the connector SecretSync\n * and its projection into the broker's file store (K8sSpecUtils.getSecretStoresSpec),\n * the pre-8.10-backup mirror check, and the upgrade-copy crossing check\n * (GenerationController.crossesInto810) all read THIS constant. #9703 was those\n * disagreeing \u2014 where Console writes a secret must equal where the cluster reads it.\n *\n * Was the 8.10 GA line (`8.10.0`) until the connector-secret changes landed on\n * `8.10.0-alpha5` (2026-09-02). The floor moved down to that alpha because the\n * ordering requirement below was already met at that time: no\n * `8.10.0-alpha5` cluster existed yet, on any stage. So no cluster's secrets\n * were left in the legacy store under the new floor.\n *\n * Why alphas normally stay out: routing is version-based with no persisted \"which\n * store holds this cluster's secrets\" marker, so a cluster above the floor while the\n * feature gate is off has its secrets in the legacy store, and enabling the gate\n * later reroutes it to a store nobody ever populated. The copy that would fix that\n * (GenerationController.crossesInto810 -> copyConnectorSecretsToNewManager) only\n * fires on a generation CHANGE, and alpha generations have no upgrade chain at all\n * (`upgradeableFrom = []`, ReleaseTrainController.handleAlpha) \u2014 an alpha cluster is\n * created on its generation and stays there. So an alpha cohort above this floor\n * could never be migrated; the ~200 8.10.0-alpha4 clusters on prod are exactly that\n * cohort, and they stay below THIS floor (`alpha4 < alpha5`), so they are unaffected.\n *\n * The remaining ordering requirement is that a stage's gate be enabled before the\n * first at-floor cluster exists on it \u2014 which for SNAPSHOT-tagged clusters (at-floor\n * by design, since they run the newest operator) still has to be respected by hand. A\n * future move to `8.10.0-alpha6` or higher must check the same thing this move did:\n * no cluster already sitting at that version on any stage.\n */\nexport const CONNECTOR_SECRET_SYNC_MIN_VERSION = '8.10.0-alpha5'\n\n/**\n * Version floors for App Integrations (#8536). The fix landed as patches on\n * the 8.9 stream (zeebe >=8.9.14 AND connectorBridge >=8.9.7), everything\n * from 8.10.0-alpha4 on already carries both fixes. Shared by\n * isAppIntegrationsVersionSupported (c4 settings tile + console-backend\n * update guard) so the gate logic and its error message stay in lockstep.\n */\nexport const APP_INTEGRATIONS_MIN_VERSION = '8.10.0-alpha4'\nexport const APP_INTEGRATIONS_89_STREAM_ZEEBE_MIN_VERSION = '8.9.14'\nexport const APP_INTEGRATIONS_89_STREAM_CONNECTOR_BRIDGE_MIN_VERSION = '8.9.7'\n\n/**\n * Minor selector for the 8.9 stream, read with `VersionCheckOperator.SameMinor`\n * by `isAppIntegrationsVersionSupported`. It picks the stream, it is not a\n * floor: the floors are the two constants above.\n */\nexport const APP_INTEGRATIONS_89_STREAM_MINOR = '8.9.0'\n\n/**\n * The cluster details header calls the secrets \"cluster secrets\" instead of\n * the legacy name (c4).\n *\n * Aliased to CONNECTOR_SECRET_SYNC_MIN_VERSION rather than repeating the\n * literal: the label must follow the store the cluster actually uses, so the\n * two answer the same question.\n */\nexport const CLUSTER_SECRETS_LABEL_MIN_VERSION =\n CONNECTOR_SECRET_SYNC_MIN_VERSION\n\n/**\n * Minimum cluster (zeebe) version that runs the analytics exporter, thus the\n * floor for optional telemetry.\n *\n * The number comes from the operator: `AnalyticsExporterSupported` in\n * `pkg/features/features.go` reads `>= 8.10.0-alpha1`, and below it\n * `zeebe_statefulset.go` adds no exporter env vars at all. A cluster under the\n * floor sends no analytics telemetry, OPTIONAL or CONTRACTUAL, thus the row\n * has no state to show and the toggle has nothing to switch.\n */\nexport const OPTIONAL_TELEMETRY_MIN_VERSION = '8.10.0-alpha1'\n\n// ---------------------------------------------------------------------------\n// Suspend and resume\n// ---------------------------------------------------------------------------\n\n/**\n * Lowest ALPHA generation that a suspended cluster can resume from. A cluster\n * on an alpha under this floor stays suspended forever.\n * `isGenerationResumable` reads this floor. The c4 resume actions and the\n * console-backend wake-up guards share that function.\n *\n * The floor applies to alphas only. A release generation under it, 8.9 or\n * 8.8 for example, resumes as before. Those lines have an upgrade chain, so a\n * customer can move off them. An alpha has no upgrade chain\n * (`upgradeableFrom = []`, `ReleaseTrainController.handleAlpha`). A cluster\n * starts on its alpha generation and stays there, so the only way off an old\n * alpha is a new cluster. A bulk operation suspends that cohort, and this floor\n * then refuses the wake-up.\n *\n * Spelled `8.10.0-alpha5` and not `-0`, because the cohort to retire is every\n * alpha under alpha5. It holds the same value as\n * `CONNECTOR_SECRET_SYNC_MIN_VERSION` today. The alphas under that floor keep\n * their connector secrets in the legacy store, and no migration can move them.\n * It stays a constant of its own, because it answers a different question and\n * the two values can move apart.\n */\nexport const RESUME_ALPHA_MIN_VERSION = '8.10.0-alpha5'\n\n// ---------------------------------------------------------------------------\n// Generation upgrades\n// ---------------------------------------------------------------------------\n\n/**\n * The cluster serves the public upgrade status endpoint\n * `cluster/v2/status/upgrade` (PDP-3067, camunda/camunda#61876). From this\n * floor the upgrade gate of console-backend asks the cluster if it finished\n * the migrations of its last upgrade. Under it, the 24-hour grace period\n * decides.\n *\n * Spelled `8.10.0`, which sorts over all `8.10.0-*` prereleases. The endpoint\n * lands in the first release of the 8.10 line. The alphas do not have it, and\n * they stay on the grace period.\n */\nexport const UPGRADE_READINESS_MIN_VERSION = '8.10.0'\n"],
|
|
5
|
+
"mappings": "AA4CO,MAAM,uBAAuB;AAG7B,MAAM,wBAAwB;AAO9B,MAAM,yBAAyB;AAW/B,MAAM,oCAAoC;AAG1C,MAAM,4CAA4C;AAOlD,MAAM,qCAAqC;AAO3C,MAAM,4CAA4C;AAMlD,MAAM,gCAAgC;AAMtC,MAAM,mCAAmC;AAczC,MAAM,kCAAkC;AAGxC,MAAM,mCAAmC;AAGzC,MAAM,qCAAqC;AAU3C,MAAM,kCAAkC;AAOxC,MAAM,yCAAyC;AAO/C,MAAM,qCAAqC;AAU3C,MAAM,gDAAgD;AAGtD,MAAM,sCAAsC;AAM5C,MAAM,mCAAmC;AAOzC,MAAM,qCAAqC;AAC3C,MAAM,mCAAmC;AAmBzC,MAAM,kCAAkC;AAOxC,MAAM,yCAAyC;AAM/C,MAAM,6CAA6C;AAgBnD,MAAM,2CAA2C;AAOjD,MAAM,sCAAsC;AAG5C,MAAM,sCAAsC;AAG5C,MAAM,sCAAsC;AAO5C,MAAM,sCAAsC;AAwB5C,MAAM,uCAAuC;AAO7C,MAAM,qCAAqC;AAG3C,MAAM,6BAA6B;AAGnC,MAAM,0BAA0B;AAGhC,MAAM,sBAAsB;AAM5B,MAAM,8BAA8B;AAGpC,MAAM,kBAAkB;AAUxB,MAAM,+BAA+B;AAGrC,MAAM,4BAA4B;AAGlC,MAAM,wCAAwC;AAG9C,MAAM,4BAA4B;AAOlC,MAAM,kCAAkC;AAOxC,MAAM,+BAA+B;AASrC,MAAM,6BAA6B;AAGnC,MAAM,kCAAkC;AAaxC,MAAM,iCAAiC;AAavC,MAAM,4BAA4B;AAclC,MAAM,qBAAqB;AAuC3B,MAAM,oCAAoC;AAS1C,MAAM,+BAA+B;AACrC,MAAM,+CAA+C;AACrD,MAAM,0DAA0D;AAOhE,MAAM,mCAAmC;AAUzC,MAAM,oCACX;AAYK,MAAM,iCAAiC;AA2BvC,MAAM,2BAA2B;AAiBjC,MAAM,gCAAgC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { DashboardDefinition, DashboardNewTile, DashboardTile, DashboardTileKind, DashboardTileLayout, StoredDashboardDefinition } from '../dtos/Dashboard.dto.js';
|
|
2
|
+
/**
|
|
3
|
+
* The rules of a dashboard definition, as pure functions.
|
|
4
|
+
*
|
|
5
|
+
* control-backend places a tile that a client adds with `placeNewTile`.
|
|
6
|
+
* The grid of control-frontend lays tiles out with its own rules, and reads
|
|
7
|
+
* only `tilesOverlap` from here, so both sides agree on what an overlap is.
|
|
8
|
+
*/
|
|
9
|
+
/** DASHBOARD_TILE_DEFAULT_SIZE is the size of a new tile of each kind. */
|
|
10
|
+
export declare const DASHBOARD_TILE_DEFAULT_SIZE: Record<DashboardTileKind, {
|
|
11
|
+
w: number;
|
|
12
|
+
h: number;
|
|
13
|
+
}>;
|
|
14
|
+
/** dashboardTileDefaultSize is the size of a new tile. */
|
|
15
|
+
export declare const dashboardTileDefaultSize: (tile: DashboardNewTile) => {
|
|
16
|
+
w: number;
|
|
17
|
+
h: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* isSafeDashboardLink reports whether a link tile can hold `url`.
|
|
21
|
+
*
|
|
22
|
+
* A path of Control (`/clusters`) or an `https://` address. A `javascript:`
|
|
23
|
+
* address would run code in the session of each reader, and `//host` leaves
|
|
24
|
+
* Control while it looks like a path, so both are refused.
|
|
25
|
+
*
|
|
26
|
+
* A browser deletes a tab or a line break from an address before it reads
|
|
27
|
+
* it, so `/<tab>/host` is `//host`. Thus a link holds no control character
|
|
28
|
+
* and no space, and a path must resolve to the origin that it started from.
|
|
29
|
+
*/
|
|
30
|
+
export declare const isSafeDashboardLink: (url: string) => boolean;
|
|
31
|
+
/**
|
|
32
|
+
* dashboardDefinitionBytes is the size of a definition as the backend
|
|
33
|
+
* measures it: UTF-8 bytes of its JSON text.
|
|
34
|
+
*/
|
|
35
|
+
export declare const dashboardDefinitionBytes: (definition: DashboardDefinition) => number;
|
|
36
|
+
/** dashboardDefinitionFits reports whether the backend stores `definition`. */
|
|
37
|
+
export declare const dashboardDefinitionFits: (definition: DashboardDefinition) => boolean;
|
|
38
|
+
/** emptyDashboardDefinition is the definition of a new dashboard. */
|
|
39
|
+
export declare const emptyDashboardDefinition: () => DashboardDefinition;
|
|
40
|
+
/**
|
|
41
|
+
* upgradeDashboardDefinition brings a stored definition to the current
|
|
42
|
+
* version.
|
|
43
|
+
*
|
|
44
|
+
* It answers `undefined` for a version that this code does not know. That is
|
|
45
|
+
* a row that NEWER code wrote, for example during a rollout. Do not guess at
|
|
46
|
+
* it, and do not write over it.
|
|
47
|
+
*/
|
|
48
|
+
export declare const upgradeDashboardDefinition: (stored: {
|
|
49
|
+
schemaVersion?: unknown;
|
|
50
|
+
}) => DashboardDefinition | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* toStoredDashboardDefinition is the shape that a write stores: the lowest
|
|
53
|
+
* version that holds the tiles.
|
|
54
|
+
*
|
|
55
|
+
* A pod with older code then still reads the row, during a rollout and after
|
|
56
|
+
* a rollback. Only a dashboard that uses a new kind needs the new version.
|
|
57
|
+
*/
|
|
58
|
+
export declare const toStoredDashboardDefinition: (definition: DashboardDefinition) => StoredDashboardDefinition;
|
|
59
|
+
/**
|
|
60
|
+
* placeNewTile gives a tile of `size` the place after the last tile, in
|
|
61
|
+
* reading order.
|
|
62
|
+
*
|
|
63
|
+
* The last row is the row of the tile that starts lowest. The new tile goes
|
|
64
|
+
* to the right of the tiles in that row, if it fits there and covers no cell
|
|
65
|
+
* of another tile. If not, it goes under every tile, at the left edge.
|
|
66
|
+
*
|
|
67
|
+
* A new tile never goes into a gap between two tiles: a reader who added it
|
|
68
|
+
* from another page must find it at the end. Small tiles that a reader adds
|
|
69
|
+
* one after the other fill a row, and do not make a tall column.
|
|
70
|
+
*/
|
|
71
|
+
export declare const placeNewTile: (tiles: readonly Pick<DashboardTile, "layout">[], size: {
|
|
72
|
+
w: number;
|
|
73
|
+
h: number;
|
|
74
|
+
}) => DashboardTileLayout;
|
|
75
|
+
/**
|
|
76
|
+
* placeNewTiles places tiles one after the other, each with
|
|
77
|
+
* `placeNewTile`, and answers the tiles with their layout.
|
|
78
|
+
*/
|
|
79
|
+
export declare const placeNewTiles: (tiles: readonly Pick<DashboardTile, "layout">[], added: readonly DashboardNewTile[]) => DashboardTile[];
|
|
80
|
+
/**
|
|
81
|
+
* tilesOverlap reports whether two tiles cover one cell of the grid.
|
|
82
|
+
*
|
|
83
|
+
* The backend refuses a definition with an overlap. The grid of the
|
|
84
|
+
* frontend never writes one, so an overlap is a client that is wrong.
|
|
85
|
+
*/
|
|
86
|
+
export declare const tilesOverlap: (a: DashboardTileLayout, b: DashboardTileLayout) => boolean;
|