Refine rest API request and error handle (#22)

* split v1 and v2

* add constants and errors

* refine and fix tests

* fix

* add pai errors

* update lib

* for a clear api folder structure (#24)

* set linebreak-style to false

* explicit query parameters in get methods (#25)

* Yiyi/update clients (#26)

* update

* update

* update vc client

* reset

Co-authored-by: yiyione <yiyi_one@foxmail.com>

* add group client (#27)

Co-authored-by: yiyione <yiyi_one@foxmail.com>

* change all client method names (#28)

Co-authored-by: yiyione <yiyi_one@foxmail.com>

* change method names and fix tests (#29)

* change all client method names

* fix tests

Co-authored-by: yiyione <yiyi_one@foxmail.com>

Co-authored-by: yiyione <yiyi_one@foxmail.com>
Co-authored-by: Yuqing Yang <yuqyang@microsoft.com>
Co-authored-by: Yi Yi <yiyi@microsoft.com>
This commit is contained in:
yiyione 2020-04-17 17:41:40 +08:00 коммит произвёл GitHub
Родитель 12954bd562
Коммит 777ce897a2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
150 изменённых файлов: 1997 добавлений и 4800 удалений

2
lib/cli.d.ts поставляемый
Просмотреть файл

@ -1,2 +0,0 @@
#!/usr/bin/env node
export {};

Просмотреть файл

@ -1,18 +0,0 @@
#!/usr/bin/env node
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const commands_1 = require("./commands");
/**
* the main entry point of the command line tool `pai`
*/
(async () => {
// Util.debugMode = true;
const cli = new commands_1.CliEngine();
commands_1.registerBuiltinCommands(cli);
await cli.load();
const result = await cli.evaluate();
cli.toScreen(result);
await cli.store();
})().catch(err => console.error(err));

34
lib/client/authnClient.d.ts поставляемый
Просмотреть файл

@ -1,34 +0,0 @@
import { IAuthnInfo } from '../models/authn';
import { IPAICluster } from '../models/cluster';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Authn client.
*/
export declare class AuthnClient extends OpenPAIBaseClient {
private authnInfo?;
constructor(cluster: IPAICluster);
/**
* Get authn information.
*/
info(): Promise<IAuthnInfo>;
/**
* OpenID Connect login.
*/
oidcLogin(queryString?: string): Promise<any>;
/**
* OpenID Connect logout.
*/
oidcLogout(queryString?: string): Promise<any>;
/**
* Get list of available tokens (portal token + application token).
*/
getTokens(token?: string): Promise<any>;
/**
* Create an application access token.
*/
createApplicationToken(token?: string): Promise<any>;
/**
* Revoke a token.
*/
deleteToken(deleteToken: string, accessToken?: string): Promise<any>;
}

Просмотреть файл

@ -1,94 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const request = require("request-promise-native");
const util_1 = require("../commom/util");
const baseClient_1 = require("./baseClient");
/**
* OpenPAI Authn client.
*/
class AuthnClient extends baseClient_1.OpenPAIBaseClient {
constructor(cluster) {
super(cluster);
}
/**
* Get authn information.
*/
async info() {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/info`);
if (this.authnInfo === undefined) {
this.authnInfo = JSON.parse(await request.get(url));
}
return this.authnInfo;
}
/**
* OpenID Connect login.
*/
async oidcLogin(queryString) {
const url = queryString ?
util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/oidc/login?${queryString}`) :
util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/oidc/login`);
const res = await request.get(url);
return res;
}
/**
* OpenID Connect logout.
*/
async oidcLogout(queryString) {
const url = queryString ?
util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/oidc/logout?${queryString}`) :
util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/oidc/logout`);
const res = await request.get(url);
return res;
}
/**
* Get list of available tokens (portal token + application token).
*/
async getTokens(token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/token`);
if (token === undefined) {
token = await super.token();
}
const res = await request.get(url, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return JSON.parse(res);
}
/**
* Create an application access token.
*/
async createApplicationToken(token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/token/application`);
if (token === undefined) {
token = await super.token();
}
const res = await request.post(url, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return JSON.parse(res);
}
/**
* Revoke a token.
*/
async deleteToken(deleteToken, accessToken) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/token/${deleteToken}`);
if (accessToken === undefined) {
accessToken = await super.token();
}
const res = await request.delete(url, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
});
return JSON.parse(res);
}
}
exports.AuthnClient = AuthnClient;

43
lib/client/azureBlobClient.d.ts поставляемый
Просмотреть файл

@ -1,43 +0,0 @@
import { PagedAsyncIterableIterator } from '@azure/core-paging';
import { BlobItem, BlobPrefix, ContainerListBlobHierarchySegmentResponse } from '@azure/storage-blob';
import { IStorageDetail } from '../models/storage';
import { IFileInfo, IStorageNodeClient } from '../models/storageOperation';
export declare type BlobIter = PagedAsyncIterableIterator<({
kind: 'prefix';
} & BlobPrefix) | ({
kind: 'blob';
} & BlobItem), ContainerListBlobHierarchySegmentResponse>;
export declare type BlobEntity = {
done?: boolean | undefined;
value: ({
kind: 'prefix';
} & BlobPrefix) | ({
kind: 'blob';
} & BlobItem);
};
export declare type BlobValue = ({
kind: 'prefix';
} & BlobPrefix) | ({
kind: 'blob';
} & BlobItem);
/**
* Azure Blob Storage Client.
*/
export declare class AzureBlobClient implements IStorageNodeClient {
mkdirAllowRecursive: boolean;
config: IStorageDetail;
private client;
constructor(config: IStorageDetail);
/**
* Get status of a path.
* @param path The path.
*/
getinfo(path: string): Promise<IFileInfo>;
listdir(path: string): Promise<string[]>;
makedir(path: string, mode?: string | undefined): Promise<void>;
upload(localPath: string, remotePath: string, opts?: {} | undefined): Promise<void>;
download(remotePath: string, localPath: string, opts?: {} | undefined): Promise<void>;
delete(path: string): Promise<void>;
deleteFolder(path: string): Promise<void>;
private deleteBlobsByHierarchy;
}

Просмотреть файл

@ -1,196 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const storage_blob_1 = require("@azure/storage-blob");
const Path = require("path");
/**
* Azure Blob Storage Client.
*/
class AzureBlobClient {
constructor(config) {
this.mkdirAllowRecursive = true;
this.config = config;
const data = config.data;
if (config.type !== 'azureBlob' ||
!data ||
!data.containerName ||
!(data.accountKey || data.accountSASToken)) {
throw new Error(`WrongStorageDetail: ${JSON.stringify(config)}`);
}
if (data.accountKey) { // use the accountKey
const credential = new storage_blob_1.StorageSharedKeyCredential(data.accountName, data.accountKey);
const blobClient = new storage_blob_1.BlobServiceClient(`https://${data.accountName}.blob.core.windows.net`, credential);
this.client = blobClient.getContainerClient(data.containerName);
}
else { // SAS token
let url = data.accountSASToken;
if (!url.startsWith('https://')) {
url = `https://${data.accountName}.blob.core.windows.net${data.accountSASToken}`;
}
const blobClient = new storage_blob_1.BlobServiceClient(url);
this.client = blobClient.getContainerClient(data.containerName);
}
}
/**
* Get status of a path.
* @param path The path.
*/
async getinfo(path) {
const blobClient = this.client.getBlockBlobClient(path);
try {
const properties = await blobClient.getProperties();
if (!properties.metadata || !properties.metadata.hdi_isfolder) {
return {
size: properties.contentLength,
type: 'file',
mtime: properties.lastModified
};
}
else {
return {
type: 'directory',
mtime: properties.lastModified
};
}
}
catch (err) {
if (err.details && err.details.errorCode === 'BlobNotFound') {
const iter = await this.client.listBlobsByHierarchy('/', {
prefix: path.endsWith('/') ? path : path + '/',
includeMetadata: true
}).byPage({
continuationToken: undefined,
maxPageSize: 1
}).next();
const prefixes = iter.value.segment.blobPrefixes;
const blobs = iter.value.segment.blobItems;
if ((prefixes && prefixes.length > 0) || blobs.length > 0) {
return {
type: 'directory'
};
}
}
console.log(err);
throw err;
}
}
async listdir(path) {
try {
const result = [];
const currentPrefixes = new Set();
let currentContinuationToken;
let iter;
do {
iter = await this.client.listBlobsByHierarchy('/', {
prefix: path.endsWith('/') ? path : path + '/',
includeMetadata: true
}).byPage({
continuationToken: currentContinuationToken,
maxPageSize: 20
}).next();
currentContinuationToken = iter.value.continuationToken;
const prefixes = iter.value.segment.blobPrefixes;
if (prefixes) {
prefixes.forEach(item => {
result.push(Path.basename(item.name));
currentPrefixes.add(item.name);
});
}
const blobs = iter.value.segment.blobItems;
for (const blob of blobs) {
if (blob.metadata && blob.metadata.hdi_isfolder && blob.metadata.hdi_isfolder === 'true') {
if (currentPrefixes.has(`${blob.name}/`)) {
continue;
}
}
result.push(Path.basename(blob.name));
}
} while (currentContinuationToken);
return result;
}
catch (err) {
console.log(err);
throw err;
}
}
async makedir(path, mode) {
try {
await this.client.getBlockBlobClient(path).upload('', 0, {
metadata: {
hdi_isfolder: 'true'
}
});
}
catch (err) {
console.log(err);
throw err;
}
}
async upload(localPath, remotePath, opts) {
try {
const blobClient = this.client.getBlockBlobClient(remotePath);
await blobClient.uploadFile(localPath);
}
catch (err) {
console.log(err);
throw err;
}
}
async download(remotePath, localPath, opts) {
try {
const blobClient = this.client.getBlockBlobClient(remotePath);
await blobClient.downloadToFile(localPath);
}
catch (err) {
console.log(err);
throw err;
}
}
async delete(path) {
try {
const blobClient = this.client.getBlockBlobClient(path);
await blobClient.delete();
}
catch (err) {
console.log(err);
throw err;
}
}
async deleteFolder(path) {
try {
const info = await this.getinfo(path);
if (info.type === 'file') {
await this.client.deleteBlob(path);
}
else {
await this.deleteBlobsByHierarchy(this.client, path);
}
}
catch (err) {
console.log(err);
throw err;
}
}
async deleteBlobsByHierarchy(client, prefix) {
const iter = client.listBlobsByHierarchy('/', {
prefix: prefix.endsWith('/') ? prefix : prefix + '/'
});
let blobItem = await iter.next();
while (!blobItem.done) {
const blob = blobItem.value;
if (blob.kind === 'blob') {
await client.deleteBlob(blob.name);
}
else {
try {
await client.deleteBlob(blob.name.slice(0, -1));
}
catch { }
await this.deleteBlobsByHierarchy(client, blob.name);
}
blobItem = await iter.next();
}
}
}
exports.AzureBlobClient = AzureBlobClient;

28
lib/client/baseClient.d.ts поставляемый
Просмотреть файл

@ -1,28 +0,0 @@
import { ILoginInfo } from '../models/authn';
import { IPAICluster } from '../models/cluster';
/**
* OpenPAI basic client.
*/
export declare class OpenPAIBaseClient {
protected static readonly TIMEOUT: number;
/**
* get cluster configuration / info
*/
config: any;
protected cluster: IPAICluster;
private cacheToken?;
constructor(cluster: IPAICluster);
/**
* parse information from pai_uri
* refer to tests/unit_tests/baseClient.spec.ts
*/
static parsePaiUri(cluster: IPAICluster): IPAICluster;
/**
* Get OpenPAI access token, will call /api/v1/token.
*/
token(): Promise<string>;
/**
* Basic login.
*/
login(): Promise<ILoginInfo>;
}

Просмотреть файл

@ -1,87 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const request = require("request-promise-native");
const url_1 = require("url");
const util_1 = require("../commom/util");
/**
* OpenPAI basic client.
*/
class OpenPAIBaseClient {
constructor(cluster) {
/**
* get cluster configuration / info
*/
this.config = {
/**
* username from cluster config
*/
username: () => {
return this.cluster.username;
},
/**
* Get OpenPAI cluster info, will call /api/v1.
*/
clusterInfo: async () => {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/`);
const res = await request.get(url);
return JSON.parse(res);
}
};
this.cluster = OpenPAIBaseClient.parsePaiUri(cluster);
}
/**
* parse information from pai_uri
* refer to tests/unit_tests/baseClient.spec.ts
*/
static parsePaiUri(cluster) {
if (cluster.pai_uri) {
const paiUri = new url_1.URL(util_1.Util.fixUrl(cluster.pai_uri));
if (!cluster.https) {
cluster.https = paiUri.protocol === 'https:';
}
if (!cluster.rest_server_uri) {
cluster.rest_server_uri = paiUri.host + '/rest-server';
}
if (!cluster.alias) {
cluster.alias = paiUri.hostname;
}
}
return cluster;
}
/**
* Get OpenPAI access token, will call /api/v1/token.
*/
async token() {
if (this.cluster.token) {
return this.cluster.token;
}
else if (!this.cacheToken || this.cacheToken.expireTime < Date.now()) {
const res = await this.login();
this.cacheToken = {
expireTime: Date.now() + 3600 * 1000,
token: res.token
};
}
return this.cacheToken.token;
}
/**
* Basic login.
*/
async login() {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/basic/login`);
const res = await request.post(url, {
form: {
expiration: 4000,
password: this.cluster.password,
username: this.cluster.username
},
json: true,
timeout: OpenPAIBaseClient.TIMEOUT
});
return res;
}
}
exports.OpenPAIBaseClient = OpenPAIBaseClient;
OpenPAIBaseClient.TIMEOUT = 60 * 1000;

13
lib/client/baseStorageNode.d.ts поставляемый
Просмотреть файл

@ -1,13 +0,0 @@
import { IStorageNode, IFileInfo } from '../models/storageOperation';
export declare class baseStorageNode implements IStorageNode {
getinfo(path: string): Promise<IFileInfo>;
listdir(path: string): Promise<string[]>;
makedir(path: string, mode?: string | undefined): void;
upload(localPath: string, remotePath: string, opts?: {} | undefined): void;
download(remotePath: string, localPath: string, opts?: {} | undefined): void;
delete(path: string): void;
existsSync(path: string): boolean;
isdirSync(path: string): boolean;
uploadFolder(localPath: string, remotePath: string, opts?: {} | undefined): void;
downloadFolder(remotePath: string, localPath: string, opts?: {} | undefined): void;
}

Просмотреть файл

@ -1,51 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
class baseStorageNode {
getinfo(path) {
throw new Error("Method not implemented.");
}
listdir(path) {
throw new Error("Method not implemented.");
}
makedir(path, mode) {
throw new Error("Method not implemented.");
}
upload(localPath, remotePath, opts) {
throw new Error("Method not implemented.");
}
download(remotePath, localPath, opts) {
throw new Error("Method not implemented.");
}
delete(path) {
throw new Error("Method not implemented.");
}
existsSync(path) {
throw new Error("Method not implemented.");
}
isdirSync(path) {
throw new Error("Method not implemented.");
}
uploadFolder(localPath, remotePath, opts) {
throw new Error("Method not implemented.");
}
downloadFolder(remotePath, localPath, opts) {
throw new Error("Method not implemented.");
}
}
exports.baseStorageNode = baseStorageNode;

31
lib/client/cacheClient.d.ts поставляемый
Просмотреть файл

@ -1,31 +0,0 @@
import { Identifiable } from '../commom/identifiable';
/**
* for some stable API calling (the results may not change for a long while)
* the cache mechanism will help to reduce communication consumption
*/
export interface ICacheRecord {
name: string;
time: number;
value: object | string | number;
}
/**
* CacheClient will delegate some OpenPAIClient methods
*/
export declare class CacheClient extends Identifiable<ICacheRecord, string> {
functions: {
[index: string]: Function;
};
protected expiration: number;
protected objects: {
[index: string]: Object;
};
constructor(records?: ICacheRecord[]);
setDaysToExpire(days: number): void;
/**
* register a method with Cache reading (e.g. storageClient.getStorageByName)
* -> RecentlyUsedCacheInMemory().functions.getStorageByName(skipCache, name)
* @param skipCache if true, will always call exec() to get new value and add a record
*/
delegate(thisArgs: Object, func: Function): void;
protected uidOf(element: ICacheRecord): string;
}

Просмотреть файл

@ -1,56 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const identifiable_1 = require("../commom/identifiable");
const util_1 = require("../commom/util");
function now() {
return new Date().getTime();
}
function days2ms(days) {
return 1000 * 3600 * 24 * days;
}
/**
* CacheClient will delegate some OpenPAIClient methods
*/
class CacheClient extends identifiable_1.Identifiable {
constructor(records) {
super(records);
this.functions = {};
this.expiration = days2ms(7); // in unit of ms, default is 7 days
this.objects = {};
}
setDaysToExpire(days) {
this.expiration = days2ms(days);
}
/**
* register a method with Cache reading (e.g. storageClient.getStorageByName)
* -> RecentlyUsedCacheInMemory().functions.getStorageByName(skipCache, name)
* @param skipCache if true, will always call exec() to get new value and add a record
*/
delegate(thisArgs, func) {
if (func.name in this.objects) {
throw new Error(`AlreadyBound: ${func.name}`);
}
this.objects[func.name] = thisArgs;
this.functions[func.name] = async (skipCache, ...args) => {
const name = [func.name].concat(args).join(':');
util_1.Util.debug(`try to read (skipCache = ${skipCache}) the value of key ${name}`);
if (!skipCache) {
const idx = this.indexOf(name);
if (idx > -1 && (now() - this.data[idx].time) < this.expiration) {
util_1.Util.debug(`found valid cache created @ ${new Date(this.data[idx].time)}`);
return this.data[idx].value;
}
}
const value = await func.call(this.objects[func.name], ...args);
this.add({ name: name, time: now(), value: value });
util_1.Util.debug('no cached record found, create it', value);
return value;
};
}
uidOf(element) {
return element.name;
}
}
exports.CacheClient = CacheClient;

69
lib/client/jobClient.d.ts поставляемый
Просмотреть файл

@ -1,69 +0,0 @@
import { IPAICluster } from '../models/cluster';
import { IJobConfig, IJobConfigV1 } from '../models/jobConfig';
import { IJobInfo, IJobSshInfo, IJobStatus } from '../models/jobStatus';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Job client.
*/
export declare class JobClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster);
/**
* List jobs, will call /api/v1/jobs.
* @param query The query string.
*/
list(query?: string): Promise<IJobInfo[]>;
/**
* Get job status, will call /api/v2/user/{userName}/jobs/{jobName}.
* @param userName The user name.
* @param jobName The job name.
*/
get(userName: string, jobName: string): Promise<IJobStatus>;
/**
* Delete a job, will call /api/v2/user/{userName}/jobs/{jobName}.
* @param userName The user name.
* @param jobName The job name.
* @param token Specific an access token (optional).
*/
delete(userName: string, jobName: string, token?: string): Promise<IJobStatus>;
/**
* Get job framework info, will call /api/v2/jobs/{userName}~{jobName}.
* @param userName The user name.
* @param jobName The job name.
*/
getFrameworkInfo(userName: string, jobName: string): Promise<IJobStatus>;
/**
* Get job config, will call /api/v2/jobs/{userName}~{jobName}/config.
* @param userName The user name.
* @param jobName The job name.
*/
getConfig(userName: string, jobName: string): Promise<IJobConfig>;
/**
* Start or stop a job, will call /api/v1/user/{userName}/jobs/{jobName}/executionType.
* @param userName The user name.
* @param jobName The job name.
* @param type 'START' or 'STOP'.
* @param token Specific an access token (optional).
*/
execute(userName: string, jobName: string, type: 'START' | 'STOP', token?: string): Promise<any>;
/**
* Submit a job, will call /api/v2/jobs.
* @param jobConfig The job config.
*/
submit(jobConfig: IJobConfig, token?: string): Promise<void>;
/**
* Submit a v1 job, will call /api/v1/user/{username}/jobs.
* @param jobConfig The job config.
*/
submitV1(userName: string, jobConfig: IJobConfigV1, token?: string): Promise<void>;
/**
* Get job SSH information, will call /api/v1/user/${userName}/jobs/${jobName}/ssh.
* @param userName The user name.
* @param jobName The job name.
*/
getSshInfo(userName: string, jobName: string): Promise<IJobSshInfo>;
/**
* Get job SSH information, will call /api/v1/jobs/${jobName}/ssh.
* @param jobName The job name.
*/
getSshInfo(jobName: string): Promise<IJobSshInfo>;
}

Просмотреть файл

@ -1,148 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const yaml = require("js-yaml");
const request = require("request-promise-native");
const util_1 = require("../commom/util");
const baseClient_1 = require("./baseClient");
/**
* OpenPAI Job client.
*/
class JobClient extends baseClient_1.OpenPAIBaseClient {
constructor(cluster) {
super(cluster);
}
/**
* List jobs, will call /api/v1/jobs.
* @param query The query string.
*/
async list(query) {
const url = query === undefined ?
util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/jobs`) :
util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/jobs?${query}`);
return JSON.parse(await request.get(url));
}
/**
* Get job status, will call /api/v2/user/{userName}/jobs/{jobName}.
* @param userName The user name.
* @param jobName The job name.
*/
async get(userName, jobName) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/jobs/${jobName}`);
const res = await request.get(url);
return JSON.parse(res);
}
/**
* Delete a job, will call /api/v2/user/{userName}/jobs/{jobName}.
* @param userName The user name.
* @param jobName The job name.
* @param token Specific an access token (optional).
*/
async delete(userName, jobName, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/jobs/${jobName}`);
if (token === undefined) {
token = await super.token();
}
const res = await request.delete(url, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
timeout: baseClient_1.OpenPAIBaseClient.TIMEOUT
});
return JSON.parse(res);
}
/**
* Get job framework info, will call /api/v2/jobs/{userName}~{jobName}.
* @param userName The user name.
* @param jobName The job name.
*/
async getFrameworkInfo(userName, jobName) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}`);
const res = await request.get(url);
return JSON.parse(res);
}
/**
* Get job config, will call /api/v2/jobs/{userName}~{jobName}/config.
* @param userName The user name.
* @param jobName The job name.
*/
async getConfig(userName, jobName) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}/config`);
const res = await request.get(url);
return yaml.safeLoad(res);
}
/**
* Start or stop a job, will call /api/v1/user/{userName}/jobs/{jobName}/executionType.
* @param userName The user name.
* @param jobName The job name.
* @param type 'START' or 'STOP'.
* @param token Specific an access token (optional).
*/
async execute(userName, jobName, type, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/jobs/${jobName}/executionType`);
if (token === undefined) {
token = await super.token();
}
const res = await request.put(url, {
body: JSON.stringify({
value: type
}),
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
timeout: baseClient_1.OpenPAIBaseClient.TIMEOUT
});
return JSON.parse(res);
}
/**
* Submit a job, will call /api/v2/jobs.
* @param jobConfig The job config.
*/
async submit(jobConfig, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/jobs`);
const text = yaml.safeDump(jobConfig);
if (token === undefined) {
token = await super.token();
}
await request.post(url, {
body: text,
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'text/yaml'
},
timeout: baseClient_1.OpenPAIBaseClient.TIMEOUT
});
}
/**
* Submit a v1 job, will call /api/v1/user/{username}/jobs.
* @param jobConfig The job config.
*/
async submitV1(userName, jobConfig, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/user/${userName}/jobs`);
if (token === undefined) {
token = await super.token();
}
return await request.post(url, {
form: jobConfig,
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
json: true,
timeout: baseClient_1.OpenPAIBaseClient.TIMEOUT
});
}
async getSshInfo(param1, param2) {
const userName = param2 ? param1 : undefined;
const jobName = param2 ? param2 : param1;
const url = userName ?
util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/user/${userName}/jobs/${jobName}/ssh`) :
util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/jobs/${jobName}/ssh`);
const res = await request.get(url);
return JSON.parse(res);
}
}
exports.JobClient = JobClient;

38
lib/client/openPAIClient.d.ts поставляемый
Просмотреть файл

@ -1,38 +0,0 @@
import { AuthnClient } from '..';
import { IPAICluster } from '../models/cluster';
import { OpenPAIBaseClient } from './baseClient';
import { CacheClient, ICacheRecord } from './cacheClient';
import { JobClient } from './jobClient';
import { StorageClient } from './storageClient';
import { UserClient } from './userClient';
import { VirtualClusterClient } from './virtualClusterClient';
/**
* OpenPAI Client.
*/
export declare class OpenPAIClient extends OpenPAIBaseClient {
/**
* OpenPAI Job Client.
*/
job: JobClient;
/**
* OpenPAI User Client.
*/
user: UserClient;
/**
* OpenPAI Virtual Cluster Client.
*/
virtualCluster: VirtualClusterClient;
/**
* OpenPAI Authn Client.
*/
authn: AuthnClient;
/**
* OpenPAI Storage Client.
*/
storage: StorageClient;
/**
* OpenPAI cluster cache client
*/
cache: CacheClient;
constructor(cluster: IPAICluster, cache?: ICacheRecord[]);
}

Просмотреть файл

@ -1,28 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const __1 = require("..");
const baseClient_1 = require("./baseClient");
const cacheClient_1 = require("./cacheClient");
const jobClient_1 = require("./jobClient");
const storageClient_1 = require("./storageClient");
const userClient_1 = require("./userClient");
const virtualClusterClient_1 = require("./virtualClusterClient");
/**
* OpenPAI Client.
*/
class OpenPAIClient extends baseClient_1.OpenPAIBaseClient {
constructor(cluster, cache) {
super(cluster);
this.job = new jobClient_1.JobClient(cluster);
this.user = new userClient_1.UserClient(cluster);
this.virtualCluster = new virtualClusterClient_1.VirtualClusterClient(cluster);
this.authn = new __1.AuthnClient(cluster);
this.storage = new storageClient_1.StorageClient(cluster);
this.cache = new cacheClient_1.CacheClient(cache);
this.cache.delegate(this.storage, this.storage.getStorages);
this.cache.delegate(this.storage, this.storage.getStorageByName);
}
}
exports.OpenPAIClient = OpenPAIClient;

31
lib/client/storageClient.d.ts поставляемый
Просмотреть файл

@ -1,31 +0,0 @@
import { IPAICluster } from '../models/cluster';
import { IStorageConfig, IStorageDetail, IStorageServer, IStorageSummary } from '../models/storage';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Job client.
*/
export declare class StorageClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster);
/**
* Get storage informations.
* @param names Filter storage server with names, default name empty will be ignored.
*/
getServer(names?: string, token?: string): Promise<IStorageServer[]>;
/**
* Get storage information.
* @param storage The storage name.
*/
getServerByName(storage: string, token?: string): Promise<IStorageServer>;
/**
* Get storage config.
* @param names Filter storage server with names, default name empty will be ignored.
*/
getConfig(names?: string, token?: string): Promise<IStorageConfig[]>;
/**
* Get storage config.
* @param storage The storage name.
*/
getConfigByName(storage: string, token?: string): Promise<IStorageConfig>;
getStorages(token?: string): Promise<IStorageSummary>;
getStorageByName(name: string, token?: string): Promise<IStorageDetail>;
}

Просмотреть файл

@ -1,113 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
// tslint:disable-next-line:match-default-export-name
const axios_1 = require("axios");
const util_1 = require("../commom/util");
const baseClient_1 = require("./baseClient");
/**
* OpenPAI Job client.
*/
class StorageClient extends baseClient_1.OpenPAIBaseClient {
constructor(cluster) {
super(cluster);
}
/**
* Get storage informations.
* @param names Filter storage server with names, default name empty will be ignored.
*/
async getServer(names, token) {
const query = names ? `?names=${names}` : '';
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storage/server${query}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res = await axios_1.default.get(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
/**
* Get storage information.
* @param storage The storage name.
*/
async getServerByName(storage, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storage/server/${storage}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res = await axios_1.default.get(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
/**
* Get storage config.
* @param names Filter storage server with names, default name empty will be ignored.
*/
async getConfig(names, token) {
const query = names ? `?names=${names}` : '';
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storage/config${query}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res = await axios_1.default.get(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
/**
* Get storage config.
* @param storage The storage name.
*/
async getConfigByName(storage, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storage/config/${storage}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res = await axios_1.default.get(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
async getStorages(token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storages`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res = await axios_1.default.get(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
async getStorageByName(name, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storages/${name}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res = await axios_1.default.get(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
}
exports.StorageClient = StorageClient;

25
lib/client/storageNode.d.ts поставляемый
Просмотреть файл

@ -1,25 +0,0 @@
import { IStorageDetail } from '../models/storage';
import { IFileInfo, IStorageNode, IStorageNodeClient } from '../models/storageOperation';
/**
* StorageNode class.
*/
export declare class StorageNode implements IStorageNode {
config: IStorageDetail;
client: IStorageNodeClient;
constructor(config: IStorageDetail);
/**
* create client according to type.
*/
createClient(): IStorageNodeClient;
getinfo(path: string): Promise<IFileInfo>;
listdir(path: string): Promise<string[]>;
makedir(path: string, mode?: string | undefined): Promise<void>;
upload(localPath: string, remotePath: string, opts?: {} | undefined): Promise<void>;
download(remotePath: string, localPath: string, opts?: {} | undefined): Promise<void>;
delete(path: string): Promise<void>;
existsSync(path: string): boolean;
isdirSync(path: string): boolean;
uploadFolder(localPath: string, remotePath: string, opts?: {} | undefined): Promise<void>;
downloadFolder(remotePath: string, localPath: string, opts?: {} | undefined): Promise<void>;
deleteFolder(path: string): Promise<void>;
}

Просмотреть файл

@ -1,86 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const __1 = require("..");
/**
* StorageNode class.
*/
class StorageNode {
constructor(config) {
this.config = config;
this.client = this.createClient();
}
/**
* create client according to type.
*/
createClient() {
switch (this.config.type) {
case 'azureBlob': return new __1.AzureBlobClient(this.config);
default: throw new Error(`NotImplemented`);
}
}
async getinfo(path) {
return this.client.getinfo(path);
}
async listdir(path) {
return this.client.listdir(path);
}
async makedir(path, mode) {
try {
return this.client.makedir(path);
}
catch (e) {
if (!this.client.mkdirAllowRecursive) {
// do something recursively
}
}
}
async upload(localPath, remotePath, opts) {
return this.client.upload(localPath, remotePath, opts);
}
async download(remotePath, localPath, opts) {
return this.client.download(remotePath, localPath, opts);
}
async delete(path) {
return this.client.delete(path);
}
existsSync(path) {
throw new Error('Method not implemented.');
}
isdirSync(path) {
throw new Error('Method not implemented.');
}
/* handle folder opation
* if this.client has corresponding folder operation, use it
* otherwise implement it recursively
*/
async uploadFolder(localPath, remotePath, opts) {
if (this.client.uploadFolder) {
return this.client.uploadFolder(localPath, remotePath, opts);
}
else {
// TODO: handle upload recursively here
throw new Error('Method not implemented.');
}
}
async downloadFolder(remotePath, localPath, opts) {
if (this.client.downloadFolder) {
return this.client.downloadFolder(remotePath, localPath, opts);
}
else {
// TODO: handle download recursively here
throw new Error('Method not implemented.');
}
}
async deleteFolder(path) {
if (this.client.deleteFolder) {
return this.client.deleteFolder(path);
}
else {
// TODO: handle deletion recursively here
throw new Error('Method not implemented.');
}
}
}
exports.StorageNode = StorageNode;

10
lib/client/storageOperation.d.ts поставляемый
Просмотреть файл

@ -1,10 +0,0 @@
import { StorageClient } from './storageClient';
import { StorageNode } from './storageNode';
/**
* StorageOperation class
*/
export declare class StorageOperation {
private client;
constructor(client: StorageClient);
getStorageNode(name: string, index?: number): Promise<StorageNode>;
}

Просмотреть файл

@ -1,34 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const storageNode_1 = require("./storageNode");
/**
* StorageOperation class
*/
class StorageOperation {
constructor(client) {
this.client = client;
}
async getStorageNode(name, index = 0) {
const storageConfig = await this.client.getConfigByName(name);
const mountInfo = storageConfig.mountInfos[index];
const server = await this.client.getServerByName(mountInfo.server);
return new storageNode_1.StorageNode(mountInfo, server);
}
}
exports.StorageOperation = StorageOperation;

104
lib/client/userClient.d.ts поставляемый
Просмотреть файл

@ -1,104 +0,0 @@
import { IPAICluster } from '../models/cluster';
import { IUserInfo } from '../models/user';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI User client.
*/
export declare class UserClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster);
/**
* Get user information.
* @param userName The user name.
* @param token Specific an access token (optional).
*/
get(userName: string, token?: string): Promise<IUserInfo>;
/**
* Get all users.
* @param token Specific an access token (optional).
*/
list(token?: string): Promise<IUserInfo[]>;
/**
* Create a new user.
* @param username username in [\w.-]+ format.
* @param password password at least 6 characters.
* @param admin true | false.
* @param email email address or empty string.
* @param virtualCluster ["vcname1 in [A-Za-z0-9_]+ format", "vcname2 in [A-Za-z0-9_]+ format"].
* @param extension { "extension-key1": "extension-value1" }.
* @param token Specific an access token (optional).
*/
create(username: string, password: string, admin: boolean, email: string, virtualCluster: string[], extension?: {}, token?: string): Promise<any>;
/**
* Update user extension data.
* @param userName The user name.
* @param extension The new extension.
* {
* "extension": {
* "key-you-wannat-add-or-update-1": "value1",
* "key-you-wannat-add-or-update-2": {...},
* "key-you-wannat-add-or-update-3": [...]
* }
* @param token Specific an access token (optional).
*/
updateExtension(userName: string, extension: {}, token?: string): Promise<any>;
/**
* Delete a user.
* @param userName The user name.
* @param token Specific an access token (optional).
*/
delete(userName: string, token?: string): Promise<any>;
/**
* Update user's virtual cluster.
* @param userName The user name.
* @param virtualCluster The new virtualCluster.
* {
* "virtualCluster": ["vcname1 in [A-Za-z0-9_]+ format", "vcname2 in [A-Za-z0-9_]+ format"]
* }
* @param token Specific an access token (optional).
*/
updateVirtualcluster(userName: string, virtualCluster: string[], token?: string): Promise<any>;
/**
* Update user's password.
* @param userName The user name.
* @param oldPassword password at least 6 characters, admin could ignore this params.
* @param newPassword password at least 6 characters.
* @param token Specific an access token (optional).
*/
updatePassword(userName: string, oldPassword?: string, newPassword?: string, token?: string): Promise<any>;
/**
* Update user's email.
* @param userName The user name.
* @param email The new email.
* @param token Specific an access token (optional).
*/
updateEmail(userName: string, email: string, token?: string): Promise<any>;
/**
* Update user's admin permission.
* @param userName The user name.
* @param admin true | false.
* @param token Specific an access token (optional).
*/
updateAdminPermission(userName: string, admin: boolean, token?: string): Promise<any>;
/**
* Update user's group list.
* @param userName The user name.
* @param grouplist The new group list.
* @param token Specific an access token (optional).
*/
updateGroupList(userName: string, grouplist: string[], token?: string): Promise<any>;
/**
* Add group into user's group list.
* @param userName The user name.
* @param groupname The new groupname in [A-Za-z0-9_]+ format.
* @param token Specific an access token (optional).
*/
addGroup(userName: string, groupname: string, token?: string): Promise<any>;
/**
* Remove group from user's group list.
* @param userName The user name.
* @param groupname The groupname in [A-Za-z0-9_]+ format.
* @param token Specific an access token (optional).
*/
removeGroup(userName: string, groupname: string, token?: string): Promise<any>;
private sendPutRequestWithToken;
}

