Skip to content

Commit

Permalink
Bugfixes/fix gpu validation (#69)
Browse files Browse the repository at this point in the history
* fix gpu validation + add tests

* fix equal error

* fix validation + added better tests

* fix test snapshot name
  • Loading branch information
baktun14 authored Mar 20, 2024
1 parent 14c27f5 commit 9fbeb2c
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 32 deletions.
48 changes: 25 additions & 23 deletions src/sdl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import crypto from "node:crypto";
const Endpoint_SHARED_HTTP = 0;
const Endpoint_RANDOM_PORT = 1;
const Endpoint_LEASED_IP = 2;
export const GPU_SUPPORTED_VENDORS = ["nvidia", "amd"];

function isArray<T>(obj: any): obj is Array<T> {
return Array.isArray(obj);
Expand Down Expand Up @@ -68,42 +69,43 @@ export class SDL {
const data = YAML.load(yaml) as v3Sdl;

for (const [name, profile] of Object.entries(data.profiles.compute)) {
if (version === "beta3") {
SDL.validateGPU(name, profile.resources.gpu);
}

SDL.validateGPU(name, profile.resources.gpu);
SDL.validateStorage(name, profile.resources.storage);
}

return data;
}

static validateGPU(name: string, gpu: v3ResourceGPU | undefined) {
if (!gpu) {
throw new Error("GPU resource is required for profile " + name);
}
if (gpu) {
if (typeof gpu.units === "undefined") {
console.log(JSON.stringify(gpu, null, 2));
throw new Error("GPU units must be specified for profile " + name);
}

if (typeof gpu.units === "undefined") {
console.log(JSON.stringify(gpu, null, 2));
throw new Error("GPU units must be specified for profile " + name);
}
const units = parseInt(gpu.units.toString());

const units = parseInt(gpu.units.toString());
if (units === 0 && gpu.attributes !== undefined) {
throw new Error("GPU must not have attributes if units is 0");
}

if (units == 0 && gpu.attributes !== undefined) {
throw new Error("GPU must not have attributes if units is 0");
}
if (units > 0 && gpu.attributes === undefined) {
throw new Error("GPU must have attributes if units is not 0");
}

if (units > 0 && gpu.attributes === undefined) {
throw new Error("GPU must not have attributes if units is 0");
}
if (units > 0 && gpu.attributes?.vendor === undefined) {
throw new Error("GPU must specify a vendor if units is not 0");
}

if (units > 0 && gpu.attributes?.vendor === undefined) {
throw new Error("GPU must specify a vendor if units is not 0");
}
if (units > 0 && !GPU_SUPPORTED_VENDORS.some(vendor => vendor in (gpu.attributes?.vendor || {}))) {
throw new Error(`GPU must be one of the supported vendors (${GPU_SUPPORTED_VENDORS.join(",")}).`);
}

const vendor: string = Object.keys(gpu.attributes?.vendor || {})[0];

if (units > 0 && gpu.attributes?.vendor?.nvidia === undefined) {
throw Error("GPU must specify models if units is not 0");
if (units > 0 && !!gpu.attributes?.vendor[vendor] && !Array.isArray(gpu.attributes.vendor[vendor])) {
throw new Error(`GPU configuration must be an array of GPU models with optional ram.`);
}
}
}

Expand Down
50 changes: 50 additions & 0 deletions tests/fixtures/gpu_basic_no_model.sdl.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
version: "2.0"
services:
web:
image: nginx
expose:
- port: 80
accept:
- ahostname.com
to:
- global: true
- port: 12345
to:
- global: true
proto: udp
profiles:
compute:
web:
resources:
cpu:
units: "100m"
gpu:
units: 1
attributes:
vendor:
nvidia:
memory:
size: "128Mi"
storage:
- size: "1Gi"
placement:
westcoast:
attributes:
region: us-west
foo: bar
signedBy:
anyOf:
- 1
- 2
allOf:
- 3
- 4
pricing:
web:
denom: uakt
amount: 50
deployment:
web:
westcoast:
profile: web
count: 2
50 changes: 50 additions & 0 deletions tests/fixtures/gpu_invalid_vendor.sdl.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
version: "2.0"
services:
web:
image: nginx
expose:
- port: 80
accept:
- ahostname.com
to:
- global: true
- port: 12345
to:
- global: true
proto: udp
profiles:
compute:
web:
resources:
cpu:
units: "100m"
gpu:
units: 1
attributes:
vendor:
invalidvendor:
memory:
size: "128Mi"
storage:
- size: "1Gi"
placement:
westcoast:
attributes:
region: us-west
foo: bar
signedBy:
anyOf:
- 1
- 2
allOf:
- 3
- 4
pricing:
web:
denom: uakt
amount: 50
deployment:
web:
westcoast:
profile: web
count: 2
25 changes: 25 additions & 0 deletions tests/sdl_gpu_invalid_vendor.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import fs from "fs";

import { SDL } from "../src/sdl";

describe("test sdl gpu invalid vendor", () => {
it("SDL: GPU must throw if the vendor is invalid", () => {
const invalidSDL = fs.readFileSync("./tests/fixtures/gpu_invalid_vendor.sdl.yml", "utf8");

const t = () => {
SDL.fromString(invalidSDL, "beta3");
};

expect(t).toThrow(`GPU must be one of the supported vendors (nvidia,amd).`);
});

it("SDL: GPU without vendor name should throw", () => {
const invalidSDL = fs.readFileSync("./tests/fixtures/gpu_invalid_no_vendor_name.sdl.yml", "utf8");

const t = () => {
SDL.fromString(invalidSDL, "beta3");
};

expect(t).toThrow(`GPU must be one of the supported vendors (nvidia,amd).`);
});
});
2 changes: 1 addition & 1 deletion tests/test_sdl_gpu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const testSDL = fs.readFileSync("./tests/fixtures/gpu_basic.sdl.yml", "utf8");

const expectedManifest = JSON.parse(fs.readFileSync("./tests/fixtures/gpu_basic.manifest.json", "utf8"));

tap.test("SDL: GPU Manifest", async t => {
tap.test("SDL: GPU Manifest", t => {
t.plan(1);

const sdl = SDL.fromString(testSDL, "beta3");
Expand Down
9 changes: 1 addition & 8 deletions tests/test_sdl_gpu_attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import fs from "fs";
import { SDL } from "../src/sdl";

tap.test("SDL: fromString", async t => {
t.plan(4);
t.plan(3);

const validSDL = fs.readFileSync("./tests/fixtures/gpu_no_gpu_valid.sdl.yml", "utf8");
const hasAttrSDL = fs.readFileSync("./tests/fixtures/gpu_no_gpu_invalid_has_attributes.sdl.yml", "utf8");
const noVendorSdl = fs.readFileSync("./tests/fixtures/gpu_invalid_no_vendor.sdl.yml", "utf8");
const noModelsSdl = fs.readFileSync("./tests/fixtures/gpu_invalid_no_models.sdl.yml", "utf8");

t.doesNotThrow(() => SDL.fromString(validSDL, "beta3"), "accept if GPU units is 0, and no attributes are present");

Expand All @@ -24,10 +23,4 @@ tap.test("SDL: fromString", async t => {
new Error("GPU must specify a vendor if units is not 0"),
"throw an error if GPU units is not 0, and the vendor is not present"
);

t.throws(
() => SDL.fromString(noModelsSdl, "beta3"),
new Error("GPU must specify models if units is not 0"),
"throw an error if GPU units is not 0, and the models are not present"
);
});

0 comments on commit 9fbeb2c

Please sign in to comment.