Open nielm opened 2 years ago
FWIW, here is some code I used to cleanly setup and teardown the emulator for use in unit testing:
@RunWith(JUnit4.class)
public class SpannerIntegrationTest {
private static Process emulatorProcess = null;
private static DatabaseClient dbClient = null;
private static Spanner spanner = null;
// Use custom ports to avoid collision with other emulators.
private static final int EMULATOR_PORT = 29010;
private static final String EMULATOR_HOST = "localhost:" + EMULATOR_PORT;
private static final int EMULATOR_REST_PORT = 29020;
@BeforeClass
public static void startEmulator() throws IOException, InterruptedException, ExecutionException {
assertThat(emulatorProcess).isNull();
emulatorProcess =
new ProcessBuilder()
.inheritIO()
.command(
"gcloud",
"emulators",
"spanner",
"start",
"--host-port=" + EMULATOR_HOST,
"--rest-port=" + EMULATOR_REST_PORT)
.start();
// check for startup failure
if (emulatorProcess.waitFor(5, TimeUnit.SECONDS)) {
assertWithMessage("Emulator failed to start").fail();
emulatorProcess = null;
}
System.err.println("Spanner Emulator started");
spanner =
SpannerOptions.newBuilder()
.setEmulatorHost(EMULATOR_HOST)
.setProjectId("dummy-project-id")
.build()
.getService();
InstanceConfig config =
spanner.getInstanceAdminClient().listInstanceConfigs().iterateAll().iterator().next();
InstanceId instanceId = InstanceId.of("dummy-project-id", "test");
System.err.println("Creating instance");
Instance instance =
spanner
.getInstanceAdminClient()
.createInstance(
InstanceInfo.newBuilder(instanceId)
.setInstanceConfigId(config.getId())
.setNodeCount(1)
.build())
.get();
System.err.println("Creating database");
Database db =
spanner
.getDatabaseAdminClient()
.createDatabase(
"test",
"test",
ImmutableList.of(
"CREATE TABLE test1 (key INT64, value STRING(MAX)) PRIMARY KEY(key)",
"CREATE TABLE test2 (key INT64, value STRING(MAX)) PRIMARY KEY(key)"))
.get();
dbClient = spanner.getDatabaseClient(db.getId());
System.err.println("Emulator ready");
}
@AfterClass
public static void endEmulator() throws InterruptedException, IOException {
spanner.close();
spanner = null;
dbClient = null;
if (emulatorProcess != null && emulatorProcess.isAlive()) {
System.err.println("Stopping Spanner Emulator");
emulatorProcess.destroy();
if (!emulatorProcess.waitFor(5, TimeUnit.SECONDS)) {
emulatorProcess.destroyForcibly();
}
if (!emulatorProcess.waitFor(5, TimeUnit.SECONDS)) {
assertWithMessage("Emulator could not be killed").fail();
}
}
emulatorProcess = null;
// Cleanup any leftover emulator processes
System.err.println("Stopping Spanner Emulator subprocesses");
new ProcessBuilder()
.inheritIO()
.command(
"bash",
"-c",
"kill $(ps -xo pid,command | grep 'spanner_emulator.*"
+ EMULATOR_PORT
+ "' | cut -f1 \"-d \" )")
.start()
.waitFor();
System.err.println("Emulator stopped");
}
Hey nielm@, could u please contribute this sample to the https://github.com/cloudspannerecosystem/emulator-samples repo ?
Hey nielm@, could u please contribute this sample to the https://github.com/cloudspannerecosystem/emulator-samples repo ?
I cleaned up the code a bit, and made it more generic so that it could be used as a JUnit @ClassRule in unit tests. https://github.com/cloudspannerecosystem/emulator-samples/pull/6
If emulator is run interactively via gcloud, sending CTRL-C in the terminal shuts down the emulator and subprocesses cleanly.
However, killing the gcloud process with -INT -HUP or -TERM does not do this clean shutdown, leaving emulator_main and gateway_main processes still running, which makes it difficult to programmatically start up and shut down the emulator for testing.
These processes must be found and killed manually.