
    Ki8                        d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	m
Z
 ddlmZmZmZ ddlmZ ddlmZ ddlmZ  ej        e          Z e            dz  Zd	efd
Zd	efdZd	eeef         fdZdeeef         d	dfdZd	e
fdZd	e
fdZ ej                     Z!ddeded	efdZ" G d de          Z#dS )a  Singularity/Apptainer persistent container environment.

Security-hardened with --containall, --no-home, capability dropping.
Supports configurable resource limits and optional filesystem persistence
via writable overlay directories that survive across sessions.
    N)Path)AnyDictOptional)get_hermes_home)BaseEnvironment)is_interruptedzsingularity_snapshots.jsonreturnc                  x    t          j        d          rdS t          j        d          rdS t          d          )zLocate the apptainer or singularity CLI binary.

    Returns the executable name (``"apptainer"`` or ``"singularity"``).
    Raises ``RuntimeError`` with install instructions if neither is found.
    	apptainersingularityzNeither 'apptainer' nor 'singularity' was found in PATH. Install Apptainer (https://apptainer.org/docs/admin/main/installation.html) or Singularity and ensure the CLI is available.)shutilwhichRuntimeError     ;/home/ubuntu/hermes-agent/tools/environments/singularity.py_find_singularity_executabler      sI     |K   {|M"" }
	:  r   c                  p   t                      } 	 t          j        | dgddd          }nB# t          $ r t	          d|  d          t          j        $ r t	          d|  d          w xY w|j        d	k    r>|j                                        d
d         }t	          d|  d|j         d|           | S )zPreflight check: resolve the executable and verify it responds.

    Returns the executable name on success.
    Raises ``RuntimeError`` with an actionable message on failure.
    versionT
   capture_outputtexttimeoutz:Singularity backend selected but the resolved executable 'z1' could not be executed. Check your installation.'z6 version' timed out. The runtime may be misconfigured.r   N   z version' failed (exit code z): )	r   
subprocessrunFileNotFoundErrorr   TimeoutExpired
returncodestderrstrip)exeresultr#   s      r   _ensure_singularity_availabler'   -   s    '
(
(C
)	
 
 
  
 
 
> > > >
 
 	
 $ 
 
 
KKKK
 
 	


 A$$&&tt,OOO1BOOvOO
 
 	
 Js	   + ?A*c                      t                                           r<	 t          j        t                                                     S # t
          $ r Y nw xY wi S N)_SNAPSHOT_STOREexistsjsonloads	read_text	Exceptionr   r   r   _load_snapshotsr0   O   s\     	:o7799::: 	 	 	D	Is   *A 
AAdatac                     t           j                            dd           t                               t	          j        | d                     d S )NTparentsexist_ok   )indent)r*   parentmkdir
write_textr,   dumps)r1   s    r   _save_snapshotsr<   X   sG       ===tz$q999:::::r   c                     t          j        d          } | r(t          |           }|                    dd           |S ddlm}  |            dz  }t          d          }|                                rnt          j        |t           j                  rO|t          j        dd	          z  d
z  }|                    dd           t          
                    d|           |S |                    dd           |S )a   Get the best directory for Singularity sandboxes.

    Resolution order:
      1. TERMINAL_SCRATCH_DIR (explicit override)
      2. TERMINAL_SANDBOX_DIR / singularity (shared sandbox root)
      3. /scratch (common on HPC clusters)
      4. ~/.hermes/sandboxes/singularity (fallback)
    TERMINAL_SCRATCH_DIRTr3   r   )get_sandbox_dirr   z/scratchUSERhermeszhermes-agentz Using /scratch for sandboxes: %s)osgetenvr   r9   tools.environments.baser?   r+   accessW_OKloggerinfo)custom_scratchscratch_pathr?   sandboxscratchuser_scratchs         r   _get_scratch_dirrN   a   s
    Y566N N++4$777777777o-/G:G~~ BIgrw77 68!<!<<~M4$7776EEEMM$M...Nr   c                      t          j        d          } | r(t          |           }|                    dd           |S t	                      }|dz  }|                    dd           |S )z1Get the Apptainer cache directory for SIF images.APPTAINER_CACHEDIRTr3   z