Просмотреть файл

@ -1,228 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const request = require("request-promise-native");
const util_1 = require("../commom/util");
const baseClient_1 = require("./baseClient");
/**
* OpenPAI User client.
*/
class UserClient extends baseClient_1.OpenPAIBaseClient {
constructor(cluster) {
super(cluster);
}
/**
* Get user information.
* @param userName The user name.
* @param token Specific an access token (optional).
*/
async get(userName, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}`);
if (token === undefined) {
token = await super.token();
}
const res = await request.get(url, {
headers: {
Authorization: `Bearer ${token}`
}
});
return JSON.parse(res);
}
/**
* Get all users.
* @param token Specific an access token (optional).
*/
async list(token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/`);
if (token === undefined) {
token = await super.token();
}
const res = await request.get(url, {
headers: {
Authorization: `Bearer ${token}`
}
});
return JSON.parse(res);
}
/**
* Create a new user.
* @param username username in [\w.-]+ format.
* @param password password at least 6 characters.
* @param admin true | false.
* @param email email address or empty string.
* @param virtualCluster ["vcname1 in [A-Za-z0-9_]+ format", "vcname2 in [A-Za-z0-9_]+ format"].
* @param extension { "extension-key1": "extension-value1" }.
* @param token Specific an access token (optional).
*/
async create(username, password, admin, email, virtualCluster, extension, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/`);
if (token === undefined) {
token = await super.token();
}
const res = await request.post(url, {
body: JSON.stringify({ username, email, password, admin, virtualCluster, extension }),
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return JSON.parse(res);
}
/**
* Update user extension data.
* @param userName The user name.
* @param extension The new extension.
* {
* "extension": {
* "key-you-wannat-add-or-update-1": "value1",
* "key-you-wannat-add-or-update-2": {...},
* "key-you-wannat-add-or-update-3": [...]
* }
* @param token Specific an access token (optional).
*/
async updateExtension(userName, extension, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/extension`);
if (token === undefined) {
token = await super.token();
}
const res = await this.sendPutRequestWithToken(url, { extension }, token);
return JSON.parse(res);
}
/**
* Delete a user.
* @param userName The user name.
* @param token Specific an access token (optional).
*/
async delete(userName, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}`);
if (token === undefined) {
token = await super.token();
}
const res = await request.delete(url, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return JSON.parse(res);
}
/**
* Update user's virtual cluster.
* @param userName The user name.
* @param virtualCluster The new virtualCluster.
* {
* "virtualCluster": ["vcname1 in [A-Za-z0-9_]+ format", "vcname2 in [A-Za-z0-9_]+ format"]
* }
* @param token Specific an access token (optional).
*/
async updateVirtualcluster(userName, virtualCluster, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/virtualcluster`);
if (token === undefined) {
token = await super.token();
}
const res = await this.sendPutRequestWithToken(url, { virtualCluster }, token);
return JSON.parse(res);
}
/**
* Update user's password.
* @param userName The user name.
* @param oldPassword password at least 6 characters, admin could ignore this params.
* @param newPassword password at least 6 characters.
* @param token Specific an access token (optional).
*/
async updatePassword(userName, oldPassword, newPassword, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/password`);
if (token === undefined) {
token = await super.token();
}
const res = await this.sendPutRequestWithToken(url, { oldPassword, newPassword }, token);
return JSON.parse(res);
}
/**
* Update user's email.
* @param userName The user name.
* @param email The new email.
* @param token Specific an access token (optional).
*/
async updateEmail(userName, email, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/email`);
if (token === undefined) {
token = await super.token();
}
const res = await this.sendPutRequestWithToken(url, { email }, token);
return JSON.parse(res);
}
/**
* Update user's admin permission.
* @param userName The user name.
* @param admin true | false.
* @param token Specific an access token (optional).
*/
async updateAdminPermission(userName, admin, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/admin`);
if (token === undefined) {
token = await super.token();
}
const res = await this.sendPutRequestWithToken(url, { admin }, token);
return JSON.parse(res);
}
/**
* Update user's group list.
* @param userName The user name.
* @param grouplist The new group list.
* @param token Specific an access token (optional).
*/
async updateGroupList(userName, grouplist, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/grouplist`);
if (token === undefined) {
token = await super.token();
}
const res = await this.sendPutRequestWithToken(url, { grouplist }, token);
return JSON.parse(res);
}
/**
* Add group into user's group list.
* @param userName The user name.
* @param groupname The new groupname in [A-Za-z0-9_]+ format.
* @param token Specific an access token (optional).
*/
async addGroup(userName, groupname, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/group`);
if (token === undefined) {
token = await super.token();
}
const res = await this.sendPutRequestWithToken(url, { groupname }, token);
return JSON.parse(res);
}
/**
* Remove group from user's group list.
* @param userName The user name.
* @param groupname The groupname in [A-Za-z0-9_]+ format.
* @param token Specific an access token (optional).
*/
async removeGroup(userName, groupname, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/group`);
if (token === undefined) {
token = await super.token();
}
const res = await request.delete(url, {
body: JSON.stringify({ groupname }),
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return JSON.parse(res);
}
async sendPutRequestWithToken(url, body, token) {
return await request.put(url, {
body: JSON.stringify(body),
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
}
}
exports.UserClient = UserClient;

47
lib/client/virtualClusterClient.d.ts поставляемый
Просмотреть файл

@ -1,47 +0,0 @@
import { IPAICluster } from '../models/cluster';
import { INodeResource, IVirtualCluster } from '../models/virtualCluster';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Virtual Cluster client.
*/
export declare class VirtualClusterClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster);
/**
* list all virtual clusters.
*/
list(): Promise<{
[id: string]: IVirtualCluster;
}>;
/**
* get a virtual cluster.
* @param vcName The name of virtual cluster.
*/
get(vcName: string): Promise<IVirtualCluster>;
/**
* get virtual cluster node resource.
*/
getNodeResource(): Promise<{
[id: string]: INodeResource;
}>;
/**
* Create or update a new virtual cluster.
* @param vcName The name of the new virtual cluster.
* @param vcCapacity The new capacity.
* @param vcMaxCapacity The new max capacity, range of [vcCapacity, 100].
* @param token Specific an access token (optional).
*/
createOrUpdate(vcName: string, vcCapacity: number, vcMaxCapacity: number, token?: string): Promise<any>;
/**
* Delete a virtual cluster.
* @param vcName The virtual cluster name.
* @param token Specific an access token (optional).
*/
delete(vcName: string, token?: string): Promise<any>;
/**
* Change a virtual cluster's status.
* @param vcName The virtual cluster name.
* @param vcStatus The new status 'running' | 'stopped'.
* @param token Specific an access token (optional).
*/
changeStatus(vcName: string, vcStatus: 'running' | 'stopped', token?: string): Promise<any>;
}

