diff --git a/.github/workflows/grid_client_nightly.yml b/.github/workflows/grid_client_nightly.yml index 07623b11be..c6f40644d9 100644 --- a/.github/workflows/grid_client_nightly.yml +++ b/.github/workflows/grid_client_nightly.yml @@ -26,15 +26,15 @@ jobs: - uses: actions/checkout@v4 if: ${{ env.NETWORK == 'qa' }} with: - ref: refs/tags/v2.3.5 + ref: refs/tags/v2.4.3 - uses: actions/checkout@v4 if: ${{ env.NETWORK == 'test' }} with: - ref: refs/tags/v2.3.5 + ref: refs/tags/v2.4.3 - uses: actions/checkout@v4 if: ${{ env.NETWORK == 'main' }} with: - ref: refs/tags/v2.3.5 + ref: refs/tags/v2.4.3 - name: Set up node 18 uses: actions/setup-node@v3 diff --git a/.github/workflows/mass_deployments.yml b/.github/workflows/mass_deployments.yml index bf10d35e46..475b1b84cf 100644 --- a/.github/workflows/mass_deployments.yml +++ b/.github/workflows/mass_deployments.yml @@ -19,6 +19,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + ref: refs/tags/v2.4.3 - name: Set up node 18 uses: actions/setup-node@v3 with: diff --git a/lerna.json b/lerna.json index dbd7ddb204..4b44c82e1f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", - "version": "2.5.0-rc1", + "version": "2.5.0-rc2", "npmClient": "yarn" } diff --git a/packages/UI/package.json b/packages/UI/package.json index c650311c7f..0dad113737 100644 --- a/packages/UI/package.json +++ b/packages/UI/package.json @@ -1,6 +1,6 @@ { "name": "@threefold/ui", - "version": "2.5.0-rc1", + "version": "2.5.0-rc2", "private": false, "main": "dist/threefold-ui.umd.js", "publishConfig": { diff --git a/packages/graphql_client/package.json b/packages/graphql_client/package.json index 40040a0a0d..76ac37b111 100644 --- a/packages/graphql_client/package.json +++ b/packages/graphql_client/package.json @@ -1,6 +1,6 @@ { "name": "@threefold/graphql_client", - "version": "2.5.0-rc1", + "version": "2.5.0-rc2", "main": "dist/index.js", "types": "dist/index.d.ts", "license": "MIT", @@ -8,7 +8,7 @@ "build": "tsc" }, "dependencies": { - "@threefold/types": "^2.5.0-rc1", + "@threefold/types": "^2.5.0-rc2", "ts-mixer": "^6.0.2" }, "devDependencies": { diff --git a/packages/grid_client/package.json b/packages/grid_client/package.json index c7410a2287..fd431c70c1 100644 --- a/packages/grid_client/package.json +++ b/packages/grid_client/package.json @@ -1,7 +1,7 @@ { "name": "@threefold/grid_client", "author": "Ahmed Hanafy", - "version": "2.5.0-rc1", + "version": "2.5.0-rc2", "license": "ISC", "homepage": "https://github.com/threefoldtech/tfgrid-sdk-ts/tree/development/packages/grid_client/README.md", "repository": { @@ -14,9 +14,9 @@ "dependencies": { "@jimber/pkid": "1.0.4", "@noble/secp256k1": "^1.7.1", - "@threefold/rmb_direct_client": "^2.5.0-rc1", - "@threefold/tfchain_client": "^2.5.0-rc1", - "@threefold/types": "^2.5.0-rc1", + "@threefold/rmb_direct_client": "^2.5.0-rc2", + "@threefold/tfchain_client": "^2.5.0-rc2", + "@threefold/types": "^2.5.0-rc2", "algosdk": "^1.19.0", "appdata-path": "^1.0.0", "await-lock": "^2.2.2", diff --git a/packages/grid_client/tests/modules/applications/casperlabs.test.ts b/packages/grid_client/tests/modules/applications/casperlabs.test.ts new file mode 100644 index 0000000000..8b381d7316 --- /dev/null +++ b/packages/grid_client/tests/modules/applications/casperlabs.test.ts @@ -0,0 +1,240 @@ +import axios from "axios"; +import { setTimeout } from "timers/promises"; + +import { FilterOptions, GatewayNameModel, generateString, GridClient, MachinesModel, randomChoice } from "../../../src"; +import { config, getClient } from "../../client_loader"; +import { bytesToGB, generateInt, getOnlineNode, log, splitIP } from "../../utils"; + +jest.setTimeout(900000); + +let gridClient: GridClient; +let deploymentName: string; + +beforeAll(async () => { + gridClient = await getClient(); + deploymentName = "cl" + generateString(10); + gridClient.clientOptions.projectName = `casperlabs/${deploymentName}`; + gridClient._connect(); + return gridClient; +}); + +//Private IP Regex +const ipRegex = /(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/; + +test("TC2683 - Applications: Deploy Casperlabs", async () => { + /********************************************** + Test Suite: Grid3_Client_TS (Automated) + Test Cases: TC2683 - Applications: Deploy Casperlabs + Scenario: + - Generate Test Data/casperlabs Config/Gateway Config. + - Select a Node To Deploy the casperlabs on. + - Select a Gateway Node To Deploy the gateway on. + - Deploy the casperlabs solution. + - Assert that the generated data matches + the deployment details. + - Pass the IP of the Created casperlabs to the Gateway + Config. + - Deploy the Gateway. + - Assert that the generated data matches + the deployment details. + - Assert that the Gateway points at the IP + of the created casperlabs. + - Assert that the returned domain is working + and returns correct data. + **********************************************/ + + //Test Data + const name = "gw" + generateString(10).toLowerCase(); + const tlsPassthrough = false; + const cpu = 2; + const memory = 4; + const rootfsSize = 2; + const diskSize = 100; + const networkName = generateString(15); + const vmName = generateString(15); + const diskName = generateString(15); + const mountPoint = "/data"; + const publicIp = false; + const ipRangeClassA = "10." + generateInt(1, 255) + ".0.0/16"; + const ipRangeClassB = "172." + generateInt(16, 31) + ".0.0/16"; + const ipRangeClassC = "192.168.0.0/16"; + const ipRange = randomChoice([ipRangeClassA, ipRangeClassB, ipRangeClassC]); + const metadata = "{'deploymentType': 'casperlabs'}"; + const description = "test deploying Casperlabs via ts grid3 client"; + + //GatewayNode Selection + const gatewayNodes = await gridClient.capacity.filterNodes({ + gateway: true, + farmId: 1, + availableFor: await gridClient.twins.get_my_twin_id(), + } as FilterOptions); + if (gatewayNodes.length == 0) throw new Error("no gateway nodes available to complete this test"); + const GatewayNode = gatewayNodes[generateInt(0, gatewayNodes.length - 1)]; + + //Node Selection + const nodes = await gridClient.capacity.filterNodes({ + cru: cpu, + mru: memory, + sru: rootfsSize + diskSize, + farmId: 1, + availableFor: await gridClient.twins.get_my_twin_id(), + } as FilterOptions); + const nodeId = await getOnlineNode(nodes); + if (nodeId == -1) throw new Error("no nodes available to complete this test"); + const domain = name + "." + GatewayNode.publicConfig.domain; + + //VM Model + const vms: MachinesModel = { + name: deploymentName, + network: { + name: networkName, + ip_range: ipRange, + }, + machines: [ + { + name: vmName, + node_id: nodeId, + cpu: cpu, + memory: 1024 * memory, + rootfs_size: rootfsSize, + disks: [ + { + name: diskName, + size: diskSize, + mountpoint: mountPoint, + }, + ], + flist: "https://hub.grid.tf/tf-official-apps/casperlabs-latest.flist", + entrypoint: "/sbin/zinit init", + public_ip: publicIp, + planetary: true, + mycelium: false, + env: { + SSH_KEY: config.ssh_key, + CASPERLABS_HOSTNAME: domain, + }, + }, + ], + metadata: metadata, + description: description, + }; + const res = await gridClient.machines.deploy(vms); + log(res); + + //Contracts Assertions + expect(res.contracts.created).toHaveLength(1); + expect(res.contracts.updated).toHaveLength(0); + expect(res.contracts.deleted).toHaveLength(0); + + const vmsList = await gridClient.machines.list(); + log(vmsList); + + //VM List Assertions + expect(vmsList.length).toBeGreaterThanOrEqual(1); + expect(vmsList).toContain(vms.name); + + const result = await gridClient.machines.getObj(vms.name); + log(result); + + //VM Assertions + expect(result[0].nodeId).toBe(nodeId); + expect(result[0].status).toBe("ok"); + expect(result[0].flist).toBe(vms.machines[0].flist); + expect(result[0].entrypoint).toBe(vms.machines[0].entrypoint); + expect(result[0].mounts).toHaveLength(1); + expect(result[0].interfaces[0]["network"]).toBe(vms.network.name); + expect(result[0].interfaces[0]["ip"]).toContain(splitIP(vms.network.ip_range)); + expect(result[0].interfaces[0]["ip"]).toMatch(ipRegex); + expect(result[0].capacity["cpu"]).toBe(cpu); + expect(result[0].capacity["memory"]).toBe(memory * 1024); + expect(result[0].planetary).toBeDefined(); + expect(result[0].publicIP).toBeNull(); + expect(result[0].description).toBe(description); + expect(result[0].rootfs_size).toBe(bytesToGB(rootfsSize)); + expect(result[0].mounts[0]["name"]).toBe(diskName); + expect(result[0].mounts[0]["size"]).toBe(bytesToGB(diskSize)); + expect(result[0].mounts[0]["mountPoint"]).toBe(mountPoint); + expect(result[0].mounts[0]["state"]).toBe("ok"); + + const backends = ["http://[" + result[0].planetary + "]:80"]; + log(backends); + + //Name Gateway Model + const gw: GatewayNameModel = { + name: name, + node_id: GatewayNode.nodeId, + tls_passthrough: tlsPassthrough, + backends: backends, + }; + + const gatewayRes = await gridClient.gateway.deploy_name(gw); + log(gatewayRes); + + //Contracts Assertions + expect(gatewayRes.contracts.created).toHaveLength(1); + expect(gatewayRes.contracts.updated).toHaveLength(0); + expect(gatewayRes.contracts.deleted).toHaveLength(0); + expect(gatewayRes.contracts.created[0].contractType.nodeContract.nodeId).toBe(GatewayNode.nodeId); + + const gatewayResult = await gridClient.gateway.getObj(gw.name); + log(gatewayResult); + + //Gateway Assertions + expect(gatewayResult[0].name).toBe(name); + expect(gatewayResult[0].status).toBe("ok"); + expect(gatewayResult[0].type).toContain("name"); + expect(gatewayResult[0].domain).toContain(name); + expect(gatewayResult[0].tls_passthrough).toBe(tlsPassthrough); + expect(gatewayResult[0].backends).toStrictEqual(backends); + + const site = "https://" + gatewayResult[0].domain; + let reachable = false; + + for (let i = 0; i < 180; i++) { + const wait = await setTimeout(5000, "Waiting for gateway to be ready"); + log(wait); + + await axios + .get(site) + .then(res => { + log("gateway is reachable"); + log(res.status); + log(res.statusText); + log(res.data); + expect(res.status).toBe(200); + expect(res.statusText).toBe("OK"); + expect(res.data).toContain("Your Casper node is now running succesfully on the ThreeFold Grid."); + reachable = true; + }) + .catch(() => { + log("gateway is not reachable"); + }); + if (reachable) { + break; + } else if (i == 180) { + throw new Error("Gateway is unreachable after multiple retries"); + } + } +}); + +afterAll(async () => { + const vmNames = await gridClient.machines.list(); + for (const name of vmNames) { + const res = await gridClient.machines.delete({ name }); + log(res); + expect(res.created).toHaveLength(0); + expect(res.updated).toHaveLength(0); + expect(res.deleted).toBeDefined(); + } + + const gwNames = await gridClient.gateway.list(); + for (const name of gwNames) { + const res = await gridClient.gateway.delete_name({ name }); + log(res); + expect(res.created).toHaveLength(0); + expect(res.updated).toHaveLength(0); + expect(res.deleted).toBeDefined(); + } + + return await gridClient.disconnect(); +}, 130000); diff --git a/packages/grid_client/tests/modules/applications/funkwhale.test.ts b/packages/grid_client/tests/modules/applications/funkwhale.test.ts new file mode 100644 index 0000000000..b67c45f9cf --- /dev/null +++ b/packages/grid_client/tests/modules/applications/funkwhale.test.ts @@ -0,0 +1,243 @@ +import axios from "axios"; +import { setTimeout } from "timers/promises"; + +import { FilterOptions, GatewayNameModel, generateString, GridClient, MachinesModel, randomChoice } from "../../../src"; +import { config, getClient } from "../../client_loader"; +import { bytesToGB, generateInt, getOnlineNode, log, splitIP } from "../../utils"; + +jest.setTimeout(900000); + +let gridClient: GridClient; +let deploymentName: string; + +beforeAll(async () => { + gridClient = await getClient(); + deploymentName = "fw" + generateString(10); + gridClient.clientOptions.projectName = `funkwhale/${deploymentName}`; + gridClient._connect(); + return gridClient; +}); + +//Private IP Regex +const ipRegex = /(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/; + +test("TC2685 - Applications: Deploy Funkwhale", async () => { + /********************************************** + Test Suite: Grid3_Client_TS (Automated) + Test Cases: TC2685 - Applications: Deploy Funkwhale + Scenario: + - Generate Test Data/funkwhale Config/Gateway Config. + - Select a Node To Deploy the funkwhale on. + - Select a Gateway Node To Deploy the gateway on. + - Deploy the funkwhale solution. + - Assert that the generated data matches + the deployment details. + - Pass the IP of the Created funkwhale to the Gateway + Config. + - Deploy the Gateway. + - Assert that the generated data matches + the deployment details. + - Assert that the Gateway points at the IP + of the created funkwhale. + - Assert that the returned domain is working + and returns correct data. + **********************************************/ + + //Test Data + const name = "gw" + generateString(10).toLowerCase(); + const tlsPassthrough = false; + const cpu = 1; + const memory = 2; + const rootfsSize = 2; + const diskSize = 50; + const networkName = generateString(15); + const vmName = generateString(15); + const diskName = generateString(15); + const mountPoint = "/data"; + const publicIp = false; + const ipRangeClassA = "10." + generateInt(1, 255) + ".0.0/16"; + const ipRangeClassB = "172." + generateInt(16, 31) + ".0.0/16"; + const ipRangeClassC = "192.168.0.0/16"; + const ipRange = randomChoice([ipRangeClassA, ipRangeClassB, ipRangeClassC]); + const metadata = "{'deploymentType': 'funkwhale'}"; + const description = "test deploying Funkwhale via ts grid3 client"; + + //GatewayNode Selection + const gatewayNodes = await gridClient.capacity.filterNodes({ + gateway: true, + farmId: 1, + availableFor: await gridClient.twins.get_my_twin_id(), + } as FilterOptions); + if (gatewayNodes.length == 0) throw new Error("no gateway nodes available to complete this test"); + const GatewayNode = gatewayNodes[generateInt(0, gatewayNodes.length - 1)]; + + //Node Selection + const nodes = await gridClient.capacity.filterNodes({ + cru: cpu, + mru: memory, + sru: rootfsSize + diskSize, + farmId: 1, + availableFor: await gridClient.twins.get_my_twin_id(), + } as FilterOptions); + const nodeId = await getOnlineNode(nodes); + if (nodeId == -1) throw new Error("no nodes available to complete this test"); + const domain = name + "." + GatewayNode.publicConfig.domain; + + //VM Model + const vms: MachinesModel = { + name: deploymentName, + network: { + name: networkName, + ip_range: ipRange, + }, + machines: [ + { + name: vmName, + node_id: nodeId, + cpu: cpu, + memory: 1024 * memory, + rootfs_size: rootfsSize, + disks: [ + { + name: diskName, + size: diskSize, + mountpoint: mountPoint, + }, + ], + flist: "https://hub.grid.tf/tf-official-apps/funkwhale-dec21.flist", + entrypoint: "/init.sh", + public_ip: publicIp, + planetary: true, + mycelium: false, + env: { + SSH_KEY: config.ssh_key, + FUNKWHALE_HOSTNAME: domain, + DJANGO_SUPERUSER_EMAIL: "admin123@funk.whale", + DJANGO_SUPERUSER_USERNAME: "admin123", + DJANGO_SUPERUSER_PASSWORD: "admin123", + }, + }, + ], + metadata: metadata, + description: description, + }; + const res = await gridClient.machines.deploy(vms); + log(res); + + //Contracts Assertions + expect(res.contracts.created).toHaveLength(1); + expect(res.contracts.updated).toHaveLength(0); + expect(res.contracts.deleted).toHaveLength(0); + + const vmsList = await gridClient.machines.list(); + log(vmsList); + + //VM List Assertions + expect(vmsList.length).toBeGreaterThanOrEqual(1); + expect(vmsList).toContain(vms.name); + + const result = await gridClient.machines.getObj(vms.name); + log(result); + + //VM Assertions + expect(result[0].nodeId).toBe(nodeId); + expect(result[0].status).toBe("ok"); + expect(result[0].flist).toBe(vms.machines[0].flist); + expect(result[0].entrypoint).toBe(vms.machines[0].entrypoint); + expect(result[0].mounts).toHaveLength(1); + expect(result[0].interfaces[0]["network"]).toBe(vms.network.name); + expect(result[0].interfaces[0]["ip"]).toContain(splitIP(vms.network.ip_range)); + expect(result[0].interfaces[0]["ip"]).toMatch(ipRegex); + expect(result[0].capacity["cpu"]).toBe(cpu); + expect(result[0].capacity["memory"]).toBe(memory * 1024); + expect(result[0].planetary).toBeDefined(); + expect(result[0].publicIP).toBeNull(); + expect(result[0].description).toBe(description); + expect(result[0].rootfs_size).toBe(bytesToGB(rootfsSize)); + expect(result[0].mounts[0]["name"]).toBe(diskName); + expect(result[0].mounts[0]["size"]).toBe(bytesToGB(diskSize)); + expect(result[0].mounts[0]["mountPoint"]).toBe(mountPoint); + expect(result[0].mounts[0]["state"]).toBe("ok"); + + const backends = ["http://[" + result[0].planetary + "]:80"]; + log(backends); + + //Name Gateway Model + const gw: GatewayNameModel = { + name: name, + node_id: GatewayNode.nodeId, + tls_passthrough: tlsPassthrough, + backends: backends, + }; + + const gatewayRes = await gridClient.gateway.deploy_name(gw); + log(gatewayRes); + + //Contracts Assertions + expect(gatewayRes.contracts.created).toHaveLength(1); + expect(gatewayRes.contracts.updated).toHaveLength(0); + expect(gatewayRes.contracts.deleted).toHaveLength(0); + expect(gatewayRes.contracts.created[0].contractType.nodeContract.nodeId).toBe(GatewayNode.nodeId); + + const gatewayResult = await gridClient.gateway.getObj(gw.name); + log(gatewayResult); + + //Gateway Assertions + expect(gatewayResult[0].name).toBe(name); + expect(gatewayResult[0].status).toBe("ok"); + expect(gatewayResult[0].type).toContain("name"); + expect(gatewayResult[0].domain).toContain(name); + expect(gatewayResult[0].tls_passthrough).toBe(tlsPassthrough); + expect(gatewayResult[0].backends).toStrictEqual(backends); + + const site = "https://" + gatewayResult[0].domain; + let reachable = false; + + for (let i = 0; i < 180; i++) { + const wait = await setTimeout(5000, "Waiting for gateway to be ready"); + log(wait); + + await axios + .get(site) + .then(res => { + log("gateway is reachable"); + log(res.status); + log(res.statusText); + log(res.data); + expect(res.status).toBe(200); + expect(res.statusText).toBe("OK"); + expect(res.data).toContain("Funkwhale"); + reachable = true; + }) + .catch(() => { + log("gateway is not reachable"); + }); + if (reachable) { + break; + } else if (i == 180) { + throw new Error("Gateway is unreachable after multiple retries"); + } + } +}); + +afterAll(async () => { + const vmNames = await gridClient.machines.list(); + for (const name of vmNames) { + const res = await gridClient.machines.delete({ name }); + log(res); + expect(res.created).toHaveLength(0); + expect(res.updated).toHaveLength(0); + expect(res.deleted).toBeDefined(); + } + + const gwNames = await gridClient.gateway.list(); + for (const name of gwNames) { + const res = await gridClient.gateway.delete_name({ name }); + log(res); + expect(res.created).toHaveLength(0); + expect(res.updated).toHaveLength(0); + expect(res.deleted).toBeDefined(); + } + + return await gridClient.disconnect(); +}, 130000); diff --git a/packages/grid_client/tests/modules/applications/peertube.test.ts b/packages/grid_client/tests/modules/applications/peertube.test.ts new file mode 100644 index 0000000000..3fed6df2ad --- /dev/null +++ b/packages/grid_client/tests/modules/applications/peertube.test.ts @@ -0,0 +1,250 @@ +import axios from "axios"; +import { setTimeout } from "timers/promises"; + +import { FilterOptions, GatewayNameModel, generateString, GridClient, MachinesModel, randomChoice } from "../../../src"; +import { config, getClient } from "../../client_loader"; +import { bytesToGB, generateInt, getOnlineNode, log, splitIP } from "../../utils"; + +jest.setTimeout(900000); + +let gridClient: GridClient; +let deploymentName: string; + +beforeAll(async () => { + gridClient = await getClient(); + deploymentName = "pt" + generateString(10); + gridClient.clientOptions.projectName = `peertube/${deploymentName}`; + gridClient._connect(); + return gridClient; +}); + +//Private IP Regex +const ipRegex = /(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/; + +test("TC2684 - Applications: Deploy Peertube", async () => { + /********************************************** + Test Suite: Grid3_Client_TS (Automated) + Test Cases: TC2684 - Applications: Deploy Peertube + Scenario: + - Generate Test Data/peertube Config/Gateway Config. + - Select a Node To Deploy the peertube on. + - Select a Gateway Node To Deploy the gateway on. + - Deploy the peertube solution. + - Assert that the generated data matches + the deployment details. + - Pass the IP of the Created peertube to the Gateway + Config. + - Deploy the Gateway. + - Assert that the generated data matches + the deployment details. + - Assert that the Gateway points at the IP + of the created peertube. + - Assert that the returned domain is working + and returns correct data. + **********************************************/ + + //Test Data + const name = "gw" + generateString(10).toLowerCase(); + const tlsPassthrough = false; + const cpu = 1; + const memory = 2; + const rootfsSize = 2; + const diskSize = 15; + const networkName = generateString(15); + const vmName = generateString(15); + const diskName = generateString(15); + const mountPoint = "/data"; + const publicIp = false; + const ipRangeClassA = "10." + generateInt(1, 255) + ".0.0/16"; + const ipRangeClassB = "172." + generateInt(16, 31) + ".0.0/16"; + const ipRangeClassC = "192.168.0.0/16"; + const ipRange = randomChoice([ipRangeClassA, ipRangeClassB, ipRangeClassC]); + const metadata = "{'deploymentType': 'peertube'}"; + const description = "test deploying Peertube via ts grid3 client"; + + //GatewayNode Selection + const gatewayNodes = await gridClient.capacity.filterNodes({ + gateway: true, + farmId: 1, + availableFor: await gridClient.twins.get_my_twin_id(), + } as FilterOptions); + if (gatewayNodes.length == 0) throw new Error("no gateway nodes available to complete this test"); + const GatewayNode = gatewayNodes[generateInt(0, gatewayNodes.length - 1)]; + + //Node Selection + const nodes = await gridClient.capacity.filterNodes({ + cru: cpu, + mru: memory, + sru: rootfsSize + diskSize, + farmId: 1, + availableFor: await gridClient.twins.get_my_twin_id(), + } as FilterOptions); + const nodeId = await getOnlineNode(nodes); + if (nodeId == -1) throw new Error("no nodes available to complete this test"); + const domain = name + "." + GatewayNode.publicConfig.domain; + + //VM Model + const vms: MachinesModel = { + name: deploymentName, + network: { + name: networkName, + ip_range: ipRange, + }, + machines: [ + { + name: vmName, + node_id: nodeId, + cpu: cpu, + memory: 1024 * memory, + rootfs_size: rootfsSize, + disks: [ + { + name: diskName, + size: diskSize, + mountpoint: mountPoint, + }, + ], + flist: "https://hub.grid.tf/tf-official-apps/peertube-v3.1.1.flist", + entrypoint: "/sbin/zinit init", + public_ip: publicIp, + planetary: true, + mycelium: false, + env: { + SSH_KEY: config.ssh_key, + PEERTUBE_WEBSERVER_HOSTNAME: domain, + PEERTUBE_ADMIN_EMAIL: "admin123@peer.tube", + PT_INITIAL_ROOT_PASSWORD: "admin123", + }, + }, + ], + metadata: metadata, + description: description, + }; + const res = await gridClient.machines.deploy(vms); + log(res); + + //Contracts Assertions + expect(res.contracts.created).toHaveLength(1); + expect(res.contracts.updated).toHaveLength(0); + expect(res.contracts.deleted).toHaveLength(0); + + const vmsList = await gridClient.machines.list(); + log(vmsList); + + //VM List Assertions + expect(vmsList.length).toBeGreaterThanOrEqual(1); + expect(vmsList).toContain(vms.name); + + const result = await gridClient.machines.getObj(vms.name); + log(result); + + //VM Assertions + expect(result[0].nodeId).toBe(nodeId); + expect(result[0].status).toBe("ok"); + expect(result[0].flist).toBe(vms.machines[0].flist); + expect(result[0].entrypoint).toBe(vms.machines[0].entrypoint); + expect(result[0].mounts).toHaveLength(1); + expect(result[0].interfaces[0]["network"]).toBe(vms.network.name); + expect(result[0].interfaces[0]["ip"]).toContain(splitIP(vms.network.ip_range)); + expect(result[0].interfaces[0]["ip"]).toMatch(ipRegex); + expect(result[0].capacity["cpu"]).toBe(cpu); + expect(result[0].capacity["memory"]).toBe(memory * 1024); + expect(result[0].planetary).toBeDefined(); + expect(result[0].publicIP).toBeNull(); + expect(result[0].description).toBe(description); + expect(result[0].rootfs_size).toBe(bytesToGB(rootfsSize)); + expect(result[0].mounts[0]["name"]).toBe(diskName); + expect(result[0].mounts[0]["size"]).toBe(bytesToGB(diskSize)); + expect(result[0].mounts[0]["mountPoint"]).toBe(mountPoint); + expect(result[0].mounts[0]["state"]).toBe("ok"); + + const backends = ["http://[" + result[0].planetary + "]:9000"]; + log(backends); + + //Name Gateway Model + const gw: GatewayNameModel = { + name: name, + node_id: GatewayNode.nodeId, + tls_passthrough: tlsPassthrough, + backends: backends, + }; + + const gatewayRes = await gridClient.gateway.deploy_name(gw); + log(gatewayRes); + + //Contracts Assertions + expect(gatewayRes.contracts.created).toHaveLength(1); + expect(gatewayRes.contracts.updated).toHaveLength(0); + expect(gatewayRes.contracts.deleted).toHaveLength(0); + expect(gatewayRes.contracts.created[0].contractType.nodeContract.nodeId).toBe(GatewayNode.nodeId); + + const gatewayResult = await gridClient.gateway.getObj(gw.name); + log(gatewayResult); + + //Gateway Assertions + expect(gatewayResult[0].name).toBe(name); + expect(gatewayResult[0].status).toBe("ok"); + expect(gatewayResult[0].type).toContain("name"); + expect(gatewayResult[0].domain).toContain(name); + expect(gatewayResult[0].tls_passthrough).toBe(tlsPassthrough); + expect(gatewayResult[0].backends).toStrictEqual(backends); + + const site = "https://" + gatewayResult[0].domain; + let reachable = false; + const header = + "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"; + + for (let i = 0; i < 180; i++) { + const wait = await setTimeout(5000, "Waiting for gateway to be ready"); + log(wait); + + await axios + .get(site, { + headers: { + Accept: header, + }, + }) + .then(res => { + log("gateway is reachable"); + log(res.status); + log(res.statusText); + log(res.data); + expect(res.status).toBe(200); + expect(res.statusText).toBe("OK"); + expect(res.data).toContain( + "PeerTube, an ActivityPub-federated video streaming platform using P2P directly in your web browser.", + ); + reachable = true; + }) + .catch(() => { + log("gateway is not reachable"); + }); + if (reachable) { + break; + } else if (i == 180) { + throw new Error("Gateway is unreachable after multiple retries"); + } + } +}); + +afterAll(async () => { + const vmNames = await gridClient.machines.list(); + for (const name of vmNames) { + const res = await gridClient.machines.delete({ name }); + log(res); + expect(res.created).toHaveLength(0); + expect(res.updated).toHaveLength(0); + expect(res.deleted).toBeDefined(); + } + + const gwNames = await gridClient.gateway.list(); + for (const name of gwNames) { + const res = await gridClient.gateway.delete_name({ name }); + log(res); + expect(res.created).toHaveLength(0); + expect(res.updated).toHaveLength(0); + expect(res.deleted).toBeDefined(); + } + + return await gridClient.disconnect(); +}, 130000); diff --git a/packages/grid_http_server/package.json b/packages/grid_http_server/package.json index 5fee6d6bc6..3b4ad42ce8 100644 --- a/packages/grid_http_server/package.json +++ b/packages/grid_http_server/package.json @@ -1,7 +1,7 @@ { "name": "@threefold/grid_http_server", "author": "Ahmed Hanafy", - "version": "2.5.0-rc1", + "version": "2.5.0-rc2", "license": "ISC", "homepage": "https://github.com/threefoldtech/tfgrid-sdk-ts/blob/development/packages/grid_http_server/README.md", "repository": { @@ -12,7 +12,7 @@ "access": "public" }, "dependencies": { - "@threefold/grid_client": "^2.5.0-rc1", + "@threefold/grid_client": "^2.5.0-rc2", "express": "^4.18.1", "http-server": "^14.1.1", "typescript": "^4.7.4" diff --git a/packages/grid_rmb_server/package.json b/packages/grid_rmb_server/package.json index 917fa242c9..4f7290a7bd 100644 --- a/packages/grid_rmb_server/package.json +++ b/packages/grid_rmb_server/package.json @@ -1,7 +1,7 @@ { "name": "@threefold/grid_rmb_server", "author": "Ahmed Hanafy", - "version": "2.5.0-rc1", + "version": "2.5.0-rc2", "license": "ISC", "homepage": "https://github.com/threefoldtech/tfgrid-sdk-ts/blob/development/packages/grid_rmb_server/README.md", "repository": { @@ -12,12 +12,12 @@ "access": "public" }, "dependencies": { - "@threefold/grid_client": "^2.5.0-rc1", - "@threefold/rmb_peer_server": "^2.5.0-rc1", + "@threefold/grid_client": "^2.5.0-rc2", + "@threefold/rmb_peer_server": "^2.5.0-rc2", "typescript": "^4.7.4" }, "devDependencies": { - "@threefold/rmb_peer_client": "^2.5.0-rc1", + "@threefold/rmb_peer_client": "^2.5.0-rc2", "ts-node": "^10.9.1" }, "main": "./dist/index.js", diff --git a/packages/gridproxy_client/package.json b/packages/gridproxy_client/package.json index d8b4f436c2..179af24f74 100644 --- a/packages/gridproxy_client/package.json +++ b/packages/gridproxy_client/package.json @@ -1,6 +1,6 @@ { "name": "@threefold/gridproxy_client", - "version": "2.5.0-rc1", + "version": "2.5.0-rc2", "description": "gridproxy_client help to interact with gridproxy based on network", "main": "dist/public_api.js", "types": "dist/public_api.d.ts", diff --git a/packages/monitoring/package.json b/packages/monitoring/package.json index b8c3b2320e..a4a2e0973a 100644 --- a/packages/monitoring/package.json +++ b/packages/monitoring/package.json @@ -1,6 +1,6 @@ { "name": "@threefold/monitoring", - "version": "2.5.0-rc1", + "version": "2.5.0-rc2", "description": "Threefold monitoring package", "license": "Apache-2.0", "main": "dist/index.js", @@ -16,9 +16,9 @@ "access": "public" }, "dependencies": { - "@threefold/rmb_direct_client": "^2.5.0-rc1", - "@threefold/tfchain_client": "^2.5.0-rc1", - "@threefold/types": "^2.5.0-rc1", + "@threefold/rmb_direct_client": "^2.5.0-rc2", + "@threefold/tfchain_client": "^2.5.0-rc2", + "@threefold/types": "^2.5.0-rc2", "axios": "^0.27.2", "chalk": "4.1.2", "ts-node": "^10.9.1", diff --git a/packages/playground/package.json b/packages/playground/package.json index 25127fe339..74698bd507 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -1,6 +1,6 @@ { "name": "@threefold/playground", - "version": "2.5.0-rc1", + "version": "2.5.0-rc2", "private": true, "scripts": { "dev": "vite", @@ -12,10 +12,10 @@ }, "dependencies": { "@mdi/font": "^7.2.96", - "@threefold/graphql_client": "^2.5.0-rc1", - "@threefold/grid_client": "^2.5.0-rc1", - "@threefold/gridproxy_client": "^2.5.0-rc1", - "@threefold/types": "^2.5.0-rc1", + "@threefold/graphql_client": "^2.5.0-rc2", + "@threefold/grid_client": "^2.5.0-rc2", + "@threefold/gridproxy_client": "^2.5.0-rc2", + "@threefold/types": "^2.5.0-rc2", "@types/ip": "^1.1.3", "@types/md5": "^2.3.5", "await-lock": "^2.2.2", diff --git a/packages/playground/playground-charts/Chart.yaml b/packages/playground/playground-charts/Chart.yaml index 9f1105de9b..38a2e93113 100644 --- a/packages/playground/playground-charts/Chart.yaml +++ b/packages/playground/playground-charts/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "v2.5.0-rc1" +appVersion: "v2.5.0-rc2" diff --git a/packages/playground/playground-charts/values.yaml b/packages/playground/playground-charts/values.yaml index ba41c8bd9c..153992d8a2 100644 --- a/packages/playground/playground-charts/values.yaml +++ b/packages/playground/playground-charts/values.yaml @@ -8,7 +8,7 @@ image: repository: ghcr.io/threefoldtech/playground pullPolicy: Always # Overrides the image tag whose default is the chart appVersion. - tag: "2.5.0-rc1" + tag: "2.5.0-rc2" env: - name: "MODE" diff --git a/packages/playground/src/components/form_validator.vue b/packages/playground/src/components/form_validator.vue index 450f09b3e0..26d51520c0 100644 --- a/packages/playground/src/components/form_validator.vue +++ b/packages/playground/src/components/form_validator.vue @@ -48,6 +48,21 @@ export default { if (statusMap.value.get(uid) !== status) { statusMap.value.set(uid, status); } + + const el = serviceMap.value.get(uid)?.$el; + if (status === ValidatorStatus.Valid && el) { + const input = + el instanceof HTMLElement + ? el + : el && typeof el === "object" && "value" in el && el.value instanceof HTMLElement + ? el.value + : null; + + if (input) { + input.classList.remove("weblet-layout-error"); + setTimeout(() => input.classList.remove("weblet-layout-error-transition"), 152); + } + } }, reset() { diff --git a/packages/playground/src/components/manage_gateway_dialog.vue b/packages/playground/src/components/manage_gateway_dialog.vue index 71d133eaf9..7ba2149584 100644 --- a/packages/playground/src/components/manage_gateway_dialog.vue +++ b/packages/playground/src/components/manage_gateway_dialog.vue @@ -273,7 +273,9 @@ export default { loadingGateways.value = true; updateGrid(grid, { projectName: props.vm.projectName }); - const { gateways: gws, failedToList } = await loadDeploymentGateways(grid!); + const { gateways: gws, failedToList } = await loadDeploymentGateways(grid!, { + filter: gw => gw.backends.some(bk => bk.includes(ip)), + }); gateways.value = gws; failedToListGws.value = failedToList; } catch (error) { diff --git a/packages/playground/src/components/networks.vue b/packages/playground/src/components/networks.vue index bcaf99923b..3565068016 100644 --- a/packages/playground/src/components/networks.vue +++ b/packages/playground/src/components/networks.vue @@ -1,100 +1,107 @@