55import static io .quarkus .test .common .LauncherUtil .waitForCapturedListeningData ;
66import static io .quarkus .test .common .LauncherUtil .waitForStartedFunction ;
77import static java .lang .ProcessBuilder .Redirect .DISCARD ;
8- import static java .lang .ProcessBuilder .Redirect .PIPE ;
98
10- import java .io .FileOutputStream ;
119import java .io .IOException ;
12- import java .io .InputStream ;
1310import java .net .ServerSocket ;
11+ import java .nio .file .FileSystemException ;
1412import java .nio .file .Files ;
1513import java .nio .file .Path ;
16- import java .nio .file .Paths ;
1714import java .util .ArrayList ;
1815import java .util .Collections ;
1916import java .util .HashMap ;
2421import java .util .concurrent .TimeUnit ;
2522import java .util .function .Function ;
2623
27- import org .apache .commons .io .input .TeeInputStream ;
2824import org .apache .commons .lang3 .RandomStringUtils ;
25+ import org .jboss .logging .Logger ;
2926
3027import io .quarkus .runtime .util .ContainerRuntimeUtil ;
3128import io .quarkus .test .common .http .TestHTTPResourceManager ;
3229import io .smallrye .config .common .utils .StringUtil ;
3330
3431public class DefaultDockerContainerLauncher implements DockerContainerArtifactLauncher {
3532
33+ private static final Logger log = Logger .getLogger (DefaultDockerContainerLauncher .class );
34+
3635 private int httpPort ;
3736 private int httpsPort ;
3837 private long waitTimeSeconds ;
@@ -42,16 +41,11 @@ public class DefaultDockerContainerLauncher implements DockerContainerArtifactLa
4241 private String containerImage ;
4342 private boolean pullRequired ;
4443 private Map <Integer , Integer > additionalExposedPorts ;
45-
4644 private final Map <String , String > systemProps = new HashMap <>();
47-
4845 private boolean isSsl ;
49-
50- private String containerName ;
51-
46+ private final String containerName = "quarkus-integration-test-" + RandomStringUtils .random (5 , true , false );
5247 private String containerRuntimeBinaryName ;
53-
54- private ExecutorService executorService = Executors .newSingleThreadExecutor ();
48+ private final ExecutorService executorService = Executors .newSingleThreadExecutor ();
5549
5650 @ Override
5751 public void init (DockerContainerArtifactLauncher .DockerInitContext initContext ) {
@@ -77,7 +71,7 @@ public void start() throws IOException {
7771 containerRuntimeBinaryName = determineBinary ();
7872
7973 if (pullRequired ) {
80- System . out . println ("Pulling container image '" + containerImage + "'" );
74+ log . infof ("Pulling container image '%s'" , containerImage );
8175 try {
8276 int pullResult = new ProcessBuilder ().redirectError (DISCARD ).redirectOutput (DISCARD )
8377 .command (containerRuntimeBinaryName , "pull" , containerImage ).start ().waitFor ();
@@ -98,21 +92,21 @@ public void start() throws IOException {
9892 httpsPort = getRandomPort ();
9993 }
10094
101- List <String > args = new ArrayList <>();
95+ final List <String > args = new ArrayList <>();
10296 args .add (containerRuntimeBinaryName );
10397 args .add ("run" );
10498 if (!argLine .isEmpty ()) {
10599 args .addAll (argLine );
106100 }
107101 args .add ("--name" );
108- containerName = "quarkus-integration-test-" + RandomStringUtils .random (5 , true , false );
109102 args .add (containerName );
103+ args .add ("-i" ); // Interactive, write logs to stdout
110104 args .add ("--rm" );
111105 args .add ("-p" );
112106 args .add (httpPort + ":" + httpPort );
113107 args .add ("-p" );
114108 args .add (httpsPort + ":" + httpsPort );
115- for (var entry : additionalExposedPorts .entrySet ()) {
109+ for (Map . Entry < Integer , Integer > entry : additionalExposedPorts .entrySet ()) {
116110 args .add ("-p" );
117111 args .add (entry .getKey () + ":" + entry .getValue ());
118112 }
@@ -125,7 +119,7 @@ public void start() throws IOException {
125119 if (DefaultJarLauncher .HTTP_PRESENT ) {
126120 args .addAll (toEnvVar ("quarkus.http.port" , "" + httpPort ));
127121 args .addAll (toEnvVar ("quarkus.http.ssl-port" , "" + httpsPort ));
128- // this won't be correct when using the random port but it's really only used by us for the rest client tests
122+ // This won't be correct when using the random port, but it's really only used by us for the rest client tests
129123 // in the main module, since those tests hit the application itself
130124 args .addAll (toEnvVar ("test.url" , TestHTTPResourceManager .getUri ()));
131125 }
@@ -138,31 +132,31 @@ public void start() throws IOException {
138132 }
139133 args .add (containerImage );
140134
141- Path logFile = PropertyTestUtil .getLogFilePath ();
142- Files . deleteIfExists ( logFile );
143- Files .createDirectories (logFile . getParent () );
144-
145- Path containerLogFile = Paths . get ( "target" , "container.log" );
146- Files . createDirectories ( containerLogFile . getParent () );
147- FileOutputStream containerLogOutputStream = new FileOutputStream ( containerLogFile . toFile (), true );
135+ final Path logFile = PropertyTestUtil .getLogFilePath ();
136+ try {
137+ Files .deleteIfExists (logFile );
138+ Files . createDirectories ( logFile . getParent ());
139+ } catch ( FileSystemException e ) {
140+ log . warnf ( "Log file %s deletion failed, could happen on Windows, we can carry on." , logFile );
141+ }
148142
149- System . out . println ("Executing \" " + String .join (" " , args ) + " \" " );
143+ log . infof ("Executing \" %s \" " , String .join (" " , args ));
150144
151- Function <IntegrationTestStartedNotifier .Context , IntegrationTestStartedNotifier .Result > startedFunction = createStartedFunction ();
145+ final Function <IntegrationTestStartedNotifier .Context , IntegrationTestStartedNotifier .Result > startedFunction = createStartedFunction ();
152146
153- // the idea here is to obtain the logs of the application simply by redirecting all its output the a file
154- // this is done in contrast with the JarLauncher and NativeImageLauncher because in the case of the container
155- // the log itself is written inside the container
156- Process quarkusProcess = new ProcessBuilder ( args ). redirectError ( PIPE ). redirectOutput ( PIPE ). start ();
157- InputStream tee = new TeeInputStream ( quarkusProcess . getInputStream (), new FileOutputStream ( logFile .toFile ()));
158- executorService . submit (() -> tee . transferTo ( containerLogOutputStream ) );
147+ // We rely on the container writing log to stdout. If it just writes to a logfile inside itself, we would have
148+ // to mount /work/ directory to get quarkus.log.
149+ final Process containerProcess = new ProcessBuilder ( args )
150+ . redirectErrorStream ( true )
151+ . redirectOutput ( ProcessBuilder . Redirect . appendTo ( logFile .toFile ()))
152+ . start ( );
159153
160154 if (startedFunction != null ) {
161- IntegrationTestStartedNotifier .Result result = waitForStartedFunction (startedFunction , quarkusProcess ,
155+ final IntegrationTestStartedNotifier .Result result = waitForStartedFunction (startedFunction , containerProcess ,
162156 waitTimeSeconds , logFile );
163157 isSsl = result .isSsl ();
164158 } else {
165- ListeningAddress result = waitForCapturedListeningData (quarkusProcess , logFile , waitTimeSeconds );
159+ final ListeningAddress result = waitForCapturedListeningData (containerProcess , logFile , waitTimeSeconds );
166160 updateConfigForPort (result .getPort ());
167161 isSsl = result .isSsl ();
168162 }
@@ -188,10 +182,7 @@ public void includeAsSysProps(Map<String, String> systemProps) {
188182
189183 private List <String > toEnvVar (String property , String value ) {
190184 if ((property != null ) && (!property .isEmpty ())) {
191- List <String > result = new ArrayList <>(2 );
192- result .add ("--env" );
193- result .add (String .format ("%s=%s" , convertPropertyToEnvVar (property ), value ));
194- return result ;
185+ return List .of ("--env" , String .format ("%s=%s" , convertPropertyToEnvVar (property ), value ));
195186 }
196187 return Collections .emptyList ();
197188 }
@@ -203,14 +194,13 @@ private String convertPropertyToEnvVar(String property) {
203194 @ Override
204195 public void close () {
205196 try {
206- Process dockerStopProcess = new ProcessBuilder (containerRuntimeBinaryName , "stop" , containerName )
197+ final Process dockerStopProcess = new ProcessBuilder (containerRuntimeBinaryName , "stop" , containerName )
207198 .redirectError (DISCARD )
208199 .redirectOutput (DISCARD ).start ();
209200 dockerStopProcess .waitFor (10 , TimeUnit .SECONDS );
210201 } catch (IOException | InterruptedException e ) {
211- System . out . println ("Unable to stop container '" + containerName + "'" );
202+ log . errorf ("Unable to stop container '%s'" , containerName );
212203 }
213204 executorService .shutdown ();
214205 }
215-
216206}
0 commit comments