Просмотреть файл

@ -1,100 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const request = require("request-promise-native");
const util_1 = require("../commom/util");
const baseClient_1 = require("./baseClient");
/**
* OpenPAI Virtual Cluster client.
*/
class VirtualClusterClient extends baseClient_1.OpenPAIBaseClient {
constructor(cluster) {
super(cluster);
}
/**
* list all virtual clusters.
*/
async list() {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/virtual-clusters`);
const res = await request.get(url);
return JSON.parse(res);
}
/**
* get a virtual cluster.
* @param vcName The name of virtual cluster.
*/
async get(vcName) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/virtual-clusters/${vcName}`);
const res = await request.get(url);
return JSON.parse(res);
}
/**
* get virtual cluster node resource.
*/
async getNodeResource() {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/virtual-clusters/nodeResource`);
const res = await request.get(url);
return JSON.parse(res);
}
/**
* Create or update a new virtual cluster.
* @param vcName The name of the new virtual cluster.
* @param vcCapacity The new capacity.
* @param vcMaxCapacity The new max capacity, range of [vcCapacity, 100].
* @param token Specific an access token (optional).
*/
async createOrUpdate(vcName, vcCapacity, vcMaxCapacity, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/virtual-clusters/${vcName}`);
if (token === undefined) {
token = await super.token();
}
const res = await request.put(url, {
body: JSON.stringify({ vcCapacity, vcMaxCapacity }),
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return JSON.parse(res);
}
/**
* Delete a virtual cluster.
* @param vcName The virtual cluster name.
* @param token Specific an access token (optional).
*/
async delete(vcName, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/virtual-clusters/${vcName}`);
if (token === undefined) {
token = await super.token();
}
const res = await request.delete(url, {
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return JSON.parse(res);
}
/**
* Change a virtual cluster's status.
* @param vcName The virtual cluster name.
* @param vcStatus The new status 'running' | 'stopped'.
* @param token Specific an access token (optional).
*/
async changeStatus(vcName, vcStatus, token) {
const url = util_1.Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/virtual-clusters/${vcName}/status`);
if (token === undefined) {
token = await super.token();
}
const res = await request.put(url, {
body: JSON.stringify({ vcStatus }),
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return JSON.parse(res);
}
}
exports.VirtualClusterClient = VirtualClusterClient;

15
lib/client/webHdfsClient.d.ts поставляемый
Просмотреть файл

@ -1,15 +0,0 @@
import { IMountInfo, IStorageServer } from '../models/storage';
import { IFileInfo, IStorageNodeClient } from '../models/storageOperation';
/**
* Web HDFS Client.
*/
export declare class WebHdfsClient implements IStorageNodeClient {
mkdirAllowRecursive: boolean;
constructor(config: IMountInfo, server: IStorageServer);
getinfo(path: string): Promise<IFileInfo>;
listdir(path: string): Promise<string[]>;
makedir(path: string, mode?: string | undefined): Promise<void>;
upload(localPath: string, remotePath: string, opts?: {} | undefined): Promise<void>;
download(remotePath: string, localPath: string, opts?: {} | undefined): Promise<void>;
delete(path: string): Promise<void>;
}

Просмотреть файл

@ -1,32 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Web HDFS Client.
*/
class WebHdfsClient {
constructor(config, server) {
this.mkdirAllowRecursive = true;
throw new Error('Method not implemented.');
}
async getinfo(path) {
throw new Error('Method not implemented.');
}
async listdir(path) {
throw new Error('Method not implemented.');
}
async makedir(path, mode) {
throw new Error('Method not implemented.');
}
async upload(localPath, remotePath, opts) {
throw new Error('Method not implemented.');
}
async download(remotePath, localPath, opts) {
throw new Error('Method not implemented.');
}
async delete(path) {
throw new Error('Method not implemented.');
}
}
exports.WebHdfsClient = WebHdfsClient;

71
lib/commands/cliEngine.d.ts поставляемый
Просмотреть файл

@ -1,71 +0,0 @@
import * as argparse from 'argparse';
import { IPAICluster, OpenPAIClient } from '..';
import { ICacheRecord } from '../client/cacheClient';
import { Identifiable } from '../commom/identifiable';
export interface IClusterWithCache {
cluster: IPAICluster;
cache?: ICacheRecord[];
}
interface ISubParserOptions extends argparse.SubArgumentParserOptions {
name: string;
}
interface IArgumentOptions extends argparse.ArgumentOptions {
name: string | string[];
}
interface IExclusiveArgGroup {
required?: boolean;
args: IArgumentOptions[];
}
/**
* fix error TS2339: Property 'xxx' does not exist on type 'Namespace'.
*/
interface IArgument extends argparse.Namespace {
[index: string]: any;
}
export interface IResult {
command: string;
args?: IArgument;
result: any | undefined;
}
declare type CommandCallback = (a: IArgument) => any;
declare type FormmaterCallback = (r: IResult) => void;
/**
* LocalClustersManager handles the prestored array of clusters and caches
* by providing filtering, and client construction
*/
declare class LocalClustersManager extends Identifiable<IClusterWithCache, string> {
getClusterConfig(alias: string): IPAICluster;
getClusterClient(alias: string): OpenPAIClient;
protected uidOf: (a: IClusterWithCache) => string;
}
/**
* CliEngine is the executor of CLI commands processing
*/
export declare class CliEngine {
manager: LocalClustersManager;
protected clustersFileName?: string;
protected parser: argparse.ArgumentParser;
protected subparsers: argparse.SubParser;
protected executors: {
[index: string]: CommandCallback;
};
protected formatters: {
[index: string]: FormmaterCallback;
};
constructor(input?: string | IClusterWithCache[]);
load(): Promise<void>;
store(): Promise<void>;
/**
* register a sub command to the CLI engine
*/
registerCommand(subCommand: ISubParserOptions, args: IArgumentOptions[], cb: CommandCallback, exclusiveArgs?: IExclusiveArgGroup[], formatter?: FormmaterCallback): void;
/**
* to evaluate a command (e.g. ['listj`, 'your-cluster1]) and return the result
*/
evaluate(params?: string[]): Promise<IResult>;
/**
* print the result with formatter to screen
*/
toScreen(result: IResult): void;
}
export {};

Просмотреть файл

@ -1,134 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const argparse = require("argparse");
const path = require("path");
const __1 = require("..");
const identifiable_1 = require("../commom/identifiable");
const util_1 = require("../commom/util");
const utils_1 = require("./utils");
function defaultFommater(result) {
const cout = (msg) => {
if (typeof result.result === 'string') {
console.log(result.result);
}
else {
console.log(JSON.stringify(result.result || "", undefined, 4));
}
};
cout(result.result || "");
}
/**
* LocalClustersManager handles the prestored array of clusters and caches
* by providing filtering, and client construction
*/
class LocalClustersManager extends identifiable_1.Identifiable {
constructor() {
super(...arguments);
this.uidOf = (a) => a.cluster.alias;
}
getClusterConfig(alias) {
const idx = this.indexOf(alias);
if (idx > -1) {
return this.data[idx].cluster;
}
throw new Error(`AliasNotFound: ${alias}`);
}
getClusterClient(alias) {
const idx = this.indexOf(alias);
if (idx > -1) {
if (!this.data[idx].cache) {
this.data[idx].cache = []; // ! link cache space with the client
}
return new __1.OpenPAIClient(this.data[idx].cluster, this.data[idx].cache);
}
throw new Error(`AliasNotFound: ${alias}`);
}
}
/**
* CliEngine is the executor of CLI commands processing
*/
class CliEngine {
constructor(input) {
this.executors = {};
this.formatters = {};
this.manager = new LocalClustersManager();
if (input) {
if (typeof input === 'string') {
this.clustersFileName = util_1.Util.expandUser(input);
}
else {
this.manager.copyData(input);
}
}
else {
this.clustersFileName = util_1.Util.expandUser(path.join('~', '.openpai', 'clusters.json'));
}
this.parser = new argparse.ArgumentParser({
version: '0.1',
addHelp: true,
description: 'command line tool for OpenPAI (github.com/microsoft/pai)'
});
this.subparsers = this.parser.addSubparsers({ title: 'commands', dest: 'subcommand' });
}
async load() {
if (this.clustersFileName) {
const data = await utils_1.readJson(this.clustersFileName, []);
this.manager.assignData(data);
}
}
async store() {
if (this.clustersFileName) {
await utils_1.writeJson(this.clustersFileName, this.manager.getData());
}
}
/**
* register a sub command to the CLI engine
*/
registerCommand(subCommand, args, cb, exclusiveArgs, formatter) {
const addArgument = (ps, a) => {
const name = a.name;
delete a.name;
ps.addArgument(name, a);
};
const cmd = subCommand.name;
delete subCommand.name;
if (subCommand.addHelp == null) { // null or undefined
subCommand.addHelp = true;
}
const parser = this.subparsers.addParser(cmd, subCommand);
for (const arg of args) {
addArgument(parser, arg);
}
if (exclusiveArgs) {
for (const g of exclusiveArgs) {
const group = parser.addMutuallyExclusiveGroup({ required: g.required || false });
for (const arg of g.args) {
addArgument(group, arg);
}
}
}
this.executors[cmd] = cb;
this.formatters[cmd] = formatter || defaultFommater;
}
/**
* to evaluate a command (e.g. ['listj`, 'your-cluster1]) and return the result
*/
async evaluate(params) {
const args = this.parser.parseArgs(params);
const cmd = args.subcommand;
delete args.subcommand;
util_1.Util.debug(cmd, args);
const result = await Promise.resolve(this.executors[cmd](args));
return { command: cmd, args: args, result: result };
}
/**
* print the result with formatter to screen
*/
toScreen(result) {
util_1.Util.debug('results received', result);
this.formatters[result.command](result);
}
}
exports.CliEngine = CliEngine;

5
lib/commands/clusterCommands.d.ts поставляемый
Просмотреть файл

@ -1,5 +0,0 @@
import { CliEngine } from './cliEngine';
/**
* register commands related to cluster management
*/
export declare const registerClusterCommands: (cli: CliEngine) => void;

Просмотреть файл

@ -1,37 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const __1 = require("..");
const utils_1 = require("./utils");
/**
* register commands related to cluster management
*/
exports.registerClusterCommands = (cli) => {
cli.registerCommand({ name: 'listc', help: 'list clusters' }, [], (a) => {
const result = cli.manager.getData();
return result.map((x) => x.cluster);
}, undefined, (r) => {
const clusters = r.result;
const rows = [
['alias', 'uri', 'user', 'https']
];
clusters.forEach(cluster => rows.push([
cluster.alias, cluster.pai_uri, cluster.username, cluster.https
]));
utils_1.table2Console(rows);
});
cli.registerCommand({ name: 'addc', help: 'add cluster' }, [
{ name: 'pai_uri', help: 'url of OpenPAI cluster like http(s)://x.x.x' },
{ name: 'username' },
{ name: 'token' },
{ name: ['--alias', '-a'], help: 'cluster alias' }
], (a) => {
cli.manager.add({ cluster: __1.OpenPAIClient.parsePaiUri(a) });
});
cli.registerCommand({ name: 'delc', help: 'delete cluster' }, [
{ name: 'alias', help: 'alias to remove' }
], (a) => {
cli.manager.remove(a.alias);
});
};

6
lib/commands/index.d.ts поставляемый
Просмотреть файл

@ -1,6 +0,0 @@
import { CliEngine } from './cliEngine';
/**
* register all sub commands
*/
declare const registerBuiltinCommands: (cli: CliEngine) => void;
export { CliEngine, registerBuiltinCommands };

Просмотреть файл

@ -1,18 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const cliEngine_1 = require("./cliEngine");
exports.CliEngine = cliEngine_1.CliEngine;
const clusterCommands_1 = require("./clusterCommands");
const jobCommands_1 = require("./jobCommands");
const storageCommands_1 = require("./storageCommands");
/**
* register all sub commands
*/
const registerBuiltinCommands = (cli) => {
clusterCommands_1.registerClusterCommands(cli);
jobCommands_1.registerJobCommands(cli);
storageCommands_1.registerStorageCommands(cli);
};
exports.registerBuiltinCommands = registerBuiltinCommands;

5
lib/commands/jobCommands.d.ts поставляемый
Просмотреть файл

@ -1,5 +0,0 @@
import { CliEngine } from './cliEngine';
/**
* register job realted commands
*/
export declare function registerJobCommands(cli: CliEngine): void;

Просмотреть файл

@ -1,84 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const yaml = require("js-yaml");
const fs = require("fs-extra");
const util_1 = require("../commom/util");
const utils_1 = require("./utils");
const assert = require("assert");
/**
* register job realted commands
*/
function registerJobCommands(cli) {
cli.registerCommand({ name: 'listj', help: 'list jobs', aliases: ['list-jobs'] }, [
{ name: 'alias', help: 'cluster alias' }
], async (a) => {
const client = cli.manager.getClusterClient(a.alias);
if (a.all) {
return client.job.list();
}
return client.job.list(`username=${a.user || client.config.username()}`);
}, [
{
args: [
{ name: ['--user', '-u'], help: 'username (default is user in cluster config)' },
{ name: ['--all', '-a'], help: 'list jobs from all users', action: 'storeTrue' }
]
}
], (r) => {
const jobs = r.result;
const rows = [
['name', 'user', 'state', 'VC', '#GPU', '#Task', 'createdTime', 'completedTime']
];
jobs.forEach(job => rows.push([
job.name, job.username, job.state, job.virtualCluster,
job.totalGpuNumber, job.totalTaskNumber,
new Date(job.createdTime).toLocaleString(), new Date(job.completedTime).toLocaleString()
]));
utils_1.table2Console(rows);
});
cli.registerCommand({ name: 'subj', help: "submit job" }, [
{ name: 'alias', help: 'cluster alias' },
{ name: 'cfgfile', help: 'config file' }
], async (a) => {
const client = cli.manager.getClusterClient(a.alias);
const config = yaml.safeLoad(fs.readFileSync(util_1.Util.expandUser(a.cfgfile), 'utf8'));
return client.job.submit(config);
});
cli.registerCommand({ name: 'getj', help: "get job details", aliases: ['job-info'] }, [
{ name: ['--user'], help: 'username' },
{ name: 'alias', help: 'cluster alias' },
{ name: 'job', help: 'config file' },
], async (a) => {
const client = cli.manager.getClusterClient(a.alias);
return client.job.getFrameworkInfo(a.user || client.config.username(), a.job);
});
cli.registerCommand({ name: 'ssh', help: 'ssh to the job container' }, [
{ name: ['--user'], help: 'username on the openpai cluster' },
{ name: ['--login-name', '-l'], help: 'the username to login as on the remote machine', defaultValue: 'root' },
{ name: ['--identity-file', '-i'], help: 'the file to load identity (private key)' },
{ name: 'alias', help: 'cluster alias' },
{ name: 'job', help: 'config file' },
{ name: 'taskrole', help: 'task role', nargs: '?' },
{ name: 'taskindex', help: 'task index', nargs: '?' }
], async (a) => {
const client = cli.manager.getClusterClient(a.alias);
const jobinfo = await client.job.getFrameworkInfo(a.user || client.config.username(), a.job);
a.taskrole = a.taskrole || Object.keys(jobinfo.taskRoles)[0];
a.taskindex = a.taskindex || 0;
const container = jobinfo.taskRoles[a.taskrole].taskStatuses[a.taskindex];
assert('ssh' in container.containerPorts, `ssh port is not declared when submitting`);
const cmd = ['ssh', '-oStrictHostKeyChecking=no'];
if (a.identity_file) {
cmd.push('-i', a.identity_file);
}
if (a.login_name) {
cmd.push('-l', a.login_name);
}
cmd.push('-p', container.containerPorts['ssh']);
cmd.push(container.containerIp);
return (cmd.join(' '));
});
}
exports.registerJobCommands = registerJobCommands;

5
lib/commands/storageCommands.d.ts поставляемый
Просмотреть файл

@ -1,5 +0,0 @@
import { CliEngine } from './cliEngine';
/**
* register storage related commands
*/
export declare function registerStorageCommands(cli: CliEngine): void;

Просмотреть файл

@ -1,25 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
/**
* register storage related commands
*/
function registerStorageCommands(cli) {
cli.registerCommand({ name: 'lists', help: 'list storages', aliases: ['list-storages'] }, [
{ name: 'alias', help: 'cluster alias' },
{ name: ['--skip-cache', '-k'], help: 'skip cached record, fetch latest value', action: 'storeTrue' }
], async (a) => {
const client = cli.manager.getClusterClient(a.alias);
return await client.cache.functions.getStorages(a.skip_cache);
});
cli.registerCommand({ name: 'getinfo', help: 'get info of destination path in storage' }, [
{ name: 'alias', help: 'cluster alias' },
{ name: 'storage', help: 'storage name' },
{ name: ['--skip-cache', '-k'], help: 'skip cached record, fetch latest value', action: 'storeTrue' }
], async (a) => {
const client = cli.manager.getClusterClient(a.alias);
return await client.cache.functions.getStorageByName(a.skip_cache, a.storage);
});
}
exports.registerStorageCommands = registerStorageCommands;

6
lib/commands/utils.d.ts поставляемый
Просмотреть файл

@ -1,6 +0,0 @@
/**
* utils functions
*/
export declare function readJson<T extends object>(pth: string, val?: T): Promise<T>;
export declare function writeJson<T extends object>(pth: string, val: T): Promise<void>;
export declare function table2Console(rows: any[][]): void;

Просмотреть файл

@ -1,40 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs-extra");
const path_1 = require("path");
const util_1 = require("../commom/util");
const table_1 = require("table");
/**
* utils functions
*/
async function readJson(pth, val) {
try {
const data = await fs.readJson(pth);
util_1.Util.debug(`data loaded from ${pth}`, data);
return data;
}
catch (e) {
console.warn(e.message);
if (val == null) {
throw new Error(e);
}
return val;
}
}
exports.readJson = readJson;
async function writeJson(pth, val) {
await fs.ensureDir(path_1.dirname(pth));
await fs.writeJSON(pth, val);
util_1.Util.debug(`saved to ${pth}`);
}
exports.writeJson = writeJson;
function table2Console(rows) {
const config = {
border: table_1.getBorderCharacters(`ramac`)
};
const output = table_1.table(rows, config);
console.log(output);
}
exports.table2Console = table2Console;

16
lib/commom/identifiable.d.ts поставляемый
Просмотреть файл

@ -1,16 +0,0 @@
/**
* the container for identifiable objects(:T) with unique ID (:U)
*/
export declare abstract class Identifiable<T, U> {
protected data: T[];
constructor(data?: T[]);
getData: () => T[];
copyData(data: T[]): void;
assignData(data: T[]): void;
identities(): U[];
indexOf(uid: U): number;
add(element: T, denyIfExists?: boolean): void;
remove(uid: U): void;
protected abstract uidOf(element: T): U;
protected uidEq(element: T, uid: U): boolean;
}

Просмотреть файл

@ -1,56 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
/**
* the container for identifiable objects(:T) with unique ID (:U)
*/
class Identifiable {
constructor(data) {
this.data = [];
this.getData = () => this.data;
if (data) {
this.data = data;
}
}
copyData(data) {
this.data = JSON.parse(JSON.stringify(data));
}
assignData(data) {
Object.assign(this.data, data);
}
identities() {
return this.data.map((a) => {
return this.uidOf(a);
});
}
indexOf(uid) {
return this.identities().indexOf(uid);
}
add(element, denyIfExists = false) {
const uid = this.uidOf(element);
if (uid == null) {
throw new Error(`UnIdentifiable`);
}
const idx = this.indexOf(uid);
if (denyIfExists && idx > -1) {
throw new Error(`AlreadyExists: of ${this.uidOf(element)}`);
}
if (idx === -1) {
this.data.push(element);
}
else {
this.data[idx] = element;
}
}
remove(uid) {
const idx = this.indexOf(uid);
if (idx > -1) {
this.data.splice(idx, 1);
}
}
uidEq(element, uid) {
return this.uidOf(element) === uid;
}
}
exports.Identifiable = Identifiable;

12
lib/commom/util.d.ts поставляемый
Просмотреть файл

@ -1,12 +0,0 @@
/**
* Utility class.
*/
declare class UtilClass {
https: boolean;
debugMode: boolean;
fixUrl(url: string, https?: boolean): string;
expandUser(url: string): string;
debug(msg?: string, obj?: object): void;
}
export declare const Util: UtilClass;
export {};

Просмотреть файл

@ -1,43 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Utility class.
*/
class UtilClass {
constructor() {
this.https = false;
this.debugMode = false;
}
fixUrl(url, https) {
if (!/^[a-zA-Z]+?\:\/\//.test(url)) {
url = `http${https ? 's' : ''}://` + url;
}
return url;
}
expandUser(url) {
if (/~/.test(url)) {
const home = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE;
if (home) {
url = url.replace('~', home);
}
else {
throw new Error(`could not resolve ~ for ${url}`);
}
}
return url;
}
debug(msg, obj) {
if (!this.debugMode) {
return;
}
if (msg) {
console.debug(msg);
}
if (obj) {
console.debug(obj);
}
}
}
exports.Util = new UtilClass();

17
lib/index.d.ts поставляемый
Просмотреть файл

@ -1,17 +0,0 @@
import { AuthnClient } from './client/authnClient';
import { AzureBlobClient } from './client/azureBlobClient';
import { JobClient } from './client/jobClient';
import { OpenPAIClient } from './client/openPAIClient';
import { StorageClient } from './client/storageClient';
import { UserClient } from './client/userClient';
import { VirtualClusterClient } from './client/virtualClusterClient';
import { IAuthnInfo, ILoginInfo } from './models/authn';
import { IPAICluster } from './models/cluster';
import { IJobConfig } from './models/jobConfig';
import { IJobFrameworkInfo, IJobInfo, IJobSshInfo, IJobStatus } from './models/jobStatus';
import { IMountInfo, IStorageConfig, IStorageDetail, IStorageServer, IStorageSummary } from './models/storage';
import { IFileInfo } from './models/storageOperation';
import { ITokenItem } from './models/token';
import { IUserInfo } from './models/user';
import { INodeResource, IVirtualCluster } from './models/virtualCluster';
export { OpenPAIClient, JobClient, UserClient, VirtualClusterClient, AuthnClient, StorageClient, IPAICluster, IJobConfig, IJobInfo, IJobFrameworkInfo, IJobSshInfo, IUserInfo, ITokenItem, IVirtualCluster, INodeResource, IAuthnInfo, ILoginInfo, IStorageServer, IStorageConfig, IStorageSummary, IStorageDetail, IMountInfo, IJobStatus, AzureBlobClient, IFileInfo };

Просмотреть файл

@ -1,19 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
// tslint:disable-next-line:missing-jsdoc
const authnClient_1 = require("./client/authnClient");
exports.AuthnClient = authnClient_1.AuthnClient;
const azureBlobClient_1 = require("./client/azureBlobClient");
exports.AzureBlobClient = azureBlobClient_1.AzureBlobClient;
const jobClient_1 = require("./client/jobClient");
exports.JobClient = jobClient_1.JobClient;
const openPAIClient_1 = require("./client/openPAIClient");
exports.OpenPAIClient = openPAIClient_1.OpenPAIClient;
const storageClient_1 = require("./client/storageClient");
exports.StorageClient = storageClient_1.StorageClient;
const userClient_1 = require("./client/userClient");
exports.UserClient = userClient_1.UserClient;
const virtualClusterClient_1 = require("./client/virtualClusterClient");
exports.VirtualClusterClient = virtualClusterClient_1.VirtualClusterClient;

17
lib/models/authn.d.ts поставляемый
Просмотреть файл

@ -1,17 +0,0 @@
/**
* OpenPAI authn information.
*/
export interface IAuthnInfo {
authn_type: string;
loginURI: string;
loginURIMethod: 'get' | 'post';
}
/**
* OpenPAI authn basic login information.
*/
export interface ILoginInfo {
admin: boolean;
hasGitHubPAT: boolean;
token: string;
user: string;
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

26
lib/models/cluster.d.ts поставляемый
Просмотреть файл

@ -1,26 +0,0 @@
/**
* OpenPAI cluster.
*/
export interface IPAICluster {
info?: IPAIClusterInfo;
alias?: string;
username?: string;
password?: string;
token?: string;
https?: boolean;
pai_uri?: string;
rest_server_uri?: string;
grafana_uri?: string;
k8s_dashboard_uri?: string;
web_portal_uri?: string;
protocol_version?: string;
}
/**
* OpenPAI cluster info.
*/
export interface IPAIClusterInfo {
name?: string;
version?: string;
launcherType?: 'yarn' | 'k8s';
authnMethod?: 'basic' | 'OIDC';
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

213
lib/models/job.d.ts поставляемый
Просмотреть файл

@ -1,213 +0,0 @@
/**
* OpenPAI Job Info.
*/
export interface IJobInfo {
name: string;
username: string;
state: 'WAITING' | 'RUNNING' | 'SUCCEEDED' | 'STOPPED' | 'FAILED' | 'UNKNOWN';
/** raw frameworkState from frameworklauncher */
subState: 'FRAMEWORK_COMPLETED' | 'FRAMEWORK_WAITING';
executionType: 'START' | 'STOP';
retries: number;
retryDetails: {
/** Job failed due to user or unknown error. */
user: number;
/** Job failed due to platform error. */
platform: number;
/** Job cannot get required resource to run within timeout. */
resource: number;
};
createdTime: number;
completedTime: number;
virtualCluster: string;
appExitCode: number;
totalGpuNumber: number;
totalTaskNumber: number;
totalTaskRoleNumber: number;
}
/**
* OpenPAI Job status.
*/
export interface IJobStatus {
name?: string;
jobStatus?: any | null;
taskRoles?: any | null;
}
/**
* OpenPAI Job Framework Infomation.
*/
export interface IJobFrameworkInfo {
summarizedFrameworkInfo?: any | null;
aggregatedFrameworkRequest?: any | null;
aggregatedFrameworkStatus?: any | null;
}
/**
* OpenPAI v1 job config.
*/
export interface IJobConfigV1 {
jobName: string;
image: string;
dataDir?: string;
authFile?: string;
codeDir: string;
outputDir: string;
taskRoles: [{
name: string;
taskNumber: number;
cpuNumber: number;
gpuNumber: number;
memoryMB: number;
command: string;
}];
[key: string]: any;
}
/**
* OpenPAI Job Config Protocol.
*/
export interface IJobConfig {
/** Protocol version, current version is 2. */
protocolVersion: string | number;
name: string;
/** Component type, should be "job" here. */
type: string;
/** Component version, Default is latest. */
version?: string | number;
contributor?: string;
description?: string;
/** Each item is the protocol for data, script, dockerimage, or output type. */
prerequisites?: {
/** If omitted, follow the protocolVersion in root. */
protocolVersion?: string | number;
name: string;
/** Component type. Must be one of the following: data, script, dockerimage, or output. Prerequisites.type cannot be "job". */
type: string;
/** Component version, Default is latest. */
version?: string;
contributor?: string;
description?: string;
/** Only available when the type is dockerimage. */
auth?: {
username?: string;
/** If a password is needed, it should be referenced as a secret. */
password?: string;
registryuri?: string;
};
/** Only when the type is data can the uri be a list. */
uri: string | string[];
}[];
/**
* If specified, the whole parameters object can be referenced as `$parameters`.
* Scope of reference `$parameters`: the reference is shared among all task roles.
*/
parameters?: {};
/**
* If sensitive information including password or API key is needed in the protocol,
* it should be specified here in secrets section and referenced as `$secrets`.
* Scope of reference `$secrets`: the reference is shared among all task roles and docker image's `auth` field.
* A system that supports PAI protocol should keep the secret information away from
* unauthorized users (how to define unauthorized user is out of the scope of this protocol).
* For example, the yaml file used for job cloning, the stdout/stderr should protect all information marked as secrets.
*/
secrets?: {};
/** Default is 0. */
jobRetryCount?: number;
/**
* Task roles are different types of task in the protocol.
* One job may have one or more task roles, each task role has one or more instances, and each instance runs inside one container.
*/
taskRoles: {
/** Name of the taskRole, string in ^[A-Za-z0-9\-._~]+$ format. */
[name: string]: {
/** Default is 1, instances of a taskRole, no less than 1. */
instances?: number;
/**
* Completion poclicy for the job, https://
* github.com/Microsoft/pai/blob/master/subprojects/frameworklauncher/yarn/doc/USERMANUAL.md#ApplicationCompletionPolicy.
* Number of failed tasks to fail the entire job, null or no less than 1,
* if set to null means the job will always succeed regardless any task failure.
*/
completion?: {
/**
* Number of failed tasks to fail the entire job, null or no less than 1,
* if set to null means the job will always succeed regardless any task failure.
* Default is 1.
*/
minFailedInstances?: number | string;
/**
* Number of succeeded tasks to succeed the entire job, null or no less than 1,
* if set to null means the job will only succeed until all tasks are completed and minFailedInstances is not triggered.
* Default is null.
*/
minSucceededInstances?: number | string;
};
/** Default is 0. */
taskRetryCount?: number;
/** Should reference to a dockerimage defined in prerequisites. */
dockerImage: string;
/**
* Scope of the reference `$data`, `$output`, `$script`: the reference is only valid inside this task role.
* User cannot reference them from another task role. Reference for `$parameters` is global and shared among task roles.
*/
/** Select data defined in prerequisites, target can be referenced as `$data` in this task role. */
data?: string;
/** Select output defined in prerequisites, target can be referenced as `$output` in this task role. */
output?: string;
/** Select script defined in prerequisites, target can be referenced as `$script` in this task role. */
script?: string;
extraContainerOptions?: {
/** Config the /dev/shm in a docker container, https://docs.docker.com/compose/compose-file/#shm_size. */
shmMB?: number;
};
resourcePerInstance: {
/** CPU number, unit is CPU vcore. */
cpu: number;
/** Memory number, unit is MB. */
memoryMB: number;
gpu: number;
ports?: {
/** Port number for the port label. Only for host network, portLabel string in ^[A-Za-z0-9\-._~]+$ format. */
[portLabel: string]: number;
};
};
commands: string[];
};
};
/**
* To handle that a component may interact with different component differently,
* user is encouraged to place the codes handling such difference in the "deployments" field,
* e.g., a job may get input data through wget, hdfc -dfs cp, copy, or just directly read from remote storage.
* This logic can be placed here.
* In summary, the deployments field is responsible to make sure the job to run properly in a deployment specific runtime environment.
* One could have many deployments, but only one deployment can be activated at runtime by specifying in "defaults".
* User can choose the deployment and specify in "defaults" at submission time.
*/
deployments?: {
name: string;
taskRoles: {
/** Should be in taskRoles. */
[name: string]: {
/** Execute before the taskRole's command. */
preCommands: string[];
/** Execute after the taskRole's command. */
postCommands: string[];
};
};
}[];
/** Optional, default cluster specific settings. */
defaults?: {
virtualCluster?: string;
/** Should reference to deployment defined in deployments */
deployment?: string;
};
/** Optional, extra field, object, save any information that plugin may use. */
extras?: {
submitFrom?: string;
};
}
/**
* OpenPAI Job SSH Information.
*/
export interface IJobSshInfo {
containers?: any | null;
keyPair?: any | null;
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

164
lib/models/jobConfig.d.ts поставляемый
Просмотреть файл

@ -1,164 +0,0 @@
/**
* OpenPAI v1 job config.
*/
export interface IJobConfigV1 {
jobName: string;
image: string;
dataDir?: string;
authFile?: string;
codeDir: string;
outputDir: string;
taskRoles: [{
name: string;
taskNumber: number;
cpuNumber: number;
gpuNumber: number;
memoryMB: number;
command: string;
}];
[key: string]: any;
}
/**
* OpenPAI Job Config Protocol.
* https://github.com/microsoft/openpai-protocol/blob/master/schemas/v2/schema.yaml
*/
export interface IJobConfig {
/** Protocol version, current version is 2. */
protocolVersion: string | number;
name: string;
/** Component type, should be "job" here. */
type: string;
/** Component version, Default is latest. */
version?: string | number;
contributor?: string;
description?: string;
/** Each item is the protocol for data, script, dockerimage, or output type. */
prerequisites?: {
/** If omitted, follow the protocolVersion in root. */
protocolVersion?: string | number;
name: string;
/** Component type. Must be one of the following: data, script, dockerimage, or output. Prerequisites.type cannot be "job". */
type: string;
/** Component version, Default is latest. */
version?: string;
contributor?: string;
description?: string;
/** Only available when the type is dockerimage. */
auth?: {
username?: string;
/** If a password is needed, it should be referenced as a secret. */
password?: string;
registryuri?: string;
};
/** Only when the type is data can the uri be a list. */
uri: string | string[];
}[];
/**
* If specified, the whole parameters object can be referenced as `$parameters`.
* Scope of reference `$parameters`: the reference is shared among all task roles.
*/
parameters?: {};
/**
* If sensitive information including password or API key is needed in the protocol,
* it should be specified here in secrets section and referenced as `$secrets`.
* Scope of reference `$secrets`: the reference is shared among all task roles and docker image's `auth` field.
* A system that supports PAI protocol should keep the secret information away from
* unauthorized users (how to define unauthorized user is out of the scope of this protocol).
* For example, the yaml file used for job cloning, the stdout/stderr should protect all information marked as secrets.
*/
secrets?: {};
/** Default is 0. */
jobRetryCount?: number;
/**
* Task roles are different types of task in the protocol.
* One job may have one or more task roles, each task role has one or more instances, and each instance runs inside one container.
*/
taskRoles: {
/** Name of the taskRole, string in ^[A-Za-z0-9\-._~]+$ format. */
[name: string]: {
/** Default is 1, instances of a taskRole, no less than 1. */
instances?: number;
/**
* Completion poclicy for the job, https://
* github.com/Microsoft/pai/blob/master/subprojects/frameworklauncher/yarn/doc/USERMANUAL.md#ApplicationCompletionPolicy.
* Number of failed tasks to fail the entire job, null or no less than 1,
* if set to null means the job will always succeed regardless any task failure.
*/
completion?: {
/**
* Number of failed tasks to fail the entire job, null or no less than 1,
* if set to null means the job will always succeed regardless any task failure.
* Default is 1.
*/
minFailedInstances?: number | string;
/**
* Number of succeeded tasks to succeed the entire job, null or no less than 1,
* if set to null means the job will only succeed until all tasks are completed and minFailedInstances is not triggered.
* Default is null.
*/
minSucceededInstances?: number | string;
};
/** Default is 0. */
taskRetryCount?: number;
/** Should reference to a dockerimage defined in prerequisites. */
dockerImage: string;
/**
* Scope of the reference `$data`, `$output`, `$script`: the reference is only valid inside this task role.
* User cannot reference them from another task role. Reference for `$parameters` is global and shared among task roles.
*/
/** Select data defined in prerequisites, target can be referenced as `$data` in this task role. */
data?: string;
/** Select output defined in prerequisites, target can be referenced as `$output` in this task role. */
output?: string;
/** Select script defined in prerequisites, target can be referenced as `$script` in this task role. */
script?: string;
extraContainerOptions?: {
/** Config the /dev/shm in a docker container, https://docs.docker.com/compose/compose-file/#shm_size. */
shmMB?: number;
};
resourcePerInstance: {
/** CPU number, unit is CPU vcore. */
cpu: number;
/** Memory number, unit is MB. */
memoryMB: number;
gpu: number;
ports?: {
/** Port number for the port label. Only for host network, portLabel string in ^[A-Za-z0-9\-._~]+$ format. */
[portLabel: string]: number;
};
};
commands: string[];
};
};
/**
* To handle that a component may interact with different component differently,
* user is encouraged to place the codes handling such difference in the "deployments" field,
* e.g., a job may get input data through wget, hdfc -dfs cp, copy, or just directly read from remote storage.
* This logic can be placed here.
* In summary, the deployments field is responsible to make sure the job to run properly in a deployment specific runtime environment.
* One could have many deployments, but only one deployment can be activated at runtime by specifying in "defaults".
* User can choose the deployment and specify in "defaults" at submission time.
*/
deployments?: {
name: string;
taskRoles: {
/** Should be in taskRoles. */
[name: string]: {
/** Execute before the taskRole's command. */
preCommands: string[];
/** Execute after the taskRole's command. */
postCommands: string[];
};
};
}[];
/** Optional, default cluster specific settings. */
defaults?: {
virtualCluster?: string;
/** Should reference to deployment defined in deployments */
deployment?: string;
};
/** Optional, extra field, object, save any information that plugin may use. */
extras?: {
submitFrom?: string;
};
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

118
lib/models/jobStatus.d.ts поставляемый
Просмотреть файл

@ -1,118 +0,0 @@
/**
* OpenPAI Job Info.
*/
export interface IJobInfo {
name: string;
username: string;
state: 'WAITING' | 'RUNNING' | 'SUCCEEDED' | 'STOPPED' | 'FAILED' | 'UNKNOWN';
/** raw frameworkState from frameworklauncher */
subState: 'FRAMEWORK_COMPLETED' | 'FRAMEWORK_WAITING';
executionType: 'START' | 'STOP';
retries: number;
retryDetails: {
/** Job failed due to user or unknown error. */
user: number;
/** Job failed due to platform error. */
platform: number;
/** Job cannot get required resource to run within timeout. */
resource: number;
};
createdTime: number;
completedTime: number;
virtualCluster: string;
appExitCode: number;
totalGpuNumber: number;
totalTaskNumber: number;
totalTaskRoleNumber: number;
}
export interface IAppExitSpec {
code: number;
phrase: string;
issuer: string;
causer: string;
type: string;
stage: string;
behavior: string;
reaction: string;
repro: string[];
}
export interface IJobStatusDetails {
username: string;
state: 'WAITING' | 'RUNNING' | 'SUCCEEDED' | 'STOPPED' | 'FAILED' | 'UNKNOWN';
subState: string;
executionType: 'START' | 'STOP';
retries: number;
retryDetails: {
user?: number;
platform?: number;
resource?: number;
};
retryDelayTime?: any;
createdTime: number;
completedTime: number | null;
appId: string;
appProgress: number;
appTrackingUrl: string;
appLaunchedTime: number | null;
appCompletedTime: number | null;
appExitCode: number | null;
appExitSpec: IAppExitSpec | null;
appExitDiagnostics: string | null;
appExitMessages: {
container?: string | null;
runtime?: string | null;
launcher?: string | null;
} | null;
appExitTriggerMessage: string | null;
appExitTriggerTaskRoleName: string | null;
appExitTriggerTaskIndex: number | null;
appExitType: string | null;
virtualCluster: string | null;
}
export interface ITaskStatus {
taskIndex: number;
taskState: string;
containerId: string;
containerIp: string;
containerPorts: {
[index: string]: number | number[] | string;
};
containerGpus?: any;
containerLog: string;
containerExitCode: number | null;
hived?: {
affinityGroupName?: any;
lazyPreempted?: any;
lazyPreemptionStatus?: any;
};
}
/**
* OpenPAI Job status.
*/
export interface IJobStatus {
name: string;
jobStatus: IJobStatusDetails;
taskRoles: {
[index: string]: {
taskRoleStatus: {
name: string;
};
taskStatuses: ITaskStatus[];
};
};
}
/**
* OpenPAI Job Framework Infomation.
*/
export interface IJobFrameworkInfo {
summarizedFrameworkInfo?: any | null;
aggregatedFrameworkRequest?: any | null;
aggregatedFrameworkStatus?: any | null;
}
/**
* OpenPAI Job SSH Information.
*/
export interface IJobSshInfo {
containers?: any | null;
keyPair?: any | null;
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

58
lib/models/storage.d.ts поставляемый
Просмотреть файл

@ -1,58 +0,0 @@
/**
* OpenPAI storage information.
*/
export interface IStorageServer {
spn: string;
type: 'nfs' | 'samba' | 'azurefile' | 'azureblob' | 'hdfs' | 'other';
data: {
[prop: string]: any;
};
extension: any;
}
export interface IStorageConfig {
name: string;
gpn?: string;
default: boolean;
servers?: string[];
mountInfos: IMountInfo[];
}
export interface IMountInfo {
mountPoint: string;
server: string;
path: string;
permission?: string;
}
export interface IStorageSummaryItem {
name: string;
share: boolean;
volumeName: string;
}
export interface IStorageSummary {
storages: IStorageSummaryItem[];
}
export interface INfsCfg {
server: string;
path: string;
}
export interface ISambaCfg {
address: string;
username?: string;
password?: string;
}
export interface IAzureFileCfg {
shareName: string;
accountName?: string;
accountKey?: string;
}
export interface IAzureBlobCfg {
containerName: string;
accountName?: string;
accountKey?: string;
accountSASToken?: string;
}
export interface IStorageDetail extends IStorageSummaryItem {
type: 'nfs' | 'samba' | 'azureFile' | 'azureBlob' | 'other' | 'unknown';
data: INfsCfg | ISambaCfg | IAzureBlobCfg | IAzureFileCfg | Object;
secretName?: string;
mountOptions?: string[];
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

41
lib/models/storageOperation.d.ts поставляемый
Просмотреть файл

@ -1,41 +0,0 @@
/**
* An abstract File System based on file operation
*/
import { IStorageDetail } from './storage';
export interface IFileInfo {
mode: string;
owner: string;
group: string;
size: number;
blksize: number;
type: 'file' | 'directory';
atime: Date;
mtime: Date;
}
export interface IStorageNode {
config: IStorageDetail;
client?: IStorageNodeClient;
getinfo(path: string): Promise<IFileInfo>;
listdir(path: string): Promise<string[]>;
makedir(path: string, mode?: string): Promise<void>;
upload(localPath: string, remotePath: string, opts?: {}): Promise<void>;
download(remotePath: string, localPath: string, opts?: {}): Promise<void>;
delete(path: string): Promise<void>;
existsSync(path: string): boolean;
isdirSync(path: string): boolean;
uploadFolder(localPath: string, remotePath: string, opts?: {}): Promise<void>;
downloadFolder(remotePath: string, localPath: string, opts?: {}): Promise<void>;
deleteFolder(path: string): Promise<void>;
}
export interface IStorageNodeClient {
mkdirAllowRecursive: boolean;
getinfo(path: string): Promise<IFileInfo>;
listdir(path: string): Promise<string[]>;
makedir(path: string, mode?: string): Promise<void>;
upload(localPath: string, remotePath: string, opts?: {}): Promise<void>;
download(remotePath: string, localPath: string, opts?: {}): Promise<void>;
delete(path: string): Promise<void>;
uploadFolder?(localPath: string, remotePath: string, opts?: {}): Promise<void>;
downloadFolder?(remotePath: string, localPath: string, opts?: {}): Promise<void>;
deleteFolder?(path: string): Promise<void>;
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

7
lib/models/token.d.ts поставляемый
Просмотреть файл

@ -1,7 +0,0 @@
/**
* OpenPAI access token.
*/
export interface ITokenItem {
token: string;
expireTime: number;
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

11
lib/models/user.d.ts поставляемый
Просмотреть файл

@ -1,11 +0,0 @@
/**
* OpenPAI User Info.
*/
export interface IUserInfo {
username?: string | null;
grouplist?: string[] | null;
email?: string | null;
extension?: any | null;
admin?: boolean;
virtualCluster?: string[] | null;
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

41
lib/models/virtualCluster.d.ts поставляемый
Просмотреть файл

@ -1,41 +0,0 @@
/**
* OpenPAI Virtual Cluster.
*/
export interface IVirtualCluster {
/** capacity percentage this virtual cluster can use of entire cluster */
capacity: number;
/** max capacity percentage this virtual cluster can use of entire cluster */
maxCapacity: number;
/** used capacity percentage this virtual cluster can use of entire cluster */
usedCapacity: number;
numActiveJobs: number;
numJobs: number;
numPendingJobs: number;
resourcesUsed: {
memory: number;
vCores: number;
GPUs: number;
};
resourcesTotal: {
memory: number;
vCores: number;
GPUs: number;
};
dedicated: boolean;
/** available node list for this virtual cluster */
nodeList: string[];
/**
* RUNNING: vc is enabled.
* STOPPED: vc is disabled, without either new job or running job.
* DRAINING: intermedia state from RUNNING to STOPPED, in waiting on existing job.
*/
status: 'RUNNING' | 'STOPPED' | 'DRAINING';
}
/**
* OpenPAI Virtual Cluster Node Resource.
*/
export interface INodeResource {
gpuTotal: number;
gpuUsed: number;
gpuAvaiable: number;
}

Просмотреть файл

@ -1,4 +0,0 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });

235
package-lock.json сгенерированный
Просмотреть файл

@ -1,5 +1,5 @@
{
"name": "openpai-js-sdk",
"name": "@microsoft/openpai-js-sdk",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
@ -112,14 +112,20 @@
"@babel/highlight": "^7.8.3"
}
},
"@babel/helper-validator-identifier": {
"version": "7.9.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
"integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
"dev": true
},
"@babel/highlight": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
"integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
"version": "7.9.0",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
"integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.9.0",
"chalk": "^2.0.0",
"esutils": "^2.0.2",
"js-tokens": "^4.0.0"
}
},
@ -185,6 +191,12 @@
"integrity": "sha512-0CFu/g4mDSNkodVwWijdlr8jH7RoplRWNgovjFLEZeT+QEbbZXjBmCe3HwaWheAlCbHwomTwzZoSedeOycABug==",
"dev": true
},
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"@types/mocha": {
"version": "5.2.7",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
@ -572,20 +584,6 @@
"type-detect": "^4.0.0"
}
},
"deep-equal": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
"dev": true,
"requires": {
"is-arguments": "^1.0.4",
"is-date-object": "^1.0.1",
"is-regex": "^1.0.4",
"object-is": "^1.0.1",
"object-keys": "^1.1.1",
"regexp.prototype.flags": "^1.2.0"
}
},
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@ -627,9 +625,9 @@
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
},
"es-abstract": {
"version": "1.17.4",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz",
"integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==",
"version": "1.17.5",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
"integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
@ -667,12 +665,6 @@
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true
},
"events": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
@ -878,12 +870,6 @@
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
},
"is-arguments": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
"integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
"dev": true
},
"is-buffer": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
@ -976,6 +962,15 @@
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
@ -1053,27 +1048,10 @@
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}
}
},
"mocha": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.2.tgz",
"integrity": "sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A==",
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz",
"integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==",
"dev": true,
"requires": {
"ansi-colors": "3.2.3",
@ -1088,7 +1066,7 @@
"js-yaml": "3.13.1",
"log-symbols": "2.2.0",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"mkdirp": "0.5.4",
"ms": "2.1.1",
"node-environment-flags": "1.0.5",
"object.assign": "4.1.0",
@ -1096,8 +1074,8 @@
"supports-color": "6.0.0",
"which": "1.3.1",
"wide-align": "1.1.3",
"yargs": "13.3.0",
"yargs-parser": "13.1.1",
"yargs": "13.3.2",
"yargs-parser": "13.1.2",
"yargs-unparser": "1.6.0"
},
"dependencies": {
@ -1110,6 +1088,15 @@
"ms": "^2.1.1"
}
},
"mkdirp": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
"integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
@ -1130,20 +1117,15 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"nock": {
"version": "10.0.6",
"resolved": "https://registry.npmjs.org/nock/-/nock-10.0.6.tgz",
"integrity": "sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w==",
"version": "12.0.3",
"resolved": "https://registry.npmjs.org/nock/-/nock-12.0.3.tgz",
"integrity": "sha512-QNb/j8kbFnKCiyqi9C5DD0jH/FubFGj5rt9NQFONXwQm3IPB0CULECg/eS3AU1KgZb/6SwUa4/DTRKhVxkGABw==",
"dev": true,
"requires": {
"chai": "^4.1.2",
"debug": "^4.1.0",
"deep-equal": "^1.0.0",
"json-stringify-safe": "^5.0.1",
"lodash": "^4.17.5",
"mkdirp": "^0.5.0",
"propagate": "^1.0.0",
"qs": "^6.5.1",
"semver": "^5.5.0"
"lodash": "^4.17.13",
"propagate": "^2.0.0"
},
"dependencies": {
"debug": {
@ -1189,12 +1171,6 @@
"integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
"dev": true
},
"object-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz",
"integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==",
"dev": true
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@ -1233,9 +1209,9 @@
}
},
"p-limit": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
"integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
@ -1297,9 +1273,9 @@
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
},
"propagate": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz",
"integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
"integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
"dev": true
},
"psl": {
@ -1317,16 +1293,6 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"regexp.prototype.flags": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
"integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1"
}
},
"request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
@ -1514,24 +1480,46 @@
"strip-ansi": "^4.0.0"
}
},
"string.prototype.trimleft": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
"integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
"string.prototype.trimend": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
"integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"function-bind": "^1.1.1"
"es-abstract": "^1.17.5"
}
},
"string.prototype.trimleft": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
"integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5",
"string.prototype.trimstart": "^1.0.0"
}
},
"string.prototype.trimright": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
"integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
"integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"function-bind": "^1.1.1"
"es-abstract": "^1.17.5",
"string.prototype.trimend": "^1.0.0"
}
},
"string.prototype.trimstart": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
"integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
}
},
"strip-ansi": {
@ -1543,6 +1531,12 @@
"ansi-regex": "^3.0.0"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
@ -1625,15 +1619,27 @@
}
}
},
"tsconfig-paths": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
"integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.0",
"strip-bom": "^3.0.0"
}
},
"tslib": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.0.tgz",
"integrity": "sha512-BmndXUtiTn/VDDrJzQE7Mm22Ix3PxgLltW9bSNLoeCY31gnG2OPx0QqJnuc9oMIKioYrz487i6K9o4Pdn0j+Kg=="
},
"tslint": {
"version": "5.20.1",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz",
"integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==",
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.1.tgz",
"integrity": "sha512-kd6AQ/IgPRpLn6g5TozqzPdGNZ0q0jtXW4//hRcj10qLYBaa3mTUU2y2MCG+RXZm8Zx+KZi0eA+YCrMyNlF4UA==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
@ -1644,10 +1650,10 @@
"glob": "^7.1.1",
"js-yaml": "^3.13.1",
"minimatch": "^3.0.4",
"mkdirp": "^0.5.1",
"mkdirp": "^0.5.3",
"resolve": "^1.3.2",
"semver": "^5.3.0",
"tslib": "^1.8.0",
"tslib": "^1.10.0",
"tsutils": "^2.29.0"
},
"dependencies": {
@ -1656,6 +1662,15 @@
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"dev": true,
"requires": {
"minimist": "^1.2.5"
}
}
}
},
@ -1842,9 +1857,9 @@
"dev": true
},
"yargs": {
"version": "13.3.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
"integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
"version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
"dev": true,
"requires": {
"cliui": "^5.0.0",
@ -1856,7 +1871,7 @@
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.1.1"
"yargs-parser": "^13.1.2"
},
"dependencies": {
"ansi-regex": {
@ -1888,9 +1903,9 @@
}
},
"yargs-parser": {
"version": "13.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
"integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
"version": "13.1.2",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",

Просмотреть файл

@ -10,7 +10,10 @@
"scripts": {
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
"lint": "tslint -p tsconfig.json",
"test": "mocha -r ts-node/register tests/**/*.spec.ts"
"pretest": "tslint --config tslint.json --project tsconfig.json",
"test": "mocha -r ts-node/register -r tsconfig-paths/register tests/**/*.spec.ts",
"prebuild": "tslint --config tslint.json --project tsconfig.json",
"build": "tsc -p tsconfig.build.json"
},
"homepage": "https://github.com/Microsoft/openpaisdk",
"repository": {
@ -37,12 +40,13 @@
"chai": "^4.2.0",
"dirty-chai": "^2.0.1",
"minimist": ">=1.2.2",
"mocha": "^6.2.0",
"mocha": "^6.2.3",
"mock-fs": "^4.11.0",
"nock": "^10.0.6",
"nock": "^12.0.3",
"prettier": "^1.18.2",
"ts-node": "^8.3.0",
"tslint": "^5.18.0",
"tsconfig-paths": "^3.9.0",
"tslint": "^6.1.1",
"tslint-config-prettier": "^1.18.0",
"tslint-microsoft-contrib": "^6.2.0",
"typescript": "^3.5.3"
@ -50,7 +54,7 @@
"dependencies": {
"@azure/storage-blob": "^12.1.0",
"argparse": "^1.0.10",
"axios": "^0.19.0",
"axios": "^0.19.2",
"fs-extra": "^8.1.0",
"js-yaml": "^3.13.1",
"request": "^2.88.0",

13
src/api/index.ts Normal file
Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import * as ApiV1 from './v1';
import * as ApiV2 from './v2';
/**
* Export
*/
export {
ApiV1,
ApiV2
};

Просмотреть файл

@ -1,12 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IAuthnInfo, IPAICluster } from '@api/v2';
import { Util } from '@pai/commom/util';
import * as request from 'request-promise-native';
import { Util } from '../commom/util';
import { IAuthnInfo } from '../models/authn';
import { IPAICluster } from '../models/cluster';
import { OpenPAIBaseClient } from './baseClient';
/**
@ -23,7 +21,7 @@ export class AuthnClient extends OpenPAIBaseClient {
* Get authn information.
*/
public async info(): Promise<IAuthnInfo> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/info`);
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/authn/info`);
if (this.authnInfo === undefined) {
this.authnInfo = JSON.parse(await request.get(url));
}
@ -36,8 +34,9 @@ export class AuthnClient extends OpenPAIBaseClient {
*/
public async oidcLogin(queryString?: string): Promise<any> {
const url: string = queryString ?
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/oidc/login?${queryString}`) :
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/oidc/login`);
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/authn/oidc/login?${queryString}`) :
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/authn/oidc/login`);
// tslint:disable-next-line:no-unnecessary-local-variable
const res: string = await request.get(url);
return res;
@ -48,8 +47,9 @@ export class AuthnClient extends OpenPAIBaseClient {
*/
public async oidcLogout(queryString?: string): Promise<any> {
const url: string = queryString ?
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/oidc/logout?${queryString}`) :
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/oidc/logout`);
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/authn/oidc/logout?${queryString}`) :
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/authn/oidc/logout`);
// tslint:disable-next-line:no-unnecessary-local-variable
const res: string = await request.get(url);
return res;

Просмотреть файл

@ -1,14 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { ILoginInfo, IPAICluster, IPAIClusterInfo, IToken } from '@api/v2';
import { Util } from '@pai/commom/util';
import * as request from 'request-promise-native';
import { URL } from 'url';
import { Util } from '../commom/util';
import { ILoginInfo } from '../models/authn';
import { IPAICluster, IPAIClusterInfo } from '../models/cluster';
import { ITokenItem } from '../models/token';
/**
* OpenPAI basic client.
*/
@ -37,7 +34,7 @@ export class OpenPAIBaseClient {
};
protected cluster: IPAICluster;
private cacheToken?: ITokenItem;
private cacheToken?: IToken;
constructor(cluster: IPAICluster) {
this.cluster = OpenPAIBaseClient.parsePaiUri(cluster);
@ -70,7 +67,7 @@ export class OpenPAIBaseClient {
public async token(): Promise<string> {
if (this.cluster.token) {
return this.cluster.token;
} else if (!this.cacheToken || this.cacheToken.expireTime < Date.now()) {
} else if (!this.cacheToken || this.cacheToken.expireTime! < Date.now()) {
const res: ILoginInfo = await this.login();
this.cacheToken = {
expireTime: Date.now() + 3600 * 1000,
@ -84,8 +81,8 @@ export class OpenPAIBaseClient {
* Basic login.
*/
public async login(): Promise<ILoginInfo> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/authn/basic/login`);
const res: ILoginInfo = await request.post(url, {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/token`);
return await request.post(url, {
form: {
expiration: 4000,
password: this.cluster.password,
@ -94,7 +91,5 @@ export class OpenPAIBaseClient {
json: true,
timeout: OpenPAIBaseClient.TIMEOUT
});
return res;
}
}

Просмотреть файл

@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// tslint:disable-next-line:missing-jsdoc
import { AuthnClient } from './authnClient';
import { OpenPAIBaseClient } from './baseClient';
import { JobClient } from './jobClient';
import { OpenPAIClient } from './openPAIClient';
export {
OpenPAIClient,
JobClient,
AuthnClient,
OpenPAIBaseClient
};

Просмотреть файл

@ -1,13 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import * as yaml from 'js-yaml';
import { IJobInfo, IJobSshInfo, IJobStatus, IPAICluster } from '@api/v2';
import { Util } from '@pai/commom/util';
import { IJobConfig } from '@protocol/v1';
import * as request from 'request-promise-native';
import { Util } from '../commom/util';
import { IPAICluster } from '../models/cluster';
import { IJobConfig, IJobConfigV1 } from '../models/jobConfig';
import { IJobFrameworkInfo, IJobInfo, IJobSshInfo, IJobStatus } from '../models/jobStatus';
import { OpenPAIBaseClient } from './baseClient';
/**
@ -35,7 +33,7 @@ export class JobClient extends OpenPAIBaseClient {
* @param jobName The job name.
*/
public async get(userName: string, jobName: string): Promise<IJobStatus> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/jobs/${jobName}`);
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/user/${userName}/jobs/${jobName}`);
const res: string = await request.get(url);
return JSON.parse(res);
}
@ -47,7 +45,7 @@ export class JobClient extends OpenPAIBaseClient {
* @param token Specific an access token (optional).
*/
public async delete(userName: string, jobName: string, token?: string): Promise<IJobStatus> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/jobs/${jobName}`);
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/user/${userName}/jobs/${jobName}`);
if (token === undefined) {
token = await super.token();
}
@ -62,25 +60,25 @@ export class JobClient extends OpenPAIBaseClient {
}
/**
* Get job framework info, will call /api/v2/jobs/{userName}~{jobName}.
* Get job framework info.
* @param userName The user name.
* @param jobName The job name.
*/
public async getFrameworkInfo(userName: string, jobName: string): Promise<IJobStatus> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}`);
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/jobs/${userName}~${jobName}`);
const res: string = await request.get(url);
return JSON.parse(res);
}
/**
* Get job config, will call /api/v2/jobs/{userName}~{jobName}/config.
* Get job config.
* @param userName The user name.
* @param jobName The job name.
*/
public async getConfig(userName: string, jobName: string): Promise<IJobConfig> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}/config`);
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/jobs/${userName}/jobs/${jobName}/config`);
const res: any = await request.get(url);
return yaml.safeLoad(res);
return JSON.parse(res);
}
/**
@ -91,7 +89,7 @@ export class JobClient extends OpenPAIBaseClient {
* @param token Specific an access token (optional).
*/
public async execute(userName: string, jobName: string, type: 'START' | 'STOP', token?: string): Promise<any> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/user/${userName}/jobs/${jobName}/executionType`);
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/user/${userName}/jobs/${jobName}/executionType`);
if (token === undefined) {
token = await super.token();
}
@ -109,31 +107,10 @@ export class JobClient extends OpenPAIBaseClient {
}
/**
* Submit a job, will call /api/v2/jobs.
* Submit a job, will call /api/v1/user/{username}/jobs.
* @param jobConfig The job config.
*/
public async submit(jobConfig: IJobConfig, token?: string): Promise<void> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/jobs`);
const text: string = yaml.safeDump(jobConfig);
if (token === undefined) {
token = await super.token();
}
await request.post(url, {
body: text,
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'text/yaml'
},
timeout: OpenPAIBaseClient.TIMEOUT
}
);
}
/**
* Submit a v1 job, will call /api/v1/user/{username}/jobs.
* @param jobConfig The job config.
*/
public async submitV1(userName: string, jobConfig: IJobConfigV1, token?: string): Promise<void> {
public async submit(userName: string, jobConfig: IJobConfig, token?: string): Promise<void> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v1/user/${userName}/jobs`);
if (token === undefined) {
token = await super.token();

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { AuthnClient, JobClient } from '@api/v1/clients';
import { IPAICluster } from '@api/v2';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Client.
*/
export class OpenPAIClient extends OpenPAIBaseClient {
/**
* OpenPAI Job Client.
*/
public job: JobClient;
/**
* OpenPAI Authn Client.
*/
public authn: AuthnClient;
constructor(cluster: IPAICluster) {
super(cluster);
this.job = new JobClient(cluster);
this.authn = new AuthnClient(cluster);
}
}

17
src/api/v1/index.ts Normal file
Просмотреть файл

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IJobConfig } from '@protocol/v1';
import { AuthnClient, JobClient, OpenPAIBaseClient, OpenPAIClient } from './clients';
/**
* Export OpenPAI RestAPI V1.
*/
export {
AuthnClient,
JobClient,
OpenPAIClient,
IJobConfig,
OpenPAIBaseClient
};

