Skip to content

Commit

Permalink
Fix exiting in integrated-devnet (#132) [skip ci]
Browse files Browse the repository at this point in the history
Co-authored-by: Jorma <RedFox20@users.noreply.github.com>
Co-authored-by: RedFox <jorma@tempus.finance>
  • Loading branch information
3 people committed Jul 7, 2022
1 parent 913d616 commit 2c986d0
Show file tree
Hide file tree
Showing 14 changed files with 186 additions and 13 deletions.
6 changes: 3 additions & 3 deletions src/devnet/docker-devnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ export class DockerDevnet extends IntegratedDevnet {
protected async spawnChildProcess(): Promise<ChildProcess> {
await this.pullImage();

console.log(`Starting the "${CONTAINER_NAME}" Docker container`);
const formattedImage = `${this.image.repository}:${this.image.tag}`;
console.log(`Starting the "${CONTAINER_NAME}" Docker container (${formattedImage})`);
const args = [
"run",
"--detach",
"--rm",
"--name",
CONTAINER_NAME,
"-p",
`${this.host}:${this.port}:${DEVNET_DOCKER_INTERNAL_PORT}`,
`${this.image.repository}:${this.image.tag}`
formattedImage
].concat(this.args || []);
return spawn("docker", args);
}
Expand Down
67 changes: 57 additions & 10 deletions src/devnet/integrated-devnet.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import axios from "axios";
import { ChildProcess } from "child_process";
import { HardhatPluginError } from "hardhat/plugins";
import { PLUGIN_NAME } from "../constants";

function sleep(amountMillis: number): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, amountMillis);
});
}

const DEVNET_ALIVE_URL = "is_alive";

