Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/firebase experiments #731

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,23 @@ endif::add-copy-button-to-env-var[]
a|`import-only`, `export-only`, `import-export`
|

a|icon:lock[title=Fixed at build time] [[quarkus-google-cloud-firebase-devservices_quarkus-google-cloud-devservices-firebase-emulator-cli-experiments]] [.property-path]##link:#quarkus-google-cloud-firebase-devservices_quarkus-google-cloud-devservices-firebase-emulator-cli-experiments[`quarkus.google.cloud.devservices.firebase.emulator.cli.experiments`]##

[.description]
--
Indicates the set of experimental features from firebase to enable (using the firebase experiment:enable command line option).


ifdef::add-copy-button-to-env-var[]
Environment variable: env_var_with_copy_button:+++QUARKUS_GOOGLE_CLOUD_DEVSERVICES_FIREBASE_EMULATOR_CLI_EXPERIMENTS+++[]
endif::add-copy-button-to-env-var[]
ifndef::add-copy-button-to-env-var[]
Environment variable: `+++QUARKUS_GOOGLE_CLOUD_DEVSERVICES_FIREBASE_EMULATOR_CLI_EXPERIMENTS+++`
endif::add-copy-button-to-env-var[]
--
|list of string
|

a|icon:lock[title=Fixed at build time] [[quarkus-google-cloud-firebase-devservices_quarkus-google-cloud-devservices-firebase-emulator-cli-debug]] [.property-path]##link:#quarkus-google-cloud-firebase-devservices_quarkus-google-cloud-devservices-firebase-emulator-cli-debug[`quarkus.google.cloud.devservices.firebase.emulator.cli.debug`]##

[.description]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,23 @@ endif::add-copy-button-to-env-var[]
a|`import-only`, `export-only`, `import-export`
|

a|icon:lock[title=Fixed at build time] [[quarkus-google-cloud-firebase-devservices_quarkus-google-cloud-devservices-firebase-emulator-cli-experiments]] [.property-path]##link:#quarkus-google-cloud-firebase-devservices_quarkus-google-cloud-devservices-firebase-emulator-cli-experiments[`quarkus.google.cloud.devservices.firebase.emulator.cli.experiments`]##

[.description]
--
Indicates the set of experimental features from firebase to enable (using the firebase experiment:enable command line option).


ifdef::add-copy-button-to-env-var[]
Environment variable: env_var_with_copy_button:+++QUARKUS_GOOGLE_CLOUD_DEVSERVICES_FIREBASE_EMULATOR_CLI_EXPERIMENTS+++[]
endif::add-copy-button-to-env-var[]
ifndef::add-copy-button-to-env-var[]
Environment variable: `+++QUARKUS_GOOGLE_CLOUD_DEVSERVICES_FIREBASE_EMULATOR_CLI_EXPERIMENTS+++`
endif::add-copy-button-to-env-var[]
--
|list of string
|

a|icon:lock[title=Fixed at build time] [[quarkus-google-cloud-firebase-devservices_quarkus-google-cloud-devservices-firebase-emulator-cli-debug]] [.property-path]##link:#quarkus-google-cloud-firebase-devservices_quarkus-google-cloud-devservices-firebase-emulator-cli-debug[`quarkus.google.cloud.devservices.firebase.emulator.cli.debug`]##

[.description]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkiverse.googlecloudservices.firebase.deployment;

import java.util.Optional;
import java.util.Set;

