Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(k8s) read logs from host (in ci) #1749

Merged
merged 11 commits into from
Mar 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ export async function genBootnodeDef(
export async function genNodeDef(
namespace: string,
nodeSetup: Node,
inCI: boolean = false,
): Promise<any> {
const nodeResource = new NodeResource(namespace, nodeSetup);
return nodeResource.generateSpec();
return nodeResource.generateSpec(inCI);
}

export function genChaosDef(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export class KubeClient extends Client {
localMagicFilepath: string;
remoteDir: string;
dataDir: string;
inCI: boolean;

constructor(configPath: string, namespace: string, tmpDir: string) {
super(configPath, namespace, tmpDir, "kubectl", "kubernetes");
Expand All @@ -72,6 +73,10 @@ export class KubeClient extends Client {
this.localMagicFilepath = `${tmpDir}/finished.txt`;
this.remoteDir = DEFAULT_REMOTE_DIR;
this.dataDir = DEFAULT_DATA_DIR;
// Use the same env vars from spawn/run
this.inCI =
process.env.RUN_IN_CONTAINER === "1" ||
process.env.ZOMBIENET_IMAGE !== undefined;
}

async validateAccess(): Promise<boolean> {
Expand Down Expand Up @@ -728,6 +733,71 @@ export class KubeClient extends Client {
since: number | undefined = undefined,
withTimestamp = false,
): Promise<string> {
if (!this.inCI) {
// we can just return the logs from kube
const logs = await this.getNodeLogsFromKube(
podName,
since,
withTimestamp,
);
return logs;
}

// if we are running in CI, could be the case that k8s had rotate the logs,
// so the simple `kubectl logs` will retrieve only a part of them.
// We should read it from host filesystem to ensure we are reading all the logs.

// First get the logs files to check if we need to read from disk or not
const logFiles = await this.gzippedLogFiles(podName);
debug("logFiles", logFiles);
let logs = "";
if (logFiles.length === 0) {
logs = await this.getNodeLogsFromKube(podName, since, withTimestamp);
} else {
// need to read the files in order and accumulate in logs
const promises = logFiles.map((file) =>
this.readgzippedLogFile(podName, file),
);
const results = await Promise.all(promises);
for (const r of results) {
logs += r;
}

// now read the actual log from kube
logs += await this.getNodeLogsFromKube(podName);
}

return logs;
}

async gzippedLogFiles(podName: string): Promise<string[]> {
const [podId, podStatus] = await this.getPodIdAndStatus(podName);
debug("podId", podId);
debug("podStatus", podStatus);
// we can only get compressed files from `Running` pods
if (podStatus !== "Running") return [];

// log dir in ci /var/log/pods/<nsName>_<podName>_<podId>/<podName>
const logsDir = `/var/log/pods/${this.namespace}_${podName}_${podId}/${podName}`;
// ls dir sorting asc one file per line (only compressed files)
// note: use coreutils here since some images (paras) doesn't have `ls`
const args = ["exec", podName, "--", "/cfg/coreutils", "ls", "-1", logsDir];
const result = await this.runCommand(args, {
scoped: true,
allowFail: false,
});

return result.stdout
.split("\n")
.filter((f) => f !== "0.log")
.map((fileName) => `${logsDir}/${fileName}`);
}

async getNodeLogsFromKube(
podName: string,
since: number | undefined = undefined,
withTimestamp = false,
) {
const args = ["logs"];
if (since && since > 0) args.push(`--since=${since}s`);
if (withTimestamp) args.push("--timestamps=true");
Expand All @@ -749,6 +819,33 @@ export class KubeClient extends Client {
}
}

async readgzippedLogFile(podName: string, file: string): Promise<string> {
const args = ["exec", podName, "--", "zcat", file];
const result = await this.runCommand(args, {
scoped: true,
allowFail: false,
});

return result.stdout;
}

async getPodIdAndStatus(podName: string): Promise<string[]> {
// kubectl get pod <podName> -n <nsName> -o jsonpath='{.metadata.uid}'
const args = [
"get",
"pod",
podName,
"-o",
'jsonpath={.metadata.uid}{","}{.status.phase}',
];
const result = await this.runCommand(args, {
scoped: true,
allowFail: false,
});

return result.stdout.split(",");
}

async dumpLogs(path: string, podName: string) {
const dstFileName = `${path}/logs/${podName}.log`;
const logs = await this.getNodeLogs(podName);
Expand Down Expand Up @@ -903,6 +1000,7 @@ export class KubeClient extends Client {
debug(result);
fileUploadCache[fileHash] = fileName;
}

getLogsCommand(name: string): string {
return `kubectl logs -f ${name} -c ${name} -n ${this.namespace}`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,33 +23,51 @@ export class NodeResource {
protected readonly nodeSetupConfig: Node,
) {}

public async generateSpec() {
const volumes = await this.generateVolumes();
const volumeMounts = this.generateVolumesMounts();
public async generateSpec(inCI: boolean = false) {
// DEBUG LOCAL
inCI = true;
const volumes = await this.generateVolumes(inCI);
const volumeMounts = this.generateVolumesMounts(inCI);
const containersPorts = await this.generateContainersPorts();
const initContainers = this.generateInitContainers();
const containers = await this.generateContainers(
volumeMounts,
containersPorts,
);

return this.generatePodSpec(initContainers, containers, volumes);
return this.generatePodSpec(initContainers, containers, volumes, inCI);
}

private async generateVolumes(): Promise<Volume[]> {
return [
private async generateVolumes(inCI: boolean): Promise<Volume[]> {
const volumes: Volume[] = [
{ name: "tmp-cfg" },
{ name: "tmp-data" },
{ name: "tmp-relay-data" },
];

if (inCI)
volumes.push({
name: "pods",
hostPath: { path: "/var/log/pods", type: "" },
});

return volumes;
}

private generateVolumesMounts() {
return [
private generateVolumesMounts(inCI: boolean) {
const volMount = [
{ name: "tmp-cfg", mountPath: "/cfg", readOnly: false },
{ name: "tmp-data", mountPath: "/data", readOnly: false },
{ name: "tmp-relay-data", mountPath: "/relay-data", readOnly: false },
];

if (inCI)
volMount.push({
name: "pods",
mountPath: "/var/log/pods",
readOnly: true /* set to false for debugging */,
});
return volMount;
}

private async generateContainersPorts(): Promise<ContainerPort[]> {
Expand Down Expand Up @@ -174,6 +192,7 @@ export class NodeResource {
initContainers: Container[],
containers: Container[],
volumes: Volume[],
inCI: boolean = false,
): PodSpec {
const { name, zombieRole } = this.nodeSetupConfig;
const zombieRoleLabel = this.computeZombieRoleLabel();
Expand Down Expand Up @@ -203,7 +222,7 @@ export class NodeResource {
restartPolicy,
volumes,
securityContext: {
fsGroup: 1000,
fsGroup: inCI ? 0 : 1000,
runAsUser: 1000,
runAsGroup: 1000,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface VolumeMount {

export interface Volume {
name: string;
hostPath?: any;
}

export interface ContainerPort {
Expand Down
2 changes: 1 addition & 1 deletion javascript/packages/orchestrator/src/spawner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const spawnNode = async (
debug(`creating node: ${node.name}`);
const podDef = await (node.name === "bootnode"
? genBootnodeDef(namespace, node)
: genNodeDef(namespace, node));
: genNodeDef(namespace, node, opts.inCI));

const finalFilesToCopyToNode = [...filesToCopy];

Expand Down
4 changes: 4 additions & 0 deletions javascript/words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ framesystem
fullpaths
genshiro
gurke
gzipped
Gzipped
hasher
hashers
hostpath
Expand Down Expand Up @@ -138,6 +140,7 @@ propery
protobuf
rawdata
rawmetric
readgzipped
relaychain
resourse
runtimes
Expand Down Expand Up @@ -177,6 +180,7 @@ willbe
xcmp
xinfra
xzvf
zcat
zipkin
zndsl
zombienet
Expand Down
Loading