Просмотреть файл

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IPAICluster, IPAIClusterInfo } from '@api/v2';
import { Util } from '@pai/commom/util';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Api client.
*/
export class ApiClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Get OpenPAI cluster info.
*/
public async getClusterInfo(): Promise<IPAIClusterInfo> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/info`,
this.cluster.https
);
return await this.httpClient.get(url);
}
}

Просмотреть файл

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IAuthnInfo, ILoginInfo, IPAICluster } from '@api/v2';
import { Util } from '@pai/commom/util';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Authn client.
*/
export class AuthnClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* User login with Azure AD.
*/
public async oidcLogin(queryString?: string): Promise<string> {
return await this.oidcRequest('login', queryString);
}
/**
* User logout from Azure AD.
*/
public async oidcLogout(queryString?: string): Promise<string> {
return await this.oidcRequest('logout', queryString);
}
/**
* Get an access token using username and password.
* @param username Username, set undefined to use the username in cluster setting.
* @param password Password, set undefined to use the password in cluster setting.
*/
public async basicLogin(username?: string, password?: string): Promise<ILoginInfo> {
return await this.httpClient.login(username, password);
}
/**
* Revoke current login token.
*/
public async basicLogout(): Promise<string> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/authn/basic/logout`,
this.cluster.https
);
return await this.httpClient.get(url);
}
private async oidcRequest(req: 'login' | 'logout', queryString?: string): Promise<string> {
const url: string = queryString ?
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/authn/oidc/${req}?${queryString}`, this.cluster.https) :
Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/authn/oidc/${req}`, this.cluster.https);
return await this.httpClient.get(url, {
200: (data) => data
});
}
}