import io.quarkiverse.googlecloudservices.firebase.deployment.testcontainers.FirebaseEmulatorContainer;
import io.quarkus.runtime.annotations.ConfigRoot;
Expand Down Expand Up @@ -199,6 +200,12 @@ interface Cli {
*/
Optional<FirebaseEmulatorContainer.ImportExport> importExport();

/**
* Indicates the set of experimental features from firebase to enable (using the firebase experiment:enable
* command line option).
*/
Optional<Set<String>> experiments();

/**
* Enable firebase emulators debugging.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ public DevServicesResultBuildItem start(DockerStatusBuildItem dockerStatusBuildI

// Try starting the container if conditions are met
try {
devService = startContainerIfAvailable(dockerStatusBuildItem, projectConfig, firebaseBuildTimeConfig,
devService = startContainerIfAvailable(
dockerStatusBuildItem,
closeBuildItem,
projectConfig,
firebaseBuildTimeConfig,
globalDevServicesConfig.timeout);
} catch (Throwable t) {
LOGGER.warn("Unable to start Firebase dev service", t);
Expand All @@ -87,9 +91,11 @@ public DevServicesResultBuildItem start(DockerStatusBuildItem dockerStatusBuildI
* @param dockerStatusBuildItem, Docker status
* @param config, Configuration for the Firebase service
* @param timeout, Optional timeout for starting the service
* @param closeBuildItem
* @return Running service item, or null if the service couldn't be started
*/
private DevServicesResultBuildItem.RunningDevService startContainerIfAvailable(DockerStatusBuildItem dockerStatusBuildItem,
CuratedApplicationShutdownBuildItem closeBuildItem,
FirebaseDevServiceProjectConfig projectConfig,
FirebaseDevServiceConfig config,
Optional<Duration> timeout) {
Expand All @@ -111,7 +117,7 @@ private DevServicesResultBuildItem.RunningDevService startContainerIfAvailable(D
return null;
}

return startContainer(dockerStatusBuildItem, projectConfig, config, timeout);
return startContainer(closeBuildItem, projectConfig, config, timeout);
}

private boolean isEnabled(FirebaseDevServiceConfig config) {
Expand All @@ -125,12 +131,12 @@ private boolean isEnabled(FirebaseDevServiceConfig config) {
/**
* Starts the Pub/Sub emulator container with provided configuration.
*
* @param dockerStatusBuildItem, Docker status
* @param closeBuildItem The close build item to handle shutdown of the container
* @param config, Configuration for the Firebase 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,
private DevServicesResultBuildItem.RunningDevService startContainer(CuratedApplicationShutdownBuildItem closeBuildItem,
FirebaseDevServiceProjectConfig projectConfig,
FirebaseDevServiceConfig config,
Optional<Duration> timeout) {
Expand All @@ -155,6 +161,8 @@ private DevServicesResultBuildItem.RunningDevService startContainer(DockerStatus
.forEach((e, h) -> LOGGER.info("Google Cloud emulator config property " + e + " set to " + h));
}

closeBuildItem.addCloseTask(emulatorContainer::close, true);

// Return running service item with container details
return new DevServicesResultBuildItem.RunningDevService(FirebaseBuildSteps.FEATURE,
emulatorContainer.getContainerId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ private void handleCliConfig(FirebaseDevServiceConfig.Firebase.Emulator.Cli cli,
cli.javaToolOptions().ifPresent(cliConfig::withJavaToolOptions);
cli.emulatorData().map(FirebaseEmulatorConfigBuilder::asPath).ifPresent(cliConfig::withEmulatorData);
cli.importExport().ifPresent(cliConfig::withImportExport);
cli.experiments().ifPresent(cliConfig::withExperiments);
cli.debug().ifPresent(cliConfig::withDebug);

cliConfig.done();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ private FirebaseEmulatorContainer.HostingConfig readHosting(Object hosting, Path

var publicDir = Optional
.ofNullable(hostingMap.get("public"))
.or(() -> Optional.ofNullable(hostingMap.get("source")))
.map(f -> this.resolvePath(f, customFirebaseJson));

LOGGER.debug("Hosting configured with public directory {}", publicDir);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ public record DockerConfig(
* @param javaToolOptions The options to pass to the java based emulators
* @param emulatorData The path to the directory where to store the emulator data
* @param importExport Specify whether to import, export or do both with the emulator data
* @param experiments Firebase experiments to enable on the docker image
* @param debug Whether to run with the --debug flag
*/
public record CliArgumentsConfig(
Expand All @@ -193,13 +194,15 @@ public record CliArgumentsConfig(
Optional<String> javaToolOptions,
Optional<Path> emulatorData,
ImportExport importExport,
Optional<Set<String>> experiments,
boolean debug) {
public static final CliArgumentsConfig DEFAULT = new CliArgumentsConfig(
Optional.empty(),
Optional.empty(),
Optional.empty(),
Optional.empty(),
ImportExport.IMPORT_EXPORT,
Optional.of(new HashSet<>()),
false);
}

Expand Down Expand Up @@ -609,6 +612,7 @@ public class CliBuilder {
private String javaToolOptions;
private Path emulatorData;
private ImportExport importExport;
private Set<String> experiments;
private boolean debug;

/**
Expand All @@ -620,6 +624,7 @@ private CliBuilder() {
this.javaToolOptions = Builder.this.cliArguments.javaToolOptions.orElse(null);
this.emulatorData = Builder.this.cliArguments.emulatorData.orElse(null);
this.importExport = Builder.this.cliArguments.importExport;
this.experiments = Builder.this.cliArguments.experiments.orElse(new HashSet<>());
this.debug = Builder.this.cliArguments.debug;
}

Expand Down Expand Up @@ -690,6 +695,28 @@ public CliBuilder withDebug(boolean debug) {
return this;
}

/**
* Add the firebase experiments setting
*
* @param experiments The experiments to enable
* @return The builder
*/
public CliBuilder withExperiments(Set<String> experiments) {
this.experiments = new HashSet<>(experiments);
return this;
}

/**
* Add a single firebase experiment to the set
*
* @param experiment The experiment to add
* @return The builder
*/
public CliBuilder addExperiment(String experiment) {
this.experiments.add(experiment);
return this;
}

/**
* Finish the builder
*
Expand All @@ -702,6 +729,7 @@ public Builder done() {
Optional.ofNullable(this.javaToolOptions),
Optional.ofNullable(this.emulatorData),
this.importExport,
Optional.of(this.experiments),
this.debug);
return Builder.this;
}
Expand Down Expand Up @@ -924,7 +952,7 @@ public FirebaseEmulatorContainer(EmulatorConfig emulatorConfig) {
LOGGER.debug("Mounting {} to the container hosting path", hostingPath);

// Mount volume for static hosting content
this.withFileSystemBind(hostingPath, containerHostingPath(emulatorConfig), BindMode.READ_ONLY);
this.withFileSystemBind(hostingPath, containerHostingPath(emulatorConfig), BindMode.READ_WRITE);
}

if (this.services.containsKey(Emulator.CLOUD_FUNCTIONS)) {
Expand All @@ -938,7 +966,7 @@ public FirebaseEmulatorContainer(EmulatorConfig emulatorConfig) {
LOGGER.debug("Mounting {} to the container functions sources path", functionsPath);

// Mount volume for functions
this.withFileSystemBind(functionsPath, containerFunctionsPath(emulatorConfig), BindMode.READ_ONLY);
this.withFileSystemBind(functionsPath, containerFunctionsPath(emulatorConfig), BindMode.READ_WRITE);
}
}

Expand Down Expand Up @@ -1023,14 +1051,15 @@ public ImageFromDockerfile build() {
this.initialSetup();
this.authenticateToFirebase();
this.setupJavaToolOptions();
this.setupUserAndGroup();
this.downloadEmulators();
this.addFirebaseJson();
this.includeFirestoreFiles();
this.includeStorageFiles();
this.setupExperiments();
this.setupDataImportExport();
this.setupHosting();
this.setupFunctions();
this.addFirebaseJson();
this.includeFirestoreFiles();
this.includeStorageFiles();
this.setupUserAndGroup();
this.runExecutable();

return result;
Expand Down Expand Up @@ -1153,8 +1182,36 @@ private void setupJavaToolOptions() {
toolOptions -> dockerBuilder.env("JAVA_TOOL_OPTIONS", toolOptions));
}

private void setupExperiments() {
emulatorConfig.cliArguments.experiments().ifPresent(
experimentsSet -> {
var experiments = String.join(",", experimentsSet);
LOGGER.debug("Firebase experiments found, enabling experiments: {}", experiments);
dockerBuilder.env("FIREBASE_CLI_EXPERIMENTS", String.join(",", experiments));
});
}

private void addFirebaseJson() {
dockerBuilder.workDir(FIREBASE_ROOT);
/*
* Workaround for https://github.com/firebase/firebase-tools/issues/5903#issuecomment-1568239576
*
* Remove the conditional and just set FIREBASE_ROOT as workdir once the upstream bug is fixed.
*/
if (isEmulatorEnabled(Emulator.FIREBASE_HOSTING)) {
var hostingPath = containerHostingPath(emulatorConfig);

LOGGER.debug(
"Hosting emulator detected. Setting workdir to {} as a workaround for an upstream bug in firebase-tools",
hostingPath);

dockerBuilder.workDir(hostingPath);
} else {
LOGGER.debug("No hosting emulator detected. Using default workdir");
dockerBuilder.workDir(FIREBASE_ROOT);
}
/*
* Workaround ends <-- https://github.com/firebase/firebase-tools/issues/5903#issuecomment-1568239576
*/

emulatorConfig.customFirebaseJson().ifPresentOrElse(
this::includeCustomFirebaseJson,
Expand Down Expand Up @@ -1256,6 +1313,8 @@ private void runExecutable() {
List<String> arguments = new ArrayList<>();

arguments.add("emulators:start");
arguments.add("--config");
arguments.add(FIREBASE_ROOT + "/firebase.json");

emulatorConfig.cliArguments().projectId()
.map(id -> "--project")
Expand All @@ -1268,7 +1327,9 @@ private void runExecutable() {
arguments.add("--debug");
}

if (emulatorConfig.cliArguments().importExport.isDoExport()) {
if (emulatorConfig.cliArguments().importExport.isDoImport()) {
LOGGER.debug("Import requested. Importing data on startup");

emulatorConfig
.cliArguments()
.emulatorData()
Expand All @@ -1287,6 +1348,8 @@ private void runExecutable() {
}

if (emulatorConfig.cliArguments().importExport.isDoExport()) {
LOGGER.debug("Export requested. Saving data on exit");

emulatorConfig
.cliArguments()
.emulatorData()
Expand Down Expand Up @@ -1340,11 +1403,22 @@ public void stop() {
* kill (SIGKILL) command instead of a stop (SIGTERM) command. This will kill the container instantly
* and prevent firebase from writing the "--export-on-exit" data to the mounted directory.
*/
LOGGER.debug("Requesting to stopping the container to give export a chance to finish");

this.getDockerClient().stopContainerCmd(this.getContainerId()).exec();

LOGGER.debug("Stopping abd removing the container");

super.stop();
}

@Override
public void close() {
LOGGER.debug("Emulator is being closed");

this.stop();
}

/**
* Configures the Pub/Sub emulator container.
*/
Expand Down Expand Up @@ -1420,6 +1494,10 @@ private void writeOutputFrame(OutputFrame frame, Level level) {
}

private String getEmulatorEndpoint(Emulator emulator) {
if (emulator.equals(Emulator.CLOUD_STORAGE)) {
return "http://" + this.getHost() + ":" + emulatorPort(emulator);
}

return this.getHost() + ":" + emulatorPort(emulator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -37,6 +38,7 @@ void setUp() {
Optional.of("-Xmx"),
Optional.of("data"),
Optional.of(FirebaseEmulatorContainer.ImportExport.EXPORT_ONLY),
Optional.of(Set.of("webframeworks")),
Optional.of(true)),
Optional.empty(),
new TestUI(
Expand Down Expand Up @@ -89,6 +91,7 @@ void testBuild() {
assertEquals("MY_TOKEN", emulatorConfig.cliArguments().token().orElse(null));
assertEquals("-Xmx", emulatorConfig.cliArguments().javaToolOptions().orElse(null));
assertPathEndsWith("data", emulatorConfig.cliArguments().emulatorData().orElse(null));
assertEquals(Set.of("webframeworks"), emulatorConfig.cliArguments().experiments().orElse(null));
assertEquals(FirebaseEmulatorContainer.ImportExport.EXPORT_ONLY, emulatorConfig.cliArguments().importExport());
assertTrue(emulatorConfig.cliArguments().debug());

Expand Down Expand Up @@ -174,6 +177,7 @@ record TestCli(
Optional<String> javaToolOptions,
Optional<String> emulatorData,
Optional<FirebaseEmulatorContainer.ImportExport> importExport,
Optional<Set<String>> experiments,
Optional<Boolean> debug) implements FirebaseDevServiceConfig.Firebase.Emulator.Cli {
}

Expand Down
Loading
Loading