Skip to content

Commit

Permalink
feat: Add instance termination warning to the enclave manager UI (#2356)
Browse files Browse the repository at this point in the history
## Description
We describe the instance termination policy on the tier page and on the
cloud UI but those might not be seen until the user starts using the EM
UI. The user sees the enclaves deletion as a bug because the instance
replacement is done in the background. This change adds an instance
termination warning message to the EM UI.

Most of the work was done in the following PR to make the cloud instance
config available to the EM UI:
#2350


![image](https://github.com/kurtosis-tech/kurtosis/assets/125146/84294828-c603-4f90-bbb9-a457b0428d40)

## Is this change user facing?
YES
  • Loading branch information
laurentluce authored Apr 4, 2024
1 parent a407cb6 commit bb39d2c
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 1 deletion.
4 changes: 4 additions & 0 deletions enclave-manager/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ func (c *WebServer) ValidateRequestAuthorization(
}

func (c *WebServer) GetCloudInstanceConfig(ctx context.Context, req *connect.Request[emptypb.Empty]) (*connect.Response[kurtosis_backend_server_rpc_api_bindings.GetCloudInstanceConfigResponse], error) {
if !c.enforceAuth {
return nil, stacktrace.NewError("This method is only available in the cloud")
}

reqToken := req.Header().Get("Authorization")
splitToken := strings.Split(reqToken, "Bearer")
if len(splitToken) != numberOfElementsAuthHeader {
Expand Down
3 changes: 3 additions & 0 deletions enclave-manager/web/packages/app/src/client/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ export const KURTOSIS_CLOUD_PROTOCOL = "https";
export const KURTOSIS_CLOUD_HOST = "cloud.kurtosis.com";
export const KURTOSIS_CLOUD_CONNECT_PAGE = "connect";
export const KURTOSIS_CLOUD_EM_PAGE = "enclave-manager";
export const KURTOSIS_CLOUD_SUBSCRIPTION_PAGE = "subscription";
export const KURTOSIS_CLOUD_INSTANCE_MAX_UPTIME_IN_HOURS = 12;

// Cloud
export const KURTOSIS_CLOUD_UI_URL =
process.env.REACT_APP_KURTOSIS_CLOUD_UI_URL || `${KURTOSIS_CLOUD_PROTOCOL}://${KURTOSIS_CLOUD_HOST}`;
export const KURTOSIS_CLOUD_CONNECT_URL = `${KURTOSIS_CLOUD_UI_URL}/${KURTOSIS_CLOUD_CONNECT_PAGE}`;
export const KURTOSIS_CLOUD_SUBSCRIPTION_URL = `${KURTOSIS_CLOUD_UI_URL}/${KURTOSIS_CLOUD_SUBSCRIPTION_PAGE}`;
export const KURTOSIS_CLOUD_EM_URL = `${KURTOSIS_CLOUD_UI_URL}/${KURTOSIS_CLOUD_EM_PAGE}`;
export const KURTOSIS_PACKAGE_INDEXER_URL =
process.env.REACT_APP_KURTOSIS_PACKAGE_INDEXER_URL || `${KURTOSIS_CLOUD_PROTOCOL}://${KURTOSIS_CLOUD_HOST}:9770`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,8 @@ export abstract class KurtosisClient {
return this.client.getStarlarkPackagePlanYaml(request, this.getHeaderOptions());
});
}

async getCloudInstanceConfig() {
return this.client.getCloudInstanceConfig({}, this.getHeaderOptions());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FiPlus } from "react-icons/fi";
import { Outlet, Params } from "react-router-dom";
import { KurtosisEnclavesRouteObject } from "../types";
import { GoToEnclaveOverviewButton } from "./components/GotToEncalaveOverviewButton";
import { InstanceTerminationWarning } from "./components/InstanceTermination";
import { KurtosisEnclavesBreadcrumbs } from "./components/KurtosisEnclaveBreadcrumbs";
import { Artifact } from "./enclave/artifact/Artifact";
import { Enclave } from "./enclave/Enclave";
Expand All @@ -19,7 +20,11 @@ registerBreadcrumbHandler("enclavesHandle", KurtosisEnclavesBreadcrumbs);
export const enclaveRoutes = (): KurtosisEnclavesRouteObject[] => [
{
path: "/enclaves?",
handle: { type: "enclavesHandle" as "enclavesHandle", crumb: () => ({ name: "Enclaves", destination: "/" }) },
handle: {
type: "enclavesHandle" as "enclavesHandle",
crumb: () => ({ name: "Enclaves", destination: "/" }),
extraControls: (state: RemoveFunctions<EnclavesState>, params: Params<string>) => <InstanceTerminationWarning />,
},
id: "enclaves",
element: <EnclaveList />,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ExternalLinkIcon } from "@chakra-ui/icons";
import { Box, Link, Text } from "@chakra-ui/react";
import { useEffect, useState } from "react";
import {
KURTOSIS_CLOUD_INSTANCE_MAX_UPTIME_IN_HOURS,
KURTOSIS_CLOUD_SUBSCRIPTION_URL,
} from "../../../client/constants";
import { useKurtosisClient } from "../../../client/enclaveManager/KurtosisClientContext";

export const InstanceTerminationWarning = () => {
const [cloudInstanceRemainingHours, setCloudInstanceRemainingHours] = useState(0);
const kurtosisClient = useKurtosisClient();

useEffect(() => {
if (kurtosisClient.isRunningInCloud()) {
fetchCloudInstanceCreationTime();
}
});

const fetchCloudInstanceCreationTime = async () => {
try {
const cloudInstanceConfigResponse = await kurtosisClient.getCloudInstanceConfig();
const upTime = Math.floor((Date.now() - new Date(cloudInstanceConfigResponse.created).getTime()) / (3600 * 1000));
const remainingHours = KURTOSIS_CLOUD_INSTANCE_MAX_UPTIME_IN_HOURS - upTime - 1;
setCloudInstanceRemainingHours(remainingHours);
} catch (error) {
console.error(error);
}
};

if (cloudInstanceRemainingHours <= 0) {
return null;
}
return (
<Box borderWidth="1px" borderRadius="lg" borderColor="red" p={1}>
<Text fontSize="xs">
Your cloud instance will terminate in {cloudInstanceRemainingHours} hour(s) if you do not have a{" "}
<Link href={`${KURTOSIS_CLOUD_SUBSCRIPTION_URL}`} isExternal>
subscription <ExternalLinkIcon mx="1px" />
</Link>
</Text>
</Box>
);
};

0 comments on commit bb39d2c

Please sign in to comment.