Skip to content

Commit

Permalink
feat: reconnect to service logs and link (#1742)
Browse files Browse the repository at this point in the history
## Description:
* automatically reconnect service logs when connection fails
* add link to get to cloud ui when running in cloud context

## Is this change user facing?
YES

## References (if applicable):
<!-- Add relevant Github Issues, Discord threads, or other helpful
information. -->
  • Loading branch information
adschwartz committed Nov 10, 2023
1 parent d43bcad commit 57468d0
Show file tree
Hide file tree
Showing 15 changed files with 76 additions and 29 deletions.
2 changes: 2 additions & 0 deletions api/rust/src/api_container_api.rs
Expand Up @@ -214,9 +214,11 @@ pub struct RunStarlarkPackageArgs {
#[prost(enumeration = "KurtosisFeatureFlag", repeated, tag = "11")]
pub experimental_features: ::prost::alloc::vec::Vec<i32>,
/// Defaults to empty
/// Deprecated: This value isn't used in the APIC anymore
#[prost(string, optional, tag = "12")]
pub cloud_instance_id: ::core::option::Option<::prost::alloc::string::String>,
/// Defaults to empty
/// Deprecated: This value isn't used in the APIC anymore
#[prost(string, optional, tag = "13")]
pub cloud_user_id: ::core::option::Option<::prost::alloc::string::String>,
/// Defaults to empty
Expand Down
Expand Up @@ -470,13 +470,15 @@ export declare class RunStarlarkPackageArgs extends Message<RunStarlarkPackageAr

/**
* Defaults to empty
* Deprecated: This value isn't used in the APIC anymore
*
* @generated from field: optional string cloud_instance_id = 12;
*/
cloudInstanceId?: string;

/**
* Defaults to empty
* Deprecated: This value isn't used in the APIC anymore
*
* @generated from field: optional string cloud_user_id = 13;
*/
Expand Down
11 changes: 9 additions & 2 deletions enclave-manager/web/src/client/constants.ts
@@ -1,12 +1,19 @@
import { isDefined } from "../utils";

export const KURTOSIS_EM_DEFAULT_HOST = process.env.REACT_APP_KURTOSIS_DEFAULT_HOST || "localhost";

export const KURTOSIS_DEFAULT_EM_API_PORT = isDefined(process.env.REACT_APP_KURTOSIS_DEFAULT_EM_API_PORT)
? parseInt(process.env.REACT_APP_KURTOSIS_DEFAULT_EM_API_PORT)
: 8081;

export const KURTOSIS_EM_API_DEFAULT_URL =
process.env.REACT_APP_KURTOSIS_DEFAULT_URL || `http://${KURTOSIS_EM_DEFAULT_HOST}:${KURTOSIS_DEFAULT_EM_API_PORT}`;

export const KURTOSIS_CLOUD_HOST = "cloud.kurtosis.com";
export const KURTOSIS_CLOUD_CONNECT_PAGE = "connect";
export const KURTOSIS_CLOUD_UI_URL = process.env.REACT_APP_KURTOSIS_CLOUD_UI_URL || `https://${KURTOSIS_CLOUD_HOST}`;

export const KURTOSIS_CLOUD_CONNECT_URL = `https://${KURTOSIS_CLOUD_HOST}/${KURTOSIS_CLOUD_CONNECT_PAGE}`;

export const KURTOSIS_PACKAGE_INDEXER_URL =
process.env.REACT_APP_KURTOSIS_PACKAGE_INDEXER_URL || "https://cloud.kurtosis.com:9770";
export const KURTOSIS_CLOUD_HOST = process.env.REACT_APP_KURTOSIS_CLOUD_UI_URL || "https://cloud.kurtosis.com";
process.env.REACT_APP_KURTOSIS_PACKAGE_INDEXER_URL || `https://${KURTOSIS_CLOUD_HOST}:9770`;
@@ -1,11 +1,11 @@
import { createPromiseClient } from "@connectrpc/connect";
import { createConnectTransport } from "@connectrpc/connect-web";
import { KurtosisEnclaveManagerServer } from "enclave-manager-sdk/build/kurtosis_enclave_manager_api_connect";
import { KURTOSIS_CLOUD_HOST, KURTOSIS_DEFAULT_EM_API_PORT } from "../constants";
import { KURTOSIS_CLOUD_UI_URL, KURTOSIS_DEFAULT_EM_API_PORT } from "../constants";
import { KurtosisClient } from "./KurtosisClient";

function constructGatewayURL(remoteHost: string): string {
return `${KURTOSIS_CLOUD_HOST}/gateway/ips/${remoteHost}/ports/${KURTOSIS_DEFAULT_EM_API_PORT}`;
return `${KURTOSIS_CLOUD_UI_URL}/gateway/ips/${remoteHost}/ports/${KURTOSIS_DEFAULT_EM_API_PORT}`;
}

export class AuthenticatedKurtosisClient extends KurtosisClient {
Expand Down
Expand Up @@ -26,6 +26,7 @@ import {
import { EnclaveFullInfo } from "../../emui/enclaves/types";
import { assertDefined, asyncResult, isDefined } from "../../utils";
import { RemoveFunctions } from "../../utils/types";
import { KURTOSIS_CLOUD_HOST } from "../constants";

export abstract class KurtosisClient {
protected readonly client: PromiseClient<typeof KurtosisEnclaveManagerServer>;
Expand Down Expand Up @@ -54,6 +55,10 @@ export abstract class KurtosisClient {
console.log("baseApplicationUrl", this.baseApplicationUrl);
}

isRunningInCloud() {
return this.cloudUrl.host.toLowerCase().includes(KURTOSIS_CLOUD_HOST);
}

abstract getHeaderOptions(): { headers?: Headers };

getCloudBasePathUrl() {
Expand Down
7 changes: 5 additions & 2 deletions enclave-manager/web/src/components/Navigation.tsx
@@ -1,8 +1,11 @@
import { Flex, IconButton, IconButtonProps, Image, Tooltip } from "@chakra-ui/react";
import { PropsWithChildren } from "react";
import { NavbarProps } from "../emui/Navbar";

export const Navigation = ({ baseApplicationUrl, children }: PropsWithChildren & NavbarProps) => {
export type NavigationProps = {
baseApplicationUrl: URL;
};

export const Navigation = ({ baseApplicationUrl, children }: PropsWithChildren & NavigationProps) => {
return (
<Flex
as={"nav"}
Expand Down
9 changes: 9 additions & 0 deletions enclave-manager/web/src/emui/Navbar.tsx
@@ -1,5 +1,8 @@
import { FiHome } from "react-icons/fi";
import { PiLinkSimpleBold } from "react-icons/pi";
import { Link, useLocation } from "react-router-dom";
import { KURTOSIS_CLOUD_CONNECT_URL } from "../client/constants";
import { useKurtosisClient } from "../client/enclaveManager/KurtosisClientContext";
import { NavButton, Navigation } from "../components/Navigation";

export type NavbarProps = {
Expand All @@ -8,6 +11,7 @@ export type NavbarProps = {

export const Navbar = ({ baseApplicationUrl }: NavbarProps) => {
const location = useLocation();
const kurtosisClient = useKurtosisClient();

return (
<Navigation baseApplicationUrl={baseApplicationUrl}>
Expand All @@ -18,6 +22,11 @@ export const Navbar = ({ baseApplicationUrl }: NavbarProps) => {
isActive={location.pathname === "/" || location.pathname.startsWith("/enclave")}
/>
</Link>
{kurtosisClient.isRunningInCloud() && (
<Link to={KURTOSIS_CLOUD_CONNECT_URL}>
<NavButton label={"Link your CLI"} Icon={<PiLinkSimpleBold />} isActive={true} />
</Link>
)}
{/*<Link to={"/catalog"}>*/}
{/* <NavButton label={"View catalog"} Icon={<FiPackage />} isActive={location.pathname.startsWith("/catalog")} />*/}
{/*</Link>*/}
Expand Down
Expand Up @@ -5,7 +5,7 @@ import { useEffect, useState } from "react";
import { useKurtosisClient } from "../../../../../client/enclaveManager/KurtosisClientContext";
import { LogLineProps } from "../../../../../components/enclaves/logs/LogLine";
import { LogViewer } from "../../../../../components/enclaves/logs/LogViewer";
import { isDefined, stringifyError } from "../../../../../utils";
import { isDefined } from "../../../../../utils";
import { EnclaveFullInfo } from "../../../types";

const serviceLogLineToLogLineProps = (lines: string[], timestamp?: Timestamp): LogLineProps[] => {
Expand All @@ -20,37 +20,56 @@ type ServiceLogsProps = {
service: ServiceInfo;
};

export async function reTryCatch<R>(
callback: (isRetry: boolean) => Promise<R>,
times: number = 1,
isRetry: boolean = false,
): Promise<R> {
try {
return await callback(isRetry);
} catch (error) {
if (times > 0) {
console.info(`retrying another ${times} times`);
return await reTryCatch(callback, times - 1, true);
} else {
console.info("retry: giving up and throwing error");
throw error;
}
}
}

export const ServiceLogs = ({ enclave, service }: ServiceLogsProps) => {
const kurtosisClient = useKurtosisClient();
const [logLines, setLogLines] = useState<LogLineProps[]>([]);

useEffect(() => {
let cancelled = false;
let canceled = false;
const abortController = new AbortController();
(async () => {
setLogLines([]);
setLogLines([]);
const callback = async (isRetry: boolean) => {
// TODO: when we have a way to track where we left off, we don't have to clear and re-read everything
if (isRetry) setLogLines([]);
console.info("Created a new logging stream");
try {
for await (const lineGroup of await kurtosisClient.getServiceLogs(abortController, enclave, [service])) {
if (cancelled) {
return;
}
if (canceled) return;
const lineGroupForService = lineGroup.serviceLogsByServiceUuid[service.serviceUuid];
if (!isDefined(lineGroupForService)) {
continue;
}
if (!isDefined(lineGroupForService)) continue;
const parsedLines = serviceLogLineToLogLineProps(lineGroupForService.line, lineGroupForService.timestamp);
setLogLines((logLines) => [...logLines, ...parsedLines]);
}
} catch (error: any) {
if (cancelled) {
if (canceled) {
console.info("The logging stream was successfully canceled (not an error)", error);
return;
}
// TODO: We need a way to show this error
alert(`Error: ${stringifyError(error)}`);
console.error("An unhandled error occurred while streaming logs", error);
throw error;
}
})();
};
reTryCatch(callback, 100);
return () => {
cancelled = true;
canceled = true;
abortController.abort();
};
}, [enclave, service, kurtosisClient]);
Expand Down
6 changes: 3 additions & 3 deletions engine/server/webapp/asset-manifest.json
@@ -1,10 +1,10 @@
{
"files": {
"main.js": "./static/js/main.bb8e0f41.js",
"main.js": "./static/js/main.af2d0c90.js",
"index.html": "./index.html",
"main.bb8e0f41.js.map": "./static/js/main.bb8e0f41.js.map"
"main.af2d0c90.js.map": "./static/js/main.af2d0c90.js.map"
},
"entrypoints": [
"static/js/main.bb8e0f41.js"
"static/js/main.af2d0c90.js"
]
}
2 changes: 1 addition & 1 deletion engine/server/webapp/index.html
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Kurtosis Enclave Manager"/><title>Kurtosis Enclave Manager</title><script defer="defer" src="./static/js/main.bb8e0f41.js"></script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Kurtosis Enclave Manager"/><title>Kurtosis Enclave Manager</title><script defer="defer" src="./static/js/main.af2d0c90.js"></script></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
3 changes: 3 additions & 0 deletions engine/server/webapp/static/js/main.af2d0c90.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions engine/server/webapp/static/js/main.af2d0c90.js.map

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions engine/server/webapp/static/js/main.bb8e0f41.js

This file was deleted.

1 change: 0 additions & 1 deletion engine/server/webapp/static/js/main.bb8e0f41.js.map

This file was deleted.

0 comments on commit 57468d0

Please sign in to comment.