@@ -39,16 +39,20 @@ public class HiveContainer extends GenericContainer<HiveContainer> {
3939 // For 10-20 minute startup: use "drill-hive-test:latest" (build with docker build)
4040 private static final String HIVE_IMAGE = System .getProperty ("hive.image" , "drill-hive-test:fast" );
4141 private static final String FALLBACK_IMAGE = "apache/hive:3.1.3" ;
42- private static final boolean USE_PREINITIALIZED = HIVE_IMAGE .contains ("preinitialized" );
4342 private static final int METASTORE_PORT = 9083 ;
4443 private static final int HIVESERVER2_PORT = 10000 ;
4544 private static final int HIVESERVER2_HTTP_PORT = 10002 ;
4645
4746 private static HiveContainer instance ;
48- private boolean dataInitialized = false ;
47+ private static String initializationError = null ;
48+ private final boolean usePreinitialized ;
49+ private final boolean useFallbackImage ;
4950
50- private HiveContainer () {
51- this (getHiveImage ());
51+ private HiveContainer (String dockerImageName , boolean useFallback ) {
52+ super (DockerImageName .parse (dockerImageName ).asCompatibleSubstituteFor ("apache/hive" ));
53+ this .useFallbackImage = useFallback ;
54+ this .usePreinitialized = dockerImageName .contains ("preinitialized" );
55+ configureContainer ();
5256 }
5357
5458 private static String getHiveImage () {
@@ -57,24 +61,44 @@ private static String getHiveImage() {
5761 return HIVE_IMAGE ;
5862 }
5963
60- private HiveContainer (String dockerImageName ) {
61- super (DockerImageName .parse (dockerImageName ).asCompatibleSubstituteFor ("apache/hive" ));
64+ /**
65+ * Checks if Docker is available on the system.
66+ */
67+ public static boolean isDockerAvailable () {
68+ try {
69+ org .testcontainers .DockerClientFactory .instance ().client ();
70+ return true ;
71+ } catch (Exception e ) {
72+ return false ;
73+ }
74+ }
75+
76+ /**
77+ * Returns any initialization error that occurred.
78+ */
79+ public static String getInitializationError () {
80+ return initializationError ;
81+ }
6282
83+ private void configureContainer () {
6384 withExposedPorts (METASTORE_PORT , HIVESERVER2_PORT , HIVESERVER2_HTTP_PORT );
6485
6586 // Set environment variables for Hive configuration
6687 withEnv ("SERVICE_NAME" , "hiveserver2" );
6788 // Don't set IS_RESUME - let the entrypoint initialize the schema
6889
6990 // Wait strategy depends on image type:
70- // - Standard image: Wait for data initialization to complete (20 minutes)
71- // - Pre-initialized image: Wait for services to start only (2 minutes)
72- if (USE_PREINITIALIZED ) {
91+ if (usePreinitialized ) {
7392 // Pre-initialized image: schema and data already exist, just wait for services
7493 waitingFor (Wait .forLogMessage (".*Hive container ready \\ (pre-initialized\\ )!.*" , 1 )
7594 .withStartupTimeout (Duration .ofMinutes (2 )));
95+ } else if (useFallbackImage ) {
96+ // Fallback to apache/hive:3.1.3 - wait for HiveServer2 to be ready
97+ // This image uses a different startup sequence
98+ waitingFor (Wait .forLogMessage (".*Starting HiveServer2.*" , 1 )
99+ .withStartupTimeout (Duration .ofMinutes (5 )));
76100 } else {
77- // Standard image: wait for both HiveServer2 to start AND test data to be initialized
101+ // Custom image: wait for both HiveServer2 to start AND test data to be initialized
78102 // Allow up to 20 minutes: Metastore + HiveServer2 startup (~5-10 min) + data initialization (~5-10 min)
79103 // This is only on first run; container reuse makes subsequent tests fast (~1 second)
80104 waitingFor (Wait .forLogMessage (".*Test data loaded and ready for queries.*" , 1 )
@@ -84,49 +108,88 @@ private HiveContainer(String dockerImageName) {
84108 // Enable reuse for faster test execution
85109 withReuse (true );
86110
87- logger .info ("Hive container configured with image: {}" , dockerImageName );
111+ logger .info ("Hive container configured with image: {}" , getDockerImageName () );
88112 }
89113
90114 /**
91115 * Gets the singleton instance of HiveContainer.
92116 * Container is started on first access and reused for all subsequent tests.
117+ * If the custom image is not available, falls back to apache/hive:3.1.3.
93118 *
94- * @return Shared HiveContainer instance
119+ * @return Shared HiveContainer instance, or null if Docker is unavailable
120+ * @throws RuntimeException if container fails to start
95121 */
96122 public static synchronized HiveContainer getInstance () {
97- if (instance == null ) {
98- System .out .println ("========================================" );
99- System .out .println ("Starting Hive Docker container..." );
100- if (USE_PREINITIALIZED ) {
101- System .out .println ("Using pre-initialized image (~1 minute startup)" );
102- } else {
103- System .out .println ("Using standard image (~15 minute startup on first run)" );
104- }
105- System .out .println ("Image: " + HIVE_IMAGE );
106- System .out .println ("========================================" );
107- logger .info ("Creating new Hive container instance" );
108- instance = new HiveContainer ();
109-
110- System .out .println ("Pulling Docker image and starting container..." );
111- long startTime = System .currentTimeMillis ();
112- instance .start ();
113- long elapsedSeconds = (System .currentTimeMillis () - startTime ) / 1000 ;
114-
115- System .out .println ("========================================" );
116- System .out .println ("Hive container started successfully!" );
117- System .out .println ("Startup time: " + elapsedSeconds + " seconds" );
118- System .out .println ("Metastore: " + instance .getMetastoreUri ());
119- System .out .println ("JDBC: " + instance .getJdbcUrl ());
120- System .out .println ("Container will be reused for all tests" );
121- if (USE_PREINITIALIZED ) {
122- System .out .println ("Tip: Build pre-initialized image with build-preinitialized-image.sh" );
123- }
124- System .out .println ("========================================" );
125- logger .info ("Hive container started and ready for tests" );
126- } else {
123+ if (instance != null ) {
127124 logger .debug ("Reusing existing Hive container instance" );
125+ return instance ;
126+ }
127+
128+ // Check if Docker is available
129+ if (!isDockerAvailable ()) {
130+ initializationError = "Docker is not available. Please install and start Docker to run Hive tests." ;
131+ logger .error (initializationError );
132+ throw new RuntimeException (initializationError );
133+ }
134+
135+ System .out .println ("========================================" );
136+ System .out .println ("Starting Hive Docker container..." );
137+ System .out .println ("Requested image: " + HIVE_IMAGE );
138+ System .out .println ("========================================" );
139+
140+ // Try the requested image first
141+ try {
142+ instance = tryStartContainer (HIVE_IMAGE , false );
143+ return instance ;
144+ } catch (Exception e ) {
145+ logger .warn ("Failed to start container with image '{}': {}" , HIVE_IMAGE , e .getMessage ());
146+ System .out .println ("Failed to start with " + HIVE_IMAGE + ", trying fallback image..." );
147+ }
148+
149+ // Fall back to apache/hive:3.1.3
150+ System .out .println ("Falling back to: " + FALLBACK_IMAGE );
151+ try {
152+ instance = tryStartContainer (FALLBACK_IMAGE , true );
153+ return instance ;
154+ } catch (Exception e ) {
155+ initializationError = "Failed to start Hive container with both custom and fallback images: " + e .getMessage ();
156+ logger .error (initializationError , e );
157+ throw new RuntimeException (initializationError , e );
128158 }
129- return instance ;
159+ }
160+
161+ private static HiveContainer tryStartContainer (String imageName , boolean isFallback ) {
162+ boolean usePreinit = imageName .contains ("preinitialized" );
163+ if (usePreinit ) {
164+ System .out .println ("Using pre-initialized image (~1 minute startup)" );
165+ } else if (isFallback ) {
166+ System .out .println ("Using fallback apache/hive image (~5 minute startup)" );
167+ } else {
168+ System .out .println ("Using custom image (~15 minute startup on first run)" );
169+ }
170+ System .out .println ("Image: " + imageName );
171+
172+ logger .info ("Creating Hive container with image: {}" , imageName );
173+ HiveContainer container = new HiveContainer (imageName , isFallback );
174+
175+ System .out .println ("Pulling Docker image and starting container..." );
176+ long startTime = System .currentTimeMillis ();
177+ container .start ();
178+ long elapsedSeconds = (System .currentTimeMillis () - startTime ) / 1000 ;
179+
180+ System .out .println ("========================================" );
181+ System .out .println ("Hive container started successfully!" );
182+ System .out .println ("Startup time: " + elapsedSeconds + " seconds" );
183+ System .out .println ("Metastore: " + container .getMetastoreUri ());
184+ System .out .println ("JDBC: " + container .getJdbcUrl ());
185+ System .out .println ("Container will be reused for all tests" );
186+ if (isFallback ) {
187+ System .out .println ("NOTE: Using fallback image - test data must be created via JDBC" );
188+ }
189+ System .out .println ("========================================" );
190+ logger .info ("Hive container started and ready for tests" );
191+
192+ return container ;
130193 }
131194
132195 /**
@@ -179,6 +242,25 @@ public Integer getHiveServer2Port() {
179242 return getMappedPort (HIVESERVER2_PORT );
180243 }
181244
245+ /**
246+ * Checks if this container is using the fallback image (apache/hive:3.1.3).
247+ * When using the fallback image, test data must be created via JDBC.
248+ *
249+ * @return true if using fallback image
250+ */
251+ public boolean isUsingFallbackImage () {
252+ return useFallbackImage ;
253+ }
254+
255+ /**
256+ * Checks if this container is using a pre-initialized image.
257+ *
258+ * @return true if using pre-initialized image
259+ */
260+ public boolean isUsingPreinitializedImage () {
261+ return usePreinitialized ;
262+ }
263+
182264 @ Override
183265 protected void doStart () {
184266 super .doStart ();
0 commit comments