fix: render kafka consumer groups concisely

This commit is contained in:
Codex
2026-07-10 01:45:30 +02:00
parent 02672e07c6
commit 374ff576b1
3 changed files with 34 additions and 4 deletions
+31 -3
View File
@@ -103,6 +103,8 @@ interface PlatformKafkaConfig {
management: {
defaultTailLimit: number;
maxTailLimit: number;
defaultGroupListLimit: number;
maxGroupListLimit: number;
defaultShadowTopic: string;
shadowProducerId: string;
};
@@ -189,7 +191,7 @@ export function kafkaHelp(): Record<string, unknown> {
"bun scripts/cli.ts platform-infra kafka status --target NC01 [--full|--raw]",
"bun scripts/cli.ts platform-infra kafka validate --target NC01 [--full|--raw]",
"bun scripts/cli.ts platform-infra kafka topics --node NC01",
"bun scripts/cli.ts platform-infra kafka groups --node NC01",
"bun scripts/cli.ts platform-infra kafka groups --node NC01 [--limit N] [--full|--raw]",
"bun scripts/cli.ts platform-infra kafka offsets --node NC01 [--topic hwlab.event.v1]",
"bun scripts/cli.ts platform-infra kafka tail --node NC01 --topic hwlab.event.v1 --limit 5",
"bun scripts/cli.ts platform-infra kafka produce --node NC01 --topic hwlab.event.v1 --key <trace-or-session>",
@@ -317,6 +319,8 @@ function parseManagement(root: Record<string, unknown>): PlatformKafkaConfig["ma
return {
defaultTailLimit: positiveInteger(management, "defaultTailLimit", "management"),
maxTailLimit: positiveInteger(management, "maxTailLimit", "management"),
defaultGroupListLimit: positiveInteger(management, "defaultGroupListLimit", "management"),
maxGroupListLimit: positiveInteger(management, "maxGroupListLimit", "management"),
defaultShadowTopic: topicNameField(management, "defaultShadowTopic", "management"),
shadowProducerId: y.stringField(management, "shadowProducerId", "management"),
};
@@ -332,6 +336,7 @@ function validateConfig(kafka: PlatformKafkaConfig): void {
if (!kafka.topics.some((topic) => topic.name === kafka.validation.smokeTopic)) throw new Error(`${configLabel}.validation.smokeTopic must reference one of topics[].name`);
if (!kafka.topics.some((topic) => topic.name === kafka.management.defaultShadowTopic)) throw new Error(`${configLabel}.management.defaultShadowTopic must reference one of topics[].name`);
if (kafka.management.defaultTailLimit > kafka.management.maxTailLimit) throw new Error(`${configLabel}.management.defaultTailLimit must be <= management.maxTailLimit`);
if (kafka.management.defaultGroupListLimit > kafka.management.maxGroupListLimit) throw new Error(`${configLabel}.management.defaultGroupListLimit must be <= management.maxGroupListLimit`);
if (kafka.defaults.switch.shadowConsumeEnabled) throw new Error(`${configLabel}.defaults.switch.shadowConsumeEnabled must stay false for shadow-produce-only stage`);
const topicNames = new Set<string>();
for (const topic of kafka.topics) {
@@ -480,6 +485,7 @@ async function topics(config: UniDeskConfig, options: KafkaInspectOptions): Prom
async function groups(config: UniDeskConfig, options: KafkaInspectOptions): Promise<Record<string, unknown>> {
const kafka = readKafkaConfig();
const target = resolveTarget(kafka, options.targetId);
const limit = boundedGroupListLimit(kafka, options.limit);
const result = await capture(config, target.route, ["sh"], groupsScript(kafka, target));
const parsed = parseJsonOutput(result.stdout);
return {
@@ -489,6 +495,7 @@ async function groups(config: UniDeskConfig, options: KafkaInspectOptions): Prom
target: targetSummary(target),
config: compactConfigSummary(kafka, target),
query: parsed ?? null,
presentation: { limit },
remote: options.raw && parsed !== null ? parsed : compactCapture(result, { full: options.full || result.exitCode !== 0 }),
};
}
@@ -1415,6 +1422,8 @@ function compactConfigSummary(kafka: PlatformKafkaConfig, target: KafkaTarget):
defaultShadowTopic: kafka.management.defaultShadowTopic,
defaultTailLimit: kafka.management.defaultTailLimit,
maxTailLimit: kafka.management.maxTailLimit,
defaultGroupListLimit: kafka.management.defaultGroupListLimit,
maxGroupListLimit: kafka.management.maxGroupListLimit,
shadowProducerId: kafka.management.shadowProducerId,
},
valuesPrinted: false,
@@ -1612,13 +1621,25 @@ function renderTopics(result: Record<string, unknown>): RenderedCliResult {
function renderGroups(result: Record<string, unknown>): RenderedCliResult {
const query = record(result.query);
const groups = arrayRecords(query.groups).length > 0 ? arrayRecords(query.groups).map((item) => [stringValue(item.name)]) : (Array.isArray(query.groups) ? query.groups.map((item) => [String(item)]) : []);
const presentation = record(result.presentation);
const limit = Number(presentation.limit);
const names = Array.isArray(query.groups)
? query.groups.flatMap((item) => {
if (typeof item === "string" && item.length > 0) return [item];
const value = record(item).name;
return typeof value === "string" && value.length > 0 ? [value] : [];
})
: [];
const shown = names.slice(0, Number.isInteger(limit) && limit > 0 ? limit : names.length);
return rendered(result, "platform-infra kafka groups", [
"PLATFORM-INFRA KAFKA GROUPS",
...table(["TARGET", "NAMESPACE", "CLUSTER", "POD"], [[stringValue(query.target), stringValue(query.namespace), stringValue(query.cluster), stringValue(query.pod)]]),
"",
...(groups.length === 0 ? ["GROUPS -"] : table(["GROUP"], groups)),
...table(["TOTAL", "SHOWN", "OMITTED"], [[String(names.length), String(shown.length), String(Math.max(0, names.length - shown.length))]]),
"",
...(shown.length === 0 ? ["GROUPS -"] : table(["GROUP"], shown.map((name) => [name]))),
...remoteErrorLines(result),
...(names.length > shown.length ? ["", `NEXT bun scripts/cli.ts platform-infra kafka groups --node ${stringValue(query.target)} --raw`] : []),
]);
}
@@ -1804,6 +1825,13 @@ function boundedLimit(kafka: PlatformKafkaConfig, limit: number | null): number
return value;
}
function boundedGroupListLimit(kafka: PlatformKafkaConfig, limit: number | null): number {
const value = limit ?? kafka.management.defaultGroupListLimit;
if (!Number.isInteger(value) || value < 1) throw new Error("group list limit must be a positive integer");
if (value > kafka.management.maxGroupListLimit) throw new Error(`limit ${value} exceeds ${configLabel}.management.maxGroupListLimit=${kafka.management.maxGroupListLimit}`);
return value;
}
function buildShadowPayload(kafka: PlatformKafkaConfig, target: KafkaTarget, options: KafkaProduceOptions, topic: string): string {
let userPayload: unknown = null;
if (options.payload !== null) {
+1 -1
View File
@@ -453,7 +453,7 @@ export function platformInfraHelp(): unknown {
"bun scripts/cli.ts platform-infra kafka status --target NC01",
"bun scripts/cli.ts platform-infra kafka validate --target NC01",
"bun scripts/cli.ts platform-infra kafka topics --node NC01",
"bun scripts/cli.ts platform-infra kafka groups --node NC01",
"bun scripts/cli.ts platform-infra kafka groups --node NC01 [--limit N] [--full|--raw]",
"bun scripts/cli.ts platform-infra kafka offsets --node NC01 --topic hwlab.event.v1",
"bun scripts/cli.ts platform-infra kafka tail --node NC01 --topic hwlab.event.v1 --limit 5",
"bun scripts/cli.ts platform-infra kafka produce --node NC01 --topic hwlab.event.v1 --key <trace-or-session>",