Skip to content

Commit

Permalink
fix(deployment): templates new deploy (#403)
Browse files Browse the repository at this point in the history
  • Loading branch information
baktun14 authored Oct 14, 2024
1 parent 9f5d7a8 commit c0d50b1
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export const DeploymentListRow: React.FunctionComponent<Props> = ({ deployment,
{isManagedWallet ? (
<PriceValue denom={deployment.escrowAccount.balance.denom} value={escrowBalanceInDenom} />
) : (
`${escrowBalanceInDenom}&nbsp;{denomData?.label}`
`${escrowBalanceInDenom} ${denomData?.label}`
)}
</strong>
</div>
Expand All @@ -249,7 +249,7 @@ export const DeploymentListRow: React.FunctionComponent<Props> = ({ deployment,
{isManagedWallet ? (
<PriceValue denom={deployment.escrowAccount.balance.denom} value={udenomToDenom(amountSpent || 0, 2)} />
) : (
`${udenomToDenom(amountSpent || 0, 2)}&nbsp;${denomData?.label}`
`${udenomToDenom(amountSpent || 0, 2)} ${denomData?.label}`
)}
</strong>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export const NewDeploymentContainer: FC = () => {
const [activeStep, setActiveStep] = useState<number | null>(null);
const [selectedTemplate, setSelectedTemplate] = useState<TemplateCreation | null>(null);
const [editedManifest, setEditedManifest] = useState<string | null>(null);
const [isInit, setIsInit] = useState(false);
const deploySdl = useAtomValue(sdlStore.deploySdl);
const { getDeploymentData } = useLocalNotes();
const { getTemplateById } = useTemplates();
Expand All @@ -33,12 +32,14 @@ export const NewDeploymentContainer: FC = () => {

useEffect(() => {
const queryStep = searchParams?.get("step");
const _activeStep = getStepIndexByParam(queryStep);
const _activeStep = getStepIndexByParam(queryStep as RouteStep);
setActiveStep(_activeStep);
}, [searchParams]);

useEffect(() => {
if (!templates || editedManifest || isInit) return;
const templateId = searchParams?.get("templateId");
const isCreating = !!activeStep && activeStep > getStepIndexByParam(RouteStep.chooseTemplate);
if (!templates || (isCreating && !!editedManifest && !!templateId)) return;

const template = getRedeployTemplate() || getGalleryTemplate();

Expand All @@ -54,11 +55,9 @@ export const NewDeploymentContainer: FC = () => {
if (queryStep !== RouteStep.editDeployment) {
router.replace(UrlService.newDeployment({ ...searchParams, step: RouteStep.editDeployment }));
}

setIsInit(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [templates, editedManifest, searchParams, router, toggleCmp, hasComponent, isInit]);
}, [templates, editedManifest, searchParams, router, toggleCmp, hasComponent, activeStep]);

const getRedeployTemplate = () => {
let template: Partial<TemplateCreation> | null = null;
Expand Down Expand Up @@ -112,7 +111,7 @@ export const NewDeploymentContainer: FC = () => {
return null;
};

function getStepIndexByParam(step) {
function getStepIndexByParam(step: (typeof RouteStep)[keyof typeof RouteStep] | null) {
switch (step) {
case RouteStep.editDeployment:
return 1;
Expand Down
8 changes: 4 additions & 4 deletions apps/deploy-web/src/components/sdl/MemoryFormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ export const MemoryFormControl: React.FunctionComponent<Props> = ({ control, ser
name={`services.${serviceIndex}.profile.ramUnit`}
defaultValue=""
render={({ field }) => (
<Select value={field.value || ""} onValueChange={field.onChange}>
<Select value={field.value?.toLowerCase() || ""} onValueChange={field.onChange}>
<SelectTrigger className="ml-1 w-[75px]">
<SelectValue placeholder="Select unit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{memoryUnits.map(t => {
return (
<SelectItem key={t.id} value={t.suffix}>
<SelectItem key={t.id} value={t.suffix.toLowerCase()}>
{t.suffix}
</SelectItem>
);
Expand All @@ -99,11 +99,11 @@ export const MemoryFormControl: React.FunctionComponent<Props> = ({ control, ser
<Slider
value={[field.value || 0]}
min={1}
max={512}
max={5120}
step={1}
color="secondary"
aria-label="RAM"
onValueChange={newValue => field.onChange(newValue)}
onValueChange={newValue => field.onChange(newValue[0])}
className="pt-2"
/>

Expand Down
6 changes: 3 additions & 3 deletions apps/deploy-web/src/components/sdl/PersistentStorage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ export const PersistentStorage: React.FunctionComponent<Props> = ({ currentServi
name={`services.${serviceIndex}.profile.persistentStorageUnit`}
defaultValue=""
render={({ field }) => (
<Select value={field.value || ""} onValueChange={field.onChange}>
<Select value={field.value?.toLowerCase() || ""} onValueChange={field.onChange}>
<SelectTrigger className="ml-1 w-[75px]">
<SelectValue placeholder="Select unit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{storageUnits.map(t => {
return (
<SelectItem key={t.id} value={t.suffix}>
<SelectItem key={t.id} value={t.suffix.toLowerCase()}>
{t.suffix}
</SelectItem>
);
Expand All @@ -115,7 +115,7 @@ export const PersistentStorage: React.FunctionComponent<Props> = ({ currentServi
</div>

{currentService.profile.hasPersistentStorage && (
<Slider value={[field.value || 0]} min={1} max={512} step={1} onValueChange={newValue => field.onChange(newValue)} className="pt-2" />
<Slider value={[field.value || 0]} min={1} max={5120} step={1} onValueChange={newValue => field.onChange(newValue)} className="pt-2" />
)}

<FormMessage className={cn({ "pt-2": !!fieldState.error })} />
Expand Down
14 changes: 8 additions & 6 deletions apps/deploy-web/src/components/sdl/SSHKeyFromControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,24 @@ export const SSHKeyFormControl: FC<SSHKeyInputProps> = ({ control, serviceIndex,
</CustomTooltip>
</div>
}
placeholder="ssh-..."
placeholder="Enter your own pub key: ssh-.."
className="flex-grow"
inputClassName="pr-[100px]"
value={field.value}
onChange={event => field.onChange(event.target.value || "")}
startIcon={<Key className="ml-2 text-xs text-muted-foreground" />}
endIcon={
<Button onClick={generateSSHKeys} type="button" size="sm" className="h-full" data-testid="generate-ssh-keys-btn">
Generate
</Button>
}
data-testid="ssh-public-key-input"
/>
)}
/>

<div className="mt-2 flex items-center justify-end space-x-2">
<span className="text-sm text-muted-foreground">Or</span>
<Button onClick={generateSSHKeys} type="button" size="xs" data-testid="generate-ssh-keys-btn">
Generate new key
</Button>
</div>

{hasGenerated && (
<div className="mt-2 text-sm text-muted-foreground">
<h4 className="text-lg">How to use</h4>
Expand Down
6 changes: 3 additions & 3 deletions apps/deploy-web/src/components/sdl/StorageFormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ export const StorageFormControl: React.FunctionComponent<Props> = ({ control, se
name={`services.${serviceIndex}.profile.storageUnit`}
defaultValue=""
render={({ field }) => (
<Select value={field.value || ""} onValueChange={field.onChange}>
<Select value={field.value?.toLowerCase() || ""} onValueChange={field.onChange}>
<SelectTrigger className="ml-1 w-[75px]">
<SelectValue placeholder="Select unit" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
{storageUnits.map(t => {
return (
<SelectItem key={t.id} value={t.suffix}>
<SelectItem key={t.id} value={t.suffix.toLowerCase()}>
{t.suffix}
</SelectItem>
);
Expand All @@ -102,7 +102,7 @@ export const StorageFormControl: React.FunctionComponent<Props> = ({ control, se
<Slider
value={[field.value || 0]}
min={1}
max={512}
max={5120}
step={1}
color="secondary"
aria-label="Storage"
Expand Down
21 changes: 14 additions & 7 deletions apps/deploy-web/src/types/sdlBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { z } from "zod";

import { memoryUnits, validationConfig } from "@src/utils/akash/units";
import { memoryUnits, storageUnits, validationConfig } from "@src/utils/akash/units";
import { endpointNameValidationRegex } from "@src/utils/deploymentData/v1beta3";
import { bytesToShrink } from "@src/utils/unitUtils";
import { roundDecimal } from "@src/utils/mathHelpers";

const VALID_IMAGE_NAME = /^[a-z0-9\-_/:.]+$/;
const VALID_IMAGE_NAME =
/^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])(?:(?:\.(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]))+)?(?::[0-9]+)?\/)?[a-z0-9]+(?:(?:(?:[._]|__|[-]*)[a-z0-9]+)+)?(?:\/[a-z0-9]+(?:(?:(?:[._]|__|[-]*)[a-z0-9]+)+)?)*(?::[a-zA-Z0-9_.-]+)?(?:@[a-zA-Z0-9_.:+-]+)?$/;

export const ProfileGpuModelSchema = z.object({
vendor: z.string().min(1, { message: "Vendor is required." }),
Expand Down Expand Up @@ -226,8 +229,11 @@ const validateGpuAmount = (value: number, serviceCount: number, context: z.Refin
};

const validateMemoryAmount = (value: number, ramUnit: string, serviceCount: number, context: z.RefinementCtx) => {
const currentUnit = memoryUnits.find(u => ramUnit === u.suffix);
const currentUnit = memoryUnits.find(u => ramUnit.toLowerCase() === u.suffix.toLowerCase());
const _value = (value || 0) * (currentUnit?.value || 0);
const maxValue = bytesToShrink(validationConfig.maxMemory);
const maxGroupValue = bytesToShrink(validationConfig.maxGroupMemory);

if (serviceCount === 1 && _value < validationConfig.minMemory) {
context.addIssue({
code: z.ZodIssueCode.custom,
Expand All @@ -239,15 +245,15 @@ const validateMemoryAmount = (value: number, ramUnit: string, serviceCount: numb
} else if (serviceCount === 1 && _value > validationConfig.maxMemory) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: `Maximum amount of memory for a single service instance is 512 Gi.`,
message: `Maximum amount of memory for a single service instance is ${roundDecimal(maxValue.value, 2)} ${maxValue.unit}.`,
path: ["profile", "ram"],
fatal: true
});
return z.NEVER;
} else if (serviceCount > 1 && serviceCount * _value > validationConfig.maxGroupMemory) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: `Maximum total amount of memory for a single service instance group is 1024 Gi.`,
message: `Maximum total amount of memory for a single service instance group is ${roundDecimal(maxGroupValue.value, 2)} ${maxGroupValue.unit}.`,
path: ["profile", "ram"],
fatal: true
});
Expand All @@ -256,8 +262,9 @@ const validateMemoryAmount = (value: number, ramUnit: string, serviceCount: numb
};

const validateStorageAmount = (value: number, storageUnit: string, serviceCount: number, context: z.RefinementCtx) => {
const currentUnit = memoryUnits.find(u => storageUnit === u.suffix);
const currentUnit = storageUnits.find(u => storageUnit.toLowerCase() === u.suffix.toLowerCase());
const _value = (value || 0) * (currentUnit?.value || 0);
const maxValue = bytesToShrink(validationConfig.maxStorage);

if (serviceCount === 1 && _value < validationConfig.minStorage) {
context.addIssue({
Expand All @@ -270,7 +277,7 @@ const validateStorageAmount = (value: number, storageUnit: string, serviceCount:
} else if (serviceCount === 1 && _value > validationConfig.maxStorage) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: `Maximum amount of storage for a single service instance is 32 Ti.`,
message: `Maximum amount of storage for a single service instance is ${roundDecimal(maxValue.value, 2)} ${maxValue.unit}.`,
path: ["profile", "storage"],
fatal: true
});
Expand Down
27 changes: 19 additions & 8 deletions apps/deploy-web/src/utils/akash/units.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
// https://github.com/akash-network/akash-api/blob/ea71fbd0bee740198034bf1b0261c90baea88be0/go/node/deployment/v1beta3/validation_config.go
//github.com/akash-network/akash-api/blob/d05c262a17178a33e3e5383dcceea384d6260a17/go/node/deployment/v1beta3/validation_config.go
const maxUnitCPU = 384;
const maxUnitGPU = 24;
const maxUnitMemory = 2 * 1024 ** 4; // 2 Ti
const maxUnitStorage = 32 * 1024 ** 4; // 32 Ti
const maxUnitCount = 50;
const maxGroupCount = 20;
const maxGroupUnits = 20;

export const validationConfig = {
maxCpuAmount: 256,
maxGroupCpuCount: 512,
maxGpuAmount: 100,
maxGroupGpuCount: 512,
maxCpuAmount: maxUnitCPU,
maxGroupCpuCount: maxUnitCPU * maxUnitCount,
maxGpuAmount: maxUnitGPU,
maxGroupGpuCount: maxUnitGPU * maxUnitCount,
minMemory: 1024, // 1 Mi
minStorage: 5 * 1024, // 5 Mi
maxMemory: 512 * 1024 ** 3, // 512 Gi
maxGroupMemory: 1024 * 1024 ** 3, // 1024 Gi
maxStorage: 32 * 1024 ** 4 // 32 Ti
maxMemory: maxUnitMemory,
maxGroupMemory: maxUnitMemory * maxUnitCount,
maxStorage: maxUnitStorage,
maxGroupStorage: maxUnitStorage * maxUnitCount,
maxGroupCount: maxGroupCount,
maxGroupUnits: maxGroupUnits
};

export const memoryUnits = [
Expand Down
1 change: 1 addition & 0 deletions apps/deploy-web/src/utils/sdl/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export const SSH_EXPOSE = {

export const defaultSshVMService: ServiceType = {
...defaultService,
image: sshVmDistros[0],
expose: []
};

Expand Down
1 change: 1 addition & 0 deletions apps/deploy-web/tests/deploy-hello-world.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { setupLeap } from "./fixture/wallet-setup";
import { DeployHelloWorldPage } from "./pages/DeployHelloWorldPage";

test("deploy hello world", async ({ extPage: page, context }) => {
test.setTimeout(300_000);
await setupLeap(context, page);

const helloWorldPage = new DeployHelloWorldPage(context, page, "new-deployment", "hello-world-card");
Expand Down

0 comments on commit c0d50b1

Please sign in to comment.