Skip to content

Commit

Permalink
Download logs (#222)
Browse files Browse the repository at this point in the history
* Download logs

* All tests

* Fix typing check

* Do not log

* Align types

* No ansi in my log files please

* Unwanted space in env
  • Loading branch information
ThetaSinner authored Jul 17, 2024
1 parent 8e2cd6b commit 3601ebc
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 1 deletion.
25 changes: 25 additions & 0 deletions crates/trycp_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ pub enum Request {
#[serde(with = "serde_bytes")]
message: Vec<u8>,
},

/// Request the logs for a player's conductor and keystore.
DownloadLogs {
/// The conductor id.
id: String,
},
}

/// Message response types.
Expand Down Expand Up @@ -153,3 +159,22 @@ pub enum MessageToClient {
response: std::result::Result<MessageResponse, String>,
},
}

/// Messages returned directly by the TryCp server, rather than relayed from Holochain
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case", tag = "type", content = "data")]
pub enum TryCpServerResponse {
/// See [DownloadLogsResponse].
DownloadLogs(DownloadLogsResponse),
}

/// The successful response type for a [Request::DownloadLogs] request.
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct DownloadLogsResponse {
/// The lair keystore stderr log.
pub lair_stderr: Vec<u8>,
/// The holochain conductor stdout log.
pub conductor_stdout: Vec<u8>,
/// The holochain conductor stderr log.
pub conductor_stderr: Vec<u8>,
}
55 changes: 55 additions & 0 deletions crates/trycp_server/src/download_logs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use crate::{
get_player_dir, player_config_exists, CONDUCTOR_STDERR_LOG_FILENAME,
CONDUCTOR_STDOUT_LOG_FILENAME, LAIR_STDERR_LOG_FILENAME,
};
use snafu::{ResultExt, Snafu};
use trycp_api::{DownloadLogsResponse, MessageResponse, TryCpServerResponse};

#[derive(Debug, Snafu)]
pub(crate) enum DownloadLogsError {
#[snafu(display("No player with this ID is configured {}", id))]
PlayerNotConfigured { id: String },
#[snafu(display("Could not read lair stderr log for player with ID {}: {}", id, source))]
LairStdErr { id: String, source: std::io::Error },
#[snafu(display(
"Could not read holochain stdout log for player with ID {}: {}",
id,
source
))]
HolochainStdout { id: String, source: std::io::Error },
#[snafu(display(
"Could not read holochain stderr log for player with ID {}: {}",
id,
source
))]
HolochainStderr { id: String, source: std::io::Error },
#[snafu(display("Could not serialize response: {}", source))]
SerializeResponse { source: rmp_serde::encode::Error },
}

pub(crate) fn download_logs(id: String) -> Result<MessageResponse, DownloadLogsError> {
if !player_config_exists(&id) {
return Err(DownloadLogsError::PlayerNotConfigured { id });
}

let player_dir = get_player_dir(&id);

let lair_stderr = player_dir.join(LAIR_STDERR_LOG_FILENAME);
let lair_stderr = std::fs::read(&lair_stderr).context(LairStdErr { id: id.clone() })?;

let conductor_stdout = player_dir.join(CONDUCTOR_STDOUT_LOG_FILENAME);
let conductor_stdout =
std::fs::read(&conductor_stdout).context(HolochainStdout { id: id.clone() })?;

let conductor_stderr = player_dir.join(CONDUCTOR_STDERR_LOG_FILENAME);
let conductor_stderr = std::fs::read(&conductor_stderr).context(HolochainStderr { id })?;

Ok(MessageResponse::Bytes(
rmp_serde::to_vec_named(&TryCpServerResponse::DownloadLogs(DownloadLogsResponse {
lair_stderr,
conductor_stdout,
conductor_stderr,
}))
.context(SerializeResponse)?,
))
}
9 changes: 9 additions & 0 deletions crates/trycp_server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod admin_call;
mod app_interface;
mod configure_player;
mod download_dna;
mod download_logs;
mod reset;
mod save_dna;
mod shutdown;
Expand Down Expand Up @@ -252,6 +253,14 @@ async fn ws_message(
Err(e) => serialize_resp(request_id, Err::<(), _>(e.to_string())),
}
}
Request::DownloadLogs { id } => spawn_blocking(move || {
serialize_resp(
request_id,
download_logs::download_logs(id).map_err(|e| e.to_string()),
)
})
.await
.unwrap(),
};

Ok(Some(Message::Binary(response)))
Expand Down
4 changes: 4 additions & 0 deletions crates/trycp_server/src/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ pub fn startup(id: String, log_level: Option<String>) -> Result<(), Error> {
.arg("--piped")
.arg("-c")
.arg(CONDUCTOR_CONFIG_FILENAME)
// Disable ANSI color codes in Holochain output, which should be set any time the output
// is being written to a file.
// See https://docs.rs/tracing-subscriber/0.3.18/tracing_subscriber/fmt/struct.Layer.html#method.with_ansi
.env("NO_COLOR", "1")
.env("RUST_BACKTRACE", "full")
.env("RUST_LOG", rust_log)
.stdin(Stdio::piped())
Expand Down
13 changes: 13 additions & 0 deletions ts/src/trycp/conductor/conductor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { makeLogger } from "../../logger.js";
import { AgentsAppsOptions, AppOptions, IConductor } from "../../types.js";
import {
AdminApiResponseAppAuthenticationTokenIssued,
DownloadLogsResponse,
TryCpClient,
TryCpConductorLogLevel,
} from "../index.js";
Expand Down Expand Up @@ -312,6 +313,18 @@ export class TryCpConductor implements IConductor {
return response;
}

async downloadLogs() {
const response = await this.tryCpClient.call({
type: "download_logs",
id: this.id,
});
assert(response !== TRYCP_SUCCESS_RESPONSE);
assert(typeof response === "object");
assert("type" in response);
assert(response.type === "download_logs");
return response.data;
}

/**
* Attach a signal handler.
*
Expand Down
22 changes: 21 additions & 1 deletion ts/src/trycp/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ export type TryCpRequest =
| RequestDisconnectAppInterface
| RequestCallAppInterface
| RequestCallAppInterfaceEncoded
| RequestCallAdminInterface;
| RequestCallAdminInterface
| RequestDownloadLogs;

/**
* Request to download a DNA from a URL.
Expand Down Expand Up @@ -249,8 +250,22 @@ export type TryCpResponseErrorValue = string | Error;
export type TryCpApiResponse =
| AdminApiResponse
| AppApiResponse
| TryCpControlResponse
| ApiErrorResponse;

export type TryCpControlResponse = DownloadLogsResponseType;

export interface DownloadLogsResponseType {
type: "download_logs";
data: DownloadLogsResponse;
}

export interface DownloadLogsResponse {
lair_stderr: Uint8Array;
conductor_stdout: Uint8Array;
conductor_stderr: Uint8Array;
}

/**
* Error response from the Admin or App API.
*
Expand Down Expand Up @@ -479,6 +494,11 @@ export interface RequestCallAdminInterface {
message: RequestAdminInterfaceMessage;
}

export interface RequestDownloadLogs {
type: "download_logs";
id: ConductorId;
}

/**
* The types of all possible calls to the Admin API.
*
Expand Down
17 changes: 17 additions & 0 deletions ts/test/trycp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,20 @@ test("TryCP Server - App API - get app info", async (t) => {
await conductor.disconnectClient();
await localTryCpServer.stop();
});

test("TryCP Client - download logs", async (t) => {
const localTryCpServer = await TryCpServer.start();
const tryCpClient = await createTryCpClient();
const conductor = await createTryCpConductor(tryCpClient);
const logs = await conductor.downloadLogs();

t.true(logs.lair_stderr.length === 0, "lair stderr logs are empty");
t.true(
logs.conductor_stdout.length > 0,
"conductor stdout logs are not empty"
);
t.true(logs.conductor_stderr.length === 0, "conductor stderr logs are empty");

await conductor.shutDown();
await localTryCpServer.stop();
});

0 comments on commit 3601ebc

Please sign in to comment.