3232import java .io .Reader ;
3333import java .nio .file .Files ;
3434import java .nio .file .Path ;
35+ import java .nio .file .Paths ;
3536import java .util .ArrayList ;
3637import java .util .Arrays ;
3738import java .util .HashMap ;
3839import java .util .List ;
3940import java .util .Map ;
41+ import java .util .function .BiFunction ;
42+ import java .util .function .Consumer ;
4043
4144public class ContainerSupport {
42- // TODO put anything container related in here, maybe even with function references(for logging), take care
43-
4445 public String containerTool ;
4546 public String bundleContainerTool ;
4647 public String containerToolVersion ;
@@ -49,17 +50,28 @@ public class ContainerSupport {
4950 public String bundleContainerImage ;
5051 public Path dockerfile ;
5152
52- private static final List <String > SUPPORTED_CONTAINER_TOOLS = List .of ("podman" , "docker" );
53- private static final String CONTAINER_TOOL_JSON_KEY = "containerTool" ;
54- private static final String CONTAINER_TOOL_VERSION_JSON_KEY = "containerToolVersion" ;
55- private static final String CONTAINER_IMAGE_JSON_KEY = "containerImage" ;
53+ public static final List <String > SUPPORTED_CONTAINER_TOOLS = List .of ("podman" , "docker" );
54+ public static final String CONTAINER_TOOL_JSON_KEY = "containerTool" ;
55+ public static final String CONTAINER_TOOL_VERSION_JSON_KEY = "containerToolVersion" ;
56+ public static final String CONTAINER_IMAGE_JSON_KEY = "containerImage" ;
5657
5758 private static final String BUNDLE_INFO_MESSAGE_PREFIX = "Native Image Bundles: " ;
5859
5960 public static final Path CONTAINER_GRAAL_VM_HOME = Path .of ("/graalvm" );
6061
62+ private final BiFunction <String , Throwable , Error > errorFunction ;
63+ private final Consumer <String > warningPrinter ;
64+ private final Consumer <String > messagePrinter ;
65+
6166 public ContainerSupport (Path dockerfile , Path bundleStageDir ) {
67+ this (dockerfile , bundleStageDir , Error ::new , (msg ) -> System .out .println ("Warning: " + msg ), System .out ::println );
68+ }
69+
70+ public ContainerSupport (Path dockerfile , Path bundleStageDir , BiFunction <String , Throwable , Error > errorFunction , Consumer <String > warningPrinter , Consumer <String > messagePrinter ) {
6271 this .dockerfile = dockerfile ;
72+ this .errorFunction = errorFunction ;
73+ this .warningPrinter = warningPrinter ;
74+ this .messagePrinter = messagePrinter ;
6375
6476 if (bundleStageDir != null ) {
6577 Path containerFile = bundleStageDir .resolve ("container.json" );
@@ -71,11 +83,11 @@ public ContainerSupport(Path dockerfile, Path bundleStageDir) {
7183 bundleContainerTool = containerSettings .getOrDefault (CONTAINER_TOOL_JSON_KEY , bundleContainerTool );
7284 bundleContainerToolVersion = containerSettings .getOrDefault (CONTAINER_TOOL_VERSION_JSON_KEY , bundleContainerToolVersion );
7385 } catch (IOException e ) {
74- throw new Error ("Failed to read bundle-file " + containerFile , e );
86+ throw errorFunction . apply ("Failed to read bundle-file " + containerFile , e );
7587 }
7688 if (bundleContainerTool != null ) {
7789 String containerToolVersionString = bundleContainerToolVersion == null ? "" : String .format (" (%s)" , bundleContainerToolVersion );
78- System . out . printf ("%sBundled native-image was created in a container with %s%s.%n" , BUNDLE_INFO_MESSAGE_PREFIX , bundleContainerTool , containerToolVersionString );
90+ messagePrinter . accept ( String . format ("%sBundled native-image was created in a container with %s%s.%n" , BUNDLE_INFO_MESSAGE_PREFIX , bundleContainerTool , containerToolVersionString ) );
7991 }
8092 }
8193 }
@@ -85,11 +97,11 @@ public int initializeContainerImage() {
8597 try {
8698 containerImage = BundleLauncherUtil .digest (Files .readString (dockerfile ));
8799 } catch (IOException e ) {
88- throw new Error ("Could not read Dockerfile " + dockerfile );
100+ throw errorFunction . apply ("Could not read Dockerfile " + dockerfile , e );
89101 }
90102
91103 if (bundleContainerImage != null && !bundleContainerImage .equals (containerImage )) {
92- System . out . println ( "Warning: The bundled image was created with a different dockerfile." );
104+ warningPrinter . accept ( " The bundled image was created with a different dockerfile." );
93105 }
94106
95107 if (bundleContainerTool != null && containerTool == null ) {
@@ -98,24 +110,24 @@ public int initializeContainerImage() {
98110
99111 if (containerTool != null ) {
100112 if (!isToolAvailable (containerTool )) {
101- throw new Error ("Configured container tool not available." );
113+ throw errorFunction . apply ("Configured container tool not available." , null );
102114 } else if (containerTool .equals ("docker" ) && !isRootlessDocker ()) {
103- throw new Error ("Only rootless docker is supported for containerized builds." );
115+ throw errorFunction . apply ("Only rootless docker is supported for containerized builds." , null );
104116 }
105117 containerToolVersion = getContainerToolVersion (containerTool );
106118
107119 if (bundleContainerTool != null ) {
108120 if (!containerTool .equals (bundleContainerTool )) {
109- System . out . printf ( "Warning: The bundled image was created with container tool '%s' (using '%s').%n" , bundleContainerTool , containerTool );
121+ warningPrinter . accept ( String . format ( " The bundled image was created with container tool '%s' (using '%s').%n" , bundleContainerTool , containerTool ) );
110122 } else if (containerToolVersion != null && bundleContainerToolVersion != null && !containerToolVersion .equals (bundleContainerToolVersion )) {
111- System . out . printf ( "Warning: The bundled image was created with different %s version '%s' (installed '%s').%n" , containerTool , bundleContainerToolVersion , containerToolVersion );
123+ warningPrinter . accept ( String . format ( " The bundled image was created with different %s version '%s' (installed '%s').%n" , containerTool , bundleContainerToolVersion , containerToolVersion ) );
112124 }
113125 }
114126 } else {
115127 for (String tool : SUPPORTED_CONTAINER_TOOLS ) {
116128 if (isToolAvailable (tool )) {
117129 if (tool .equals ("docker" ) && !isRootlessDocker ()) {
118- System . out . println (BUNDLE_INFO_MESSAGE_PREFIX + "Rootless context missing for docker." );
130+ messagePrinter . accept (BUNDLE_INFO_MESSAGE_PREFIX + "Rootless context missing for docker." );
119131 continue ;
120132 }
121133 containerTool = tool ;
@@ -124,7 +136,7 @@ public int initializeContainerImage() {
124136 }
125137 }
126138 if (containerTool == null ) {
127- throw new Error (String .format ("Please install one of the following tools before running containerized native image builds: %s" , SUPPORTED_CONTAINER_TOOLS ));
139+ throw errorFunction . apply (String .format ("Please install one of the following tools before running containerized native image builds: %s" , SUPPORTED_CONTAINER_TOOLS ), null );
128140 }
129141 }
130142
@@ -139,7 +151,7 @@ private int createContainer() {
139151 if (imageId == null ) {
140152 pb .inheritIO ();
141153 } else {
142- System . out . printf ("%sReusing container image %s.%n" , BUNDLE_INFO_MESSAGE_PREFIX , containerImage );
154+ messagePrinter . accept ( String . format ("%sReusing container image %s.%n" , BUNDLE_INFO_MESSAGE_PREFIX , containerImage ) );
143155 }
144156
145157 Process p = null ;
@@ -148,37 +160,37 @@ private int createContainer() {
148160 int status = p .waitFor ();
149161 if (status == 0 && imageId != null && !imageId .equals (getFirstProcessResultLine (pbCheckForImage ))) {
150162 try (var processResult = new BufferedReader (new InputStreamReader (p .getInputStream ()))) {
151- System . out . printf ("%sUpdated container image %s.%n" , BUNDLE_INFO_MESSAGE_PREFIX , containerImage );
152- processResult .lines ().forEach (System . out :: println );
163+ messagePrinter . accept ( String . format ("%sUpdated container image %s.%n" , BUNDLE_INFO_MESSAGE_PREFIX , containerImage ) );
164+ processResult .lines ().forEach (messagePrinter );
153165 }
154166 }
155167 return status ;
156168 } catch (IOException | InterruptedException e ) {
157- throw new Error (e .getMessage ());
169+ throw errorFunction . apply (e .getMessage (), e );
158170 } finally {
159171 if (p != null ) {
160172 p .destroy ();
161173 }
162174 }
163175 }
164176
165- private static boolean isToolAvailable (String tool ) {
177+ private boolean isToolAvailable (String tool ) {
166178 return Arrays .stream (System .getenv ("PATH" ).split (":" ))
167179 .map (str -> Path .of (str ).resolve (tool ))
168180 .anyMatch (Files ::isExecutable );
169181 }
170182
171- private static String getContainerToolVersion (String tool ) {
183+ private String getContainerToolVersion (String tool ) {
172184 ProcessBuilder pb = new ProcessBuilder (tool , "--version" );
173185 return getFirstProcessResultLine (pb );
174186 }
175187
176- private static boolean isRootlessDocker () {
188+ private boolean isRootlessDocker () {
177189 ProcessBuilder pb = new ProcessBuilder ("docker" , "context" , "show" );
178190 return getFirstProcessResultLine (pb ).equals ("rootless" );
179191 }
180192
181- private static String getFirstProcessResultLine (ProcessBuilder pb ) {
193+ private String getFirstProcessResultLine (ProcessBuilder pb ) {
182194 Process p = null ;
183195 try {
184196 p = pb .start ();
@@ -187,7 +199,7 @@ private static String getFirstProcessResultLine(ProcessBuilder pb) {
187199 return processResult .readLine ();
188200 }
189201 } catch (IOException | InterruptedException e ) {
190- throw new RuntimeException (e .getMessage ());
202+ throw errorFunction . apply (e .getMessage (), e );
191203 } finally {
192204 if (p != null ) {
193205 p .destroy ();
@@ -196,6 +208,22 @@ private static String getFirstProcessResultLine(ProcessBuilder pb) {
196208 }
197209
198210 public record TargetPath (Path path , boolean readonly ) {
211+ public static TargetPath readonly (Path target ) {
212+ return of (target , true );
213+ }
214+
215+ public static TargetPath of (Path target , boolean readonly ) {
216+ return new TargetPath (target , readonly );
217+ }
218+ }
219+
220+ public static Map <Path , TargetPath > mountMappingFor (Path javaHome , Path inputDir , Path outputDir ) {
221+ Map <Path , TargetPath > mountMapping = new HashMap <>();
222+ Path containerRoot = Paths .get ("/" );
223+ mountMapping .put (javaHome , TargetPath .readonly (containerRoot .resolve (CONTAINER_GRAAL_VM_HOME )));
224+ mountMapping .put (inputDir , ContainerSupport .TargetPath .readonly (containerRoot .resolve (BundleLauncher .INPUT_DIR_NAME )));
225+ mountMapping .put (outputDir , ContainerSupport .TargetPath .of (containerRoot .resolve (BundleLauncher .OUTPUT_DIR_NAME ), false ));
226+ return mountMapping ;
199227 }
200228
201229 public List <String > createContainerCommand (Map <String , String > containerEnvironment , Map <Path , TargetPath > mountMapping ) {
0 commit comments