1
1
import getpass
2
2
import itertools
3
3
import os
4
+ import shlex
4
5
import sys
5
6
from typing import Callable , Iterator , List , Tuple , Union , Dict , Optional
6
7
@@ -70,6 +71,8 @@ def get_options(cls):
70
71
ConfigOption ("remote_runpath" , default = None ): str ,
71
72
ConfigOption ("testplan_path" , default = None ): str ,
72
73
ConfigOption ("remote_workspace" , default = None ): str ,
74
+ # proposing cfg clobber_remote_workspace,
75
+ # to overwrite what's in remote_workspace when set to True
73
76
ConfigOption ("clean_remote" , default = False ): bool ,
74
77
ConfigOption ("push" , default = []): Or (list , None ),
75
78
ConfigOption ("push_exclude" , default = []): Or (list , None ),
@@ -152,33 +155,43 @@ def __init__(
152
155
status_wait_timeout : int = 60 ,
153
156
** options ,
154
157
) -> None :
155
-
156
158
if not worker_is_remote (remote_host ):
159
+ # TODO: allow connecting to local for testing purpose?
157
160
raise RuntimeError (
158
161
"Cannot create remote resource on the same host that Testplan runs."
159
162
)
160
163
options .update (self .filter_locals (locals ()))
161
164
super (RemoteResource , self ).__init__ (** options )
162
165
166
+ self .ssh_cfg = {
167
+ "host" : self .cfg .remote_host ,
168
+ "port" : self .cfg .ssh_port ,
169
+ }
170
+ # if 0 != self._execute_cmd_remote("uname"):
171
+ # raise NotImplementedError(
172
+ # "RemoteResource not supported on Windows remote hosts."
173
+ # )
174
+
163
175
self ._remote_plan_runpath = None
164
176
self ._remote_resource_runpath = None
165
177
self ._child_paths = _LocationPaths ()
166
178
self ._testplan_import_path = _LocationPaths ()
167
179
self ._workspace_paths = _LocationPaths ()
168
180
self ._working_dirs = _LocationPaths ()
169
181
170
- self .ssh_cfg = {
171
- "host" : self .cfg .remote_host ,
172
- "port" : self .cfg .ssh_port ,
173
- }
174
-
175
182
self .setup_metadata = WorkerSetupMetadata ()
176
183
self ._user = getpass .getuser ()
184
+ # TODO: allow specifying remote env
177
185
self .python_binary = (
178
186
os .environ ["PYTHON3_REMOTE_BINARY" ] if IS_WIN else sys .executable
179
187
)
180
188
self ._error_exec = []
181
189
190
+ # remote file system obj outside runpath that needs to be cleaned upon
191
+ # exit when clean_remote is True, otherwise it will break workspace
192
+ # detect etc. in next run
193
+ self ._dangling_remote_fs_obj = None
194
+
182
195
@property
183
196
def error_exec (self ) -> list :
184
197
return self ._error_exec
@@ -252,8 +265,6 @@ def _remote_working_dir(self) -> None:
252
265
def _create_remote_dirs (self ) -> None :
253
266
"""Create mandatory directories in remote host."""
254
267
255
- exist_on_remote = self ._check_workspace ()
256
-
257
268
if 0 != self ._execute_cmd_remote (
258
269
cmd = filepath_exist_cmd (self ._remote_runid_file ),
259
270
label = "runid file availability check" ,
@@ -275,7 +286,18 @@ def _create_remote_dirs(self) -> None:
275
286
label = "create remote runid file" ,
276
287
)
277
288
289
+ # TODO: remote venv setup
290
+ # TODO: testplan_lib will resolved to site-packages under venv,
291
+ # TODO: while rpyc_classic.py under bin isn't included
292
+
293
+ exist_on_remote = self ._check_workspace ()
278
294
self ._prepare_workspace (exist_on_remote )
295
+
296
+ # NOTE: if workspace under testplan_lib (testplan installed in
297
+ # NOTE: editable mode), actual testplan package will never be
298
+ # NOTE: transferred
299
+ # NOTE: nevertheless, this impl won't work with venv in the first
300
+ # NOTE: place
279
301
self ._copy_testplan_package ()
280
302
281
303
self ._execute_cmd_remote (
@@ -382,6 +404,7 @@ def _prepare_workspace(self, exist_on_remote: bool) -> None:
382
404
self ,
383
405
self .ssh_cfg ["host" ],
384
406
)
407
+ # proposed: if clobber_remote_workspace set, overwrite remote
385
408
self ._execute_cmd_remote (
386
409
cmd = link_cmd (
387
410
path = self ._workspace_paths .local ,
@@ -400,31 +423,68 @@ def _prepare_workspace(self, exist_on_remote: bool) -> None:
400
423
)
401
424
self ._transfer_data (
402
425
# join with "" to add trailing "/" to source
403
- # this will copy everything under local import path to to testplan_lib
426
+ # this will copy everything under local workspace path to fetched_workspace
404
427
source = os .path .join (self ._workspace_paths .local , "" ),
405
428
target = self ._workspace_paths .remote ,
406
429
remote_target = True ,
407
430
exclude = self .cfg .workspace_exclude ,
408
431
)
432
+
433
+ if IS_WIN :
434
+ return
435
+
409
436
self .logger .info (
410
437
"%s: creating symlink to imitate local workspace path on %s, "
411
438
"pointing to runpath/fetched_workspace" ,
412
439
self ,
413
440
self .ssh_cfg ["host" ],
414
441
)
415
- self ._execute_cmd_remote (
442
+ rmt_non_existing = None
443
+ # TODO: uncomment later
444
+ # TODO: there is another issue related to created dir cleanup
445
+ # TODO: if push given, pushed files under created dir and delete_pushed
446
+ # TODO: set to False, what to do?
447
+ # r, w = os.pipe()
448
+ # if 0 == self._execute_cmd_remote(
449
+ # cmd="/bin/bash -c "
450
+ # + shlex.quote(
451
+ # 'e=""; for i in '
452
+ # + " ".join(
453
+ # map(
454
+ # shlex.quote,
455
+ # self._workspace_paths.local.split(os.sep)[1:],
456
+ # )
457
+ # )
458
+ # + '; do e+="/${i}"; if [ ! -e "$e" ]; then echo "$e"; break; fi; done'
459
+ # ),
460
+ # label="imitate local workspace path on remote - detect non-existing",
461
+ # stdout=os.fdopen(w),
462
+ # check=False, # XXX: bash might not be there
463
+ # ):
464
+ # rmt_non_existing = os.fdopen(r).read().strip() or None
465
+ if 0 == self ._execute_cmd_remote (
416
466
cmd = mkdir_cmd (os .path .dirname (self ._workspace_paths .local )),
417
467
label = "imitate local workspace path on remote - mkdir" ,
418
468
check = False , # just best effort
419
- )
420
- self ._execute_cmd_remote (
469
+ ) and 0 == self ._execute_cmd_remote (
421
470
cmd = link_cmd (
422
471
path = self ._workspace_paths .remote ,
423
472
link = self ._workspace_paths .local ,
424
473
),
425
474
label = "imitate local workspace path on remote - ln" ,
426
475
check = False , # just best effort
427
- )
476
+ ):
477
+ # NOTE: we shall always remove created symlink
478
+ self ._dangling_remote_fs_obj = (
479
+ rmt_non_existing or self ._workspace_paths .local
480
+ )
481
+ self .logger .info (
482
+ "%s: on %s, %s and its possible descendants are "
483
+ "created to imitate local workspace path" ,
484
+ self ,
485
+ self .ssh_cfg ["host" ],
486
+ self ._dangling_remote_fs_obj ,
487
+ )
428
488
429
489
def _push_files (self ) -> None :
430
490
"""Push files and directories to remote host."""
@@ -601,10 +661,21 @@ def _clean_remote(self) -> None:
601
661
)
602
662
603
663
self ._execute_cmd_remote (
604
- cmd = f"/bin/rm -rf { self ._remote_plan_runpath } " ,
664
+ cmd = rm_cmd ( self ._remote_plan_runpath ) ,
605
665
label = "Clean remote root runpath" ,
606
666
)
607
667
668
+ if self ._dangling_remote_fs_obj :
669
+ self ._execute_cmd_remote (
670
+ cmd = rm_cmd (self ._dangling_remote_fs_obj ),
671
+ label = f"Remove imitated workspace outside runpath" ,
672
+ )
673
+ self ._dangling_remote_fs_obj = None
674
+
675
+ if self .cfg .delete_pushed :
676
+ # TODO
677
+ ...
678
+
608
679
def _pull_files (self ) -> None :
609
680
"""Pull custom files from remote host."""
610
681
0 commit comments