.apptainer)rB   rC   r   r9   rN   )	cache_dir
cache_pathrL   s      r   _get_apptainer_cache_dirrS   ~   sx    	.//I )__
555  G<'JTD111r   r   image
executablec                 (   |                      d          r#t          |                                           r| S |                     d          s| S |                     dd                              dd                              dd          }t                      }|| dz  }|                                rt          |          S t          5  |                                rt          |          cddd           S t          	                    d           t          	                    d	|            t          	                    d
|           |dz  }|
                    dd           t          j                                        }t          |          |d<   t          |          |d<   	 t          j        |dt          |          | gddd|          }|j        dk    rPt                              d           t                              d|j        dd                    | cddd           S t          	                    d           t          |          cddd           S # t          j        $ rS t                              d           |                                r|                                 | cY cddd           S t*          $ r3}t                              d|           | cY d}~cddd           S d}~ww xY w# 1 swxY w Y   dS )zGet or build a SIF image from a docker:// URL.

    Returns the path unchanged if it's already a .sif file.
    For docker:// URLs, checks the cache and builds if needed.
    z.sifz	docker:// /-:Nz&Building SIF image (one-time setup)...z  Source: %sz  Target: %stmpTr3   APPTAINER_TMPDIRrP   buildiX  )r   r   r   envr   z/SIF build failed, falling back to docker:// URLz  Error: %si  zSIF image built successfullyz2SIF build timed out, falling back to docker:// URLz2SIF build error: %s, falling back to docker:// URL)endswithr   r+   
startswithreplacerS   str_sif_build_lockrG   rH   r9   rB   environcopyr   r   r"   warningr#   r!   unlinkr/   )	rT   rU   
image_namerQ   sif_pathtmp_dirr^   r&   es	            r   _get_or_build_sifrl      s    ~~f $u++"4"4"6"6 K(( {B//77SAAII#sSSJ(**Ij....H 8}}	 ! !?? 	!x==! ! ! ! ! ! ! ! 	<===NE***NH---e#dT222joo"%g,,$'	NN !	^Wc(mmU;#$  F  A%%PQQQ}fmDSD.ABBB/! ! ! ! ! ! ! !0 KK6777x==3! ! ! ! ! ! ! !4 ( 	 	 	NNOPPP   "!!!LL=! ! ! ! ! ! ! !>  	 	 	NNOQRSSSLLLLLC! ! ! ! ! ! ! !>	?! ! ! ! ! ! ! ! ! !sW   #L>B.L-A8I'2(I''AL:L	LK?-L.L?LLLLc                        e Zd ZdZ	 	 	 	 	 	 	 ddeded	ed
ededededef fdZd Z	dddddeded	edz  dedz  de
f
dZd Z xZS )SingularityEnvironmenta  Hardened Singularity/Apptainer container with resource limits and persistence.

    Security: --containall (isolated PID/IPC/mount namespaces, no host home mount),
    --no-home, writable-tmpfs for scratch space. The container cannot see or modify
    the host filesystem outside of explicitly bound paths.

    Persistence: when enabled, the writable overlay directory is preserved across
    sessions so installed packages and files survive cleanup/restore.
    ~<   r   FdefaultrT   cwdr   cpumemorydiskpersistent_filesystemtask_idc	                 $   t                                          ||           t                      | _        t	          || j                  | _        dt          j                    j        d d          | _	        d| _
        || _        || _        d | _        || _        || _        | j        rQt!                      dz  }	|	                    dd           |	d| z  | _        | j                            dd           |                                  d S )	N)rr   r   hermes_   Fzhermes-overlaysTr3   zoverlay-)super__init__r'   rU   rl   rT   uuiduuid4hexinstance_id_instance_started_persistent_task_id_overlay_dir_cpu_memoryrN   r9   _start_instance)selfrT   rr   r   rs   rt   ru   rv   rw   overlay_base	__class__s             r   r|   zSingularityEnvironment.__init__   s    	S'222799&udo>>
<TZ\\%5crc%:<<!&0,0 	  	A+--0AALtd;;; ,/C'/C/C CD##D4#@@@r   c                    | j         ddg}|                    ddg           | j        r1| j        r*|                    dt	          | j                  g           n|                    d           	 ddlm}m}  |            D ]S}|                    d	|d
          d|d          dg           t          
                    d|d
         |d                    T |            }|rQ|                    d	|d
          d|d          dg           t          
                    d|d
         |d                    n2# t          $ r%}t                              d|           Y d }~nd }~ww xY w| j        dk    r|                    d| j         dg           | j        dk    r)|                    dt	          | j                  g           |                    t	          | j                  | j        g           	 t#          j        |ddd          }|j        dk    rt)          d|j                   d| _        t          
                    d| j        | j                   d S # t"          j        $ r t)          d          w xY w)Ninstancestartz--containallz	--no-homez	--overlayz--writable-tmpfsr   )get_credential_file_mountsget_skills_directory_mountz--bind	host_pathrZ   container_pathz:roz(Singularity: binding credential %s -> %sz(Singularity: binding skills dir %s -> %sz8Singularity: could not load credential/skills mounts: %sz--memoryMz--cpusTx   r   zFailed to start instance: z/Singularity instance %s started (persistent=%s)zInstance start timed out)rU   extendr   r   rb   appendtools.credential_filesr   r   rG   rH   r/   debugr   r   rT   r   r   r   r"   r   r#   r   r!   )r   cmdr   r   mount_entryskills_mountrk   r&   s           r   r   z&SingularityEnvironment._start_instance   s   
G4 	

NK0111  	+ 1 	+JJS):%;%;<====JJ)***	Xeeeeeeee99;;  

