Skip to content

Commit d6779e3

Browse files
authored
Merge pull request #2701 from kubernetes-client/max/release-1.1-cp-from-pod
fix: cp from pod (backport to release-1.1)
2 parents adf37cc + f3c8c2c commit d6779e3

File tree

4 files changed

+118
-2
lines changed

4 files changed

+118
-2
lines changed

src/cp.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,23 @@ export class Cp {
1818
* @param {string} containerName - The name of the container in the pod to exec the command inside.
1919
* @param {string} srcPath - The source path in the pod
2020
* @param {string} tgtPath - The target path in local
21+
* @param {string} [cwd] - The directory that is used as the parent in the pod when downloading
2122
*/
2223
public async cpFromPod(
2324
namespace: string,
2425
podName: string,
2526
containerName: string,
2627
srcPath: string,
2728
tgtPath: string,
29+
cwd?: string,
2830
): Promise<void> {
2931
const tmpFile = tmp.fileSync();
3032
const tmpFileName = tmpFile.name;
31-
const command = ['tar', 'zcf', '-', srcPath];
33+
const command = ['tar', 'cf', '-'];
34+
if (cwd) {
35+
command.push('-C', cwd);
36+
}
37+
command.push(srcPath);
3238
const writerStream = fs.createWriteStream(tmpFileName);
3339
const errStream = new WritableStreamBuffer();
3440
this.execInstance.exec(

src/cp_test.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('Cp', () => {
2222
const container = 'container';
2323
const srcPath = '/';
2424
const tgtPath = '/';
25-
const cmdArray = ['tar', 'zcf', '-', srcPath];
25+
const cmdArray = ['tar', 'cf', '-', srcPath];
2626
const path = `/api/v1/namespaces/${namespace}/pods/${pod}/exec`;
2727

2828
const query = {
@@ -38,6 +38,35 @@ describe('Cp', () => {
3838
await cp.cpFromPod(namespace, pod, container, srcPath, tgtPath);
3939
verify(fakeWebSocket.connect(`${path}?${queryStr}`, null, anyFunction())).called();
4040
});
41+
42+
it('should run create tar command to a url with cwd', async () => {
43+
const kc = new KubeConfig();
44+
const fakeWebSocket: WebSocketInterface = mock(WebSocketHandler);
45+
const exec = new Exec(kc, instance(fakeWebSocket));
46+
const cp = new Cp(kc, exec);
47+
48+
const namespace = 'somenamespace';
49+
const pod = 'somepod';
50+
const container = 'container';
51+
const srcPath = '/';
52+
const tgtPath = '/';
53+
const cwd = '/abc';
54+
const cmdArray = ['tar', 'cf', '-', '-C', cwd, srcPath];
55+
const path = `/api/v1/namespaces/${namespace}/pods/${pod}/exec`;
56+
57+
const query = {
58+
stdout: true,
59+
stderr: true,
60+
stdin: false,
61+
tty: false,
62+
command: cmdArray,
63+
container,
64+
};
65+
const queryStr = querystring.stringify(query);
66+
67+
await cp.cpFromPod(namespace, pod, container, srcPath, tgtPath, cwd);
68+
verify(fakeWebSocket.connect(`${path}?${queryStr}`, null, anyFunction())).called();
69+
});
4170
});
4271

4372
describe('cpToPod', () => {

src/test/integration/cpFromPod.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import assert from 'node:assert';
2+
import fs from 'node:fs';
3+
import path from 'node:path';
4+
import os from 'node:os';
5+
import { setTimeout } from 'node:timers/promises';
6+
import { CoreV1Api, KubeConfig, V1Pod } from '../../index.js';
7+
import { Cp } from '../../cp.js';
8+
import { generateName } from './name.js';
9+
10+
export default async function cpFromPod() {
11+
const kc = new KubeConfig();
12+
kc.loadFromDefault();
13+
14+
const coreV1Client = kc.makeApiClient(CoreV1Api);
15+
const cp = new Cp(kc);
16+
17+
const testPodName = generateName('cp-test-pod');
18+
const namespace = 'default';
19+
20+
const pod = new V1Pod();
21+
pod.metadata = { name: testPodName };
22+
pod.spec = {
23+
containers: [
24+
{
25+
name: 'test-container',
26+
image: 'busybox',
27+
command: ['sh', '-c', 'echo "test content" > /tmp/test.txt && sleep 3600'],
28+
},
29+
],
30+
restartPolicy: 'Never',
31+
};
32+
33+
console.log(`Creating pod ${testPodName}`);
34+
await coreV1Client.createNamespacedPod({ namespace, body: pod });
35+
36+
console.log('Waiting for pod to be ready...');
37+
let podReady = false;
38+
for (let i = 0; i < 30; i++) {
39+
const currentPod = await coreV1Client.readNamespacedPod({ name: testPodName, namespace });
40+
if (currentPod.status?.phase === 'Running') {
41+
podReady = true;
42+
break;
43+
}
44+
await setTimeout(1000);
45+
}
46+
47+
assert.strictEqual(podReady, true, 'Pod did not become ready in time');
48+
console.log('Pod is ready');
49+
50+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'k8s-cp-test-'));
51+
52+
try {
53+
console.log('Copying file from pod...');
54+
55+
cp.cpFromPod(namespace, testPodName, 'test-container', 'test.txt', tempDir, '/tmp');
56+
57+
// Wait for file to appear
58+
const copiedFilePath = path.join(tempDir, 'test.txt');
59+
let fileExists = false;
60+
for (let i = 0; i < 20; i++) {
61+
if (fs.existsSync(copiedFilePath)) {
62+
fileExists = true;
63+
break;
64+
}
65+
await setTimeout(500);
66+
}
67+
68+
assert.strictEqual(fileExists, true, 'File was not copied');
69+
70+
const content = fs.readFileSync(copiedFilePath, 'utf-8');
71+
assert.strictEqual(content.trim(), 'test content', 'File content does not match');
72+
73+
console.log('cpFromPod test passed!');
74+
} finally {
75+
console.log('Cleaning up...');
76+
await coreV1Client.deleteNamespacedPod({ name: testPodName, namespace });
77+
fs.rmSync(tempDir, { recursive: true, force: true });
78+
}
79+
}

src/test/integration/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import patchNamespace from './patchNamespace.js';
2+
import cpFromPod from './cpFromPod.js';
23

34
console.log('Integration testing');
45

56
await patchNamespace();
7+
await cpFromPod();

0 commit comments

Comments
 (0)