Skip to content

Commit

Permalink
add hacky workaround for EOF errors in kubectl cp
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbom committed Oct 27, 2023
1 parent 4835135 commit ca4aac7
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 4 deletions.
10 changes: 8 additions & 2 deletions src/commands/aksTCPCollection/tcpDumpCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getExtension } from '../utils/host';
import { Errorable, failed, map as errmap } from '../utils/errorable';
import * as tmpfile from '../utils/tempfile';
import { TcpDumpDataProvider, TcpDumpPanel } from '../../panels/TcpDumpPanel';
import { invokeKubectlCommand } from '../utils/kubectl';
import { getVersion, invokeKubectlCommand } from '../utils/kubectl';

export async function aksTCPDump(_context: IActionContext, target: any) {
const kubectl = await k8s.extension.kubectl.v1;
Expand Down Expand Up @@ -47,7 +47,13 @@ export async function aksTCPDump(_context: IActionContext, target: any) {
return;
}

const dataProvider = new TcpDumpDataProvider(kubectl, kubeConfigFile.filePath, clusterInfo.result.name, linuxNodesList.result);
const kubectlVersion = await getVersion(kubectl, kubeConfigFile.filePath);
if (failed(kubectlVersion)) {
vscode.window.showErrorMessage(kubectlVersion.error);
return;
}

const dataProvider = new TcpDumpDataProvider(kubectl, kubeConfigFile.filePath, kubectlVersion.result, clusterInfo.result.name, linuxNodesList.result);
const panel = new TcpDumpPanel(extension.result.extensionUri);

panel.show(dataProvider, kubeConfigFile);
Expand Down
16 changes: 16 additions & 0 deletions src/commands/utils/kubectl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ type KubeconfigCommandConfig = {
exitCodeBehaviour: NonZeroExitCodeBehaviour
};

export type K8sVersion = {
major: string,
minor: string,
gitVersion: string,
buildDate: string
};

export type KubectlVersion = {
clientVersion: K8sVersion,
serverVersion: K8sVersion
};

export function getVersion(kubectl: APIAvailable<KubectlV1>, kubeConfigFile: string): Promise<Errorable<KubectlVersion>> {
return getKubectlJsonResult(kubectl, kubeConfigFile, "version -o json");
}

export async function getExecOutput(kubectl: APIAvailable<KubectlV1>, kubeConfigFile: string, namespace: string, pod: string, podCommand: string): Promise<Errorable<KubectlV1.ShellResult>> {
const plainCommand = `exec -n ${namespace} ${pod} -- ${podCommand}`;
const config: KubeconfigCommandConfig = {
Expand Down
14 changes: 12 additions & 2 deletions src/panels/TcpDumpPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { platform } from "os";
import { relative } from "path";
import { Uri, commands, window, workspace } from "vscode";
import * as k8s from 'vscode-kubernetes-tools-api';
import * as semver from 'semver';
import { failed, map as errmap, Errorable } from "../commands/utils/errorable";
import { MessageHandler, MessageSink } from "../webview-contract/messaging";
import { BasePanel, PanelDataProvider } from "./BasePanel";
import { getExecOutput, invokeKubectlCommand } from "../commands/utils/kubectl";
import { KubectlVersion, getExecOutput, invokeKubectlCommand } from "../commands/utils/kubectl";
import { CompletedCapture, InitialState, ToVsCodeMsgDef, ToWebViewMsgDef } from "../webview-contract/webviewDefinitions/tcpDump";
import { withOptionalTempFile } from "../commands/utils/tempfile";

Expand Down Expand Up @@ -54,6 +55,7 @@ export class TcpDumpDataProvider implements PanelDataProvider<"tcpDump"> {
constructor(
readonly kubectl: k8s.APIAvailable<k8s.KubectlV1>,
readonly kubeConfigFilePath: string,
readonly kubectlVersion: KubectlVersion,
readonly clusterName: string,
readonly linuxNodesList: string[]
) { }
Expand Down Expand Up @@ -350,7 +352,15 @@ spec:
}

const localCpPath = getLocalKubectlCpPath(localCaptureUri);
const command = `cp -n ${debugPodNamespace} ${getPodName(node)}:${captureFileBasePath}${captureName}.cap ${localCpPath}`;

// `kubectl cp` can fail with an EOF error for large files, and there's currently no good workaround:
// See: https://github.com/kubernetes/kubernetes/issues/60140
// The best advice I can see is to use the 'retries' option if it is supported, and the
// 'request-timeout' option otherwise.
const clientVersion = this.kubectlVersion.clientVersion.gitVersion.replace(/^v/, "");
const isRetriesOptionSupported = semver.parse(clientVersion) && semver.gte(clientVersion, "1.23.0");
const cpEOFAvoidanceFlag = isRetriesOptionSupported ? "--retries 99" : "--request-timeout=10m";
const command = `cp -n ${debugPodNamespace} ${getPodName(node)}:${captureFileBasePath}${captureName}.cap ${localCpPath} ${cpEOFAvoidanceFlag}`;
const output = await invokeKubectlCommand(this.kubectl, this.kubeConfigFilePath, command);
if (failed(output)) {
webview.postDownloadCaptureFileResponse({
Expand Down

0 comments on commit ca4aac7

Please sign in to comment.