Просмотреть файл

@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { ILoginInfo, IPAICluster, IPAIClusterInfo } from '@api/v2';
import { PAIHttpClient } from '@pai/commom/paiHttpClient';
import { Util } from '@pai/commom/util';
import { URL } from 'url';
/**
* OpenPAI basic client.
*/
export class OpenPAIBaseClient {
protected static readonly TIMEOUT: number = 60 * 1000;
/**
* get cluster configuration / info
*/
public config: any = {
/**
* username from cluster config
*/
username: () => {
return this.cluster.username;
}
};
protected readonly httpClient: PAIHttpClient;
protected cluster: IPAICluster;
constructor(cluster: IPAICluster) {
this.cluster = OpenPAIBaseClient.parsePaiUri(cluster);
this.httpClient = new PAIHttpClient(cluster);
}
/**
* parse information from pai_uri
* refer to tests/unit_tests/baseClient.spec.ts
*/
public static parsePaiUri(cluster: IPAICluster): IPAICluster {
if (cluster.pai_uri) {
const paiUri: URL = new URL(Util.fixUrl(cluster.pai_uri!));
if (!cluster.https) {
cluster.https = paiUri.protocol === 'https:';
}
if (!cluster.rest_server_uri) {
cluster.rest_server_uri = paiUri.host + '/rest-server';
}
if (!cluster.alias) {
cluster.alias = paiUri.hostname;
}
}
return cluster;
}
/**
* Get OpenPAI access token.
*/
public async token(): Promise<string> {
if (!this.cluster.token) {
const info: ILoginInfo = await this.httpClient.login();
this.cluster.token = info.token;
}
return this.cluster.token;
}
}

