Skip to content

Commit

Permalink
preflight check for model addons
Browse files Browse the repository at this point in the history
  • Loading branch information
Feroze Mohideen committed May 17, 2024
1 parent 70abad0 commit db5f5ab
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 7 deletions.
104 changes: 101 additions & 3 deletions dashboard/src/main/home/add-on-dashboard/AddonFormContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { createContext, useMemo, useState } from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { Contract } from "@porter-dev/api-contracts";
import { useQueryClient } from "@tanstack/react-query";
import { FormProvider, useForm } from "react-hook-form";
import { useHistory } from "react-router";
Expand All @@ -8,11 +9,20 @@ import styled from "styled-components";
import Loading from "components/Loading";
import { Error as ErrorComponent } from "components/porter/Error";
import { clientAddonValidator, type ClientAddon } from "lib/addons";
import { updateExistingClusterContract } from "lib/clusters";
import { type ClientPreflightCheck } from "lib/clusters/types";
import { useAddon } from "lib/hooks/useAddon";
import { getErrorMessageFromNetworkCall } from "lib/hooks/useCluster";
import {
getErrorMessageFromNetworkCall,
preflightChecks,
} from "lib/hooks/useCluster";
import { useDefaultDeploymentTarget } from "lib/hooks/useDeploymentTarget";

import { type UpdateClusterButtonProps } from "../infrastructure-dashboard/ClusterFormContextProvider";
import { useClusterContext } from "../infrastructure-dashboard/ClusterContextProvider";
import ClusterFormContextProvider, {
type UpdateClusterButtonProps,
} from "../infrastructure-dashboard/ClusterFormContextProvider";
import PreflightChecksModal from "../infrastructure-dashboard/modals/PreflightChecksModal";

type AddonFormContextType = {
updateAddonButtonProps: UpdateClusterButtonProps;
Expand Down Expand Up @@ -43,6 +53,12 @@ const AddonFormContextProvider: React.FC<AddonFormContextProviderProps> = ({
children,
}) => {
const [updateAddonError, setUpdateAddonError] = useState<string>("");
const [failingPreflightChecks, setFailingPreflightChecks] = useState<
ClientPreflightCheck[]
>([]);
const [isCheckingQuotas, setIsCheckingQuotas] = useState<boolean>(false);
const { cluster } = useClusterContext();

const { defaultDeploymentTarget } = useDefaultDeploymentTarget();
const { updateAddon } = useAddon();
const queryClient = useQueryClient();
Expand All @@ -57,12 +73,81 @@ const AddonFormContextProvider: React.FC<AddonFormContextProviderProps> = ({
formState: { isSubmitting, errors },
} = addonForm;

const modelAddonPreflightChecks = async (
addon: ClientAddon,
projectId: number
): Promise<ClientPreflightCheck[] | undefined> => {
// TODO: figure out why data.template is undefined here. If it is defined, we can use data.template.isModelTemplate instead of hardcoding the type
if (
addon.config.type === "deepgram" &&
cluster.contract?.config &&
cluster.contract.config.cluster.cloudProvider === "AWS"
) {
let clientContract = cluster.contract.config;
if (
!clientContract.cluster.config.nodeGroups.some(
(n) => n.nodeGroupType === "CUSTOM"
)
) {
clientContract = {
...clientContract,
cluster: {
...clientContract.cluster,
config: {
...clientContract.cluster.config,
nodeGroups: [
...clientContract.cluster.config.nodeGroups,
{
nodeGroupType: "CUSTOM",
instanceType: "g4dn.xlarge",
minInstances: 0,
maxInstances: 1,
},
],
},
},
};
}
const contract = Contract.fromJsonString(
atob(cluster.contract.base64_contract),
{
ignoreUnknownFields: true,
}
);
const contractCluster = contract.cluster;
if (contractCluster) {
const newContract = new Contract({
...contract,
cluster: updateExistingClusterContract(
clientContract,
contractCluster
),
});
setIsCheckingQuotas(true);
const preflightCheckResults = await preflightChecks(
newContract,
projectId
);
return preflightCheckResults;
}
}
};

const onSubmit = handleSubmit(async (data) => {
if (!projectId) {
return;
}
setFailingPreflightChecks([]);
setUpdateAddonError("");
try {
const preflightCheckResults = await modelAddonPreflightChecks(
data,
projectId
);
if (preflightCheckResults) {
setFailingPreflightChecks(preflightCheckResults);
}
setIsCheckingQuotas(false);
await updateAddon({
projectId,
deploymentTargetId: defaultDeploymentTarget.id,
Expand Down Expand Up @@ -91,6 +176,9 @@ const AddonFormContextProvider: React.FC<AddonFormContextProviderProps> = ({
props.status = "loading";
props.isDisabled = true;
}
if (isCheckingQuotas) {
props.loadingText = "Checking quotas...";
}

if (updateAddonError) {
props.status = (
Expand All @@ -103,7 +191,7 @@ const AddonFormContextProvider: React.FC<AddonFormContextProviderProps> = ({
}

return props;
}, [isSubmitting, errors, errors?.name?.value]);
}, [isSubmitting, errors, errors?.name?.value, isCheckingQuotas]);

if (!projectId) {
return <Loading />;
Expand All @@ -120,6 +208,16 @@ const AddonFormContextProvider: React.FC<AddonFormContextProviderProps> = ({
<FormProvider {...addonForm}>
<form onSubmit={onSubmit}>{children}</form>
</FormProvider>
{failingPreflightChecks.length > 0 && (
<ClusterFormContextProvider projectId={projectId}>
<PreflightChecksModal
onClose={() => {
setFailingPreflightChecks([]);
}}
preflightChecks={failingPreflightChecks}
/>
</ClusterFormContextProvider>
)}
</Wrapper>
</AddonFormContext.Provider>
);
Expand Down
17 changes: 13 additions & 4 deletions dashboard/src/main/home/add-on-dashboard/AddonTemplates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ import addOnGrad from "assets/add-on-grad.svg";
import inferenceGrad from "assets/inference-grad.svg";

import DashboardHeader from "../cluster-dashboard/DashboardHeader";
import ClusterContextProvider from "../infrastructure-dashboard/ClusterContextProvider";
import AddonForm from "./AddonForm";
import AddonFormContextProvider from "./AddonFormContextProvider";

type Props = {
filterModels?: boolean;
};
const AddonTemplates: React.FC<Props> = ({ filterModels }) => {
const { currentProject } = useContext(Context);
const { currentProject, currentCluster } = useContext(Context);
const { search } = useLocation();
const queryParams = new URLSearchParams(search);
const history = useHistory();
Expand All @@ -40,9 +41,17 @@ const AddonTemplates: React.FC<Props> = ({ filterModels }) => {

if (templateMatch) {
return (
<AddonFormContextProvider projectId={currentProject?.id} redirectOnSubmit>
<AddonForm template={templateMatch} filterModels={filterModels} />
</AddonFormContextProvider>
<ClusterContextProvider
clusterId={currentCluster?.id}
refetchInterval={0}
>
<AddonFormContextProvider
projectId={currentProject?.id}
redirectOnSubmit
>
<AddonForm template={templateMatch} filterModels={filterModels} />
</AddonFormContextProvider>
</ClusterContextProvider>
);
}

Expand Down

0 comments on commit db5f5ab

Please sign in to comment.