1
1
import { anything , anyFunction , instance , mock , verify , when } from 'ts-mockito' ;
2
2
import * as querystring from 'querystring' ;
3
+ import { expect } from 'chai' ;
3
4
import WebSocket = require( 'isomorphic-ws' ) ;
4
-
5
+ import * as fs from 'node:fs' ;
6
+ import * as path from 'node:path' ;
7
+ import { tmpdir } from 'os' ;
8
+ import * as tar from 'tar' ;
5
9
import { CallAwaiter } from '../test' ;
6
10
import { KubeConfig } from './config' ;
7
11
import { Exec } from './exec' ;
8
12
import { Cp } from './cp' ;
9
- import { WebSocketHandler , WebSocketInterface } from './web-socket-handler' ;
13
+ import { BinaryHandler , WebSocketHandler , WebSocketInterface } from './web-socket-handler' ;
14
+ import { V1Status } from './api' ;
15
+ import { randomUUID } from 'crypto' ;
16
+ import { sleep } from './util' ;
10
17
11
18
describe ( 'Cp' , ( ) => {
19
+ let tmpDir = '' ;
20
+
21
+ beforeEach ( ( ) => {
22
+ tmpDir = `${ tmpdir ( ) } /${ randomUUID ( ) } ` ;
23
+ fs . mkdirSync ( tmpDir ) ;
24
+ } ) ;
25
+
26
+ afterEach ( ( ) => {
27
+ if ( tmpDir ) {
28
+ fs . rmSync ( tmpDir , { recursive : true , force : true } ) ;
29
+ }
30
+ } ) ;
31
+
12
32
describe ( 'cpFromPod' , ( ) => {
13
33
it ( 'should run create tar command to a url' , async ( ) => {
14
34
const kc = new KubeConfig ( ) ;
15
- const fakeWebSocket : WebSocketInterface = mock ( WebSocketHandler ) ;
16
- const exec = new Exec ( kc , instance ( fakeWebSocket ) ) ;
35
+ const fakeWebSocketInterface : WebSocketInterface = mock ( WebSocketHandler ) ;
36
+ const fakeWebSocket : WebSocket . WebSocket = mock ( WebSocket ) ;
37
+ const fakeConn : WebSocket . WebSocket = instance ( fakeWebSocket ) ;
38
+ const callAwaiter : CallAwaiter = new CallAwaiter ( ) ;
39
+ const exec = new Exec ( kc , instance ( fakeWebSocketInterface ) ) ;
17
40
const cp = new Cp ( kc , exec ) ;
18
41
19
42
const namespace = 'somenamespace' ;
20
43
const pod = 'somepod' ;
21
44
const container = 'container' ;
22
45
const srcPath = '/' ;
23
- const tgtPath = '/' ;
24
46
const cmdArray = [ 'tar' , 'zcf' , '-' , srcPath ] ;
25
- const path = `/api/v1/namespaces/${ namespace } /pods/${ pod } /exec` ;
47
+ const queryPath = `/api/v1/namespaces/${ namespace } /pods/${ pod } /exec` ;
26
48
27
49
const query = {
28
50
stdout : true ,
@@ -34,9 +56,46 @@ describe('Cp', () => {
34
56
} ;
35
57
const queryStr = querystring . stringify ( query ) ;
36
58
37
- await cp . cpFromPod ( namespace , pod , container , srcPath , tgtPath ) ;
38
- // tslint:disable-next-line:max-line-length
39
- verify ( fakeWebSocket . connect ( `${ path } ?${ queryStr } ` , null , anyFunction ( ) ) ) . called ( ) ;
59
+ when ( fakeWebSocketInterface . connect ( `${ queryPath } ?${ queryStr } ` , null , anyFunction ( ) ) ) . thenCall (
60
+ callAwaiter . resolveCall ( 'connect' , fakeConn ) ,
61
+ ) ;
62
+ when ( fakeWebSocket . close ( ) ) . thenCall ( callAwaiter . resolveCall ( 'close' ) ) ;
63
+
64
+ let complete = false ;
65
+ let lastErr = undefined ;
66
+ const promise = cp
67
+ . cpFromPod ( namespace , pod , container , srcPath , tmpDir )
68
+ . then ( ( ) => {
69
+ complete = true ;
70
+ } )
71
+ . catch ( ( err ) => {
72
+ lastErr = err ;
73
+ } ) ;
74
+ expect ( lastErr ) . to . be . undefined ;
75
+ expect ( complete ) . to . be . false ;
76
+
77
+ const binaryHandler : BinaryHandler = ( await callAwaiter . awaitCall ( 'connect' ) ) [ 2 ] ;
78
+
79
+ // simulate a network hope with a sleep
80
+ await sleep ( 1 ) ;
81
+ const contents = fs . readFileSync ( 'testdata/archive.tgz' ) ;
82
+ binaryHandler ( WebSocketHandler . StdoutStream , contents ) ;
83
+
84
+ // simulate a network hope with a sleep
85
+ await sleep ( 1 ) ;
86
+ const status : V1Status = {
87
+ status : 'Success' ,
88
+ } ;
89
+ binaryHandler ( WebSocketHandler . StatusStream , Buffer . from ( JSON . stringify ( status ) ) ) ;
90
+
91
+ await promise ;
92
+
93
+ expect ( lastErr ) . to . be . undefined ;
94
+ expect ( complete ) . to . be . true ;
95
+
96
+ const found = fs . readFileSync ( path . join ( tmpDir , 'archive.txt' ) ) . toString ( 'utf8' ) ;
97
+ const expected = fs . readFileSync ( 'testdata/archive.txt' ) . toString ( 'utf8' ) ;
98
+ expect ( found ) . to . eq ( expected ) ;
40
99
} ) ;
41
100
} ) ;
42
101
@@ -52,10 +111,11 @@ describe('Cp', () => {
52
111
const namespace = 'somenamespace' ;
53
112
const pod = 'somepod' ;
54
113
const container = 'container' ;
55
- const srcPath = 'testdata/ archive.txt' ;
114
+ const srcPath = 'archive.txt' ;
56
115
const tgtPath = '/' ;
57
116
const cmdArray = [ 'tar' , 'xf' , '-' , '-C' , tgtPath ] ;
58
- const path = `/api/v1/namespaces/${ namespace } /pods/${ pod } /exec` ;
117
+ const cwd = 'testdata/' ;
118
+ const queryPath = `/api/v1/namespaces/${ namespace } /pods/${ pod } /exec` ;
59
119
60
120
const query = {
61
121
stdout : false ,
@@ -68,14 +128,56 @@ describe('Cp', () => {
68
128
const queryStr = querystring . stringify ( query ) ;
69
129
70
130
const fakeConn : WebSocket . WebSocket = instance ( fakeWebSocket ) ;
71
- when ( fakeWebSocketInterface . connect ( `${ path } ?${ queryStr } ` , null , anyFunction ( ) ) ) . thenResolve (
72
- fakeConn ,
131
+ when ( fakeWebSocketInterface . connect ( `${ queryPath } ?${ queryStr } ` , null , anyFunction ( ) ) ) . thenCall (
132
+ callAwaiter . resolveCall ( 'connect' , fakeConn ) ,
73
133
) ;
74
- when ( fakeWebSocket . send ( anything ( ) ) ) . thenCall ( callAwaiter . resolveCall ( 'send' ) ) ;
134
+
135
+ const outFilename = path . join ( tmpDir , 'send-data.tar' ) ;
136
+ const out = fs . createWriteStream ( outFilename ) ;
137
+ when ( fakeWebSocket . send ( anything ( ) ) ) . thenCall ( ( data ) => {
138
+ const streamNum = data . readInt8 ( 0 ) ;
139
+ if ( streamNum === WebSocketHandler . StdinStream ) {
140
+ out . write ( data . subarray ( 1 ) ) ;
141
+ } else {
142
+ console . log ( streamNum ) ;
143
+ }
144
+ } ) ;
145
+
75
146
when ( fakeWebSocket . close ( ) ) . thenCall ( callAwaiter . resolveCall ( 'close' ) ) ;
76
147
77
- await cp . cpToPod ( namespace , pod , container , srcPath , tgtPath ) ;
78
- verify ( fakeWebSocketInterface . connect ( `${ path } ?${ queryStr } ` , null , anyFunction ( ) ) ) . called ( ) ;
148
+ let complete = false ;
149
+ let lastErr = undefined ;
150
+ const promise = cp
151
+ . cpToPod ( namespace , pod , container , srcPath , tgtPath , cwd )
152
+ . then ( ( ) => {
153
+ complete = true ;
154
+ } )
155
+ . catch ( ( err ) => {
156
+ lastErr = err ;
157
+ } ) ;
158
+ expect ( lastErr ) . to . be . undefined ;
159
+ expect ( complete ) . to . be . false ;
160
+
161
+ const binaryHandler : BinaryHandler = ( await callAwaiter . awaitCall ( 'connect' ) ) [ 2 ] ;
162
+
163
+ // wait for all data to be written and close called
164
+ await callAwaiter . awaitCall ( 'close' ) ;
165
+ out . close ( ) ;
166
+ await tar . x ( { f : outFilename , cwd : tmpDir } ) ;
167
+
168
+ // simulate a network hope with a sleep
169
+ await sleep ( 1 ) ;
170
+ const status : V1Status = {
171
+ status : 'Success' ,
172
+ } ;
173
+ binaryHandler ( WebSocketHandler . StatusStream , Buffer . from ( JSON . stringify ( status ) ) ) ;
174
+
175
+ await promise ;
176
+
177
+ expect ( lastErr ) . to . be . undefined ;
178
+ expect ( complete ) . to . be . true ;
179
+
180
+ verify ( fakeWebSocketInterface . connect ( `${ queryPath } ?${ queryStr } ` , null , anyFunction ( ) ) ) . called ( ) ;
79
181
} ) ;
80
182
} ) ;
81
183
} ) ;
0 commit comments