Просмотреть файл

@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { Identifiable } from '../commom/identifiable';
import { Util } from '../commom/util';
import { Identifiable } from '@pai/commom/identifiable';
import { Util } from '@pai/commom/util';
/**
* for some stable API calling (the results may not change for a long while)
@ -69,4 +69,4 @@ export class CacheClient extends Identifiable<ICacheRecord, string> {
protected uidOf(element: ICacheRecord): string {
return element.name;
}
}
}

Просмотреть файл

@ -0,0 +1,87 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IGroup, IPAICluster, IPAIResponse, IUser } from '@api/v2';
import { Util } from '@pai/commom/util';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI group client.
*/
export class GroupClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Get all group objects in the system.
*/
public async getAllGroup(): Promise<IGroup[]> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/groups`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Create a group in the system.
* @param group The group object { groupname, description, externalName, extension }.
*/
public async createGroup(group: IGroup): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/groups`,
this.cluster.https
);
return await this.httpClient.post(url, group);
}
/**
* Update a group in the system.
* @param group The group object { groupname, description, externalName, extension }.
*/
public async updateGroup(group: IGroup): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/groups`,
this.cluster.https
);
return await this.httpClient.put(url, group);
}
/**
* Get a group in the system.
* @param groupName The group name.
*/
public async getGroup(groupName: string): Promise<IGroup> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/groups/${groupName}`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Delete a group in the system.
* @param groupName The group name.
*/
public async deleteGroup(groupName: string): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/groups/${groupName}`,
this.cluster.https
);
return await this.httpClient.delete(url);
}
/**
* Get the user array of a group in the system.
* @param groupName The group name.
*/
public async getGroupMembers(groupName: string): Promise<IUser[]> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/groups/${groupName}/userlist`,
this.cluster.https
);
return await this.httpClient.get(url);
}
}