export abstract class IntegratedDevnet {
protected childProcess: ChildProcess;
private lastError: string = null;
private connected = false;

constructor(protected host: string, protected port: string) {
IntegratedDevnet.cleanupFns.push(this.cleanup.bind(this));
Expand All @@ -27,28 +29,62 @@ export abstract class IntegratedDevnet {
protected abstract cleanup(): void;

public async start(): Promise<void> {
if (await this.isServerAlive()) {
const msg = `Cannot spawn integrated-devnet: ${this.host}:${this.port} already occupied.`;
throw new HardhatPluginError(PLUGIN_NAME, msg);
}

this.childProcess = await this.spawnChildProcess();

// capture the most recent message from stderr
this.childProcess.stderr.on("data", (chunk) => {
this.lastError = chunk.toString();
});

return new Promise((resolve, reject) => {
// called on successful start of the child process
this.childProcess.on("spawn", async () => {
const startTime = new Date().getTime();
const maxWaitMillis = 60_000;
const oneSleepMillis = 500;
const maxIterations = maxWaitMillis / oneSleepMillis;
for (let i = 0; i < maxIterations; ++i) {
await sleep(oneSleepMillis);
try {
await axios.get(`http://${this.host}:${this.port}/${DEVNET_ALIVE_URL}`);

// keep checking until process has failed/exited
while (this.childProcess) {
const elapsedMillis = new Date().getTime() - startTime;
if (elapsedMillis >= maxWaitMillis) {
const msg = "integrated-devnet connection timed out!";
reject(new HardhatPluginError(PLUGIN_NAME, msg));
break;
} else if (await this.isServerAlive()) {
this.connected = true;
resolve();
} catch (err: unknown) {
// cannot connect yet, devnet is not up
break;
} else {
await sleep(oneSleepMillis);
}
}
reject(`Could not connect to integrated-devnet in ${maxWaitMillis} ms!`);
});

// this only happens if childProcess completely fails to start
this.childProcess.on("error", (error) => {
this.childProcess = null;
reject(error);
});

// handle unexpected close of process
this.childProcess.on("close", (code) => {
const isAbnormalExit = this.childProcess != null;
this.childProcess = null;
if (code !== 0 && isAbnormalExit) {
if (this.connected) {
const msg = `integrated-devnet exited with code=${code} while processing transactions`;
throw new HardhatPluginError(PLUGIN_NAME, msg);
} else {
const msg = `integrated-devnet connect exited with code=${code}:\n${this.lastError}`;
reject(new HardhatPluginError(PLUGIN_NAME, msg));
}
}
});
});
}

Expand All @@ -58,5 +94,16 @@ export abstract class IntegratedDevnet {
}

this.cleanup();
this.childProcess = null;
}

private async isServerAlive() {
try {
await axios.get(`http://${this.host}:${this.port}/is_alive`);
return true;
} catch (err: unknown) {
// cannot connect, so address is not occupied
return false;
}
}
}
17 changes: 17 additions & 0 deletions test/integrated-devnet-tests/with-docker-address-occupied/check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

set -e

trap 'kill $(jobs -p)' EXIT

source ../scripts/check-devnet-is-not-running.sh

check_devnet_is_not_running

# run devnet which will cause integrated-devnet to fail
starknet-devnet --host 127.0.0.1 --port 5050 --accounts 0 &

npx hardhat starknet-compile contracts/contract.cairo

npx hardhat test --no-compile test/integrated-devnet.test.ts 2>&1 |
../scripts/assert-contains.py "Cannot spawn integrated-devnet: 127.0.0.1:5050 already occupied."
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import "../dist/src/index.js";

module.exports = {
starknet: {
network: process.env.NETWORK
},
networks: {
integratedDevnet: {
dockerizedVersion: process.env.STARKNET_DEVNET,
url: "http://127.0.0.1:5050"
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "../../network.schema",
"integrated-devnet": true
}
12 changes: 12 additions & 0 deletions test/integrated-devnet-tests/with-docker-wrong-cli/check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

set -e

source ../scripts/check-devnet-is-not-running.sh

check_devnet_is_not_running

npx hardhat starknet-compile contracts/contract.cairo

npx hardhat test --no-compile test/integrated-devnet.test.ts 2>&1 |
../scripts/assert-contains.py "starknet-devnet: error: argument --accounts: invalid int value: 'invalid_value'"
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import "../dist/src/index.js";

module.exports = {
starknet: {
network: process.env.NETWORK
},
networks: {
integratedDevnet: {
dockerizedVersion: process.env.STARKNET_DEVNET,
url: "http://127.0.0.1:5050",
args: ["--accounts", "invalid_value"]
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "../../network.schema",
"integrated-devnet": true
}
15 changes: 15 additions & 0 deletions test/integrated-devnet-tests/with-venv-address-occupied/check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

set -e

trap 'kill $(jobs -p)' EXIT

source ../scripts/check-devnet-is-not-running.sh

check_devnet_is_not_running
starknet-devnet --host 127.0.0.1 --port 5050 --accounts 0 &

npx hardhat starknet-compile contracts/contract.cairo

npx hardhat test --no-compile test/integrated-devnet.test.ts 2>&1 |
../scripts/assert-contains.py "Cannot spawn integrated-devnet: 127.0.0.1:5050 already occupied."
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import "../dist/src/index.js";

module.exports = {
starknet: {
network: process.env.NETWORK
},
networks: {
integratedDevnet: {
venv: process.env.STARKNET_DEVNET_PATH,
url: "http://127.0.0.1:5050"
}
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "../../network.schema",
"integrated-devnet": true
}
12 changes: 12 additions & 0 deletions test/integrated-devnet-tests/with-venv-wrong-cli/check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

set -e

source ../scripts/check-devnet-is-not-running.sh

check_devnet_is_not_running

npx hardhat starknet-compile contracts/contract.cairo

npx hardhat test --no-compile test/integrated-devnet.test.ts 2>&1 |
../scripts/assert-contains.py "starknet-devnet: error: argument --accounts: invalid int value: 'invalid_value'"
14 changes: 14 additions & 0 deletions test/integrated-devnet-tests/with-venv-wrong-cli/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import "../dist/src/index.js";

module.exports = {
starknet: {
network: process.env.NETWORK
},
networks: {
integratedDevnet: {
venv: process.env.STARKNET_DEVNET_PATH,
url: "http://127.0.0.1:5050",
args: ["--accounts", "invalid_value"]
}
}
};
4 changes: 4 additions & 0 deletions test/integrated-devnet-tests/with-venv-wrong-cli/network.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "../../network.schema",
"integrated-devnet": true
}

0 comments on commit 2c986d0

Please sign in to comment.