1
1
import contextlib
2
+ import io
3
+ import tarfile
4
+ from pathlib import Path
2
5
from platform import system
3
6
from socket import socket
4
- from typing import TYPE_CHECKING , Optional
7
+ from typing import TYPE_CHECKING , Optional , Tuple
5
8
6
9
import docker .errors
7
10
from typing_extensions import Self
@@ -50,6 +53,7 @@ def __init__(
50
53
self ._network : Optional [Network ] = None
51
54
self ._network_aliases : Optional [list [str ]] = None
52
55
self ._kwargs = kwargs
56
+ self ._files : list [Tuple [Path , Path ]] = []
53
57
54
58
def with_env (self , key : str , value : str ) -> Self :
55
59
self .env [key ] = value
@@ -76,6 +80,33 @@ def with_kwargs(self, **kwargs) -> Self:
76
80
self ._kwargs = kwargs
77
81
return self
78
82
83
+ def with_copy_file_to_container (self , source_file : Path , destination_file : Path ) -> Self :
84
+ self ._files .append ((source_file , destination_file ))
85
+
86
+ return self
87
+
88
+ def copy_file_from_container (self , container_file : str , destination_file : str ) -> str :
89
+ tar_stream , _ = self ._container .get_archive (container_file )
90
+
91
+ for chunk in tar_stream :
92
+ with tarfile .open (fileobj = io .BytesIO (chunk )) as tar :
93
+ for member in tar .getmembers ():
94
+ with open (destination_file , 'wb' ) as f :
95
+ f .write (tar .extractfile (member ).read ())
96
+
97
+ return destination_file
98
+
99
+ @staticmethod
100
+ def _put_file_in_container (container , source_file : Path , destination_file : str ):
101
+ data = io .BytesIO ()
102
+
103
+ with tarfile .open (fileobj = data , mode = 'w' ) as tar :
104
+ tar .add (source_file , arcname = destination_file )
105
+
106
+ data .seek (0 )
107
+
108
+ container .put_archive ("/" , data )
109
+
79
110
def maybe_emulate_amd64 (self ) -> Self :
80
111
if is_arm ():
81
112
return self .with_kwargs (platform = "linux/amd64" )
@@ -88,7 +119,7 @@ def start(self) -> Self:
88
119
logger .info ("Pulling image %s" , self .image )
89
120
docker_client = self .get_docker_client ()
90
121
self ._configure ()
91
- self ._container = docker_client .run (
122
+ self ._container : Container = docker_client .run (
92
123
self .image ,
93
124
command = self ._command ,
94
125
detach = True ,
@@ -101,6 +132,12 @@ def start(self) -> Self:
101
132
logger .info ("Container started: %s" , self ._container .short_id )
102
133
if self ._network :
103
134
self ._network .connect (self ._container .id , self ._network_aliases )
135
+
136
+ for file in self ._files :
137
+ source , destination = file [0 ], file [1 ]
138
+
139
+ DockerContainer ._put_file_in_container (self ._container , source , destination )
140
+
104
141
return self
105
142
106
143
def stop (self , force = True , delete_volume = True ) -> None :
0 commit comments