@@ -95,20 +95,20 @@ def run_cmd(self, args: list[str], **kwargs):
9595 return subprocess .run (full_cmd , ** kwargs )
9696
9797
98- class BaseDockerBuilder :
99- """Abstract docker builder with optional hooks."""
98+ class BaseDockerHandler :
99+ """Abstract docker handler with optional hooks."""
100100
101101 @classmethod
102- def get_docker_builder (cls , mode : str , ** kwargs ):
103- builder_cls = BUILDER_REGISTRY .get (mode )
104- if not builder_cls :
102+ def get_docker_handler (cls , mode : str , ** kwargs ):
103+ handler_cls = HANDLER_REGISTRY .get (mode )
104+ if not handler_cls :
105105 raise ValueError (f"Unknown Docker build mode: { mode } " )
106- return builder_cls (** kwargs )
106+ return handler_cls (** kwargs )
107107
108108 def pre_build (self , image_name : str , dockerfile_path : Path , project_path : Path ):
109109 """Override this if you want a different or no pre_build"""
110110 log_info (f"Updating Dockerfile at { dockerfile_path } " )
111- DockerHelper ._add_copy_statements_to_dockerfile (
111+ BaseDockerHandler ._add_copy_statements_to_dockerfile (
112112 str (dockerfile_path ), find_python_packages (project_path )
113113 )
114114 runner_src = Path (__file__ ).parent .parent .resolve () / "core" / "runner.py"
@@ -121,58 +121,150 @@ def build(self, image_name: str, dockerfile_path: Path, project_path: Path):
121121 def post_build (self , image_name : str , dockerfile_path : Path , project_path : Path ):
122122 pass
123123
124- class LocalDockerBuilder (BaseDockerBuilder ):
124+ def list_images (self ):
125+ raise NotImplementedError
126+
127+ def remove_image (self , image_name : str ):
128+ raise NotImplementedError
129+
130+ def prune_images (self ):
131+ raise NotImplementedError
132+
133+ def _update_dockerfile (self , dockerfile_path : Path ):
134+ BaseDockerHandler ._add_copy_statements_to_dockerfile (
135+ str (dockerfile_path ), find_python_packages (self .project_path )
136+ )
137+
138+ @staticmethod
139+ def _add_copy_statements_to_dockerfile (
140+ dockerfile_path : str , local_packages : list [str ]
141+ ):
142+ with open (dockerfile_path , "r" ) as f :
143+ lines = f .readlines ()
144+
145+ env_index = next (
146+ (i for i , line in enumerate (lines ) if line .strip ().startswith ("ENV" )),
147+ None ,
148+ )
149+
150+ if env_index is None :
151+ raise ValueError ("No ENV found in Dockerfile." )
152+
153+ entrypoint_index = next (
154+ (
155+ i
156+ for i , line in enumerate (lines )
157+ if line .strip ().startswith ("ENTRYPOINT" )
158+ ),
159+ None ,
160+ )
161+
162+ if entrypoint_index is None :
163+ raise ValueError ("No ENTRYPOINT found in Dockerfile." )
164+
165+ copy_lines = [f"COPY { pkg } ./{ pkg } \n " for pkg in local_packages ]
166+ copy_lines .append ("COPY runner.py ./runner.py\n " )
167+
168+ updated_lines = (
169+ lines [: env_index + 1 ]
170+ + copy_lines #
171+ + lines [entrypoint_index :]
172+ )
173+ with open (dockerfile_path , "w" ) as f :
174+ f .writelines (updated_lines )
175+
176+ print ("Dockerfile updated with COPY statements." )
177+
178+ class LocalDockerHandler (BaseDockerHandler ):
125179 def build (self , image_name : str , dockerfile_path : Path , project_path : Path ):
126180 log_info (f"Building Docker image [{ image_name } ] locally" )
127181 run (
128182 ["docker" , "build" , "-t" , image_name , "-f" , str (dockerfile_path ), str (project_path )],
129183 "Error building Docker image locally" ,
130184 )
131185
132- class MinikubeDockerBuilder (BaseDockerBuilder ):
186+ def list_images (self ):
187+ run (["docker" , "image" , "ls" ], "Error listing Docker images locally" )
188+
189+ def remove_image (self , image_name : str ):
190+ run (["docker" , "rmi" , "-f" , image_name ], f"Error removing Docker image { image_name } "
191+ "locally" )
192+
193+ def prune_images (self ):
194+ run (["docker" , "image" , "prune" , "-f" ], "Error pruning Docker images "
195+ "locally" )
196+
197+ class MinikubeDockerHandler (BaseDockerHandler ):
133198 def __init__ (self , minikube_helper : MinikubeHelper ):
134199 self .minikube_helper = minikube_helper
200+ self .env = self ._get_minikube_env ()
135201
136202 def build (self , image_name : str , dockerfile_path : Path , project_path : Path ):
137203 log_info (f"Building Docker image [{ image_name } ] in Minikube context" )
138- result = self .minikube_helper .run_cmd (
139- ["docker-env" , "--shell" , "bash" ], stdout = subprocess .PIPE , check = True
140- )
141- env = DockerHelper ._parse_minikube_env (result .stdout .decode ())
142204 run (
143205 ["docker" , "build" , "-t" , image_name , "-f" , str (dockerfile_path ), str (project_path )],
144206 "Error building Docker image inside Minikube" ,
145- env = env ,
207+ env = self .env ,
208+ )
209+
210+ def list_images (self ):
211+ run (["docker" , "image" , "ls" ],"Error listing Docker images inside Minikube" , env = self .env )
212+
213+ def remove_image (self , image_name : str ):
214+ run (["docker" , "rmi" , "-f" , image_name ], f"Error removing Docker image { image_name } "
215+ "inside Minikube" , env = self .env )
216+
217+ def prune_images (self ):
218+ run (["docker" , "image" , "prune" , "-f" ], "Error pruning Docker images "
219+ "inside Minikube" , env = self .env )
220+
221+ def _get_minikube_env (self ):
222+ result = self .minikube_helper .run_cmd (
223+ ["docker-env" , "--shell" , "bash" ], stdout = subprocess .PIPE , check = True
146224 )
225+ return MinikubeDockerHandler ._parse_minikube_env (result .stdout .decode ())
147226
148- class LocalUserCustomImageDockerBuilder (LocalDockerBuilder ):
227+ @staticmethod
228+ def _parse_minikube_env (output : str ) -> dict :
229+ env = os .environ .copy ()
230+ for line in output .splitlines ():
231+ if line .startswith ("export " ):
232+ try :
233+ key , value = line .replace ("export " , "" ).split ("=" , 1 )
234+ env [key .strip ()] = value .strip ('"' )
235+ except ValueError :
236+ continue
237+ return env
238+
239+ class LocalUserCustomImageDockerHandler (LocalDockerHandler ):
149240 def pre_build (self , image_name : str , dockerfile_path : Path , project_path : Path ):
150241 pass
242+
151243 def build (self , image_name : str , dockerfile_path : Path , project_path : Path ):
152244 log_info ("Building user provided dockerfile" )
153245 super ().build (image_name , dockerfile_path , project_path )
154246
155- class MinikubeUserCustomImageDockerBuilder ( MinikubeDockerBuilder ):
247+ class MinikubeUserCustomImageDockerHandler ( MinikubeDockerHandler ):
156248 def pre_build (self , image_name : str , dockerfile_path : Path , project_path : Path ):
157249 pass
158250
159251 def build (self , image_name : str , dockerfile_path : Path , project_path : Path ):
160252 log_info ("Building user provided dockerfile" )
161253 super ().build (image_name , dockerfile_path , project_path )
162254
163- BUILDER_REGISTRY = {
164- "local" : LocalDockerBuilder ,
165- "minikube" : MinikubeDockerBuilder ,
166- "local-user" : LocalUserCustomImageDockerBuilder ,
167- "minikube-user" : MinikubeUserCustomImageDockerBuilder
255+ HANDLER_REGISTRY = {
256+ "local" : LocalDockerHandler ,
257+ "minikube" : MinikubeDockerHandler ,
258+ "local-user" : LocalUserCustomImageDockerHandler ,
259+ "minikube-user" : MinikubeUserCustomImageDockerHandler
168260}
169261
170262class DockerHelper :
171263 def __init__ (
172264 self ,
173265 image_name : str ,
174266 project_path : Path ,
175- builder : BaseDockerBuilder
267+ builder : BaseDockerHandler
176268 ):
177269 self .image_name = image_name
178270 self .project_path = project_path
@@ -194,62 +286,14 @@ def build_image(self, dockerfile_path: Path):
194286
195287 self .builder .post_build (self .image_name , dockerfile_path , self .project_path )
196288
197- def _update_dockerfile (self , dockerfile_path : Path ):
198- DockerHelper ._add_copy_statements_to_dockerfile (
199- str (dockerfile_path ), find_python_packages (self .project_path )
200- )
201-
202- @staticmethod
203- def _parse_minikube_env (output : str ) -> dict :
204- env = os .environ .copy ()
205- for line in output .splitlines ():
206- if line .startswith ("export " ):
207- try :
208- key , value = line .replace ("export " , "" ).split ("=" , 1 )
209- env [key .strip ()] = value .strip ('"' )
210- except ValueError :
211- continue
212- return env
213-
214- @staticmethod
215- def _add_copy_statements_to_dockerfile (
216- dockerfile_path : str , local_packages : list [str ]
217- ):
218- with open (dockerfile_path , "r" ) as f :
219- lines = f .readlines ()
220-
221- env_index = next (
222- (i for i , line in enumerate (lines ) if line .strip ().startswith ("ENV" )),
223- None ,
224- )
225-
226- if env_index is None :
227- raise ValueError ("No ENV found in Dockerfile." )
228-
229- entrypoint_index = next (
230- (
231- i
232- for i , line in enumerate (lines )
233- if line .strip ().startswith ("ENTRYPOINT" )
234- ),
235- None ,
236- )
237-
238- if entrypoint_index is None :
239- raise ValueError ("No ENTRYPOINT found in Dockerfile." )
240-
241- copy_lines = [f"COPY { pkg } ./{ pkg } \n " for pkg in local_packages ]
242- copy_lines .append ("COPY runner.py ./runner.py\n " )
289+ def list_images (self ):
290+ self .builder .list_images ()
243291
244- updated_lines = (
245- lines [: env_index + 1 ]
246- + copy_lines #
247- + lines [entrypoint_index :]
248- )
249- with open (dockerfile_path , "w" ) as f :
250- f .writelines (updated_lines )
292+ def remove_image (self , image_name : str ):
293+ self .builder .remove_image (image_name )
251294
252- print ("Dockerfile updated with COPY statements." )
295+ def prune_images (self ):
296+ self .builder .prune_images ()
253297
254298
255299class KubeConfigHelper :
@@ -348,7 +392,7 @@ def __init__(
348392 self .image_name = image_name
349393
350394 self .minikube_helper = MinikubeHelper ()
351- builder = BaseDockerBuilder .get_docker_builder (docker_build_mode ,
395+ builder = BaseDockerHandler .get_docker_builder (docker_build_mode ,
352396 minikube_helper = self .minikube_helper )
353397 self .docker_helper = DockerHelper (
354398 image_name = image_name ,
0 commit comments