-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #463 from zZHorizonZz/devservices
feature(devservices): added firestore dev service
- Loading branch information
Showing
13 changed files
with
299 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
...ava/io/quarkiverse/googlecloudservices/firestore/deployment/FirestoreBuildTimeConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package io.quarkiverse.googlecloudservices.firestore.deployment; | ||
|
||
import io.quarkus.runtime.annotations.ConfigItem; | ||
import io.quarkus.runtime.annotations.ConfigPhase; | ||
import io.quarkus.runtime.annotations.ConfigRoot; | ||
|
||
/** | ||
* Root configuration class for Firestore that operates at build time. | ||
* This class provides a nested structure for configuration, including | ||
* a separate group for the development service configuration. | ||
*/ | ||
@ConfigRoot(name = "google.cloud.firestore", phase = ConfigPhase.BUILD_TIME) | ||
public class FirestoreBuildTimeConfig { | ||
|
||
/** | ||
* Configuration for the Firestore dev service. | ||
* These settings will be used when Firestore service is being configured | ||
* for development purposes. | ||
*/ | ||
@ConfigItem | ||
public FirestoreDevServiceConfig devservice; | ||
} |
44 changes: 44 additions & 0 deletions
44
...va/io/quarkiverse/googlecloudservices/firestore/deployment/FirestoreDevServiceConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package io.quarkiverse.googlecloudservices.firestore.deployment; | ||
|
||
import java.util.Optional; | ||
|
||
import io.quarkus.runtime.annotations.ConfigGroup; | ||
import io.quarkus.runtime.annotations.ConfigItem; | ||
|
||
/** | ||
* Configuration group for the Firestore dev service. This class holds all the configuration properties | ||
* related to the Google Cloud Firestore service for development environments. | ||
* <p> | ||
* Here is an example of how to configure these properties: | ||
* <p> | ||
* | ||
* <pre> | ||
* quarkus.firestore-dev-service.enabled = true | ||
* quarkus.firestore-dev-service.image-name = gcr.io/google.com/cloudsdktool/google-cloud-cli # optional | ||
* quarkus.firestore-dev-service.emulatorPort = 8080 # optional | ||
* </pre> | ||
*/ | ||
@ConfigGroup | ||
public class FirestoreDevServiceConfig { | ||
|
||
/** | ||
* Indicates whether the Firestore service should be enabled or not. | ||
* The default value is 'false'. | ||
*/ | ||
@ConfigItem(defaultValue = "false") | ||
public boolean enabled; | ||
|
||
/** | ||
* Sets the Docker image name for the Google Cloud SDK. | ||
* This image is used to emulate the Firestore service in the development environment. | ||
* The default value is 'gcr.io/google.com/cloudsdktool/google-cloud-cli'. | ||
*/ | ||
@ConfigItem(defaultValue = "gcr.io/google.com/cloudsdktool/google-cloud-cli") | ||
public String imageName; | ||
|
||
/** | ||
* Specifies the emulatorPort on which the Firestore service should run in the development environment. | ||
*/ | ||
@ConfigItem | ||
public Optional<Integer> emulatorPort = Optional.empty(); | ||
} |
172 changes: 172 additions & 0 deletions
172
...io/quarkiverse/googlecloudservices/firestore/deployment/FirestoreDevServiceProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
package io.quarkiverse.googlecloudservices.firestore.deployment; | ||
|
||
import java.time.Duration; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import org.jboss.logging.Logger; | ||
import org.testcontainers.containers.FirestoreEmulatorContainer; | ||
import org.testcontainers.utility.DockerImageName; | ||
|
||
import io.quarkus.deployment.IsNormal; | ||
import io.quarkus.deployment.annotations.BuildStep; | ||
import io.quarkus.deployment.annotations.BuildSteps; | ||
import io.quarkus.deployment.builditem.*; | ||
import io.quarkus.deployment.console.ConsoleInstalledBuildItem; | ||
import io.quarkus.deployment.console.StartupLogCompressor; | ||
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig; | ||
import io.quarkus.deployment.logging.LoggingSetupBuildItem; | ||
|
||
/** | ||
* Processor responsible for managing Firestore Dev Services. | ||
* <p> | ||
* The processor starts the Firestore service in case it's not running. | ||
*/ | ||
@BuildSteps(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class) | ||
public class FirestoreDevServiceProcessor { | ||
|
||
private static final Logger LOGGER = Logger.getLogger(FirestoreDevServiceProcessor.class.getName()); | ||
|
||
// Running dev service instance | ||
private static volatile DevServicesResultBuildItem.RunningDevService devService; | ||
// Configuration for the Firestore Dev service | ||
private static volatile FirestoreDevServiceConfig config; | ||
|
||
@BuildStep | ||
public DevServicesResultBuildItem start(DockerStatusBuildItem dockerStatusBuildItem, | ||
FirestoreBuildTimeConfig buildTimeConfig, | ||
List<DevServicesSharedNetworkBuildItem> devServicesSharedNetworkBuildItem, | ||
Optional<ConsoleInstalledBuildItem> consoleInstalledBuildItem, | ||
CuratedApplicationShutdownBuildItem closeBuildItem, | ||
LaunchModeBuildItem launchMode, | ||
LoggingSetupBuildItem loggingSetupBuildItem, | ||
GlobalDevServicesConfig globalDevServicesConfig) { | ||
// If dev service is running and config has changed, stop the service | ||
if (devService != null && !buildTimeConfig.devservice.equals(config)) { | ||
stopContainer(); | ||
} else if (devService != null) { | ||
return devService.toBuildItem(); | ||
} | ||
|
||
// Set up log compressor for startup logs | ||
StartupLogCompressor compressor = new StartupLogCompressor( | ||
(launchMode.isTest() ? "(test) " : "") + "Google Cloud Firestore Dev Services Starting:", | ||
consoleInstalledBuildItem, | ||
loggingSetupBuildItem); | ||
|
||
// Try starting the container if conditions are met | ||
try { | ||
devService = startContainerIfAvailable(dockerStatusBuildItem, buildTimeConfig.devservice, | ||
globalDevServicesConfig.timeout); | ||
} catch (Throwable t) { | ||
LOGGER.warn("Unable to start Firestore dev service", t); | ||
// Dump captured logs in case of an error | ||
compressor.closeAndDumpCaptured(); | ||
return null; | ||
} finally { | ||
compressor.close(); | ||
} | ||
|
||
return devService == null ? null : devService.toBuildItem(); | ||
} | ||
|
||
/** | ||
* Start the container if conditions are met. | ||
* | ||
* @param dockerStatusBuildItem, Docker status | ||
* @param config, Configuration for the Firestore service | ||
* @param timeout, Optional timeout for starting the service | ||
* @return Running service item, or null if the service couldn't be started | ||
*/ | ||
private DevServicesResultBuildItem.RunningDevService startContainerIfAvailable(DockerStatusBuildItem dockerStatusBuildItem, | ||
FirestoreDevServiceConfig config, | ||
Optional<Duration> timeout) { | ||
if (!config.enabled) { | ||
// Firestore service explicitly disabled | ||
LOGGER.debug("Not starting Dev Services for Firestore as it has been disabled in the config"); | ||
return null; | ||
} | ||
|
||
if (!dockerStatusBuildItem.isDockerAvailable()) { | ||
LOGGER.warn("Not starting devservice because docker is not available"); | ||
return null; | ||
} | ||
|
||
return startContainer(dockerStatusBuildItem, config, timeout); | ||
} | ||
|
||
/** | ||
* Starts the Firestore emulator container with provided configuration. | ||
* | ||
* @param dockerStatusBuildItem, Docker status | ||
* @param config, Configuration for the Firestore service | ||
* @param timeout, Optional timeout for starting the service | ||
* @return Running service item, or null if the service couldn't be started | ||
*/ | ||
private DevServicesResultBuildItem.RunningDevService startContainer(DockerStatusBuildItem dockerStatusBuildItem, | ||
FirestoreDevServiceConfig config, | ||
Optional<Duration> timeout) { | ||
// Create and configure Firestore emulator container | ||
FirestoreEmulatorContainer emulatorContainer = new QuarkusFirestoreContainer( | ||
DockerImageName.parse(config.imageName).asCompatibleSubstituteFor("gcr.io/google.com/cloudsdktool/cloud-sdk"), | ||
config.emulatorPort.orElse(null)); | ||
|
||
// Set container startup timeout if provided | ||
timeout.ifPresent(emulatorContainer::withStartupTimeout); | ||
emulatorContainer.start(); | ||
|
||
// Set the config for the started container | ||
FirestoreDevServiceProcessor.config = config; | ||
|
||
// Return running service item with container details | ||
return new DevServicesResultBuildItem.RunningDevService(FirestoreBuildSteps.FEATURE, | ||
emulatorContainer.getContainerId(), | ||
emulatorContainer::close, "quarkus.google.cloud.firestore.host-override", | ||
emulatorContainer.getEmulatorEndpoint()); | ||
} | ||
|
||
/** | ||
* Stops the running Firestore emulator container. | ||
*/ | ||
private void stopContainer() { | ||
if (devService != null && devService.isOwner()) { | ||
try { | ||
// Try closing the running dev service | ||
devService.close(); | ||
} catch (Throwable e) { | ||
LOGGER.error("Failed to stop firestore container", e); | ||
} finally { | ||
devService = null; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Class for creating and configuring a Firestore emulator container. | ||
*/ | ||
private static class QuarkusFirestoreContainer extends FirestoreEmulatorContainer { | ||
|
||
private final Integer fixedExposedPort; | ||
private static final int INTERNAL_PORT = 8080; | ||
|
||
private QuarkusFirestoreContainer(DockerImageName dockerImageName, Integer fixedExposedPort) { | ||
super(dockerImageName); | ||
this.fixedExposedPort = fixedExposedPort; | ||
} | ||
|
||
/** | ||
* Configures the Firestore emulator container. | ||
*/ | ||
@Override | ||
public void configure() { | ||
super.configure(); | ||
|
||
// Expose Firestore emulatorPort | ||
if (fixedExposedPort != null) { | ||
addFixedExposedPort(fixedExposedPort, INTERNAL_PORT); | ||
} else { | ||
addExposedPort(INTERNAL_PORT); | ||
} | ||
} | ||
} | ||
} |
19 changes: 8 additions & 11 deletions
19
...src/main/java/io/quarkiverse/googlecloudservices/firestore/runtime/FirestoreProducer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.