Skip to content

Commit 5757f3d

Browse files
authored
Datasources (#26)
* implemented pre-commit prepareDvcFiles method * implemented sync and push * updated to use datasource-utils * updated to use a published version of @hkube/datasource-utils * splitted handlers to files, added error handling * fix commands order
1 parent 29bad99 commit 5757f3d

File tree

13 files changed

+378
-35
lines changed

13 files changed

+378
-35
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,5 @@ hkubectl-*
6969
helpers/syncthing/syncthing
7070
output
7171
testers
72-
!tests/testData/**
72+
!tests/testData/**
73+
tests/repos

README.md

+47-20
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Install
44
```shell
5-
curl -Lo hkubectl https://github.com/kube-HPC/hkubectl/releases/download/$(curl -s https://api.github.com/repos/kube-HPC/hkubectl/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")')/hkubectl-linux \
5+
curl -Lo hkubectl https://github.com/kube-HPC/hkubectl/releases/latest/download/hkubectl-linux \
66
&& chmod +x hkubectl \
77
&& sudo mv hkubectl /usr/local/bin/
88
```
@@ -21,8 +21,9 @@ Options:
2121
|--version|Show version number|boolean|||
2222
|--rejectUnauthorized|set to false to ignore certificate signing errors. Useful for self signed TLS certificate|boolean|||
2323
|--endpoint|url of hkube api endpoint|string|||
24+
|--pathPrefix|path prefix url of hkube api endpoint |string||/hkube/api-server/|
2425
|--verbose|verbose logging|boolean|||
25-
|--json, -j|output json to stdout|boolean|||
26+
|-j, --json|output json to stdout|boolean|||
2627
|--help|Show help|boolean|||
2728
### exec
2829
---
@@ -53,7 +54,7 @@ Options:
5354

5455
|option|description|type|required|default|
5556
|---|---|---|---|---|
56-
|--file, -f|file path/name for running pipeline. use - to read from stdin|string|true||
57+
|-f, --file|file path/name for running pipeline. use - to read from stdin|string|true||
5758
|--noWait|if true, does not wait for the execution to finish |boolean||false|
5859
|--noResult|if true, does not show the result of the execution |boolean||false|
5960
#### stored
@@ -68,7 +69,7 @@ Options:
6869
|option|description|type|required|default|
6970
|---|---|---|---|---|
7071
|name|The name of the algorithm|string|||
71-
|--file, -f|file path/name for running pipeline|string|||
72+
|-f, --file|file path/name for running pipeline|string|||
7273
|--noWait|if true, does not wait for the execution to finish |boolean||false|
7374
|--noResult|if true, does not show the result of the execution |boolean||false|
7475
#### stop
@@ -120,7 +121,7 @@ Options:
120121
|option|description|type|required|default|
121122
|---|---|---|---|---|
122123
|name|The name of the algorithm|string|||
123-
|--file, -f|file path/name for extra data|string|||
124+
|-f, --file|file path/name for extra data|string|||
124125
|--noWait|if true, does not wait for the execution to finish |boolean||false|
125126
|--noResult|if true, does not show the result of the execution |boolean||false|
126127
### algorithm
@@ -141,16 +142,16 @@ Options:
141142
|option|description|type|required|default|
142143
|---|---|---|---|---|
143144
|name|The name of the algorithm|string|||
144-
|--file, -f|the algorithm file|string|||
145+
|-f, --file|the algorithm file|string|||
145146
|--env|the algorithm env [choices: "python", "nodejs", "java"]|string|||
146147
|--codePath|the code path for the algorithm|string|||
147-
|--codeEntryPoint, --entryPoint|the code entry point for the algorithm|string|||
148+
|--codeEntryPoint, --entryPoint|the code entry point for the algorithm |string|||
148149
|--image, --algorithmImage|set algorithm image|string|||
149150
|--cpu|CPU requirements of the algorithm in cores |number|||
150151
|--gpu|GPU requirements of the algorithm in cores |number|||
151-
|--mem|memory requirements of the algorithm. Possible units are ['Ki', 'M', 'Mi', 'Gi', 'm', 'K', 'G', 'T', 'Ti', 'P', 'Pi', 'E', 'Ei']. Minimum is 4Mi|string|||
152-
|--noWait|if true, does not wait for the build to finish |boolean||false|
153-
|--setCurrent|if true, sets the new version as the current version|boolean||false|
152+
|--mem|memory requirements of the algorithm. Possible units are ['Mi', 'Gi']. Minimum is 4Mi|string|||
153+
|--noWait|if true, does not wait for the build to finish|boolean||false|
154+
|--setCurrent|if true, sets the new version as the current version |boolean||false|
154155
#### list
155156

156157
```shell
@@ -213,7 +214,7 @@ Options:
213214
|--overwrite|overwrite an existing folder|boolean|||
214215
|--cpu|CPU requirements of the algorithm in cores |number||0.1|
215216
|--gpu|GPU requirements of the algorithm in cores |number||0|
216-
|--mem|memory requirements of the algorithm. Possible units are ['Ki', 'M', 'Mi', 'Gi', 'm', 'K', 'G', 'T', 'Ti', 'P', 'Pi', 'E', 'Ei']. Minimum is 4Mi|string||512Mi|
217+
|--mem|memory requirements of the algorithm. Possible units are ['Ki', 'M', 'Mi', 'Gi', 'm', 'K', 'G', 'T', 'Ti', 'P', 'Pi', 'E', 'Ei']. Minimum is 4Mi |string||512Mi|
217218
### pipeline
218219
---
219220
```shell
@@ -243,8 +244,8 @@ Options:
243244

244245
|option|description|type|required|default|
245246
|---|---|---|---|---|
246-
|--file, -f|path for descriptor file|string|true||
247-
|--readmeFile|path for readme file. example: --readmeFile="./readme.md |string|||
247+
|-f, --file|path for descriptor file|string|true||
248+
|--readmeFile|path for readme file. example: --readmeFile="./readme.md|string|||
248249
### sync
249250
---
250251
```shell
@@ -262,9 +263,9 @@ Options:
262263

263264
|option|description|type|required|default|
264265
|---|---|---|---|---|
265-
|--algorithmName, -a|The name of the algorithm to sync data into [required]|string|true||
266-
|--folder, -f|local folder to sync.|string||./|
267-
|--bidirectional, --bidi|Sync files in both ways|boolean||false|
266+
|-a, --algorithmName|The name of the algorithm to sync data into [required]|string|true||
267+
|-f, --folder|local folder to sync.|string||./|
268+
|--bidirectional, --bidi|Sync files in both ways |boolean||false|
268269
#### create
269270

270271
```shell
@@ -276,10 +277,10 @@ Options:
276277

277278
|option|description|type|required|default|
278279
|---|---|---|---|---|
279-
|--algorithmName, -a|The name of the algorithm|string|true||
280-
|--folder, -f|local folder to build from.|string||./|
280+
|-a, --algorithmName|The name of the algorithm|string|true||
281+
|-f, --folder|local folder to build from. |string||./|
281282
|--env|algorithm runtime environment [choices: "python", "nodejs"]|string|||
282-
|--entryPoint, -e|the main file of the algorithm|string|||
283+
|-e, --entryPoint|the main file of the algorithm|string|||
283284
|--baseImage|base image for the algorithm|string|||
284285
### config
285286
---
@@ -298,4 +299,30 @@ Sets configuration options.
298299
```shell
299300
$ hkubectl config get
300301
```
301-
Gets the current configuration.
302+
Gets the current configuration.
303+
### datasource
304+
---
305+
```shell
306+
$ hkubectl datasource < command >
307+
```
308+
Execution pipelines as raw or stored
309+
#### sync
310+
311+
```shell
312+
$ hkubectl datasource sync
313+
```
314+
should be called after push updates to git, creates a new version entry on the
315+
datasource service
316+
#### prepare
317+
318+
```shell
319+
$ hkubectl datasource prepare
320+
```
321+
should be called before commiting to git, scans the directory and updates all
322+
the.dvc files
323+
#### push
324+
325+
```shell
326+
$ hkubectl datasource push
327+
```
328+
calls dvc, git push and hkube datasource hkube datasource prepare and sync

builders/dataSource.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const commands = require('../commands/datasource');
2+
3+
const dataSource = {
4+
command: 'datasource <command>',
5+
description: 'Execution pipelines as raw or stored',
6+
builder: (yargs) => {
7+
Object.values(commands).forEach((cmd) => {
8+
yargs.command(cmd);
9+
});
10+
return yargs;
11+
},
12+
handler: () => { }
13+
};
14+
15+
module.exports = { dataSource };

commands/datasource/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const { sync } = require('./sync');
2+
const { push } = require('./push');
3+
const { prepare } = require('./prepare');
4+
5+
module.exports = { sync, prepare, push };

commands/datasource/prepare.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const { parse } = require('path');
2+
const Repository = require('../../helpers/dataSource/Repository');
3+
4+
const prepare = {
5+
command: 'prepare',
6+
description: 'should be called before commiting to git, scans the directory and updates all the.dvc files',
7+
options: {},
8+
builder: {},
9+
handler: async () => {
10+
const cwd = process.cwd();
11+
const { dir, name } = parse(cwd);
12+
const hkubeFile = await Repository.readHkubeFile(cwd);
13+
const repo = new Repository(hkubeFile.repositoryName, dir, name);
14+
await repo.prepareDvcFiles();
15+
return null;
16+
}
17+
};
18+
19+
module.exports = { prepare };

commands/datasource/push.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const { parse } = require('path');
2+
const Repository = require('../../helpers/dataSource/Repository');
3+
const { handleSync } = require('./sync');
4+
5+
const push = {
6+
command: 'push',
7+
description: 'calls dvc, git push and hkube datasource hkube datasource prepare and sync',
8+
options: {},
9+
builder: {},
10+
handler: async () => {
11+
const cwd = process.cwd();
12+
const hkubeFile = await Repository.readHkubeFile(cwd);
13+
const { dir } = parse(cwd);
14+
const repo = new Repository(hkubeFile.repositoryName, dir);
15+
await repo.push();
16+
return handleSync();
17+
}
18+
};
19+
20+
module.exports = { push };

commands/datasource/sync.js

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const formatSize = require('pretty-bytes');
2+
const get = require('lodash.get');
3+
const chalk = require('chalk');
4+
const Repository = require('../../helpers/dataSource/Repository');
5+
const { api: client } = require('../../helpers/clients');
6+
const { log } = require('../../helpers/output');
7+
8+
const handleSync = async () => {
9+
const cwd = process.cwd();
10+
const hkubeFile = await Repository.readHkubeFile(cwd);
11+
let response = null;
12+
try {
13+
response = await client.post(
14+
`datasource/${hkubeFile.repositoryName}/sync`
15+
);
16+
}
17+
catch (e) {
18+
e.code && log(chalk.red`sync failed! ${e.code}`);
19+
const message = get(e, 'response.data.error.message');
20+
message && log(chalk.red(message));
21+
return null;
22+
}
23+
const { avgFileSize, totalSize } = response.data;
24+
log({
25+
...response.data,
26+
avgFileSize: formatSize(avgFileSize),
27+
totalSize: formatSize(totalSize)
28+
});
29+
return null;
30+
};
31+
32+
const sync = {
33+
command: 'sync',
34+
description: 'should be called after push updates to git, creates a new version entry on the datasource service',
35+
options: {},
36+
builder: {},
37+
handler: handleSync
38+
};
39+
40+
module.exports = { handleSync, sync };

helpers/clients.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const { default: axios } = require('axios');
2+
const pathLib = require('path');
3+
const https = require('https');
4+
5+
const api = axios.create();
6+
7+
const setupClient = (argv) => {
8+
const apiPrefix = 'api/v1';
9+
const { endpoint, pathPrefix, rejectUnauthorized } = argv || {};
10+
const tmp = pathLib.join(pathPrefix, apiPrefix);
11+
const baseURL = new URL(tmp, endpoint);
12+
api.defaults.baseURL = baseURL.toString();
13+
if (baseURL.protocol === 'https:') {
14+
const agent = new https.Agent({ rejectUnauthorized });
15+
api.defaults.httpAgent = agent;
16+
}
17+
};
18+
19+
module.exports = { api, setupClient };

helpers/dataSource/Repository.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const mimeTypes = require('mime-types');
2+
const { Repository: _Repository, createFileMeta, glob } = require('@hkube/datasource-utils');
3+
const fse = require('fs-extra');
4+
const get = require('lodash.get');
5+
const { default: simpleGit } = require('simple-git');
6+
/**
7+
* @typedef {import('@hkube/datasource-utils').LocalFileMeta} LocalFileMeta
8+
*/
9+
class Repository extends _Repository {
10+
/**
11+
* @param {string} repositoryName
12+
* @param {string} rootDir
13+
*/
14+
constructor(repositoryName, rootDir, dirName) {
15+
super(repositoryName, rootDir, dirName);
16+
this.dirName = dirName || repositoryName;
17+
this.gitClient = simpleGit({ baseDir: this.cwd });
18+
}
19+
20+
_createFileMeta(currentMeta) {
21+
const { size, path: name } = currentMeta.outs[0];
22+
const mimetype = mimeTypes.lookup(name) || '';
23+
return createFileMeta({
24+
originalname: name,
25+
size,
26+
mimetype
27+
}, null, 'cli');
28+
}
29+
30+
async prepareDvcFiles() {
31+
const dvcFiles = await glob('**/*.dvc', this.cwd);
32+
for await (const dvcPath of dvcFiles) {
33+
const dvcContent = await this.dvc.loadDvcContent(dvcPath, true);
34+
/** @type {LocalFileMeta} */
35+
let fileMeta = get(dvcContent, 'meta.hkube', {});
36+
if (dvcContent.outs[0].md5 !== get(dvcContent, 'meta.hkube.hash', null)) {
37+
const { path, ...generatedMeta } = this._createFileMeta(dvcContent);
38+
fileMeta = generatedMeta;
39+
}
40+
const metaFilePath = dvcPath.replace('.dvc', '.meta');
41+
if (await fse.pathExists(metaFilePath)) {
42+
const content = await fse.readFile(`${this.cwd}/${metaFilePath}`);
43+
fileMeta.meta = content.toString('utf8');
44+
}
45+
await this.dvc.enrichMeta(dvcPath, dvcContent, 'hkube', fileMeta);
46+
}
47+
}
48+
}
49+
50+
module.exports = Repository;

hkubectl.js

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const algorithms = require('./builders/algorithm');
77
const pipelines = require('./builders/pipeline');
88
const dryRun = require('./builders/dry-run');
99
const sync = require('./builders/sync');
10+
const { dataSource } = require('./builders/dataSource');
11+
const { setupClient } = require('./helpers/clients');
1012
const syncthing = require('./helpers/syncthing/syncthing.js');
1113

1214
global.args = {};
@@ -28,6 +30,7 @@ const main = async () => {
2830
yargs.command(dryRun);
2931
yargs.command(sync);
3032
yargs.command(config);
33+
yargs.command(dataSource);
3134
yargs.options('rejectUnauthorized', {
3235
description: 'set to false to ignore certificate signing errors. Useful for self signed TLS certificate',
3336
type: 'boolean'
@@ -55,8 +58,10 @@ const main = async () => {
5558
.help()
5659
.epilog(chalk.bold('for more information visit http://hkube.io'))
5760
.completion();
61+
5862
yargs.middleware((args) => {
5963
global.args = args;
64+
setupClient(global.args);
6065
});
6166
const args = yargs.argv;
6267
if (!args._[0]) {

0 commit comments

Comments
 (0)