Просмотреть файл

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// tslint:disable-next-line:missing-jsdoc
import { ApiClient } from './apiClient';
import { AuthnClient } from './authnClient';
import { OpenPAIBaseClient } from './baseClient';
import { CacheClient, ICacheRecord } from './cacheClient';
import { GroupClient } from './groupClient';
import { JobClient } from './jobClient';
import { JobHistoryClient } from './jobHistoryClient';
import { KubernetesClient } from './kubernetesClient';
import { OpenPAIClient } from './openPAIClient';
import { StorageClient } from './storageClient';
import { UserClient } from './userClient';
import { VirtualClusterClient } from './virtualClusterClient';
export {
OpenPAIBaseClient,
OpenPAIClient,
JobClient,
UserClient,
VirtualClusterClient,
AuthnClient,
StorageClient,
ICacheRecord,
CacheClient,
GroupClient,
ApiClient,
JobHistoryClient,
KubernetesClient
};

Просмотреть файл

@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import {
IJobConfig, IJobInfo, IJobSshInfo, IJobStatus, IPAICluster, IPAIResponse
} from '@api/v2';
import { Util } from '@pai/commom/util';
import * as yaml from 'js-yaml';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Job client.
*/
export class JobClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Submit a job in the system.
* @param jobConfig The job config.
*/
public async createJob(jobConfig: IJobConfig): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs`,
this.cluster.https
);
const text: string = yaml.safeDump(jobConfig);
return await this.httpClient.post(url, text, undefined, {
headers: {
'Content-Type': 'text/yaml'
}
});
}
/**
* Get the list of jobs.
* @param username filter jobs with username.
*/
public async listJobs(username?: string): Promise<IJobInfo[]> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs`,
this.cluster.https
);
return await this.httpClient.get(
url, undefined, undefined, { username }
);
}
/**
* Get job status.
* @param userName The user name.
* @param jobName The job name.
*/
public async getJob(userName: string, jobName: string): Promise<IJobStatus> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Get job configuration.
* This API always returns job config in v2 format (text/yaml).
* Old job config in v1 format will be converted automatically.
* @param userName The user name.
* @param jobName The job name.
*/
public async getJobConfig(userName: string, jobName: string): Promise<IJobConfig> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}/config`,
this.cluster.https
);
return await this.httpClient.get(url, {
200: (data) => yaml.safeLoad(data)
});
}
/**
* Start or stop a job.
* @param userName The user name.
* @param jobName The job name.
* @param type 'START' or 'STOP'.
*/
public async updateJobExecutionType(userName: string, jobName: string, type: 'START' | 'STOP'): Promise<any> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}/executionType`,
this.cluster.https
);
return await this.httpClient.put(url, { value: type });
}
}

Просмотреть файл

