Skip to content

Commit 0ae0ed3

Browse files
Merge pull request #300 from iExecBlockchainComputing/feature/expose-task-debug
Feature/expose task debug
2 parents 82cdac7 + 6f8a434 commit 0ae0ed3

File tree

11 files changed

+356
-19
lines changed

11 files changed

+356
-19
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ All notable changes to this project will be documented in this file.
77
### Added
88

99
- added key `isUserRejection` in `Web3ProviderError` set to `true` when the error is detected as a user rejection
10+
- `task.fetchOffchainInfo(taskid)` to get off-chain status information about the task from the workerpool
11+
- `task.fetchLogs(taskid)` to fetch app logs from the workers
1012

1113
### Changed
1214

1315
- `iexec_result_storage_proxy` default value is no more set in request params
1416
- removed deprecated request param `iexec_developer_logger`
17+
- change information exposed by `iexec task debug` for better readability
1518

1619
### Removed
1720

@@ -313,7 +316,7 @@ All notable changes to this project will be documented in this file.
313316
### Added
314317

315318
- workerpool API url configuration
316-
- `iexec task debug <taskid> [--logs]` to show offchain information
319+
- `iexec task debug <taskid> [--logs]` to show off-chain information
317320
- `ens.getDefaultDomain(address)` to get the default free to use ENS domain given an address
318321
- support for requester secrets
319322
- check dataset secret exists on requestorder check

docs/classes/IExecTaskModule.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ module exposing task methods
2323
### Methods
2424