H[)A&f&fKP`Da&f&f&fghhh>, 01   
 6577L 

Hk)B&h&h\RbEc&h&h&hijjj> - !12  
  	X 	X 	XLLSUVWWWWWWWW	X <!JJ
t|$6$6$678889q==JJ#di..1222

C
OOT%56777	;^C4QTUUUF A%%"#O#O#OPPP%)D"KKI($*:< < < < <( 	; 	; 	;9:::	;s&   0CD4 4
E#>EE#3A'I I;rW   N)r   
stdin_datacommandr   r
   c                   | j         sdddS |p| j        }|p| j        }|                     |          \  }}||||z   }	n||}	n|}	|dk    s|                    d          r
d| d| }d}| j        d	d
|d| j         dd|g}
	 dd l}g t          j	        |
t          j
        t          j        |	rt          j
        nt          j        d          |	rE	 j                            |	           j                                         n# t           $ r Y nw xY wfd}t#          j        |d          }|                                 |                                |z   }                                t-                      r                                 	                     d           n)# t          j        $ r                                  Y nw xY w|                    d           d                              dz   ddS |                                |k    r?                                 |                    d           |                     |          S |                    d                                           |                    d           d                              j        dS # t           $ r}d| ddcY d }~S d }~ww xY w)NzInstance not started)outputr"   ro   z~/zcd z && z/tmpexecz--pwdzinstance://bashz-cr   T)stdoutr#   stdinr   c                  j    	 j         D ]}                     |            d S # t          $ r Y d S w xY wr)   )r   r   r/   )line_output_chunksprocs    r   _drainz.SingularityEnvironment.execute.<locals>._drainT  s\     $ 4 4&--d33334 4    DDs   $ 
22)targetdaemon   )r   r6   rW   z
[Command interrupted]   g?   zSingularity execution error: )r   r   rr   _prepare_commandr`   rU   r   timer   PopenPIPESTDOUTDEVNULLr   writecloser/   	threadingThreadr   	monotonicpollr	   	terminatewaitr!   killjoin_timeout_resultsleepr"   )r   r   rr   r   r   effective_timeoutwork_direxec_command
sudo_stdineffective_stdinr   _timer   readerdeadlinerk   r   r   s                   @@r   executezSingularityEnvironment.execute)  sw    % 	H4BGGG#3t|?$(#'#8#8#A#A j !j&<(:5OO#(OO(O s??h11$77?===|==LH/T-//t\+0	T    N#!z/@)8Pjooj>P	  D  J$$_555J$$&&&&    D      %VDAAAFLLNNN((+<<H))++%!## 
NN$$$$		!	,,,,%4 $ $ $		$KKK***"$''."9"9<U"U&)   ??$$x//IIKKKKKK***//0ABBBC   ! ))++%$ KKK""" ggn55T_UUU 	T 	T 	TAaAAQRSSSSSSSS	Tsv   AJ= 3D	 J= 	
DJ= DBJ= F/ .J= /#GJ= G3J= 	AJ=  AJ= =
KKKKc                    | j         r	 t          j        | j        dd| j        gddd           t
                              d| j                   n8# t          $ r+}t
                              d| j        |           Y d}~nd}~ww xY wd	| _         | j	        rB| j
        r=t                      }t          | j
                  || j        <   t          |           dS dS dS )
zMStop the instance. If persistent, the overlay dir survives for next creation.r   stopT   r   zSingularity instance %s stoppedz*Failed to stop Singularity instance %s: %sNF)r   r   r   rU   r   rG   rH   r/   rf   r   r   r0   rb   r   r<   )r   rk   	snapshotss      r   cleanupzSingularityEnvironment.cleanupv  s   ! 		+b_j&$:JK#'dB    =t?OPPPP b b bKTM]_`aaaaaaaab%*D"  	' 1 	''))I'*4+<'='=Idm$I&&&&&	' 	' 	' 	's   AA 
B!B  B)ro   rp   r   r   r   Frq   )rW   )__name__
__module____qualname____doc__rb   intfloatboolr|   r   dictr   r   __classcell__)r   s   @r   rn   rn      sA         &+    	
     $      B3; 3; 3;jKT&*)-KT KT KTs KT KTtKT$JKT26KT KT KT KTZ' ' ' ' ' ' 'r   rn   )r   )$r   r,   loggingrB   r   r   tempfiler   r}   pathlibr   typingr   r   r   hermes_constantsr   rD   r   tools.interruptr	   	getLoggerr   rG   r*   rb   r   r'   r0   r<   rN   rS   Lockrc   rl   rn   r   r   r   <module>r      s"      				                  & & & & & & & & & & , , , , , , 3 3 3 3 3 3 * * * * * *		8	$	$!/##&BBc    "s    Dc3h    ;$sCx. ;T ; ; ; ;$    :
$ 
 
 
 
 !).""3 3S 3c 3C 3 3 3 3t' ' ' ' '_ ' ' ' ' 'r   