From 847bf91f4cd08d9f078da06c73521be1b454d973 Mon Sep 17 00:00:00 2001 From: "George El-Khoury (ETW)" Date: Tue, 10 Mar 2026 12:06:21 +1100 Subject: [PATCH 1/4] feat: tail logs for all pods --- k8s-deployer/scripts/tail-container-log.sh | 31 +++++++++++++--------- k8s-deployer/src/pod-log-tail.ts | 2 +- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/k8s-deployer/scripts/tail-container-log.sh b/k8s-deployer/scripts/tail-container-log.sh index 0942083..77c34aa 100755 --- a/k8s-deployer/scripts/tail-container-log.sh +++ b/k8s-deployer/scripts/tail-container-log.sh @@ -4,20 +4,27 @@ namespace=$1 service=$2 containerName=$3 filter=$4 +logFileBase=$5 -podId=$(kubectl get pods -n $namespace -l "app.kubernetes.io/name=${service}" --field-selector="status.phase=Running" -o jsonpath="{.items[].metadata.name}") +podIds=$(kubectl get pods -n $namespace -l "app.kubernetes.io/name=${service}" --field-selector="status.phase=Running" -o jsonpath="{.items[*].metadata.name}") -args="-f -n ${namespace} ${podId}" +for podId in $podIds; do + args="-f -n ${namespace} ${podId}" -if [ "${containerName}" != "" ]; -then - args="${args} -c ${containerName}" -fi + if [ "${containerName}" != "" ]; then + args="${args} -c ${containerName}" + fi -if [ "${filter}" != "" ]; -then - args="${args} -l ${filter}" -fi + if [ "${filter}" != "" ]; then + args="${args} -l ${filter}" + fi -# the output will be caught by child node process whose parent is k8s-deployer -kubectl logs $args + if [ "${logFileBase}" != "" ]; then + kubectl logs $args >> "${logFileBase}-${podId}.log" 2>&1 & + else + # the output will be caught by child node process whose parent is k8s-deployer + kubectl logs $args & + fi +done + +wait diff --git a/k8s-deployer/src/pod-log-tail.ts b/k8s-deployer/src/pod-log-tail.ts index f53db33..1338aeb 100644 --- a/k8s-deployer/src/pod-log-tail.ts +++ b/k8s-deployer/src/pod-log-tail.ts @@ -22,7 +22,7 @@ export class PodLogTail { this.tailer = NodeShell.spawn( "k8s-deployer/scripts/tail-container-log.sh", - [ this.namespace, this.service, containerName ], + [ this.namespace, this.service, containerName, "", this.logFilePath.replace(/\.log$/, "") ], { detached: true, stdio: [ 'ignore', out, err ] From 5f991e5b5faa348de37864e750c1dd0771ccb111 Mon Sep 17 00:00:00 2001 From: "George El-Khoury (ETW)" Date: Tue, 10 Mar 2026 16:57:34 +1100 Subject: [PATCH 2/4] fix: update pod log tail test to match new spawn arguments --- k8s-deployer/test/test-suite-handler.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/k8s-deployer/test/test-suite-handler.spec.ts b/k8s-deployer/test/test-suite-handler.spec.ts index 134e5fc..940472a 100644 --- a/k8s-deployer/test/test-suite-handler.spec.ts +++ b/k8s-deployer/test/test-suite-handler.spec.ts @@ -287,12 +287,12 @@ describe("Deployment happy path", async () => { chai.expect(nodeShellSpawnStub.getCall(0).calledWith( "k8s-deployer/scripts/tail-container-log.sh", - [ namespace, "comp-1", "comp-1-specific-container" ] + [ namespace, "comp-1", "comp-1-specific-container", "", "12345_t1/logs/pod-comp-1-nsChild-comp-1-specific-container" ] )).be.true chai.expect(nodeShellSpawnStub.getCall(1).calledWith( "k8s-deployer/scripts/tail-container-log.sh", - [ namespace, "comp-1-test-app", "" ] + [ namespace, "comp-1-test-app", "", "", "12345_t1/logs/pod-comp-1-test-app-nsChild" ] )).be.true }) From e0962007aa6fa8602675b8fe591ab2053211c96c Mon Sep 17 00:00:00 2001 From: "George El-Khoury (ETW)" Date: Wed, 11 Mar 2026 16:19:45 +1100 Subject: [PATCH 3/4] fix: extract tail script path constant and fix shell quoting --- k8s-deployer/scripts/tail-container-log.sh | 12 ++++++------ k8s-deployer/src/pod-log-tail.ts | 6 ++++-- k8s-deployer/test/test-suite-handler.spec.ts | 5 +++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/k8s-deployer/scripts/tail-container-log.sh b/k8s-deployer/scripts/tail-container-log.sh index 77c34aa..c1cc285 100755 --- a/k8s-deployer/scripts/tail-container-log.sh +++ b/k8s-deployer/scripts/tail-container-log.sh @@ -6,24 +6,24 @@ containerName=$3 filter=$4 logFileBase=$5 -podIds=$(kubectl get pods -n $namespace -l "app.kubernetes.io/name=${service}" --field-selector="status.phase=Running" -o jsonpath="{.items[*].metadata.name}") +podIds=$(kubectl get pods -n "${namespace}" -l "app.kubernetes.io/name=${service}" --field-selector="status.phase=Running" -o jsonpath="{.items[*].metadata.name}") for podId in $podIds; do - args="-f -n ${namespace} ${podId}" + args=("-f" "-n" "${namespace}" "${podId}") if [ "${containerName}" != "" ]; then - args="${args} -c ${containerName}" + args+=("-c" "${containerName}") fi if [ "${filter}" != "" ]; then - args="${args} -l ${filter}" + args+=("-l" "${filter}") fi if [ "${logFileBase}" != "" ]; then - kubectl logs $args >> "${logFileBase}-${podId}.log" 2>&1 & + kubectl logs "${args[@]}" >> "${logFileBase}-${podId}.log" 2>&1 & else # the output will be caught by child node process whose parent is k8s-deployer - kubectl logs $args & + kubectl logs "${args[@]}" & fi done diff --git a/k8s-deployer/src/pod-log-tail.ts b/k8s-deployer/src/pod-log-tail.ts index 1338aeb..61b690d 100644 --- a/k8s-deployer/src/pod-log-tail.ts +++ b/k8s-deployer/src/pod-log-tail.ts @@ -3,6 +3,8 @@ import * as NodeShell from "node:child_process" import * as fs from "node:fs" import { Namespace } from "./model.js" +export const TAIL_SCRIPT = "k8s-deployer/scripts/tail-container-log.sh" + export class PodLogTail { private tailer?: NodeShell.ChildProcess @@ -21,7 +23,7 @@ export class PodLogTail { const err = fs.openSync(this.logFilePath, 'a') this.tailer = NodeShell.spawn( - "k8s-deployer/scripts/tail-container-log.sh", + TAIL_SCRIPT, [ this.namespace, this.service, containerName, "", this.logFilePath.replace(/\.log$/, "") ], { detached: true, @@ -43,7 +45,7 @@ export class PodLogTail { if (wasStopped) { logger.info("PodLogTail.stop(): The log tailer with PID %s has been stopped", pid) } else { - logger.warn("PodLogTail.stop(): Unable to stop the log tailer with PID %s. Has it been stopped already?", pid) + logger.warn("PodLogTail.stop(): Unable to stop the log tailer with PID %s. Has it been stopped already?", pid) } return wasStopped diff --git a/k8s-deployer/test/test-suite-handler.spec.ts b/k8s-deployer/test/test-suite-handler.spec.ts index 940472a..1c5ed34 100644 --- a/k8s-deployer/test/test-suite-handler.spec.ts +++ b/k8s-deployer/test/test-suite-handler.spec.ts @@ -13,6 +13,7 @@ import { ShellOptions } from "../src/shell-facade.js" import { ScalarMetric, TestOutcomeType, TestStream } from "../src/test-app-client/report/schema-v1.js" import * as webapi from "../src/test-app-client/web-api/schema-v1.js" import { generatePrefixByDate } from "../src/test-suite-handler.js" +import { TAIL_SCRIPT } from "../src/pod-log-tail.js" describe("Helper functions", () => { it ("should generate readable date prefix", () => { @@ -286,12 +287,12 @@ describe("Deployment happy path", async () => { chai.expect(nodeShellSpawnStub.callCount).eq(2) chai.expect(nodeShellSpawnStub.getCall(0).calledWith( - "k8s-deployer/scripts/tail-container-log.sh", + TAIL_SCRIPT, [ namespace, "comp-1", "comp-1-specific-container", "", "12345_t1/logs/pod-comp-1-nsChild-comp-1-specific-container" ] )).be.true chai.expect(nodeShellSpawnStub.getCall(1).calledWith( - "k8s-deployer/scripts/tail-container-log.sh", + TAIL_SCRIPT, [ namespace, "comp-1-test-app", "", "", "12345_t1/logs/pod-comp-1-test-app-nsChild" ] )).be.true }) From 820c415a9e46dd213d1139df3b42c6909e12168b Mon Sep 17 00:00:00 2001 From: "George El-Khoury (ETW)" Date: Mon, 16 Mar 2026 22:40:19 +1100 Subject: [PATCH 4/4] fix: remove empty base log file created by unused stdio fds --- k8s-deployer/src/pod-log-tail.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/k8s-deployer/src/pod-log-tail.ts b/k8s-deployer/src/pod-log-tail.ts index 61b690d..95c34d8 100644 --- a/k8s-deployer/src/pod-log-tail.ts +++ b/k8s-deployer/src/pod-log-tail.ts @@ -1,6 +1,5 @@ import { logger } from "./logger.js" import * as NodeShell from "node:child_process" -import * as fs from "node:fs" import { Namespace } from "./model.js" export const TAIL_SCRIPT = "k8s-deployer/scripts/tail-container-log.sh" @@ -18,16 +17,12 @@ export class PodLogTail { start(containerName = ""): PodLogTail { if (this.tailer) throw new Error(`Tailer is already attached to process with PID: ${this.tailer.pid}`) - // creating streams - const out = fs.openSync(this.logFilePath, 'a') - const err = fs.openSync(this.logFilePath, 'a') - this.tailer = NodeShell.spawn( TAIL_SCRIPT, [ this.namespace, this.service, containerName, "", this.logFilePath.replace(/\.log$/, "") ], { detached: true, - stdio: [ 'ignore', out, err ] + stdio: 'ignore' } )