2525
- [claim](IExecTaskModule.md#claim)
26+
- [fetchLogs](IExecTaskModule.md#fetchlogs)
27+
- [fetchOffchainInfo](IExecTaskModule.md#fetchoffchaininfo)
2628
- [fetchResults](IExecTaskModule.md#fetchresults)
2729
- [obsTask](IExecTaskModule.md#obstask)
2830
- [show](IExecTaskModule.md#show)
@@ -91,6 +93,68 @@ console.log('task claimed:', claimTxHash);
9193

9294
___
9395

96+
### fetchLogs
97+
98+
▸ **fetchLogs**(`taskid`): `Promise`<{ `stderr`: `string` ; `stdout`: `string` ; `worker`: `string` }[]\>
99+
100+
**SIGNER REQUIRED, ONLY REQUESTER**
101+
102+
get the workers logs for specified task.
103+
104+
_NB_: the workerpool must declare it's API url to enable this feature, check declared API url with `IExecWorkerpoolModule.getWorkerpoolApiUrl(workerpool)`
105+
106+
example:
107+
```js
108+
const logArray = await fetchLogs('0x668cb3e53ebbcc9999997709586c5af07f502f6120906fa3506ce1f531cedc81');
109+
logsArray.forEach(({ worker, stdout, stderr }) => {
110+
console.log(`----- worker ${worker} -----`);
111+
console.log(`stdout:\n${stdout}`);
112+
console.log(`stderr:\n${stderr}`);
113+
});
114+
```
115+
116+
#### Parameters
117+
118+
| Name | Type |
119+
| :------ | :------ |
120+
| `taskid` | `string` |
121+
122+
#### Returns
123+
124+
`Promise`<{ `stderr`: `string` ; `stdout`: `string` ; `worker`: `string` }[]\>
125+
126+
___
127+
128+
### fetchOffchainInfo
129+
130+
▸ **fetchOffchainInfo**(`taskid`): `Promise`<{ `replicates`: { `exitCode?`: `number` ; `status`: `string` ; `statusHistory`: { `cause?`: `string` ; `date`: `string` ; `status`: `string` }[] ; `worker`: `string` }[] ; `task`: { `status`: `string` ; `statusHistory`: { `cause?`: `string` ; `date`: `string` ; `status`: `string` }[] } }\>
131+
132+
get off-chain status information for specified task.
133+
134+
_NB_: the workerpool must declare it's API url to enable this feature, check declared API url with `IExecWorkerpoolModule.getWorkerpoolApiUrl(workerpool)`
135+
136+
example:
137+
```js
138+
const { task, replicates } = await fetchOffchainInfo('0x668cb3e53ebbcc9999997709586c5af07f502f6120906fa3506ce1f531cedc81');
139+
140+
console.log(`task status: ${task.status}`);
141+
replicates.forEach(({ worker, status }) =>
142+
console.log(`worker ${worker} replicate status: ${status}`)
143+
);
144+
```
145+
146+
#### Parameters
147+
148+
| Name | Type |
149+
| :------ | :------ |
150+
| `taskid` | `string` |
151+
152+
#### Returns
153+
154+
`Promise`<{ `replicates`: { `exitCode?`: `number` ; `status`: `string` ; `statusHistory`: { `cause?`: `string` ; `date`: `string` ; `status`: `string` }[] ; `worker`: `string` }[] ; `task`: { `status`: `string` ; `statusHistory`: { `cause?`: `string` ; `date`: `string` ; `status`: `string` }[] } }\>
155+
156+
___
157+
94158
### fetchResults
95159

96160
▸ **fetchResults**(`taskid`): `Promise`<`Response`\>

docs/classes/IExecWorkerpoolModule.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,15 @@ ___
149149

150150
### getWorkerpoolApiUrl
151151

152-
▸ **getWorkerpoolApiUrl**(`workerpoolAddress`, `url`): `Promise`<`undefined` \| `string`\>
152+
▸ **getWorkerpoolApiUrl**(`workerpoolAddress`): `Promise`<`undefined` \| `string`\>
153153

154154
read the workerpool API url on the blockchain
155155

156156
_NB_: resolve to `undefined` if the workerpool API url was not declared.
157157

158158
example:
159159
```js
160-
const url = await getWorkerpoolApiUrl('my-workerpool.eth', 'my-workerpool.com');
160+
const url = await getWorkerpoolApiUrl('my-workerpool.eth');
161161
console.log('workerpool API url:', url);
162162
```
163163

@@ -166,7 +166,6 @@ console.log('workerpool API url:', url);
166166
| Name | Type |
167167
| :------ | :------ |
168168
| `workerpoolAddress` | `string` |
169-
| `url` | `string` |
170169

171170
#### Returns
172171

docs/classes/internal_.TaskObservable.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
▸ **subscribe**(`callbacks`): () => `void`
4242

43-
subscribe to task updates via an Observer until either `complete()` or `error(error: Error)` is called on the Observer or the subscribtion is canceled by calling the returned unsubscribe method.
43+
subscribe to task updates via an Observer until either `complete()` or `error(error: Error)` is called on the Observer or the subscription is canceled by calling the returned unsubscribe method.
4444

4545
return the `unsubscribe: () => void` method.
4646

@@ -67,7 +67,7 @@ data:
6767

6868
▸ (): `void`
6969

70-
subscribe to task updates via an Observer until either `complete()` or `error(error: Error)` is called on the Observer or the subscribtion is canceled by calling the returned unsubscribe method.
70+
subscribe to task updates via an Observer until either `complete()` or `error(error: Error)` is called on the Observer or the subscription is canceled by calling the returned unsubscribe method.
7171

7272
return the `unsubscribe: () => void` method.
7373

src/common/execution/debug.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,23 @@ export const fetchTaskOffchainInfo = async (
8080
try {
8181
const vTaskid = await bytes32Schema().validate(taskid);
8282
const workerpoolApiUrl = await getTaskOffchainApiUrl(contracts, vTaskid);
83-
return await jsonApi.get({
83+
const data = await jsonApi.get({
8484
api: workerpoolApiUrl,
8585
endpoint: `/tasks/${vTaskid}`,
8686
ApiCallErrorClass: WorkerpoolCallError,
8787
});
88+
return {
89+
task: {
90+
status: data.currentStatus,
91+
statusHistory: data.dateStatusList,
92+
},
93+
replicates: (data.replicates || []).map((replicate) => ({
94+
worker: replicate.walletAddress,
95+
exitCode: replicate.appExitCode,
96+
status: replicate.currentStatus,
97+
statusHistory: replicate.statusUpdateList,
98+
})),
99+
};
88100
} catch (error) {
89101
debug('fetchTaskOffchainInfo()', error);
90102
throw error;

src/common/utils/validator.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const uint256Schema = () =>
7070
export const booleanSchema = () => boolean();
7171

7272
export const basicUrlSchema = () =>
73-
string().matches(/^http[s]?:\/\//, '${path} "${value}" is not a valid URL');
73+
string().matches(/^https?:\/\//, '${path} "${value}" is not a valid URL');
7474

7575
const amountErrorMessage = ({ originalValue }) =>
7676
`${
@@ -816,7 +816,10 @@ export const textRecordKeySchema = () => string().required().strict(true);
816816

817817
export const textRecordValueSchema = () => string().default('').strict(true);
818818

819-
export const workerpoolApiUrlSchema = () => string().url().default('');
819+
export const workerpoolApiUrlSchema = () =>
820+
string()
821+
.matches(/^(https?:\/\/.*)?$/, '${path} "${value}" is not a valid URL') // accept empty string to reset workerpool URL
822+
.default('');
820823

821824
export const smsUrlOrMapSchema = () =>
822825
lazy((stringOrMap) => {

src/lib/IExecTaskModule.d.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ interface Task {
3636

3737
declare class TaskObservable extends Observable {
3838
/**
39-
* subscribe to task updates via an Observer until either `complete()` or `error(error: Error)` is called on the Observer or the subscribtion is canceled by calling the returned unsubscribe method.
39+
* subscribe to task updates via an Observer until either `complete()` or `error(error: Error)` is called on the Observer or the subscription is canceled by calling the returned unsubscribe method.
4040
*
4141
* return the `unsubscribe: () => void` method.
4242
*
@@ -165,6 +165,84 @@ export default class IExecTaskModule extends IExecModule {
165165
* ```
166166
*/
167167
fetchResults(taskid: Taskid): Promise<Response>;
168+
/**
169+
* **SIGNER REQUIRED, ONLY REQUESTER**
170+
*
171+
* get the workers logs for specified task.
172+
*
173+
* _NB_: the workerpool must declare it's API url to enable this feature, check declared API url with `IExecWorkerpoolModule.getWorkerpoolApiUrl(workerpool)`
174+
*
175+
* example:
176+
* ```js
177+
* const logArray = await fetchLogs('0x668cb3e53ebbcc9999997709586c5af07f502f6120906fa3506ce1f531cedc81');
178+
* logsArray.forEach(({ worker, stdout, stderr }) => {
179+
* console.log(`----- worker ${worker} -----`);
180+
* console.log(`stdout:\n${stdout}`);
181+
* console.log(`stderr:\n${stderr}`);
182+
* });
183+
* ```
184+
*/
185+
fetchLogs(
186+
taskid: Taskid,
187+
): Promise<Array<{ worker: Address; stdout: string; stderr: string }>>;
188+
/**
189+
* get off-chain status information for specified task.
190+
*
191+
* _NB_: the workerpool must declare it's API url to enable this feature, check declared API url with `IExecWorkerpoolModule.getWorkerpoolApiUrl(workerpool)`
192+
*
193+
* example:
194+
* ```js
195+
* const { task, replicates } = await fetchOffchainInfo('0x668cb3e53ebbcc9999997709586c5af07f502f6120906fa3506ce1f531cedc81');
196+
*
197+
* console.log(`task status: ${task.status}`);
198+
* replicates.forEach(({ worker, status }) =>
199+
* console.log(`worker ${worker} replicate status: ${status}`)
200+
* );
201+
* ```
202+
*/
203+
fetchOffchainInfo(taskid: Taskid): Promise<{
204+
task: {
205+
/**
206+
* task status
207+
*
208+
* see https://protocol.docs.iex.ec/for-developers/task-feedback#task-statuses
209+
*/
210+
status: string;
211+
statusHistory: Array<{
212+
/**
213+
* task status
214+
*
215+
* see https://protocol.docs.iex.ec/for-developers/task-feedback#task-statuses
216+
*/
217+
status: string;
218+
date: string;
219+
cause?: string;
220+
}>;
221+
};
222+
replicates: Array<{
223+
worker: Address;
224+
/**
225+
* app exit code (omitted when exit code is 0)
226+
*/
227+
exitCode?: number;
228+
/**
229+
* replicate status
230+
*
231+
* see https://protocol.docs.iex.ec/for-developers/task-feedback#replicate-statuses
232+
*/
233+
status: string;
234+
statusHistory: Array<{
235+
/**
236+
* replicate status
237+
*
238+
* see https://protocol.docs.iex.ec/for-developers/task-feedback#replicate-statuses
239+
*/
240+
status: string;
241+
date: string;
242+
cause?: string;
243+
}>;
244+
}>;
245+
}>;
168246
/**
169247
* Create an IExecTaskModule instance using an IExecConfig instance
170248
*/

src/lib/IExecTaskModule.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import IExecModule from './IExecModule.js';
22
import { show, obsTask, claim } from '../common/execution/task.js';
33
import { fetchTaskResults } from '../common/execution/result.js';
4+
import {
5+
fetchAllReplicatesLogs,
6+
fetchTaskOffchainInfo,
7+
} from '../common/execution/debug.js';
48

59
export default class IExecTaskModule extends IExecModule {
610
constructor(...args) {
@@ -18,5 +22,12 @@ export default class IExecTaskModule extends IExecModule {
1822
fetchTaskResults(await this.config.resolveContractsClient(), taskid, {
1923
ipfsGatewayURL: await this.config.resolveIpfsGatewayURL(),
2024
});
25+
this.fetchLogs = async (taskid) =>
26+
fetchAllReplicatesLogs(
27+
await this.config.resolveContractsClient(),
28+
taskid,
29+
);
30+
this.fetchOffchainInfo = async (taskid) =>
31+
fetchTaskOffchainInfo(await this.config.resolveContractsClient(), taskid);
2132
}
2233
}

src/lib/IExecWorkerpoolModule.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,12 @@ export default class IExecWorkerpoolModule extends IExecModule {
141141
*
142142
* example:
143143
* ```js
144-
* const url = await getWorkerpoolApiUrl('my-workerpool.eth', 'my-workerpool.com');
144+
* const url = await getWorkerpoolApiUrl('my-workerpool.eth');
145145
* console.log('workerpool API url:', url);
146146
* ```
147147
*/
148148
getWorkerpoolApiUrl(
149149
workerpoolAddress: Addressish,
150-
url: string,
151150
): Promise<string | undefined>;
152151
/**
153152
* **ONLY WORKERPOOL OWNER**

0 commit comments

Comments
 (0)