From f943a371afb027e20aa8535c80e5ee78737de079 Mon Sep 17 00:00:00 2001 From: Marcin Gordel Date: Wed, 27 Sep 2023 13:31:08 +0200 Subject: [PATCH 1/3] fix(work_context): added transfer and getIp commands to the work context api JST-460 and JST-408 --- src/task/batch.spec.ts | 12 +++++++++++- src/task/batch.ts | 7 ++++++- src/task/work.spec.ts | 28 +++++++++++++++++++++++++++- src/task/work.ts | 19 ++++++++++++++++++- tests/e2e/tasks.spec.ts | 27 +++++++++++++++++++++++++++ tests/mock/fixtures/test.txt | 1 + 6 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 tests/mock/fixtures/test.txt diff --git a/src/task/batch.spec.ts b/src/task/batch.spec.ts index 8dd8c2461..6cbbbebd6 100644 --- a/src/task/batch.spec.ts +++ b/src/task/batch.spec.ts @@ -1,4 +1,4 @@ -import { DownloadFile, Run, UploadData, UploadFile } from "../script"; +import { DownloadFile, Run, Transfer, UploadData, UploadFile } from "../script"; import { Batch } from "./batch"; import { NullStorageProvider } from "../storage"; import { ActivityMock } from "../../tests/mock/activity.mock"; @@ -28,6 +28,16 @@ describe("Batch", () => { }); }); + describe("transfer()", () => { + it("should add transfer file command", async () => { + expect(batch.transfer("http://golem.network/test.txt", "/golem/file.txt")).toBe(batch); + const cmd = batch["script"]["commands"][0] as Transfer; + expect(cmd).toBeInstanceOf(Transfer); + expect(cmd["from"]).toBe("http://golem.network/test.txt"); + expect(cmd["to"]).toBe("/golem/file.txt"); + }); + }); + describe("uploadFile()", () => { it("should add upload file command", async () => { expect(batch.uploadFile("/tmp/file.txt", "/golem/file.txt")).toBe(batch); diff --git a/src/task/batch.ts b/src/task/batch.ts index a2d173cf2..0bfde823f 100644 --- a/src/task/batch.ts +++ b/src/task/batch.ts @@ -1,4 +1,4 @@ -import { DownloadFile, Run, Script, UploadFile } from "../script"; +import { DownloadFile, Run, Script, Transfer, UploadFile } from "../script"; import { Activity, Result } from "../activity"; import { StorageProvider } from "../storage/provider"; import { Logger, sleep } from "../utils"; @@ -44,6 +44,11 @@ export class Batch { return this; } + transfer(from: string, to: string): Batch { + this.script.add(new Transfer(from, to)); + return this; + } + uploadFile(src: string, dst: string): Batch { this.script.add(new UploadFile(this.storageProvider, src, dst)); return this; diff --git a/src/task/work.spec.ts b/src/task/work.spec.ts index f1e3c9814..1c3ad2656 100644 --- a/src/task/work.spec.ts +++ b/src/task/work.spec.ts @@ -1,7 +1,7 @@ import { Batch, WorkContext } from "./index"; import { LoggerMock, YagnaMock } from "../../tests/mock"; import { ActivityStateEnum, ResultState } from "../activity"; -import { DownloadData, DownloadFile, Run, Script, UploadData, UploadFile } from "../script"; +import { DownloadData, DownloadFile, Run, Script, Transfer, UploadData, UploadFile } from "../script"; import { ActivityMock } from "../../tests/mock/activity.mock"; /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -46,6 +46,19 @@ describe("Work Context", () => { }); }); + describe("transfer()", () => { + it("should execute transfer command", async () => { + const result = ActivityMock.createResult({ stdout: "Ok" }); + runSpy.mockImplementation((cmd) => { + expect(cmd).toBeInstanceOf(Transfer); + expect(cmd["from"]).toBe("http://golem.network/test.txt"); + expect(cmd["to"]).toBe("/golem/work/test.txt"); + return Promise.resolve(result); + }); + expect(await context.transfer("http://golem.network/test.txt", "/golem/work/test.txt")).toBe(result); + }); + }); + describe("uploadFile()", () => { it("should execute upload file command", async () => { const result = ActivityMock.createResult(); @@ -190,6 +203,19 @@ describe("Work Context", () => { }); }); + describe("getIp()", () => { + it("should throw error if there is no network node", () => { + expect(context["networkNode"]).toBeUndefined(); + }); + + it("should return ip address of provider vpn network node", () => { + (context as any)["networkNode"] = { + ip: "192.168.0.2", + }; + expect(context.getIp()).toEqual("192.168.0.2"); + }); + }); + describe("beginBatch()", () => { it("should create a batch object", () => { const o = {}; diff --git a/src/task/work.ts b/src/task/work.ts index b0860ce09..d636c0f37 100644 --- a/src/task/work.ts +++ b/src/task/work.ts @@ -8,6 +8,7 @@ import { Run, Script, Start, + Transfer, UploadData, UploadFile, } from "../script"; @@ -134,6 +135,17 @@ export class WorkContext { return this.runOneCommand(run, runOptions); } + /** + * Generic transfer command, requires the user to provide a publicly readable transfer source + * + * @param from - publicly available resource for reading. Supported protocols: file, http, ftp or gftp + * @param to - file path + * @param options Additional run options. + */ + async transfer(from: string, to: string, options?: CommandOptions): Promise { + return this.runOneCommand(new Transfer(from, to), options); + } + async uploadFile(src: string, dst: string, options?: CommandOptions): Promise { return this.runOneCommand(new UploadFile(this.storageProvider, src, dst), options); } @@ -185,7 +197,12 @@ export class WorkContext { getWebsocketUri(port: number): string { if (!this.networkNode) throw new Error("There is no network in this work context"); - return this.networkNode?.getWebsocketUri(port); + return this.networkNode.getWebsocketUri(port); + } + + getIp(): string { + if (!this.networkNode) throw new Error("There is no network in this work context"); + return this.networkNode.ip.toString(); } async getState(): Promise { diff --git a/tests/e2e/tasks.spec.ts b/tests/e2e/tasks.spec.ts index c2ee777a8..0d070e86c 100644 --- a/tests/e2e/tasks.spec.ts +++ b/tests/e2e/tasks.spec.ts @@ -1,6 +1,7 @@ import { LoggerMock } from "../mock"; import { readFileSync } from "fs"; import { TaskExecutor } from "../../src"; +import fs from "fs"; const logger = new LoggerMock(false); describe("Task Executor", function () { @@ -153,4 +154,30 @@ describe("Task Executor", function () { expect(result).toEqual("Ok"); expect(readFileSync(`${process.env.GOTH_GFTP_VOLUME || ""}new_test.json`, "utf-8")).toEqual('{"test":"1234"}'); }); + + it("should run transfer file via http", async () => { + executor = await TaskExecutor.create({ + package: "golem/alpine:latest", + logger, + }); + const result = await executor.run(async (ctx) => { + const res = await ctx.transfer( + "http://registry.golem.network/download/a2bb9119476179fac36149723c3ad4474d8d135e8d2d2308eb79907a6fc74dfa", + "/golem/work/alpine.gvmi", + ); + return res.result; + }); + expect(result).toEqual("Ok"); + }); + + it("should get ip address", async () => { + executor = await TaskExecutor.create({ + package: "golem/alpine:latest", + capabilities: ["vpn"], + networkIp: "192.168.0.0/24", + logger, + }); + const result = await executor.run(async (ctx) => ctx.getIp()); + expect(["192.168.0.2", "192.168.0.3"]).toContain(result); + }); }); diff --git a/tests/mock/fixtures/test.txt b/tests/mock/fixtures/test.txt new file mode 100644 index 000000000..5cd710173 --- /dev/null +++ b/tests/mock/fixtures/test.txt @@ -0,0 +1 @@ +Hello Golem \ No newline at end of file From 72f31eb00095c8a030835c5d00c8332cbfd434ee Mon Sep 17 00:00:00 2001 From: Marcin Gordel Date: Wed, 27 Sep 2023 13:32:07 +0200 Subject: [PATCH 2/3] refactor(tests): deleted unused fixture --- tests/mock/fixtures/test.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tests/mock/fixtures/test.txt diff --git a/tests/mock/fixtures/test.txt b/tests/mock/fixtures/test.txt deleted file mode 100644 index 5cd710173..000000000 --- a/tests/mock/fixtures/test.txt +++ /dev/null @@ -1 +0,0 @@ -Hello Golem \ No newline at end of file From 777b82dd1c802a6863775315e1cd5ada189fa827 Mon Sep 17 00:00:00 2001 From: Marcin Gordel Date: Fri, 29 Sep 2023 13:03:30 +0200 Subject: [PATCH 3/3] test(examples): fixed unit test --- src/task/work.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/task/work.spec.ts b/src/task/work.spec.ts index 1c3ad2656..362d07de3 100644 --- a/src/task/work.spec.ts +++ b/src/task/work.spec.ts @@ -190,7 +190,7 @@ describe("Work Context", () => { describe("getWebsocketUri()", () => { it("should throw error if there is no network node", () => { - expect(context["networkNode"]).toBeUndefined(); + expect(() => context.getIp()).toThrow(new Error("There is no network in this work context")); }); it("should return websocket URI", () => { @@ -205,7 +205,7 @@ describe("Work Context", () => { describe("getIp()", () => { it("should throw error if there is no network node", () => { - expect(context["networkNode"]).toBeUndefined(); + expect(() => context.getIp()).toThrow(new Error("There is no network in this work context")); }); it("should return ip address of provider vpn network node", () => {