From e8d0ffc6c3de153ea59cb601e2af5e63654ab09f Mon Sep 17 00:00:00 2001 From: Maxime Beauchamp Date: Tue, 19 Mar 2024 12:13:57 -0400 Subject: [PATCH 1/4] added validation for storage class ram --- src/sdl/index.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sdl/index.ts b/src/sdl/index.ts index c72d183..c7f40dc 100644 --- a/src/sdl/index.ts +++ b/src/sdl/index.ts @@ -71,6 +71,8 @@ export class SDL { if (version === "beta3") { SDL.validateGPU(name, profile.resources.gpu); } + + SDL.validateStorage(name, profile.resources.storage); } return data; @@ -105,6 +107,28 @@ export class SDL { } } + static validateStorage(name: string, storage: v2ResourceStorage | v2ResourceStorageArray | undefined) { + if (!storage) { + throw new Error("Storage is required for service " + name); + } + + const storages = isArray(storage) ? storage : [storage]; + + for (const storage of storages) { + if (typeof storage.size === "undefined") { + throw new Error("Storage size is required for service " + name); + } + + if (!!storage.attributes) { + for (const [key, value] of Object.entries(storage.attributes)) { + if (key === "class" && value === "ram" && storage.attributes.persistent === true) { + throw new Error("Storage attribute 'ram' must have 'persistent' set to 'false' or not defined for service " + name); + } + } + } + } + } + services() { if (this.data) { return this.data.services; From 7644b24b05b534af10bd7141392522df64a17f30 Mon Sep 17 00:00:00 2001 From: Maxime Beauchamp Date: Tue, 19 Mar 2024 12:14:25 -0400 Subject: [PATCH 2/4] added tests for the ram storage validation --- ..._persistent_storage_attributes.ts.test.cjs | 17 ++++++ .../persistent_storage_invalid.sdl.yml | 52 +++++++++++++++++++ .../fixtures/persistent_storage_valid.sdl.yml | 6 +++ .../test_sdl_persistent_storage_attributes.ts | 11 ++++ 4 files changed, 86 insertions(+) create mode 100644 tests/fixtures/persistent_storage_invalid.sdl.yml diff --git a/tap-snapshots/tests/test_sdl_persistent_storage_attributes.ts.test.cjs b/tap-snapshots/tests/test_sdl_persistent_storage_attributes.ts.test.cjs index def708f..8ae11c4 100644 --- a/tap-snapshots/tests/test_sdl_persistent_storage_attributes.ts.test.cjs +++ b/tap-snapshots/tests/test_sdl_persistent_storage_attributes.ts.test.cjs @@ -155,6 +155,11 @@ Array [ "name": "wordpress-db", "readOnly": false, }, + Object { + "mount": "/dev/shm", + "name": "shm", + "readOnly": false, + }, ], }, "Resources": Object { @@ -192,6 +197,18 @@ Array [ "val": 8589934592, }, }, + Object { + "attributes": Array [ + Object { + "key": "class", + "value": "ram", + }, + ], + "name": "shm", + "size": Object { + "val": 1073741824, + }, + }, ], }, }, diff --git a/tests/fixtures/persistent_storage_invalid.sdl.yml b/tests/fixtures/persistent_storage_invalid.sdl.yml new file mode 100644 index 0000000..5d3344d --- /dev/null +++ b/tests/fixtures/persistent_storage_invalid.sdl.yml @@ -0,0 +1,52 @@ +--- +version: "2.0" + +services: + grafana: + image: grafana/grafana + expose: + - port: 3000 + as: 80 + to: + - global: true + accept: + - webdistest.localhost + params: + storage: + data: + mount: /var/lib/grafana + shm: + mount: /dev/shm +profiles: + compute: + grafana: + resources: + cpu: + units: 1 + memory: + size: 1Gi + storage: + - size: 512Mi + - name: data + size: 1Gi + attributes: + persistent: true + class: beta2 + - name: shm + size: 1Gi + attributes: + persistent: true + class: ram + placement: + westcoast: + attributes: + region: us-west + pricing: + grafana: + denom: uakt + amount: 1000 +deployment: + grafana: + westcoast: + profile: grafana + count: 1 \ No newline at end of file diff --git a/tests/fixtures/persistent_storage_valid.sdl.yml b/tests/fixtures/persistent_storage_valid.sdl.yml index c55b78c..0462bd1 100644 --- a/tests/fixtures/persistent_storage_valid.sdl.yml +++ b/tests/fixtures/persistent_storage_valid.sdl.yml @@ -45,6 +45,8 @@ services: wordpress-db: mount: /var/lib/mysql readOnly: false + shm: + mount: /dev/shm profiles: compute: wordpress: @@ -73,6 +75,10 @@ profiles: attributes: persistent: true class: beta3 + - name: shm + size: 1Gi + attributes: + class: ram placement: akash: ####################################################### diff --git a/tests/test_sdl_persistent_storage_attributes.ts b/tests/test_sdl_persistent_storage_attributes.ts index eebf8a9..e54d540 100644 --- a/tests/test_sdl_persistent_storage_attributes.ts +++ b/tests/test_sdl_persistent_storage_attributes.ts @@ -13,3 +13,14 @@ tap.test("SDL: Persistent Storage Manifest", async t => { t.matchSnapshot(result, "Manifest matches expected result"); }); + +tap.test("SDL: Persistent Storage with class 'ram' must have 'persistent' set to false", async t => { + t.plan(1); + + const invalidSDL = fs.readFileSync("./tests/fixtures/persistent_storage_invalid.sdl.yml", "utf8"); + + t.throws(() => { + const sdl = SDL.fromString(invalidSDL, "beta2"); + sdl.manifest(); + }, "Storage attribute 'ram' must have 'persistent' set to 'false' or not defined for service grafana"); +}); From 387ee06a3d42d6f4b171cb9fddbc0fc40781a5c8 Mon Sep 17 00:00:00 2001 From: Maxime Beauchamp Date: Tue, 19 Mar 2024 13:15:21 -0400 Subject: [PATCH 3/4] refactored tap test to jest --- jest.config.js | 2 +- src/sdl/index.ts | 4 +- ...persistent_storage_attributes.spec.ts.snap | 213 ++++++++++++++++++ .../sdl_persistent_storage_attributes.spec.ts | 24 ++ .../test_sdl_persistent_storage_attributes.ts | 26 --- 5 files changed, 240 insertions(+), 29 deletions(-) create mode 100644 tests/__snapshots__/sdl_persistent_storage_attributes.spec.ts.snap create mode 100644 tests/sdl_persistent_storage_attributes.spec.ts delete mode 100644 tests/test_sdl_persistent_storage_attributes.ts diff --git a/jest.config.js b/jest.config.js index 11bab35..374548a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,7 +12,7 @@ module.exports = { { displayName: "unit", ...common, - testMatch: ["/src/**/*.spec.ts"], + testMatch: ["/src/**/*.spec.ts", "/tests/**/*.spec.ts"], setupFilesAfterEnv: ["./test/setup-unit-tests.ts"] } ] diff --git a/src/sdl/index.ts b/src/sdl/index.ts index c7f40dc..f11c361 100644 --- a/src/sdl/index.ts +++ b/src/sdl/index.ts @@ -38,7 +38,7 @@ const Endpoint_RANDOM_PORT = 1; const Endpoint_LEASED_IP = 2; function isArray(obj: any): obj is Array { - return obj && obj.map !== undefined; + return Array.isArray(obj); } function isString(str: any): str is string { @@ -107,7 +107,7 @@ export class SDL { } } - static validateStorage(name: string, storage: v2ResourceStorage | v2ResourceStorageArray | undefined) { + static validateStorage(name: string, storage?: v2ResourceStorage | v2ResourceStorageArray) { if (!storage) { throw new Error("Storage is required for service " + name); } diff --git a/tests/__snapshots__/sdl_persistent_storage_attributes.spec.ts.snap b/tests/__snapshots__/sdl_persistent_storage_attributes.spec.ts.snap new file mode 100644 index 0000000..73647a7 --- /dev/null +++ b/tests/__snapshots__/sdl_persistent_storage_attributes.spec.ts.snap @@ -0,0 +1,213 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test sdl persistent storage SDL: Persistent Storage Manifest: SDL: Persistent Storage Manifest 1`] = ` +[ + { + "Name": "akash", + "Services": [ + { + "Args": null, + "Command": null, + "Count": 1, + "Env": [ + "WORDPRESS_DB_HOST=db", + "WORDPRESS_DB_USER=wordpress", + "WORDPRESS_DB_PASSWORD=testpass4you", + "WORDPRESS_DB_NAME=wordpress", + ], + "Expose": [ + { + "EndpointSequenceNumber": 0, + "ExternalPort": 0, + "Global": true, + "HTTPOptions": { + "MaxBodySize": 104857600, + "NextCases": [ + "error", + "timeout", + ], + "NextTimeout": 0, + "NextTries": 3, + "ReadTimeout": 60000, + "SendTimeout": 60000, + }, + "Hosts": null, + "IP": "", + "Port": 80, + "Proto": "TCP", + "Service": "", + }, + ], + "Image": "wordpress", + "Name": "wordpress", + "Resources": { + "cpu": { + "units": { + "val": "4000", + }, + }, + "endpoints": null, + "memory": { + "size": { + "val": 4294967296, + }, + }, + "storage": [ + { + "name": "default", + "size": { + "val": 4294967296, + }, + }, + { + "attributes": [ + { + "key": "class", + "value": "beta3", + }, + { + "key": "persistent", + "value": "true", + }, + ], + "name": "wordpress-data", + "size": { + "val": 34359738368, + }, + }, + ], + }, + "params": { + "Storage": [ + { + "mount": "/var/www/html", + "name": "wordpress-data", + "readOnly": false, + }, + ], + }, + }, + { + "Args": null, + "Command": null, + "Count": 1, + "Env": [ + "MYSQL_RANDOM_ROOT_PASSWORD=1", + "MYSQL_DATABASE=wordpress", + "MYSQL_USER=wordpress", + "MYSQL_PASSWORD=testpass4you", + ], + "Expose": [ + { + "EndpointSequenceNumber": 0, + "ExternalPort": 0, + "Global": false, + "HTTPOptions": { + "MaxBodySize": 1048576, + "NextCases": [ + "error", + "timeout", + ], + "NextTimeout": 0, + "NextTries": 3, + "ReadTimeout": 60000, + "SendTimeout": 60000, + }, + "Hosts": null, + "IP": "", + "Port": 3306, + "Proto": "TCP", + "Service": "wordpress", + }, + { + "EndpointSequenceNumber": 0, + "ExternalPort": 0, + "Global": false, + "HTTPOptions": { + "MaxBodySize": 1048576, + "NextCases": [ + "error", + "timeout", + ], + "NextTimeout": 0, + "NextTries": 3, + "ReadTimeout": 60000, + "SendTimeout": 60000, + }, + "Hosts": null, + "IP": "", + "Port": 33060, + "Proto": "TCP", + "Service": "wordpress", + }, + ], + "Image": "mariadb:10.6.4", + "Name": "db", + "Resources": { + "cpu": { + "units": { + "val": "1000", + }, + }, + "endpoints": null, + "memory": { + "size": { + "val": 1073741824, + }, + }, + "storage": [ + { + "name": "default", + "size": { + "val": 1073741824, + }, + }, + { + "attributes": [ + { + "key": "class", + "value": "beta3", + }, + { + "key": "persistent", + "value": "true", + }, + ], + "name": "wordpress-db", + "size": { + "val": 8589934592, + }, + }, + { + "attributes": [ + { + "key": "class", + "value": "ram", + }, + ], + "name": "shm", + "size": { + "val": 1073741824, + }, + }, + ], + }, + "params": { + "Storage": [ + { + "mount": "/var/lib/mysql", + "name": "wordpress-db", + "readOnly": false, + }, + { + "mount": "/dev/shm", + "name": "shm", + "readOnly": false, + }, + ], + }, + }, + ], + }, +] +`; diff --git a/tests/sdl_persistent_storage_attributes.spec.ts b/tests/sdl_persistent_storage_attributes.spec.ts new file mode 100644 index 0000000..a55d71a --- /dev/null +++ b/tests/sdl_persistent_storage_attributes.spec.ts @@ -0,0 +1,24 @@ +import fs from "fs"; + +import { SDL } from "../src/sdl"; + +describe("test sdl persistent storage", () => { + it("SDL: Persistent Storage Manifest", () => { + const validSDL = fs.readFileSync("./tests/fixtures/persistent_storage_valid.sdl.yml", "utf8"); + + const sdl = SDL.fromString(validSDL, "beta2"); + const result = sdl.manifest(); + + expect(result).toMatchSnapshot("SDL: Persistent Storage Manifest"); + }); + + it("SDL: Persistent Storage with class 'ram' must have 'persistent' set to false", () => { + const invalidSDL = fs.readFileSync("./tests/fixtures/persistent_storage_invalid.sdl.yml", "utf8"); + + const t = () => { + SDL.fromString(invalidSDL, "beta2"); + }; + + expect(t).toThrow("Storage attribute 'ram' must have 'persistent' set to 'false' or not defined for service grafana"); + }); +}); diff --git a/tests/test_sdl_persistent_storage_attributes.ts b/tests/test_sdl_persistent_storage_attributes.ts deleted file mode 100644 index e54d540..0000000 --- a/tests/test_sdl_persistent_storage_attributes.ts +++ /dev/null @@ -1,26 +0,0 @@ -import tap from "tap"; -import fs from "fs"; - -import { SDL } from "../src/sdl"; - -tap.test("SDL: Persistent Storage Manifest", async t => { - t.plan(1); - - const validSDL = fs.readFileSync("./tests/fixtures/persistent_storage_valid.sdl.yml", "utf8"); - - const sdl = SDL.fromString(validSDL, "beta2"); - const result = sdl.manifest(); - - t.matchSnapshot(result, "Manifest matches expected result"); -}); - -tap.test("SDL: Persistent Storage with class 'ram' must have 'persistent' set to false", async t => { - t.plan(1); - - const invalidSDL = fs.readFileSync("./tests/fixtures/persistent_storage_invalid.sdl.yml", "utf8"); - - t.throws(() => { - const sdl = SDL.fromString(invalidSDL, "beta2"); - sdl.manifest(); - }, "Storage attribute 'ram' must have 'persistent' set to 'false' or not defined for service grafana"); -}); From ec9298042ed32b6144b38a2ce51d2961d2e9d437 Mon Sep 17 00:00:00 2001 From: Maxime Beauchamp Date: Tue, 19 Mar 2024 13:22:03 -0400 Subject: [PATCH 4/4] remove tap snapshot for persistent storage --- ..._persistent_storage_attributes.ts.test.cjs | 218 ------------------ 1 file changed, 218 deletions(-) delete mode 100644 tap-snapshots/tests/test_sdl_persistent_storage_attributes.ts.test.cjs diff --git a/tap-snapshots/tests/test_sdl_persistent_storage_attributes.ts.test.cjs b/tap-snapshots/tests/test_sdl_persistent_storage_attributes.ts.test.cjs deleted file mode 100644 index 8ae11c4..0000000 --- a/tap-snapshots/tests/test_sdl_persistent_storage_attributes.ts.test.cjs +++ /dev/null @@ -1,218 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`tests/test_sdl_persistent_storage_attributes.ts TAP SDL: Persistent Storage Manifest > Manifest matches expected result 1`] = ` -Array [ - Object { - "Name": "akash", - "Services": Array [ - Object { - "Args": null, - "Command": null, - "Count": 1, - "Env": Array [ - "WORDPRESS_DB_HOST=db", - "WORDPRESS_DB_USER=wordpress", - "WORDPRESS_DB_PASSWORD=testpass4you", - "WORDPRESS_DB_NAME=wordpress", - ], - "Expose": Array [ - Object { - "EndpointSequenceNumber": 0, - "ExternalPort": 0, - "Global": true, - "Hosts": null, - "HTTPOptions": Object { - "MaxBodySize": 104857600, - "NextCases": Array [ - "error", - "timeout", - ], - "NextTimeout": 0, - "NextTries": 3, - "ReadTimeout": 60000, - "SendTimeout": 60000, - }, - "IP": "", - "Port": 80, - "Proto": "TCP", - "Service": "", - }, - ], - "Image": "wordpress", - "Name": "wordpress", - "params": Object { - "Storage": Array [ - Object { - "mount": "/var/www/html", - "name": "wordpress-data", - "readOnly": false, - }, - ], - }, - "Resources": Object { - "cpu": Object { - "units": Object { - "val": "4000", - }, - }, - "endpoints": null, - "memory": Object { - "size": Object { - "val": 4294967296, - }, - }, - "storage": Array [ - Object { - "name": "default", - "size": Object { - "val": 4294967296, - }, - }, - Object { - "attributes": Array [ - Object { - "key": "class", - "value": "beta3", - }, - Object { - "key": "persistent", - "value": "true", - }, - ], - "name": "wordpress-data", - "size": Object { - "val": 34359738368, - }, - }, - ], - }, - }, - Object { - "Args": null, - "Command": null, - "Count": 1, - "Env": Array [ - "MYSQL_RANDOM_ROOT_PASSWORD=1", - "MYSQL_DATABASE=wordpress", - "MYSQL_USER=wordpress", - "MYSQL_PASSWORD=testpass4you", - ], - "Expose": Array [ - Object { - "EndpointSequenceNumber": 0, - "ExternalPort": 0, - "Global": false, - "Hosts": null, - "HTTPOptions": Object { - "MaxBodySize": 1048576, - "NextCases": Array [ - "error", - "timeout", - ], - "NextTimeout": 0, - "NextTries": 3, - "ReadTimeout": 60000, - "SendTimeout": 60000, - }, - "IP": "", - "Port": 3306, - "Proto": "TCP", - "Service": "wordpress", - }, - Object { - "EndpointSequenceNumber": 0, - "ExternalPort": 0, - "Global": false, - "Hosts": null, - "HTTPOptions": Object { - "MaxBodySize": 1048576, - "NextCases": Array [ - "error", - "timeout", - ], - "NextTimeout": 0, - "NextTries": 3, - "ReadTimeout": 60000, - "SendTimeout": 60000, - }, - "IP": "", - "Port": 33060, - "Proto": "TCP", - "Service": "wordpress", - }, - ], - "Image": "mariadb:10.6.4", - "Name": "db", - "params": Object { - "Storage": Array [ - Object { - "mount": "/var/lib/mysql", - "name": "wordpress-db", - "readOnly": false, - }, - Object { - "mount": "/dev/shm", - "name": "shm", - "readOnly": false, - }, - ], - }, - "Resources": Object { - "cpu": Object { - "units": Object { - "val": "1000", - }, - }, - "endpoints": null, - "memory": Object { - "size": Object { - "val": 1073741824, - }, - }, - "storage": Array [ - Object { - "name": "default", - "size": Object { - "val": 1073741824, - }, - }, - Object { - "attributes": Array [ - Object { - "key": "class", - "value": "beta3", - }, - Object { - "key": "persistent", - "value": "true", - }, - ], - "name": "wordpress-db", - "size": Object { - "val": 8589934592, - }, - }, - Object { - "attributes": Array [ - Object { - "key": "class", - "value": "ram", - }, - ], - "name": "shm", - "size": Object { - "val": 1073741824, - }, - }, - ], - }, - }, - ], - }, -] -`