@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import {
IJobAttempt, IPAICluster, IPAIResponse
} from '@api/v2';
import { Util } from '@pai/commom/util';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Job client.
*/
export class JobHistoryClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Check if job attempts is healthy.
* @param userName The user name.
* @param jobName The job name.
*/
public async getJobAttemptsHealthz(userName: string, jobName: string): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}/job-attempts/healthz`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Get all attempts of a job.
* @param userName The user name.
* @param jobName The job name.
*/
public async getJobAttempts(userName: string, jobName: string): Promise<IJobAttempt[]> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}/job-attempts`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Get a specific attempt by attempt index.
* @param userName The user name.
* @param jobName The job name.
* @param index The job attempt index.
*/
public async getJobAttempt(
userName: string, jobName: string, index: number
): Promise<IJobAttempt> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/jobs/${userName}~${jobName}/job-attempts/${index}`,
this.cluster.https
);
return await this.httpClient.get(url);
}
}

Просмотреть файл

@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import {
IJobAttempt, IPAICluster, IPAIResponse
} from '@api/v2';
import { Util } from '@pai/commom/util';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Kubernetes client.
*/
export class KubernetesClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Get kubernetes node list. Need administrator permission.
*/
public async getK8sNodes(): Promise<any> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/kubernetes/nodes`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Get kubernetes pod list.
*/
public async getK8sPods(): Promise<any> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/kubernetes/pods`,
this.cluster.https
);
return await this.httpClient.get(url);
}
}

Просмотреть файл

@ -1,15 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { AuthnClient } from '..';
import { IPAICluster } from '../models/cluster';
import { OpenPAIBaseClient } from './baseClient';
import { CacheClient, ICacheRecord } from './cacheClient';
import { JobClient } from './jobClient';
import { StorageClient } from './storageClient';
import { UserClient } from './userClient';
import { VirtualClusterClient } from './virtualClusterClient';
import { IPAICluster } from '@api/v2';
import {
ApiClient,
AuthnClient,
CacheClient,
GroupClient,
ICacheRecord,
JobClient,
JobHistoryClient,
KubernetesClient,
OpenPAIBaseClient,
StorageClient,
UserClient,
VirtualClusterClient
} from '@api/v2/clients';
/**
* OpenPAI Client.
@ -45,6 +51,26 @@ export class OpenPAIClient extends OpenPAIBaseClient {
*/
public cache: CacheClient;
/**
* OpenPAI Group Client.
*/
public group: GroupClient;
/**
* OpenPAI API info Client.
*/
public api: ApiClient;
/**
* OpenPAI job history Client.
*/
public jobHistory: JobHistoryClient;
/**
* OpenPAI kubernetes Client.
*/
public kubernetes: KubernetesClient;
constructor(cluster: IPAICluster, cache?: ICacheRecord[]) {
super(cluster);
this.job = new JobClient(cluster);
@ -52,9 +78,13 @@ export class OpenPAIClient extends OpenPAIBaseClient {
this.virtualCluster = new VirtualClusterClient(cluster);
this.authn = new AuthnClient(cluster);
this.storage = new StorageClient(cluster);
this.group = new GroupClient(cluster);
this.api = new ApiClient(cluster);
this.jobHistory = new JobHistoryClient(cluster);
this.kubernetes = new KubernetesClient(cluster);
this.cache = new CacheClient(cache);
this.cache.delegate(this.storage, this.storage.getStorages);
this.cache.delegate(this.storage, this.storage.getStorageByName);
this.cache.delegate(this.storage, this.storage.getStorage);
}
}

Просмотреть файл

@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IPAICluster } from '@api/v2';
import { IStorageDetail, IStorageSummary } from '@api/v2/models/storage';
import { Util } from '@pai/commom/util';
import { IAzureBlobCfg } from '@pai/storage/clients/azureBlobClient';
import { IStorageDispatcher, StorageNode } from '@pai/storage/clients/storageNode';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Job client.
*/
export class StorageClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Get storage list for which current user has permissions.
*/
public async getStorages(): Promise<IStorageSummary> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/storages`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Get storage for the given name.
* @param name The name of storage.
*/
public async getStorage(name: string): Promise<IStorageDetail> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/storages/${name}`,
this.cluster.https
);
return await this.httpClient.get(url);
}
}
/**
* StorageNode accepting v2 storage detail
*/
export class StorageNodeV2 extends StorageNode<IStorageDetail> {
public storageConfigDispatcher(config: IStorageDetail): IStorageDispatcher {
if (config.type === 'azureBlob') {
return {
type: config.type,
data: config.data as IAzureBlobCfg
};
}
throw Error('NotImplemented');
}
}

Просмотреть файл

@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IPAICluster, IPAIResponse, IToken, ITokenList } from '@api/v2';
import { Util } from '@pai/commom/util';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Api client.
*/
export class TokenClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Get your currently signed tokens.
*/
public async getTokens(): Promise<ITokenList> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/tokens`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Revoke a token.
* @param deleteToken The token string.
*/
public async deleteToken(deleteToken: string): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/tokens/${deleteToken}`,
this.cluster.https
);
return await this.httpClient.delete(url);
}
/**
* Create an application access token in the system.
*/
public async createApplicationToken(): Promise<IToken> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/tokens/application`,
this.cluster.https
);
return await this.httpClient.post(url, {});
}
}

Просмотреть файл

@ -0,0 +1,139 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IPAICluster, IPAIResponse, IUser } from '@api/v2';
import { Util } from '@pai/commom/util';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI User client.
*/
export class UserClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Create a user in the system.
* @param user The user info: { username, email, password, admin, virtualCluster, extension }.
*/
public async createUser(user: IUser): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/users/`,
this.cluster.https
);
return await this.httpClient.post(url, user);
}
/**
* Get all users in the system by admin.
*/
public async getAllUser(): Promise<IUser[]> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/users/`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Update a user in the system. Admin only.
* @param user The user info: { username, email, password, admin, virtualCluster, extension }.
*/
public async updateUser(user: IUser): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/users`,
this.cluster.https
);
return this.httpClient.put(url, user);
}
/**
* Update user's own profile in the system.
* @param username The user name.
* @param email The new email address, optional.
* @param newPassword The new password, optional.
* @param oldPassword The old password, optional.
*/
public async updateUserSelf(
username: string, email?: string, newPassword?: string, oldPassword?: string
): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/users/me`,
this.cluster.https
);
return await this.httpClient.put(url, { username, email, newPassword, oldPassword });
}
/**
* Get a user's data.
* @param userName The user's name.
*/
public async getUser(userName: string): Promise<IUser> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/users/${userName}`,
this.cluster.https
);
return this.httpClient.get(url);
}
/**
* Remove a user in the system.
* Basic mode only.
* Admin only.
* @param userName The user's name.
*/
public async deleteUser(userName: string): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/users/${userName}`,
this.cluster.https
);
return await this.httpClient.delete(url);
}
/**
* Add a group to a user's grouplist.
* Basic mode only.
* Admin only.
* @param userName The user name.
* @param groupname The new groupname in [A-Za-z0-9_]+ format.
*/
public async updateUserGroup(userName: string, groupname: string): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/users/${userName}/group`,
this.cluster.https
);
return await this.httpClient.put(url, { groupname });
}
/**
* Remove a group from user's grouplist.
* Basic mode only.
* Admin only.
* @param userName The user name.
* @param groupname The groupname in [A-Za-z0-9_]+ format.
*/
public async deleteUserGroup(userName: string, groupname: string): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/users/${userName}/group`,
this.cluster.https
);
return await this.httpClient.delete(url, undefined, { data: { groupname } });
}
/**
* Update a user's grouplist.
* Basic mode only.
* Admin only.
* @param userName The user name.
* @param grouplist The new group list.
*/
public async updateUserGrouplist(userName: string, grouplist: string[]): Promise<IPAIResponse> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/users/${userName}/grouplist`,
this.cluster.https
);
return await this.httpClient.put(url, { grouplist });
}
}

Просмотреть файл

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IPAICluster, IVirtualCluster } from '@api/v2';
import { Util } from '@pai/commom/util';
import { ISkuType } from '../models/virtualCluster';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Virtual Cluster client.
*/
export class VirtualClusterClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Get the list of virtual clusters.
*/
public async listVirtualClusters(): Promise<{ [id: string]: IVirtualCluster; }> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/virtual-clusters`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Get virtual cluster status in the system.
* @param vcName The name of virtual cluster.
*/
public async getVirtualCluster(vcName: string): Promise<IVirtualCluster> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/virtual-clusters/${vcName}`,
this.cluster.https
);
return await this.httpClient.get(url);
}
/**
* Get sku types in the virtual cluster.
* @param vcName The name of virtual cluster.
*/
public async getVirtualClusterSkuTypes(vcName: string): Promise<{ [id: string]: ISkuType; }> {
const url: string = Util.fixUrl(
`${this.cluster.rest_server_uri}/api/v2/virtual-clusters/${vcName}/sku-types`,
this.cluster.https
);
return await this.httpClient.get(url);
}
}

66
src/api/v2/index.ts Normal file
Просмотреть файл

@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IJobConfig } from '@protocol/v2';
import {
ApiClient,
AuthnClient,
JobClient,
JobHistoryClient,
OpenPAIBaseClient,
OpenPAIClient,
StorageClient,
UserClient,
VirtualClusterClient
} from './clients';
import { GroupClient } from './clients/groupClient';
import { StorageNodeV2 as StorageNode } from './clients/storageClient';
import { IAuthnInfo, ILoginInfo } from './models/authn';
import { IPAICluster, IPAIClusterInfo } from './models/cluster';
import { IGroup } from './models/group';
import { IJobAttempt, IJobFrameworkInfo, IJobInfo, IJobSshInfo, IJobStatus } from './models/job';
import { IPAIResponse } from './models/paiResponse';
import { IMountInfo, IStorageConfig, IStorageDetail, IStorageServer, IStorageSummary } from './models/storage';
import { IToken, ITokenList } from './models/token';
import { IUser } from './models/user';
import { INodeResource, IVirtualCluster } from './models/virtualCluster';
/**
* Export OpenPAI RestAPI V1.
*/
export {
AuthnClient,
JobClient,
OpenPAIClient,
OpenPAIBaseClient,
StorageClient,
UserClient,
VirtualClusterClient,
IPAICluster,
IPAIClusterInfo,
IJobConfig,
IJobInfo,
IJobFrameworkInfo,
IJobSshInfo,
IJobAttempt,
IUser,
IToken,
ITokenList,
IVirtualCluster,
INodeResource,
IAuthnInfo,
ILoginInfo,
IStorageServer,
IStorageConfig,
IStorageSummary,
IStorageDetail,
IMountInfo,
IJobStatus,
StorageNode,
IGroup,
GroupClient,
ApiClient,
JobHistoryClient,
IPAIResponse
};

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* OpenPAI group.
*/
export interface IGroup {
groupname: string;
description?: string;
externalName?: string;
extension?: object;
}

Просмотреть файл

@ -123,3 +123,83 @@ export interface IJobSshInfo {
containers?: any | null;
keyPair?: any | null;
}
export interface IJobAttempt {
jobName?: string;
frameworkName?: string;
uid?: string;
userName?: string;
state?: string;
originState?: string;
maxAttemptCount?: number;
attemptIndex?: number;
jobStartedTime?: number;
attemptStartedTime?: number;
attemptCompletedTime?: number;
exitCode?: number;
exitPhrase?: string;
exitType?: string;
exitDiagnostics?: {
diagnosticsSummary?: string;
runtime?: {
exitCode?: number;
originUserExitCode?: number;
errorLogs?: {
user?: string;
platform?: string;
};
name?: string;
};
launcher?: string;
};
appExitTriggerMessage?: string;
appExitTriggerTaskRoleName?: string;
appExitTriggerTaskIndex?: number;
appExitSpec: {
code?: number;
phrase?: string;
issuer?: string;
causer?: string;
type?: string;
stage?: string;
behavior?: string;
reaction?: string;
reason?: string;
repro?: string[];
solution?: string[];
};
appExitDiagnostics?: string;
appExitMessages?: {
container?: string;
runtime?: {
exitCode?: number;
originUserExitCode?: number;
errorLogs?: {
user?: string;
platform?: string;
};
name?: string;
};
launcher?: string;
};
totalGpuNumber?: number;
totalTasknumber?: number;
totalTaskRoleNumber?: number;
taskRoles?: {
taskrole?: {
taskRoleStatus: {
name?: string;
};
taskStatuses?: {
taskIndex?: number;
taskState?: string;
containerId?: string;
containerIp?: string;
containerGpus?: string;
containerLog?: string;
containerExitCode?: number;
}[];
};
};
isLatest?: boolean;
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* OpenPAI Response.
*/
export interface IPAIResponse {
code?: string;
message?: string;
}

Просмотреть файл

@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { IAzureBlobCfg } from '@pai/storage/clients/azureBlobClient';
/**
* OpenPAI storage information.
*/
@ -55,13 +57,6 @@ export interface IAzureFileCfg {
accountKey?: string;
}
export interface IAzureBlobCfg {
containerName: string;
accountName?: string;
accountKey?: string;
accountSASToken?: string;
}
export interface IStorageDetail extends IStorageSummaryItem {
type: 'nfs' | 'samba' | 'azureFile' | 'azureBlob' | 'other' | 'unknown';
data: INfsCfg | ISambaCfg | IAzureBlobCfg | IAzureFileCfg | Object;

Просмотреть файл

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/**
* OpenPAI access token.
*/
export interface IToken {
token: string;
expireTime?: number;
application?: boolean;
}
/**
* OpenPAI token list.
*/
export interface ITokenList {
tokens?: string[];
}

Просмотреть файл

@ -4,11 +4,13 @@
/**
* OpenPAI User Info.
*/
export interface IUserInfo {
export interface IUser {
username?: string | null;
password?: string | null;
grouplist?: string[] | null;
email?: string | null;
extension?: any | null;
admin?: boolean;
virtualCluster?: string[] | null;
storageConfig?: any | null;
}

Просмотреть файл

@ -47,3 +47,12 @@ export interface INodeResource {
gpuUsed: number;
gpuAvaiable: number;
}
/**
* OpenPAI SKU type.
*/
export interface ISkuType {
cpu?: number;
memory?: number;
gpu?: number;
}

Просмотреть файл

@ -1,122 +0,0 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// tslint:disable-next-line:match-default-export-name
import axios, { AxiosResponse } from 'axios';
import { Util } from '../commom/util';
import { IPAICluster } from '../models/cluster';
import { IStorageConfig, IStorageDetail, IStorageServer, IStorageSummary } from '../models/storage';
import { OpenPAIBaseClient } from './baseClient';
/**
* OpenPAI Job client.
*/
export class StorageClient extends OpenPAIBaseClient {
constructor(cluster: IPAICluster) {
super(cluster);
}
/**
* Get storage informations.
* @param names Filter storage server with names, default name empty will be ignored.
*/
public async getServer(names?: string, token?: string): Promise<IStorageServer[]> {
const query: string = names ? `?names=${names}` : '';
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storage/server${query}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res: AxiosResponse<IStorageServer[]> = await axios.get<IStorageServer[]>(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
/**
* Get storage information.
* @param storage The storage name.
*/
public async getServerByName(storage: string, token?: string): Promise<IStorageServer> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storage/server/${storage}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res: AxiosResponse<IStorageServer> = await axios.get<IStorageServer>(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
/**
* Get storage config.
* @param names Filter storage server with names, default name empty will be ignored.
*/
public async getConfig(names?: string, token?: string): Promise<IStorageConfig[]> {
const query: string = names ? `?names=${names}` : '';
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storage/config${query}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res: AxiosResponse<IStorageConfig[]> = await axios.get<IStorageConfig[]>(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
/**
* Get storage config.
* @param storage The storage name.
*/
public async getConfigByName(storage: string, token?: string): Promise<IStorageConfig> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storage/config/${storage}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res: AxiosResponse<IStorageConfig> = await axios.get<IStorageConfig>(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
public async getStorages(token?: string): Promise<IStorageSummary> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storages`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res: AxiosResponse<IStorageSummary> = await axios.get<IStorageSummary>(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
public async getStorageByName(name: string, token?: string): Promise<IStorageDetail> {
const url: string = Util.fixUrl(`${this.cluster.rest_server_uri}/api/v2/storages/${name}`, this.cluster.https);
if (token === undefined) {
token = await super.token();
}
const res: AxiosResponse<IStorageDetail> = await axios.get<IStorageDetail>(url, {
headers: {
Authorization: `Bearer ${token}`,
'content-type': 'application/json'
}
});
return res.data;
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше