Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
## \[Unreleased\]

### Added
- A new optional field `label` was added to the types `AppWithOptions`, `ConductorOptions`, `CreateConductorOptions`, and to the function `addConductor`. It allows overriding the default label which will prefix log messages of that Conductor instance.

### Removed
### Changed
- Log messages from Conductors that were created with `addPlayers` functions are now prefixed by `Tryorama - Player #` where "#" is the Player index. This makes it easier to distinguish log messages from different conductors. ([#302](https://github.com/holochain/tryorama/pull/302))

### Fixed
- When creating conductors with `Scenario#addPlayers`, `Scenario#installAppsForPlayers`, `Scenario#installSameAppForPlayers`, `Scenario#addPlayersWithApps`, and `Scenario#addPlayersWithSameApp` each conductor is created sequentially and waits for startup. This is a workaround to avoid connection failures which can cause test failures. ([#303](https://github.com/holochain/tryorama/pull/303))
- Fixed flaky behavior in recognizing conductor startup success. ([#303](https://github.com/holochain/tryorama/pull/303))
Expand Down
11 changes: 11 additions & 0 deletions docs/tryorama.appwithoptions.label.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@holochain/tryorama](./tryorama.md) &gt; [AppWithOptions](./tryorama.appwithoptions.md) &gt; [label](./tryorama.appwithoptions.label.md)

## AppWithOptions.label property

**Signature:**

```typescript
label?: string;
```
19 changes: 19 additions & 0 deletions docs/tryorama.appwithoptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ AppBundleSource
</td><td>


</td></tr>
<tr><td>

[label?](./tryorama.appwithoptions.label.md)


</td><td>


</td><td>

string


</td><td>

_(Optional)_


</td></tr>
<tr><td>

Expand Down
13 changes: 13 additions & 0 deletions docs/tryorama.conductoroptions.label.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@holochain/tryorama](./tryorama.md) &gt; [ConductorOptions](./tryorama.conductoroptions.md) &gt; [label](./tryorama.conductoroptions.label.md)

## ConductorOptions.label property

Label to identify this conductor in logs.

**Signature:**

```typescript
label?: string;
```
19 changes: 19 additions & 0 deletions docs/tryorama.conductoroptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ URL
_(Optional)_ A bootstrap server URL for peers to discover each other.


</td></tr>
<tr><td>

[label?](./tryorama.conductoroptions.label.md)


</td><td>


</td><td>

string


</td><td>

_(Optional)_ Label to identify this conductor in logs.


</td></tr>
<tr><td>

Expand Down
2 changes: 1 addition & 1 deletion docs/tryorama.createconductoroptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Options for using the conductor factory.
**Signature:**

```typescript
export type CreateConductorOptions = Pick<ConductorOptions, "bootstrapServerUrl" | "timeout">;
export type CreateConductorOptions = Pick<ConductorOptions, "bootstrapServerUrl" | "timeout" | "label">;
```
**References:** [ConductorOptions](./tryorama.conductoroptions.md)

18 changes: 17 additions & 1 deletion docs/tryorama.scenario.addconductor.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Create and add a conductor to the scenario.
**Signature:**

```typescript
addConductor(networkConfig?: NetworkConfig): Promise<Conductor>;
addConductor(networkConfig?: NetworkConfig, label?: string): Promise<Conductor>;
```

## Parameters
Expand Down Expand Up @@ -45,6 +45,22 @@ networkConfig
_(Optional)_


</td></tr>
<tr><td>

label


</td><td>

string


</td><td>

_(Optional)_


</td></tr>
</tbody></table>
**Returns:**
Expand Down
2 changes: 1 addition & 1 deletion docs/tryorama.scenario.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ Description
</th></tr></thead>
<tbody><tr><td>

[addConductor(networkConfig)](./tryorama.scenario.addconductor.md)
[addConductor(networkConfig, label)](./tryorama.scenario.addconductor.md)


</td><td>
Expand Down
51 changes: 31 additions & 20 deletions ts/src/conductor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import { _ALLOWED_ORIGIN } from "./conductor-helpers.js";
import { makeLogger } from "./logger.js";
import { AppWithOptions } from "./scenario.js";
import { AgentsAppsOptions } from "./types.js";
import { Logger } from "winston";

const logger = makeLogger();
const defaultLogger = makeLogger();

/**
* @public
Expand Down Expand Up @@ -52,6 +53,11 @@ export interface ConductorOptions {
* Timeout for requests to Admin and App API.
*/
timeout?: number;

/**
* Label to identify this conductor in logs.
*/
label?: string;
}

/**
Expand Down Expand Up @@ -162,7 +168,7 @@ interface ConductorConfigYaml {
*/
export type CreateConductorOptions = Pick<
ConductorOptions,
"bootstrapServerUrl" | "timeout"
"bootstrapServerUrl" | "timeout" | "label"
>;

/**
Expand All @@ -180,6 +186,7 @@ export const createConductor = async (
const createConductorOptions: CreateConductorOptions = pick(options, [
"bootstrapServerUrl",
"timeout",
"label",
]);
const conductor = await Conductor.create(
signalingServerUrl,
Expand Down Expand Up @@ -211,14 +218,16 @@ export class Conductor {
private adminApiUrl: URL;
private _adminWs: AdminWebsocket | undefined;
private _appWs: AppWebsocket | undefined;
private _logger: Logger;
private readonly timeout: number;

private constructor(timeout?: number) {
private constructor(label?: string, timeout?: number) {
this.conductorProcess = undefined;
this.conductorDir = undefined;
this.adminApiUrl = new URL(HOST_URL.href);
this._adminWs = undefined;
this._appWs = undefined;
this._logger = makeLogger(label);
this.timeout = timeout ?? DEFAULT_TIMEOUT;
}

Expand All @@ -238,15 +247,15 @@ export class Conductor {
}
args.push("webrtc");
args.push(signalingServerUrl.href);
logger.debug("spawning hc sandbox with args:", args);
defaultLogger.debug("spawning hc sandbox with args:", args);
const createConductorProcess = spawn("hc", args);
createConductorProcess.stdin.write(LAIR_PASSWORD);
createConductorProcess.stdin.end();

const conductor = new Conductor(options?.timeout);
const conductor = new Conductor(options?.label, options?.timeout);
return new Promise<Conductor>((resolve, reject) => {
createConductorProcess.stdout.on("data", (data: Buffer) => {
logger.debug(`creating conductor config\n${data.toString()}`);
defaultLogger.debug(`creating conductor config\n${data.toString()}`);
const tmpDirMatches = [
...data.toString().matchAll(/DataRootPath\("(.*?)"\)/g),
];
Expand All @@ -258,7 +267,7 @@ export class Conductor {
resolve(conductor);
});
createConductorProcess.stderr.on("data", (err) => {
logger.error(`error when creating conductor config: ${err}\n`);
defaultLogger.error(`error when creating conductor config: ${err}\n`);
reject(err);
});
});
Expand Down Expand Up @@ -298,8 +307,8 @@ export class Conductor {
},
};
const yamlDump = yaml.dump(conductorConfigYaml);
logger.debug("Updated conductor config:");
logger.debug(yamlDump);
this._logger.debug("Updated conductor config:");
this._logger.debug(yamlDump);
writeFileSync(`${this.conductorDir}/${CONDUCTOR_CONFIG}`, yamlDump);
}

Expand All @@ -313,7 +322,9 @@ export class Conductor {
"error starting conductor: conductor has not been created",
);
if (this.conductorProcess) {
logger.error("error starting conductor: conductor is already running\n");
this._logger.error(
"error starting conductor: conductor is already running\n",
);
return;
}

Expand All @@ -330,7 +341,7 @@ export class Conductor {
let adminPortLogged = false;
const adminPortPromise = new Promise<string>((resolve) => {
runConductorProcess.stdout.on("data", (data: Buffer) => {
logger.info(data.toString());
this._logger.info(data.toString());

if (!adminPortLogged) {
// Once we have an admin port, the conductor is launched and usable.
Expand All @@ -344,7 +355,7 @@ export class Conductor {
});

runConductorProcess.stderr.on("data", (data: Buffer) => {
logger.error(data.toString());
this._logger.error(data.toString());
});
});
this.adminApiUrl.port = await adminPortPromise;
Expand All @@ -358,11 +369,11 @@ export class Conductor {
*/
async shutDown() {
if (!this.conductorProcess) {
logger.info("shut down conductor: conductor is not running");
this._logger.info("shut down conductor: conductor is not running");
return null;
}

logger.debug("closing admin and app web sockets\n");
this._logger.debug("closing admin and app web sockets\n");
if (this._adminWs) {
await this._adminWs.client.close();
this._adminWs = undefined;
Expand All @@ -372,7 +383,7 @@ export class Conductor {
this._appWs = undefined;
}

logger.debug("shutting down conductor\n");
this._logger.debug("shutting down conductor\n");
return new Promise<number | null>((resolve) => {
assert(this.conductorProcess);
// Kill process after timeout if terminating didn't succeed.
Expand All @@ -397,7 +408,7 @@ export class Conductor {
wsClientOptions: { origin: _ALLOWED_ORIGIN },
defaultTimeout: this.timeout,
});
logger.debug(`connected to Admin API @ ${this.adminApiUrl.href}\n`);
this._logger.debug(`connected to Admin API @ ${this.adminApiUrl.href}\n`);
}

/**
Expand All @@ -411,7 +422,7 @@ export class Conductor {
port: await getPort({ port: portNumbers(30000, 40000) }),
allowed_origins: _ALLOWED_ORIGIN,
};
logger.debug(`attaching App API to port ${request.port}\n`);
this._logger.debug(`attaching App API to port ${request.port}\n`);
const { port } = await this.adminWs().attachAppInterface(request);
return port;
}
Expand All @@ -424,7 +435,7 @@ export class Conductor {
* @returns An app websocket.
*/
async connectAppWs(token: AppAuthenticationToken, port: number) {
logger.debug(`connecting App WebSocket to port ${port}\n`);
this._logger.debug(`connecting App WebSocket to port ${port}\n`);
const appApiUrl = new URL(this.adminApiUrl.href);
appApiUrl.port = port.toString();
const appWs = await AppWebsocket.connect({
Expand Down Expand Up @@ -502,7 +513,7 @@ export class Conductor {
installed_app_id,
network_seed,
};
logger.debug(
this._logger.debug(
`installing app with id ${installed_app_id} for agent ${encodeHashToBase64(
agent_key,
)}`,
Expand Down Expand Up @@ -531,7 +542,7 @@ export const cleanAllConductors = async () => {
const conductorProcess = spawn("hc", ["sandbox", "clean"]);
return new Promise<void>((resolve) => {
conductorProcess.stdout.once("end", () => {
logger.debug("sandbox conductors cleaned\n");
defaultLogger.debug("sandbox conductors cleaned\n");
resolve();
});
});
Expand Down
16 changes: 13 additions & 3 deletions ts/src/scenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface PlayerApp extends Player, AgentApp {
export interface AppWithOptions {
appBundleSource: AppBundleSource;
options?: AppOptions;
label?: string;
}

/**
Expand Down Expand Up @@ -101,13 +102,14 @@ export class Scenario {
*
* @returns The newly added conductor instance.
*/
async addConductor(networkConfig?: NetworkConfig) {
async addConductor(networkConfig?: NetworkConfig, label?: string) {
await this.ensureLocalServices();
assert(this.serviceProcess);
assert(this.signalingServerUrl);
const defaultCreateOptions = {
timeout: this.timeout,
bootstrapServerUrl: this.bootstrapServerUrl,
label,
};
const createOptions =
networkConfig === undefined
Expand Down Expand Up @@ -143,8 +145,11 @@ export class Scenario {
): Promise<Player[]> {
await this.ensureLocalServices();
return Promise.all(
new Array(amount).fill(0).map(async () => {
const conductor = await this.addConductor(networkConfig);
new Array(amount).fill(0).map(async (_, i) => {
const conductor = await this.addConductor(
networkConfig,
this.generatePlayerLabel(i),
);
const agentPubKey = await conductor.adminWs().generateAgentPubKey();
return { conductor, agentPubKey };
}),
Expand Down Expand Up @@ -260,6 +265,7 @@ export class Scenario {
await this.ensureLocalServices();
const conductor = await this.addConductor(
appWithOptions.options?.networkConfig,
appWithOptions.label,
);
appWithOptions.options = {
...appWithOptions.options,
Expand Down Expand Up @@ -380,6 +386,10 @@ export class Scenario {
} = await runLocalServices());
}
}

private generatePlayerLabel(index: number): string {
return `Player ${this.conductors.length + index}`;
}
}

/**
Expand Down
Loading