diff --git a/Cargo.lock b/Cargo.lock index bdb3a2acc..89a7f71a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,7 +32,7 @@ dependencies = [ [[package]] name = "alerter" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "axum 0.7.5", @@ -1948,7 +1948,7 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "logger" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "monitor_client", @@ -2017,7 +2017,7 @@ dependencies = [ [[package]] name = "migrator" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "chrono", @@ -2140,7 +2140,7 @@ dependencies = [ [[package]] name = "monitor_cli" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "clap", @@ -2159,7 +2159,7 @@ dependencies = [ [[package]] name = "monitor_client" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "async_timing_util", @@ -2191,7 +2191,7 @@ dependencies = [ [[package]] name = "monitor_core" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "async_timing_util", @@ -2237,7 +2237,7 @@ dependencies = [ [[package]] name = "monitor_periphery" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "async_timing_util", @@ -2569,7 +2569,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "periphery_client" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "monitor_client", @@ -3547,7 +3547,7 @@ dependencies = [ [[package]] name = "tests" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "dotenv", @@ -4090,7 +4090,7 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "update_logger" -version = "1.4.1" +version = "1.5.0" dependencies = [ "anyhow", "logger", diff --git a/Cargo.toml b/Cargo.toml index 45c810386..0ed554153 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["bin/*", "lib/*", "client/core/rs", "client/periphery/rs"] [workspace.package] -version = "1.4.1" +version = "1.5.0" edition = "2021" authors = ["mbecker20 "] license = "GPL-3.0-or-later" diff --git a/bin/core/src/api/execute/server_template.rs b/bin/core/src/api/execute/server_template.rs index 0e89acd93..c18f154c9 100644 --- a/bin/core/src/api/execute/server_template.rs +++ b/bin/core/src/api/execute/server_template.rs @@ -129,6 +129,7 @@ impl Resolve for State { "create server", format!("created server {} ({})", server.name, server.id), ); + update.other_data = server.id; } Err(e) => { update.push_error_log( diff --git a/bin/core/src/api/read/update.rs b/bin/core/src/api/read/update.rs index a18a3642e..c8517ce8b 100644 --- a/bin/core/src/api/read/update.rs +++ b/bin/core/src/api/read/update.rs @@ -140,6 +140,7 @@ impl Resolve for State { target: u.target, status: u.status, version: u.version, + other_data: u.other_data, } }) .collect::>(); diff --git a/bin/core/src/helpers/update.rs b/bin/core/src/helpers/update.rs index 47a10bfa2..131b45c07 100644 --- a/bin/core/src/helpers/update.rs +++ b/bin/core/src/helpers/update.rs @@ -83,6 +83,7 @@ async fn update_list_item( target: update.target, status: update.status, version: update.version, + other_data: update.other_data, username, }; Ok(update) diff --git a/bin/migrator/src/legacy/v0/update.rs b/bin/migrator/src/legacy/v0/update.rs index 27b655413..d62915a52 100644 --- a/bin/migrator/src/legacy/v0/update.rs +++ b/bin/migrator/src/legacy/v0/update.rs @@ -48,6 +48,7 @@ impl TryFrom for monitor_client::entities::update::Update { .and_then(|ts| unix_from_monitor_ts(&ts).ok()), status: value.status.into(), version: value.version.map(|v| v.into()).unwrap_or_default(), + other_data: Default::default(), }; Ok(update) } diff --git a/client/core/rs/src/entities/update.rs b/client/core/rs/src/entities/update.rs index 43f498e42..b7475c729 100644 --- a/client/core/rs/src/entities/update.rs +++ b/client/core/rs/src/entities/update.rs @@ -14,6 +14,7 @@ use super::{ server::Server, server_template::ServerTemplate, Version, }; +/// Represents an action performed by Monitor. #[typeshare] #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[cfg_attr( @@ -34,23 +35,44 @@ pub struct Update { )] pub id: MongoId, + /// The operation performed #[cfg_attr(feature = "mongo", index)] pub operation: Operation, + /// The time the operation started #[cfg_attr(feature = "mongo", index)] pub start_ts: I64, + /// Whether the operation was successful #[cfg_attr(feature = "mongo", index)] pub success: bool, + /// The user id that triggered the update. + /// + /// Also can take these values for operations triggered automatically: + /// - `Procedure`: The operation was triggered as part of a procedure run + /// - `Github`: The operation was triggered by a github webhook + /// - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing. #[cfg_attr(feature = "mongo", index)] pub operator: String, + /// The target resource to which this update refers pub target: ResourceTarget, + /// Logs produced as the operation is performed pub logs: Vec, + /// The time the operation completed. pub end_ts: Option, + /// The status of the update + /// - `Queued` + /// - `InProgress` + /// - `Complete` pub status: UpdateStatus, + /// An optional version on the update, ie build version or deployed version. + #[serde(default, skip_serializing_if = "Version::is_none")] pub version: Version, + /// Some unstructured, operation specific data. Not for general usage. + #[serde(default, skip_serializing_if = "String::is_empty")] + pub other_data: String, } impl Update { @@ -81,29 +103,59 @@ impl Update { } } +/// Minimal representation of an action performed by Monitor. #[typeshare] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct UpdateListItem { + /// The id of the update pub id: String, + /// Which operation was run pub operation: Operation, + /// The starting time of the operation pub start_ts: I64, + /// Whether the operation was successful pub success: bool, + /// The username of the user performing update pub username: String, + /// The user id that triggered the update. + /// + /// Also can take these values for operations triggered automatically: + /// - `Procedure`: The operation was triggered as part of a procedure run + /// - `Github`: The operation was triggered by a github webhook + /// - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing. pub operator: String, + /// The target resource to which this update refers pub target: ResourceTarget, + /// The status of the update + /// - `Queued` + /// - `InProgress` + /// - `Complete` pub status: UpdateStatus, + /// An optional version on the update, ie build version or deployed version. + #[serde(default, skip_serializing_if = "Version::is_none")] pub version: Version, + /// Some unstructured, operation specific data. Not for general usage. + #[serde(default, skip_serializing_if = "String::is_empty")] + pub other_data: String, } +/// Represents the output of some command being run #[typeshare] #[derive(Serialize, Deserialize, Debug, Clone, Default)] pub struct Log { + /// A label for the log pub stage: String, + /// The command which was executed pub command: String, + /// The output of the command in the standard channel pub stdout: String, + /// The output of the command in the error channel pub stderr: String, + /// Whether the command run was successful pub success: bool, + /// The start time of the command execution pub start_ts: I64, + /// The end time of the command execution pub end_ts: I64, } @@ -133,6 +185,7 @@ impl Log { } } +/// Used to reference a specific resource across all resource types #[typeshare] #[derive( Serialize, @@ -249,6 +302,7 @@ impl From<&ServerTemplate> for ResourceTarget { } } +/// An update's status #[typeshare] #[derive( Serialize, @@ -264,8 +318,11 @@ impl From<&ServerTemplate> for ResourceTarget { Default, )] pub enum UpdateStatus { + /// The run is in the system but hasn't started yet Queued, + /// The run is currently running InProgress, + /// The run is complete #[default] Complete, } diff --git a/client/core/ts/src/types.ts b/client/core/ts/src/types.ts index dde44975c..ed14d7196 100644 --- a/client/core/ts/src/types.ts +++ b/client/core/ts/src/types.ts @@ -97,6 +97,7 @@ export enum SeverityLevel { Critical = "CRITICAL", } +/** Used to reference a specific resource across all resource types */ export type ResourceTarget = | { type: "System", id: string } | { type: "Build", id: string } @@ -569,13 +570,21 @@ export type DeploymentListItem = ResourceListItem; export type ListDeploymentsResponse = DeploymentListItem[]; +/** Represents the output of some command being run */ export interface Log { + /** A label for the log */ stage: string; + /** The command which was executed */ command: string; + /** The output of the command in the standard channel */ stdout: string; + /** The output of the command in the error channel */ stderr: string; + /** Whether the command run was successful */ success: boolean; + /** The start time of the command execution */ start_ts: I64; + /** The end time of the command execution */ end_ts: I64; } @@ -1181,12 +1190,17 @@ export enum Operation { DeleteVariable = "DeleteVariable", } +/** An update's status */ export enum UpdateStatus { + /** The run is in the system but hasn't started yet */ Queued = "Queued", + /** The run is currently running */ InProgress = "InProgress", + /** The run is complete */ Complete = "Complete", } +/** Represents an action performed by Monitor. */ export interface Update { /** * The Mongo ID of the update. @@ -1194,15 +1208,38 @@ export interface Update { * `{ "_id": { "$oid": "..." }, ...(rest of serialized Update) }` */ _id?: MongoId; + /** The operation performed */ operation: Operation; + /** The time the operation started */ start_ts: I64; + /** Whether the operation was successful */ success: boolean; + /** + * The user id that triggered the update. + * + * Also can take these values for operations triggered automatically: + * - `Procedure`: The operation was triggered as part of a procedure run + * - `Github`: The operation was triggered by a github webhook + * - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing. + */ operator: string; + /** The target resource to which this update refers */ target: ResourceTarget; + /** Logs produced as the operation is performed */ logs: Log[]; + /** The time the operation completed. */ end_ts?: I64; + /** + * The status of the update + * - `Queued` + * - `InProgress` + * - `Complete` + */ status: UpdateStatus; - version: Version; + /** An optional version on the update, ie build version or deployed version. */ + version?: Version; + /** Some unstructured, operation specific data. Not for general usage. */ + other_data?: string; } export type GetUpdateResponse = Update; @@ -2425,16 +2462,40 @@ export interface ListUpdates { page?: number; } +/** Minimal representation of an action performed by Monitor. */ export interface UpdateListItem { + /** The id of the update */ id: string; + /** Which operation was run */ operation: Operation; + /** The starting time of the operation */ start_ts: I64; + /** Whether the operation was successful */ success: boolean; + /** The username of the user performing update */ username: string; + /** + * The user id that triggered the update. + * + * Also can take these values for operations triggered automatically: + * - `Procedure`: The operation was triggered as part of a procedure run + * - `Github`: The operation was triggered by a github webhook + * - `Auto Redeploy`: The operation (always `Deploy`) was triggered by an attached build finishing. + */ operator: string; + /** The target resource to which this update refers */ target: ResourceTarget; + /** + * The status of the update + * - `Queued` + * - `InProgress` + * - `Complete` + */ status: UpdateStatus; - version: Version; + /** An optional version on the update, ie build version or deployed version. */ + version?: Version; + /** Some unstructured, operation specific data. Not for general usage. */ + other_data?: string; } /** Response for [ListUpdates]. */ diff --git a/frontend/src/components/resources/server-template/actions.tsx b/frontend/src/components/resources/server-template/actions.tsx index 8884a73bc..9e108cb17 100644 --- a/frontend/src/components/resources/server-template/actions.tsx +++ b/frontend/src/components/resources/server-template/actions.tsx @@ -13,14 +13,32 @@ import { DialogTitle, DialogTrigger, } from "@ui/dialog"; +import { useWebsocketMessages } from "@lib/socket"; +import { useNavigate } from "react-router-dom"; +import { Types } from "@monitor/client"; export const LaunchServer = ({ id }: { id: string }) => { + const nav = useNavigate(); const { toast } = useToast(); const [name, setName] = useState(""); const [open, setOpen] = useState(false); const { mutate } = useExecute("LaunchServer"); const template = useServerTemplate(id); + useWebsocketMessages("server-launch", (update) => { + if ( + update.target.type === "ServerTemplate" && + update.target.id === id && + update.operation === Types.Operation.LaunchServer && + update.status === Types.UpdateStatus.Complete && + update.success && + update.other_data + ) { + // The 'other_data' in this case will be created server id + nav(`/servers/${update.other_data}`); + } + }); + if (!template) return; const launch = () => { diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index bf8d93cde..f8864262e 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -57,8 +57,9 @@ function keep_line(line: string) { return true; } -export function version_is_none({ major, minor, patch }: Types.Version) { - return major === 0 && minor === 0 && patch === 0; +export function version_is_none(version?: Types.Version) { + if (!version) return true; + return version.major === 0 && version.minor === 0 && version.patch === 0; } export function resource_name(type: UsableResource, id: string) {