Skip to content

Commit

Permalink
feat(sdl): move storage and gpu validation to akashjs
Browse files Browse the repository at this point in the history
refs #133
  • Loading branch information
ygrishajev committed May 17, 2024
1 parent b5895fb commit 3a90ecd
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 185 deletions.
16 changes: 8 additions & 8 deletions deploy-web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion deploy-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
},
"dependencies": {
"@akashnetwork/akash-api": "^1.3.0",
"@akashnetwork/akashjs": "^0.9.0",
"@akashnetwork/akashjs": "^0.10.0",
"@auth0/nextjs-auth0": "^3.5.0",
"@cosmjs/encoding": "^0.29.5",
"@cosmjs/stargate": "^0.29.5",
Expand Down
8 changes: 3 additions & 5 deletions deploy-web/src/components/deployments/ManifestUpdate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type Props = {

export const ManifestUpdate: React.FunctionComponent<Props> = ({ deployment, leases, closeManifestEditor }) => {
const [parsingError, setParsingError] = useState<string | null>(null);
const [deploymentVersion, setDeploymentVersion] = useState(null);
const [deploymentVersion, setDeploymentVersion] = useState<string | null>(null);
const [editedManifest, setEditedManifest] = useState("");
const [isSendingManifest, setIsSendingManifest] = useState(false);
const [showOutsideDeploymentMessage, setShowOutsideDeploymentMessage] = useState(false);
Expand All @@ -54,7 +54,7 @@ export const ManifestUpdate: React.FunctionComponent<Props> = ({ deployment, lea
setEditedManifest(localDeploymentData?.manifest);

const yamlVersion = yaml.load(localDeploymentData?.manifest);
const version = await deploymentData.getManifestVersion(yamlVersion, true);
const version = await deploymentData.getManifestVersion(yamlVersion);

setDeploymentVersion(version);
} else {
Expand Down Expand Up @@ -113,9 +113,7 @@ export const ManifestUpdate: React.FunctionComponent<Props> = ({ deployment, lea

async function sendManifest(providerInfo: ApiProviderList, manifest: any) {
try {
const response = await sendManifestToProvider(providerInfo, manifest, deployment.dseq, localCert as LocalCert);

return response;
return await sendManifestToProvider(providerInfo, manifest, deployment.dseq, localCert as LocalCert);
} catch (err) {
enqueueSnackbar(<ManifestErrorSnackbar err={err} />, { variant: "error", autoHideDuration: null });
throw err;
Expand Down
201 changes: 37 additions & 164 deletions deploy-web/src/utils/deploymentData/v1beta3.ts
Original file line number Diff line number Diff line change
@@ -1,144 +1,20 @@
import { CustomValidationError, DeploymentGroups, getCurrentHeight, getSdl, Manifest, ManifestVersion, parseSizeStr } from "./helpers";
import { defaultInitialDeposit } from "../constants";
import { stringToBoolean } from "../stringUtils";
import path from "path";
import yaml from "js-yaml";
import { getSelectedNetwork } from "@src/hooks/useSelectedNetwork";
import { NetworkId } from "@akashnetwork/akashjs/build/types/network";

export const endpointNameValidationRegex = /^[a-z]+[-_\da-z]+$/;

function validate(yamlStr: string, yamlJson, networkId: NetworkId) {
try {
// TODO: use result of this in client code instead of just using validation
getSdl(yamlJson, "beta3", networkId);
} catch (e) {
const error = new CustomValidationError(e.message);
error.stack = e.stack;
throw error;
}

Object.keys(yamlJson.services).forEach(svcName => {
const svc = yamlJson.services[svcName];
const depl = yamlJson.deployment[svcName];

Object.keys(depl).forEach(placementName => {
const svcdepl = depl[placementName];
const compute = yamlJson.profiles.compute[svcdepl.profile];

// STORAGE VALIDATION
const storages = compute.resources.storage.map ? compute.resources.storage : [compute.resources.storage];
const volumes = {};
const attr = {};
const mounts = {};

storages?.forEach(storage => {
const name = storage.name || "default";
volumes[name] = {
name,
quantity: { val: parseSizeStr(storage.size) },
attributes:
storage.attributes &&
Object.keys(storage.attributes)
.sort()
.map(key => {
const value = storage.attributes[key].toString();
// add the storage attributes
attr[key] = value;

return {
key,
value
};
})
};
});

if (svc.params) {
(Object.keys(svc.params?.storage || {}) || []).forEach(name => {
const params = svc.params.storage[name];
if (!volumes[name]) {
throw new CustomValidationError(`Service "${svcName}" references to no-existing compute volume names "${name}".`);
}

if (!path.isAbsolute(params.mount)) {
throw new CustomValidationError(`Invalid value for "service.${svcName}.params.${name}.mount" parameter. expected absolute path.`);
}

// merge the service params attributes
attr["mount"] = params.mount;
attr["readOnly"] = params.readOnly || false;
const mount = attr["mount"];
const vlname = mounts[mount];

if (vlname) {
if (!mount) {
throw new CustomValidationError("Multiple root ephemeral storages are not allowed");
}

throw new CustomValidationError(`Mount ${mount} already in use by volume ${vlname}.`);
}

mounts[mount] = name;
});
}

(Object.keys(volumes) || []).forEach(volume => {
volumes[volume].attributes?.forEach(nd => {
attr[nd.key] = nd.value;
});

const persistent = stringToBoolean(attr["persistent"]);

if (persistent && !attr["mount"]) {
throw new CustomValidationError(
`compute.storage.${volume} has persistent=true which requires service.${svcName}.params.storage.${volume} to have mount.`
);
}
});

// GPU VALIDATION
const gpu = compute.resources.gpu;

if (gpu) {
if (gpu.units === 0 && gpu.attributes) {
throw new CustomValidationError(`Invalid GPU configuration for "${svcName}".`);
}

if (gpu.units > 0 && !gpu.attributes) {
throw new CustomValidationError(`Invalid GPU configuration for "${svcName}". Missing attributes with vendor and nvidia.`);
}

if (gpu.units > 0 && !("vendor" in gpu.attributes)) {
throw new CustomValidationError(`Invalid GPU configuration for "${svcName}". Missing vendor with nvidia in attributes.`);
}

if (gpu.units > 0 && !("nvidia" in gpu.attributes.vendor)) {
throw new CustomValidationError(`Invalid GPU configuration for "${svcName}". Missing nvidia in attributes.vendor.`);
}
import { CustomValidationError, getCurrentHeight, getSdl, Manifest, ManifestVersion, parseSizeStr } from "./helpers";
import { defaultInitialDeposit } from "../constants";

if (gpu.units > 0 && !!gpu.attributes.vendor.nvidia && !Array.isArray(gpu.attributes.vendor.nvidia)) {
throw new CustomValidationError(`Invalid GPU configuration for "${svcName}". Nvidia must be an array of GPU models with optional ram.`);
}
}
});
});
}
export const endpointNameValidationRegex = /^[a-z]+[-_\da-z]+$/;

export function getManifest(yamlJson, asString: boolean) {
const network = getSelectedNetwork();
return Manifest(yamlJson, "beta3", network.id, asString);
}

export async function getManifestVersion(yamlJson, asString = false) {
export async function getManifestVersion<T extends boolean>(yamlJson) {
const network = getSelectedNetwork();
const version = await ManifestVersion(yamlJson, "beta3", network.id);

if (asString) {
return Buffer.from(version).toString("base64");
} else {
return version;
}
return Buffer.from(version).toString("base64");
}

const getDenomFromSdl = (groups: any[]): string => {
Expand All @@ -151,43 +27,40 @@ const getDenomFromSdl = (groups: any[]): string => {
export async function NewDeploymentData(
apiEndpoint: string,
yamlStr: string,
dseq: string,
dseq: string | null,
fromAddress: string,
deposit = defaultInitialDeposit,
depositorAddress = null
depositorAddress: string | null = null
) {
const { id: networkId } = getSelectedNetwork();
const yamlJson = yaml.load(yamlStr) as any;

// Validate the integrity of the yaml
validate(yamlStr, yamlJson, networkId);

const groups = DeploymentGroups(yamlJson, "beta3", networkId);
const mani = Manifest(yamlJson, "beta3", networkId);
const denom = getDenomFromSdl(groups);
const version = await ManifestVersion(yamlJson, "beta3", networkId);
const id = {
owner: fromAddress,
dseq: dseq
};
const _deposit = {
denom,
amount: deposit.toString()
};

if (!id.dseq) {
id.dseq = (await getCurrentHeight(apiEndpoint)).toString();
try {
const { id: networkId } = getSelectedNetwork();
const sdl = getSdl(yamlStr, "beta3", networkId);
const groups = sdl.groups();
const mani = sdl.manifest();
const denom = getDenomFromSdl(groups);
const version = await sdl.manifestVersion();
const _deposit = {
denom,
amount: deposit.toString()
};

return {
sdl: sdl.data,
manifest: mani,
groups: groups,
deploymentId: {
owner: fromAddress,
dseq: dseq || (await getCurrentHeight(apiEndpoint)).toString()
},
orderId: [],
leaseId: [],
version,
deposit: _deposit,
depositor: depositorAddress || fromAddress
};
} catch (e) {
const error = new CustomValidationError(e.message);
error.stack = e.stack;
throw error;
}

return {
sdl: yamlJson,
manifest: mani,
groups: groups,
deploymentId: id,
orderId: [],
leaseId: [],
version,
deposit: _deposit,
depositor: depositorAddress || fromAddress
};
}
7 changes: 2 additions & 5 deletions deploy-web/src/utils/deploymentLocalDataUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,15 @@ export type LocalDeploymentData = {
manifestVersion?: Uint8Array;
};

export function getDeploymentLocalData(dseq: string | number) {
export function getDeploymentLocalData(dseq: string | number): LocalDeploymentData | null {
const selectedNetworkId = localStorage.getItem("selectedNetworkId");
const selectedWallet = getSelectedStorageWallet();

if (!selectedWallet) return null;

const dataStr = localStorage.getItem(`${selectedNetworkId}/${selectedWallet.address}/deployments/${dseq}.data`);
if (!dataStr) return null;

const parsedData = JSON.parse(dataStr) as LocalDeploymentData;

return parsedData;
return dataStr ? JSON.parse(dataStr) : null;
}

export function saveDeploymentManifestAndName(dseq: string, manifest: string, version: Uint8Array, address: string, name: string) {
Expand Down
2 changes: 0 additions & 2 deletions deploy-web/src/utils/init.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { setNetworkVersion } from "./constants";
import { initDeploymentData } from "./deploymentData";
import { initProtoTypes } from "./proto";
import { setMessageTypes } from "./TransactionMessageData";

export const initAppTypes = () => {
setNetworkVersion();
initProtoTypes();
setMessageTypes();
initDeploymentData();
};

0 comments on commit 3a90ecd

Please sign in to comment.