Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
nni
Commits
9392125e
"magic_pdf/vscode:/vscode.git/clone" did not exist on "1c74ed8ea8991e27935544a8adc21740a354cd39"
Unverified
Commit
9392125e
authored
Jan 21, 2022
by
liuzhe-lz
Committed by
GitHub
Jan 21, 2022
Browse files
Refactor REST server class (#4486)
parent
5a017181
Changes
19
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
563 additions
and
179 deletions
+563
-179
nni/experiment/rest.py
nni/experiment/rest.py
+5
-0
ts/nni_manager/common/log.ts
ts/nni_manager/common/log.ts
+12
-3
ts/nni_manager/common/restServer.ts
ts/nni_manager/common/restServer.ts
+1
-1
ts/nni_manager/core/nnimanager.ts
ts/nni_manager/core/nnimanager.ts
+2
-2
ts/nni_manager/main.ts
ts/nni_manager/main.ts
+2
-4
ts/nni_manager/package.json
ts/nni_manager/package.json
+3
-1
ts/nni_manager/rest_server/index.ts
ts/nni_manager/rest_server/index.ts
+175
-0
ts/nni_manager/rest_server/nniRestServer.ts
ts/nni_manager/rest_server/nniRestServer.ts
+0
-55
ts/nni_manager/rest_server/restHandler.ts
ts/nni_manager/rest_server/restHandler.ts
+6
-7
ts/nni_manager/test/rest_server/log/mock.log
ts/nni_manager/test/rest_server/log/mock.log
+1
-0
ts/nni_manager/test/rest_server/mock_netron_server.ts
ts/nni_manager/test/rest_server/mock_netron_server.ts
+44
-0
ts/nni_manager/test/rest_server/nni_manager_handlers.test.ts
ts/nni_manager/test/rest_server/nni_manager_handlers.test.ts
+4
-4
ts/nni_manager/test/rest_server/rest_server.test.ts
ts/nni_manager/test/rest_server/rest_server.test.ts
+167
-0
ts/nni_manager/test/rest_server/static/index.html
ts/nni_manager/test/rest_server/static/index.html
+4
-0
ts/nni_manager/test/rest_server/static/script.js
ts/nni_manager/test/rest_server/static/script.js
+1
-0
ts/nni_manager/training_service/common/clusterJobRestServer.ts
...i_manager/training_service/common/clusterJobRestServer.ts
+3
-2
ts/nni_manager/yarn.lock
ts/nni_manager/yarn.lock
+121
-100
ts/webui/src/components/public-child/OpenRow.tsx
ts/webui/src/components/public-child/OpenRow.tsx
+5
-0
ts/webui/src/static/const.ts
ts/webui/src/static/const.ts
+7
-0
No files found.
nni/experiment/rest.py
View file @
9392125e
...
...
@@ -3,6 +3,11 @@ from typing import Any, Optional
import
requests
# API URL must be synchronized with:
# - ts/nni_manager/rest_server/index.ts
# - ts/webui/src/static/constant.ts
# Remember to update them if the values are changed, or if this file is moved.
_logger
=
logging
.
getLogger
(
__name__
)
timeout
=
20
...
...
ts/nni_manager/common/log.ts
View file @
9392125e
...
...
@@ -83,12 +83,14 @@ export class Logger {
const
message
=
args
.
map
(
arg
=>
(
typeof
arg
===
'
string
'
?
arg
:
util
.
inspect
(
arg
))).
join
(
'
'
);
const
record
=
`[
${
datetime
}
]
${
levelName
}
(
${
this
.
name
}
)
${
message
}
\n
`
;
const
record
=
`[
${
datetime
}
]
${
levelName
}
(
${
this
.
name
}
)
${
message
}
`
;
if
(
logFile
===
undefined
)
{
if
(
!
isUnitTest
())
{
// be quite for unit test
console
.
log
(
record
);
}
}
else
{
logFile
.
write
(
record
);
logFile
.
write
(
record
+
'
\n
'
);
}
}
}
...
...
@@ -130,3 +132,10 @@ export function stopLogging(): void {
(
global
as
any
).
logFile
=
undefined
;
}
}
/* utilities */
function
isUnitTest
():
boolean
{
const
event
=
process
.
env
[
'
npm_lifecycle_event
'
]
??
''
;
return
event
.
startsWith
(
'
test
'
)
||
event
===
'
mocha
'
||
event
===
'
nyc
'
;
}
ts/nni_manager/common/restServer.ts
View file @
9392125e
...
...
@@ -14,7 +14,7 @@ import { getBasePort } from './experimentStartupInfo';
* The module who wants to use a RestServer could <b>extends</b> this abstract class
* And implement its own registerRestHandler() function to register routers
*/
export
abstract
class
RestServer
{
export
abstract
class
Legacy
RestServer
{
private
startTask
!
:
Deferred
<
void
>
;
private
stopTask
!
:
Deferred
<
void
>
;
private
server
!
:
http
.
Server
;
...
...
ts/nni_manager/core/nnimanager.ts
View file @
9392125e
...
...
@@ -25,7 +25,7 @@ import {
REPORT_METRIC_DATA
,
REQUEST_TRIAL_JOBS
,
SEND_TRIAL_JOB_PARAMETER
,
TERMINATE
,
TRIAL_END
,
UPDATE_SEARCH_SPACE
,
IMPORT_DATA
}
from
'
./commands
'
;
import
{
createDispatcherInterface
,
createDispatcherPipeInterface
,
IpcInterface
}
from
'
./ipcInterface
'
;
import
{
NNI
RestServer
}
from
'
../rest_server
/nniRestServer
'
;
import
{
RestServer
}
from
'
../rest_server
'
;
/**
* NNIManager which implements Manager interface
...
...
@@ -355,7 +355,7 @@ class NNIManager implements Manager {
await
this
.
experimentManager
.
stop
();
await
component
.
get
<
TensorboardManager
>
(
TensorboardManager
).
stop
();
await
this
.
dataStore
.
close
();
await
component
.
get
<
NNI
RestServer
>
(
NNI
RestServer
).
s
top
();
await
component
.
get
<
RestServer
>
(
RestServer
).
s
hutdown
();
}
catch
(
err
)
{
hasError
=
true
;
this
.
log
.
error
(
`
${
err
.
stack
}
`
);
...
...
ts/nni_manager/main.ts
View file @
9392125e
...
...
@@ -19,8 +19,7 @@ import { NNIManager } from './core/nnimanager';
import
{
SqlDB
}
from
'
./core/sqlDatabase
'
;
import
{
NNIExperimentsManager
}
from
'
./core/nniExperimentsManager
'
;
import
{
NNITensorboardManager
}
from
'
./core/nniTensorboardManager
'
;
import
{
NNIRestServer
}
from
'
./rest_server/nniRestServer
'
;
import
{
RestServer
}
from
'
./rest_server
'
;
function
initStartupInfo
(
startExpMode
:
string
,
experimentId
:
string
,
basePort
:
number
,
platform
:
string
,
...
...
@@ -124,9 +123,8 @@ mkDirP(getLogDir())
.
then
(
async
()
=>
{
try
{
await
initContainer
(
foreground
,
mode
);
const
restServer
:
NNI
RestServer
=
component
.
get
(
NNI
RestServer
);
const
restServer
:
RestServer
=
component
.
get
(
RestServer
);
await
restServer
.
start
();
getLogger
(
'
main
'
).
info
(
`Rest server listening on:
${
restServer
.
endPoint
}
`
);
}
catch
(
err
)
{
getLogger
(
'
main
'
).
error
(
`
${
err
.
stack
}
`
);
throw
err
;
...
...
ts/nni_manager/package.json
View file @
9392125e
...
...
@@ -14,7 +14,7 @@
"app-module-path"
:
"^2.2.0"
,
"azure-storage"
:
"^2.10.6"
,
"child-process-promise"
:
"^2.2.1"
,
"express"
:
"^4.17.
1
"
,
"express"
:
"^4.17.
2
"
,
"express-joi-validator"
:
"^2.0.1"
,
"http-proxy"
:
"^1.18.1"
,
"ignore"
:
"^5.1.8"
,
...
...
@@ -46,6 +46,7 @@
"@types/lockfile"
:
"^1.0.0"
,
"@types/mocha"
:
"^8.2.2"
,
"@types/node"
:
"^15.12.1"
,
"@types/node-fetch"
:
"<3.0.0"
,
"@types/request"
:
"^2.48.5"
,
"@types/rx"
:
"^4.1.2"
,
"@types/sqlite3"
:
"^3.1.7"
,
...
...
@@ -61,6 +62,7 @@
"eslint"
:
"^7.28.0"
,
"glob"
:
"^7.1.7"
,
"mocha"
:
"^9.0.2"
,
"node-fetch"
:
"<3.0.0"
,
"nyc"
:
"^15.1.0"
,
"request"
:
"^2.88.2"
,
"rmdir"
:
"^1.2.0"
,
...
...
ts/nni_manager/rest_server/index.ts
0 → 100644
View file @
9392125e
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/**
* Currently the REST server that dispatches web UI and `Experiment` requests.
* In future it should handle WebSocket connections as well.
*
* To add new APIs to REST server, modify `rootRouter()` function.
*
* This file contains API URL constants. They must be synchronized with:
* - nni/experiment/rest.py
* - ts/webui/src/static/const.ts
* - ts/webui/src/components/public-child/OpenRow.tsx
* Remember to update them if the values are changed, or if this file is moved.
*
* TODO:
* 1. Add a global function to handle critical error.
* 2. Refactor ClusterJobRestServer to an express-ws application so it doesn't require extra port.
* 3. Provide public API to register express app, so this can be decoupled with other modules' implementation.
* 4. Refactor NNIRestHandler. It's a mess.
* 5. Get rid of IOC.
* 6. Deal with log path mismatch between REST API and file system.
* 7. Strip slashes of URL prefix inside ExperimentStartupInfo.
**/
import
type
{
Server
}
from
'
http
'
;
import
type
{
AddressInfo
}
from
'
net
'
;
import
path
from
'
path
'
;
import
express
,
{
Request
,
Response
,
Router
}
from
'
express
'
;
import
httpProxy
from
'
http-proxy
'
;
import
{
Deferred
}
from
'
ts-deferred
'
;
import
{
Singleton
}
from
'
common/component
'
;
import
{
getBasePort
,
getPrefixUrl
}
from
'
common/experimentStartupInfo
'
;
import
{
Logger
,
getLogger
}
from
'
common/log
'
;
import
{
getLogDir
}
from
'
common/utils
'
;
import
{
createRestHandler
}
from
'
./restHandler
'
;
/**
* The singleton REST server that dispatches web UI and `Experiment` requests.
*
* RestServer must be initialized with start() after NNI manager constructing, but not necessarily after initializing.
* This is because RestServer needs NNI manager instance to register API handlers.
**/
@
Singleton
export
class
RestServer
{
private
port
:
number
;
private
urlPrefix
:
string
;
private
server
:
Server
|
null
=
null
;
private
logger
:
Logger
=
getLogger
(
'
RestServer
'
);
// I would prefer to get port and urlPrefix by constructor parameters,
// but this is impossible due to limitation of IOC.
constructor
()
{
this
.
port
=
getBasePort
();
// Stripping slashes should be done inside ExperimentInfo, but I don't want to touch it for now.
this
.
urlPrefix
=
'
/
'
+
stripSlashes
(
getPrefixUrl
());
}
// The promise is resolved when it's ready to serve requests.
// This worth nothing for now,
// but for example if we connect to tuner using WebSocket then it must be launched after promise resolved.
public
start
():
Promise
<
void
>
{
this
.
logger
.
info
(
`Starting REST server at port
${
this
.
port
}
, URL prefix: "
${
this
.
urlPrefix
}
"`
);
const
app
=
express
();
// FIXME: We should have a global handler for critical errors.
// `shutdown()` is not a callback and should not be passed to NNIRestHandler.
app
.
use
(
this
.
urlPrefix
,
rootRouter
(
this
.
shutdown
.
bind
(
this
)));
app
.
all
(
'
*
'
,
(
_req
:
Request
,
res
:
Response
)
=>
{
res
.
status
(
404
).
send
(
`Outside prefix "
${
this
.
urlPrefix
}
"`
);
});
this
.
server
=
app
.
listen
(
this
.
port
);
const
deferred
=
new
Deferred
<
void
>
();
this
.
server
.
on
(
'
listening
'
,
()
=>
{
if
(
this
.
port
===
0
)
{
// Currently for unit test, can be public feature in future.
this
.
port
=
(
<
AddressInfo
>
this
.
server
!
.
address
()).
port
;
}
this
.
logger
.
info
(
'
REST server started.
'
);
deferred
.
resolve
();
});
// FIXME: Use global handler. The event can be emitted after listening.
this
.
server
.
on
(
'
error
'
,
(
error
:
Error
)
=>
{
this
.
logger
.
error
(
'
REST server error:
'
,
error
);
deferred
.
reject
(
error
);
});
return
deferred
.
promise
;
}
public
shutdown
():
Promise
<
void
>
{
this
.
logger
.
info
(
'
Stopping REST server.
'
);
if
(
this
.
server
===
null
)
{
this
.
logger
.
warning
(
'
REST server is not running.
'
);
return
Promise
.
resolve
();
}
const
deferred
=
new
Deferred
<
void
>
();
this
.
server
.
close
(()
=>
{
this
.
logger
.
info
(
'
REST server stopped.
'
);
deferred
.
resolve
();
});
// FIXME: Use global handler. It should be aware of shutting down event and swallow errors in this stage.
this
.
server
.
on
(
'
error
'
,
(
error
:
Error
)
=>
{
this
.
logger
.
error
(
'
REST server error:
'
,
error
);
deferred
.
resolve
();
});
return
deferred
.
promise
;
}
}
/**
* You will need to modify this function if you want to add a new module, for example, project management.
*
* Each module should have a unique URL prefix and a "Router". Check express' reference about Application and Router.
* Note that the order of `use()` calls does matter so you must not put a router after web UI.
*
* In fact experiments management should have a separate prefix and module.
**/
function
rootRouter
(
stopCallback
:
()
=>
Promise
<
void
>
):
Router
{
const
router
=
Router
();
router
.
use
(
express
.
json
({
limit
:
'
50mb
'
}));
/* NNI manager APIs */
router
.
use
(
'
/api/v1/nni
'
,
createRestHandler
(
stopCallback
));
/* Download log files */
// The REST API path "/logs" does not match file system path "/log".
// Here we use an additional router to workaround this problem.
const
logRouter
=
Router
();
logRouter
.
get
(
'
*
'
,
express
.
static
(
getLogDir
()));
router
.
use
(
'
/logs
'
,
logRouter
);
/* NAS model visualization */
router
.
use
(
'
/netron
'
,
netronProxy
());
/* Web UI */
router
.
get
(
'
*
'
,
express
.
static
(
webuiPath
));
// React Router handles routing inside the browser. We must send index.html to all routes.
// path.resolve() is required by Response.sendFile() API.
router
.
get
(
'
*
'
,
(
_req
:
Request
,
res
:
Response
)
=>
{
res
.
sendFile
(
path
.
join
(
webuiPath
,
'
index.html
'
));
});
/* 404 as catch-all */
router
.
all
(
'
*
'
,
(
_req
:
Request
,
res
:
Response
)
=>
{
res
.
status
(
404
).
send
(
'
Not Found
'
);
});
return
router
;
}
function
netronProxy
():
Router
{
const
router
=
Router
();
const
proxy
=
httpProxy
.
createProxyServer
();
router
.
all
(
'
*
'
,
(
req
:
Request
,
res
:
Response
):
void
=>
{
delete
req
.
headers
.
host
;
proxy
.
web
(
req
,
res
,
{
changeOrigin
:
true
,
target
:
netronUrl
});
});
return
router
;
}
function
stripSlashes
(
str
:
string
):
string
{
return
str
.
replace
(
/^
\/
+/
,
''
).
replace
(
/
\/
+$/
,
''
);
}
let
webuiPath
:
string
=
path
.
resolve
(
'
static
'
);
let
netronUrl
:
string
=
'
https://netron.app
'
;
export
namespace
UnitTestHelpers
{
export
function
getPort
(
server
:
RestServer
):
number
{
return
(
<
any
>
server
).
port
;
}
export
function
setWebuiPath
(
mockPath
:
string
):
void
{
webuiPath
=
path
.
resolve
(
mockPath
);
}
export
function
setNetronUrl
(
mockUrl
:
string
):
void
{
netronUrl
=
mockUrl
;
}
}
ts/nni_manager/rest_server/nniRestServer.ts
deleted
100644 → 0
View file @
5a017181
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import
bodyParser
from
'
body-parser
'
;
import
express
from
'
express
'
;
import
httpProxy
from
'
http-proxy
'
;
import
path
from
'
path
'
;
import
*
as
component
from
'
../common/component
'
;
import
{
RestServer
}
from
'
../common/restServer
'
import
{
getLogDir
}
from
'
../common/utils
'
;
import
{
createRestHandler
}
from
'
./restHandler
'
;
import
{
getAPIRootUrl
,
getPrefixUrl
}
from
'
../common/experimentStartupInfo
'
;
/**
* NNI Main rest server, provides rest API to support
* # nnictl CLI tool
* # NNI WebUI
*
*/
@
component
.
Singleton
export
class
NNIRestServer
extends
RestServer
{
private
readonly
LOGS_ROOT_URL
:
string
=
'
/logs
'
;
protected
netronProxy
:
any
=
null
;
protected
API_ROOT_URL
:
string
=
'
/api/v1/nni
'
;
/**
* constructor to provide NNIRestServer's own rest property, e.g. port
*/
constructor
()
{
super
();
this
.
API_ROOT_URL
=
getAPIRootUrl
();
this
.
netronProxy
=
httpProxy
.
createProxyServer
();
}
/**
* NNIRestServer's own router registration
*/
protected
registerRestHandler
():
void
{
this
.
app
.
use
(
getPrefixUrl
(),
express
.
static
(
'
static
'
));
this
.
app
.
use
(
bodyParser
.
json
({
limit
:
'
50mb
'
}));
this
.
app
.
use
(
this
.
API_ROOT_URL
,
createRestHandler
(
this
));
this
.
app
.
use
(
this
.
LOGS_ROOT_URL
,
express
.
static
(
getLogDir
()));
this
.
app
.
all
(
'
/netron/*
'
,
(
req
:
express
.
Request
,
res
:
express
.
Response
)
=>
{
delete
req
.
headers
.
host
;
req
.
url
=
req
.
url
.
replace
(
'
/netron
'
,
'
/
'
);
this
.
netronProxy
.
web
(
req
,
res
,
{
changeOrigin
:
true
,
target
:
'
https://netron.app
'
});
});
this
.
app
.
get
(
`
${
getPrefixUrl
()}
/*`
,
(
_req
:
express
.
Request
,
res
:
express
.
Response
)
=>
{
res
.
sendFile
(
path
.
resolve
(
'
static/index.html
'
));
});
}
}
ts/nni_manager/rest_server/restHandler.ts
View file @
9392125e
...
...
@@ -13,7 +13,6 @@ import { ExperimentProfile, Manager, TrialJobStatistics } from '../common/manage
import
{
ExperimentManager
}
from
'
../common/experimentManager
'
;
import
{
TensorboardManager
,
TensorboardTaskInfo
}
from
'
../common/tensorboardManager
'
;
import
{
ValidationSchemas
}
from
'
./restValidationSchemas
'
;
import
{
NNIRestServer
}
from
'
./nniRestServer
'
;
import
{
getVersion
}
from
'
../common/utils
'
;
import
{
MetricType
}
from
'
../common/datastore
'
;
import
{
ProfileUpdateType
}
from
'
../common/manager
'
;
...
...
@@ -23,17 +22,17 @@ import { TrialJobStatus } from '../common/trainingService';
//const expressJoi = require('express-joi-validator');
class
NNIRestHandler
{
private
restServer
:
NNIRestServer
;
private
stopCallback
:
()
=>
Promise
<
void
>
;
private
nniManager
:
Manager
;
private
experimentsManager
:
ExperimentManager
;
private
tensorboardManager
:
TensorboardManager
;
private
log
:
Logger
;
constructor
(
rs
:
NNIRestServer
)
{
constructor
(
stopCallback
:
()
=>
Promise
<
void
>
)
{
this
.
nniManager
=
component
.
get
(
Manager
);
this
.
experimentsManager
=
component
.
get
(
ExperimentManager
);
this
.
tensorboardManager
=
component
.
get
(
TensorboardManager
);
this
.
restServer
=
rs
;
this
.
stopCallback
=
stopCallback
;
this
.
log
=
getLogger
(
'
NNIRestHandler
'
);
}
...
...
@@ -125,7 +124,7 @@ class NNIRestHandler {
this
.
handleError
(
err
,
res
);
this
.
log
.
error
(
err
.
message
);
this
.
log
.
error
(
`Datastore initialize failed, stopping rest server...`
);
await
this
.
restServer
.
stop
();
await
this
.
stopCallback
();
});
});
}
...
...
@@ -434,8 +433,8 @@ class NNIRestHandler {
}
}
export
function
createRestHandler
(
rs
:
NNIRestServer
):
Router
{
const
handler
:
NNIRestHandler
=
new
NNIRestHandler
(
r
s
);
export
function
createRestHandler
(
stopCallback
:
()
=>
Promise
<
void
>
):
Router
{
const
handler
:
NNIRestHandler
=
new
NNIRestHandler
(
s
topCallback
);
return
handler
.
createRestHandler
();
}
ts/nni_manager/test/rest_server/log/mock.log
0 → 100644
View file @
9392125e
I'm a log file.
ts/nni_manager/test/rest_server/mock_netron_server.ts
0 → 100644
View file @
9392125e
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
/**
* A helper HTTP server that sends request informantion back as response.
* Used to check that RestServer will send correct requests to netron.app.
*
* It will automatically dispose itself so no need for stop().
**/
import
type
{
AddressInfo
}
from
'
net
'
;
import
express
,
{
Request
,
Response
}
from
'
express
'
;
import
{
Deferred
}
from
'
ts-deferred
'
;
export
function
start
():
Promise
<
number
>
{
const
app
=
express
();
app
.
use
(
express
.
text
());
app
.
get
(
'
*
'
,
(
req
:
Request
,
res
:
Response
)
=>
{
res
.
send
({
method
:
'
GET
'
,
url
:
req
.
url
,
headers
:
req
.
headers
,
});
});
app
.
post
(
'
*
'
,
(
req
:
Request
,
res
:
Response
)
=>
{
res
.
send
({
method
:
'
POST
'
,
url
:
req
.
url
,
headers
:
req
.
headers
,
body
:
req
.
body
,
});
});
const
server
=
app
.
listen
();
server
.
unref
();
const
deferred
=
new
Deferred
<
number
>
();
server
.
on
(
'
listening
'
,
()
=>
{
deferred
.
resolve
((
<
AddressInfo
>
server
!
.
address
()).
port
);
});
return
deferred
.
promise
;
}
ts/nni_manager/test/rest_server/
restserv
er.test.ts
→
ts/nni_manager/test/rest_server/
nni_manager_handl
er
s
.test.ts
View file @
9392125e
...
...
@@ -15,7 +15,7 @@ import { TrainingService } from '../../common/trainingService';
import
{
cleanupUnitTest
,
prepareUnitTest
}
from
'
../../common/utils
'
;
import
{
MockedDataStore
}
from
'
../mock/datastore
'
;
import
{
MockedTrainingService
}
from
'
../mock/trainingService
'
;
import
{
NNI
RestServer
}
from
'
../../rest_server
/nniRestServer
'
;
import
{
RestServer
}
from
'
../../rest_server
'
;
import
{
testManagerProvider
}
from
'
../mock/nniManager
'
;
import
{
testExperimentManagerProvider
}
from
'
../mock/experimentManager
'
;
import
{
TensorboardManager
}
from
'
../../common/tensorboardManager
'
;
...
...
@@ -32,9 +32,9 @@ describe('Unit test for rest server', () => {
Container
.
bind
(
TrainingService
).
to
(
MockedTrainingService
);
Container
.
bind
(
ExperimentManager
).
provider
(
testExperimentManagerProvider
);
Container
.
bind
(
TensorboardManager
).
to
(
NNITensorboardManager
);
const
restServer
:
NNI
RestServer
=
component
.
get
(
NNI
RestServer
);
const
restServer
:
RestServer
=
component
.
get
(
RestServer
);
restServer
.
start
().
then
(()
=>
{
ROOT_URL
=
`
${
restServer
.
endPoint
}
/api/v1/nni`
;
ROOT_URL
=
`
http://localhost:8080
/api/v1/nni`
;
done
();
}).
catch
((
e
:
Error
)
=>
{
assert
.
fail
(
`Failed to start rest server:
${
e
.
message
}
`
);
...
...
@@ -42,7 +42,7 @@ describe('Unit test for rest server', () => {
});
after
(()
=>
{
component
.
get
<
NNI
RestServer
>
(
NNI
RestServer
).
s
top
();
component
.
get
<
RestServer
>
(
RestServer
).
s
hutdown
();
cleanupUnitTest
();
});
...
...
ts/nni_manager/test/rest_server/rest_server.test.ts
0 → 100644
View file @
9392125e
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import
assert
from
'
assert
'
;
import
fs
from
'
fs
'
;
import
path
from
'
path
'
;
import
fetch
from
'
node-fetch
'
;
import
{
setExperimentStartupInfo
}
from
'
common/experimentStartupInfo
'
;
import
{
RestServer
,
UnitTestHelpers
}
from
'
rest_server
'
;
import
*
as
mock_netron_server
from
'
./mock_netron_server
'
;
let
restServer
:
RestServer
;
let
endPoint
:
string
;
let
endPointWithoutPrefix
:
string
;
let
netronHost
:
string
;
const
logFileContent
=
fs
.
readFileSync
(
path
.
join
(
__dirname
,
'
log/mock.log
'
));
const
webuiIndexContent
=
fs
.
readFileSync
(
path
.
join
(
__dirname
,
'
static/index.html
'
));
const
webuiScriptContent
=
fs
.
readFileSync
(
path
.
join
(
__dirname
,
'
static/script.js
'
));
/* Test cases */
// Test cases will be run twice, the first time without URL prefix, and the second time with a URL prefix.
// NNI manager APIs are covered by old tests.
// In future RestServer should not be responsible for API implementation.
// Download a log file.
async
function
testLogs
():
Promise
<
void
>
{
const
res
=
await
fetch
(
urlJoin
(
endPoint
,
'
/logs/mock.log
'
));
const
contentType
=
res
.
headers
.
get
(
'
Content-Type
'
)
!
;
assert
.
ok
(
res
.
ok
);
assert
.
ok
(
contentType
.
startsWith
(
'
text/plain
'
));
// content type can influence browser behavior
assert
.
equal
(
await
res
.
text
(),
logFileContent
);
}
// Proxy a GET request to Netron.
async
function
testNetronGet
():
Promise
<
void
>
{
const
res
=
await
fetch
(
urlJoin
(
endPoint
,
'
/netron/mock/get-path
'
));
const
req
=
await
res
.
json
();
// the mock server send request info back as response
assert
.
ok
(
res
.
ok
);
assert
.
equal
(
req
.
headers
.
host
,
netronHost
);
assert
.
equal
(
req
.
url
,
'
/mock/get-path
'
);
}
// Proxy a POST request to Netron.
async
function
testNetronPost
():
Promise
<
void
>
{
const
postData
=
'
hello netron
'
;
const
res
=
await
fetch
(
urlJoin
(
endPoint
,
'
/netron/post-path
'
),
{
method
:
'
POST
'
,
body
:
postData
});
const
req
=
await
res
.
json
();
assert
.
ok
(
res
.
ok
);
assert
.
equal
(
req
.
url
,
'
/post-path
'
);
assert
.
equal
(
req
.
body
,
postData
);
}
// Access web UI index page.
async
function
testWebuiIndex
():
Promise
<
void
>
{
const
res
=
await
fetch
(
endPoint
);
assert
.
ok
(
res
.
ok
);
assert
.
equal
(
await
res
.
text
(),
webuiIndexContent
);
}
// Access web UI resource file (js, css, image, etc).
async
function
testWebuiResource
():
Promise
<
void
>
{
const
res
=
await
fetch
(
urlJoin
(
endPoint
,
'
/script.js
'
));
assert
.
ok
(
res
.
ok
);
assert
.
equal
(
await
res
.
text
(),
webuiScriptContent
);
}
// Access web UI routing path ("/oview", "/detail", etc).
// This should also send index page.
async
function
testWebuiRouting
():
Promise
<
void
>
{
const
res
=
await
fetch
(
urlJoin
(
endPoint
,
'
/not-exist
'
));
assert
.
ok
(
res
.
ok
);
assert
.
equal
(
await
res
.
text
(),
webuiIndexContent
);
}
// When URL prefix is set, send a request without that prefix.
async
function
testOutsidePrefix
():
Promise
<
void
>
{
const
res
=
await
fetch
(
endPointWithoutPrefix
);
assert
.
equal
(
res
.
status
,
404
);
const
res2
=
await
fetch
(
urlJoin
(
endPointWithoutPrefix
,
'
/not-exist
'
));
assert
.
equal
(
res2
.
status
,
404
);
}
/* Register test cases */
describe
(
'
## rest_server ##
'
,
()
=>
{
it
(
'
logs
'
,
()
=>
testLogs
());
it
(
'
netron get
'
,
()
=>
testNetronGet
());
it
(
'
netron post
'
,
()
=>
testNetronPost
());
it
(
'
webui index
'
,
()
=>
testWebuiIndex
());
it
(
'
webui resource
'
,
()
=>
testWebuiResource
());
it
(
'
webui routing
'
,
()
=>
testWebuiRouting
());
// I don't know how to add "between test cases hook".
// This is a workaround to reset REST server with URL prefix.
it
(
'
// re-configure rest server
'
,
()
=>
configRestServer
(
'
url/prefix
'
));
it
(
'
prefix logs
'
,
()
=>
testLogs
());
it
(
'
prefix netron get
'
,
()
=>
testNetronGet
());
it
(
'
prefix netron post
'
,
()
=>
testNetronPost
());
it
(
'
prefix webui index
'
,
()
=>
testWebuiIndex
());
it
(
'
prefix webui resource
'
,
()
=>
testWebuiResource
());
it
(
'
prefix webui routing
'
,
()
=>
testWebuiRouting
());
it
(
'
outside prefix
'
,
()
=>
testOutsidePrefix
());
});
/* Configure test environment */
before
(
async
()
=>
{
await
configRestServer
();
const
netronPort
=
await
mock_netron_server
.
start
();
netronHost
=
`localhost:
${
netronPort
}
`
;
UnitTestHelpers
.
setNetronUrl
(
'
http://
'
+
netronHost
);
});
after
(
async
()
=>
{
await
restServer
.
shutdown
();
});
async
function
configRestServer
(
urlPrefix
?:
string
)
{
if
(
restServer
!==
undefined
)
{
await
restServer
.
shutdown
();
}
// Set port, URL prefix, and log path.
// There should be a better way to do this.
// Maybe rewire? I can't get it work with TypeScript.
setExperimentStartupInfo
(
true
,
path
.
basename
(
__dirname
),
// hacking getLogDir()
0
,
// ask for a random idle port
'
local
'
,
path
.
dirname
(
__dirname
),
undefined
,
undefined
,
undefined
,
urlPrefix
);
UnitTestHelpers
.
setWebuiPath
(
path
.
join
(
__dirname
,
'
static
'
));
restServer
=
new
RestServer
();
await
restServer
.
start
();
const
port
=
UnitTestHelpers
.
getPort
(
restServer
);
endPointWithoutPrefix
=
`http://localhost:
${
port
}
`
;
endPoint
=
urlJoin
(
endPointWithoutPrefix
,
urlPrefix
??
''
);
}
function
urlJoin
(
part1
:
string
,
part2
:
string
):
string
{
if
(
part1
.
endsWith
(
'
/
'
))
{
part1
=
part1
.
slice
(
0
,
-
1
);
}
if
(
part2
.
startsWith
(
'
/
'
))
{
part2
=
part2
.
slice
(
1
);
}
if
(
part2
===
''
)
{
return
part1
;
}
return
part1
+
'
/
'
+
part2
;
}
ts/nni_manager/test/rest_server/static/index.html
0 → 100644
View file @
9392125e
<!DOCTYPE html>
<html>
mock index
</html>
ts/nni_manager/test/rest_server/static/script.js
0 → 100644
View file @
9392125e
function
mockScript
()
{
}
ts/nni_manager/training_service/common/clusterJobRestServer.ts
View file @
9392125e
...
...
@@ -10,15 +10,16 @@ import { Writable } from 'stream';
import
{
String
}
from
'
typescript-string-operations
'
;
import
*
as
component
from
'
common/component
'
;
import
{
getBasePort
,
getExperimentId
}
from
'
common/experimentStartupInfo
'
;
import
{
RestServer
}
from
'
common/restServer
'
;
import
{
Legacy
RestServer
}
from
'
common/restServer
'
;
import
{
getExperimentRootDir
,
mkDirPSync
}
from
'
common/utils
'
;
/**
* Cluster Job Training service Rest server, provides rest API to support Cluster job metrics update
*
* FIXME: This should be a router, not a separate REST server.
*/
@
component
.
Singleton
export
abstract
class
ClusterJobRestServer
extends
RestServer
{
export
abstract
class
ClusterJobRestServer
extends
Legacy
RestServer
{
private
readonly
API_ROOT_URL
:
string
=
'
/api/v1/nni-pai
'
;
private
readonly
NNI_METRICS_PATTERN
:
string
=
`NNISDK_MEb'(?<metrics>.*?)'`
;
...
...
ts/nni_manager/yarn.lock
View file @
9392125e
...
...
@@ -619,6 +619,14 @@
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.2.tgz#91daa226eb8c2ff261e6a8cbf8c7304641e095e0"
integrity sha512-Lwh0lzzqT5Pqh6z61P3c3P5nm6fzQK/MMHl9UKeneAeInVflBSz1O2EkX6gM6xfJd7FBXBY5purtLx7fUiZ7Hw==
"@types/node-fetch@<3.0.0":
version "2.5.12"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66"
integrity sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*":
version "15.12.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.1.tgz#9b60797dee1895383a725f828a869c86c6caa5c2"
...
...
@@ -1202,21 +1210,21 @@ binary-extensions@^2.0.0, binary-extensions@^2.2.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
body-parser@1.19.
0
:
version "1.19.
0
"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.
0
.tgz#
96b2709e57c9c4e09a6fd66a8fd979844f69f08a
"
integrity sha512-
dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw
==
body-parser@1.19.
1
:
version "1.19.
1
"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.
1
.tgz#
1499abbaa9274af3ecc9f6f10396c995943e31d4
"
integrity sha512-
8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA
==
dependencies:
bytes "3.1.
0
"
bytes "3.1.
1
"
content-type "~1.0.4"
debug "2.6.9"
depd "~1.1.2"
http-errors "1.
7.2
"
http-errors "1.
8.1
"
iconv-lite "0.4.24"
on-finished "~2.3.0"
qs "6.
7.0
"
raw-body "2.4.
0
"
type-is "~1.6.1
7
"
qs "6.
9.6
"
raw-body "2.4.
2
"
type-is "~1.6.1
8
"
boom@2.6.x:
version "2.6.1"
...
...
@@ -1286,10 +1294,10 @@ builtins@^1.0.3:
resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88"
integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og=
bytes@3.1.
0
:
version "3.1.
0
"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.
0
.tgz#
f6cf7933a360e0588fa9fde85651cdc7f805d1f6
"
integrity sha512-
zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2D
g==
bytes@3.1.
1
:
version "3.1.
1
"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.
1
.tgz#
3f018291cb4cbad9accb6e6970bca9c8889e879a
"
integrity sha512-
dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8F
g==
cacache@^15.0.3, cacache@^15.0.5, cacache@^15.2.0:
version "15.2.0"
...
...
@@ -1617,7 +1625,7 @@ columnify@~1.5.4:
strip-ansi "^3.0.0"
wcwidth "^1.0.0"
combined-stream@^1.0.6, combined-stream@~1.0.6:
combined-stream@^1.0.6,
combined-stream@^1.0.8,
combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
...
...
@@ -1644,12 +1652,12 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
content-disposition@0.5.
3
:
version "0.5.
3
"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.
3
.tgz#
e130caf7e7279087c5616c2007d0485698984fbd
"
integrity sha512-
ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g
==
content-disposition@0.5.
4
:
version "0.5.
4
"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.
4
.tgz#
8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe
"
integrity sha512-
FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ
==
dependencies:
safe-buffer "5.
1.2
"
safe-buffer "5.
2.1
"
content-type@~1.0.4:
version "1.0.4"
...
...
@@ -1668,10 +1676,10 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie@0.4.
0
:
version "0.4.
0
"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.
0
.tgz#
beb437e7022b3b6d49019d088665303ebe9c14ba
"
integrity sha512-
+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg
==
cookie@0.4.
1
:
version "0.4.
1
"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.
1
.tgz#
afd713fe26ebd21ba95ceb61f9a8116e50a537d1
"
integrity sha512-
ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA
==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
...
...
@@ -2074,17 +2082,17 @@ express-joi-validator@^2.0.1:
extend "2.0.x"
joi "6.x.x"
express@^4.17.
1
:
version "4.17.
1
"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.
1
.tgz#
4491fc38605cf51f8629d39c2b5d026f98a4c134
"
integrity sha512-
mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4
g==
express@^4.17.
2
:
version "4.17.
2
"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.
2
.tgz#
c18369f265297319beed4e5558753cc8c1364cb3
"
integrity sha512-
oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QG
g==
dependencies:
accepts "~1.3.7"
array-flatten "1.1.1"
body-parser "1.19.
0
"
content-disposition "0.5.
3
"
body-parser "1.19.
1
"
content-disposition "0.5.
4
"
content-type "~1.0.4"
cookie "0.4.
0
"
cookie "0.4.
1
"
cookie-signature "1.0.6"
debug "2.6.9"
depd "~1.1.2"
...
...
@@ -2098,13 +2106,13 @@ express@^4.17.1:
on-finished "~2.3.0"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
proxy-addr "~2.0.
5
"
qs "6.
7.0
"
proxy-addr "~2.0.
7
"
qs "6.
9.6
"
range-parser "~1.2.1"
safe-buffer "5.
1.2
"
send "0.17.
1
"
serve-static "1.14.
1
"
setprototypeof "1.
1.1
"
safe-buffer "5.
2.1
"
send "0.17.
2
"
serve-static "1.14.
2
"
setprototypeof "1.
2.0
"
statuses "~1.5.0"
type-is "~1.6.18"
utils-merge "1.0.1"
...
...
@@ -2283,6 +2291,15 @@ form-data@^2.5.0:
combined-stream "^1.0.6"
mime-types "^2.1.12"
form-data@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
...
...
@@ -2624,27 +2641,16 @@ http-cache-semantics@^4.1.0:
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
http-errors@1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.1"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-errors@~1.7.2:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
http-errors@1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c"
integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==
dependencies:
depd "~1.1.2"
inherits "2.0.4"
setprototypeof "1.
1.1
"
setprototypeof "1.
2.0
"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.
0
"
toidentifier "1.0.
1
"
http-proxy-agent@^4.0.1:
version "4.0.1"
...
...
@@ -2772,11 +2778,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
inherits@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
ini@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
...
...
@@ -3702,11 +3703,6 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
...
...
@@ -3761,6 +3757,13 @@ node-addon-api@^3.0.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
node-fetch@<3.0.0:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-forge@>=0.10.0, node-forge@^0.8.5:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
...
...
@@ -4542,7 +4545,7 @@ promzard@^0.3.0:
dependencies:
read "1"
proxy-addr@~2.0.
5
:
proxy-addr@~2.0.
7
:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
...
...
@@ -4575,10 +4578,10 @@ qrcode-terminal@^0.12.0:
resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819"
integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==
qs@6.
7.0
:
version "6.
7.0
"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.
7.0
.tgz#
41dc1a015e3d581f1621776be31afb2876a9b1bc
"
integrity sha512-
VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTy
Q==
qs@6.
9.6
:
version "6.
9.6
"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.
9.6
.tgz#
26ed3c8243a431b2924aca84cc90471f35d5a0ee
"
integrity sha512-
TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlL
Q==
qs@^6.7.0:
version "6.10.1"
...
...
@@ -4618,13 +4621,13 @@ range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.4.
0
:
version "2.4.
0
"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.
0
.tgz#
a1ce6fb9c9bc356ca52e89256ab59059e13d03
32"
integrity sha512-
4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3
Q==
raw-body@2.4.
2
:
version "2.4.
2
"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.
2
.tgz#
baf3e9c21eebced59dd6533ac872b71f7b61cb
32"
integrity sha512-
RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqee
Q==
dependencies:
bytes "3.1.
0
"
http-errors "1.
7.2
"
bytes "3.1.
1
"
http-errors "1.
8.1
"
iconv-lite "0.4.24"
unpipe "1.0.0"
...
...
@@ -4872,16 +4875,16 @@ rx@^4.1.0:
resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
integrity sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0:
safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
...
...
@@ -4914,10 +4917,10 @@ semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semve
dependencies:
lru-cache "^6.0.0"
send@0.17.
1
:
version "0.17.
1
"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.
1
.tgz#
c1d8b059f7900
f7
4
66
dd4938bdc44e11ddb376c8
"
integrity sha512-
BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg
==
send@0.17.
2
:
version "0.17.
2
"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.
2
.tgz#
926622
f766
01c41808012c8bf1688fe3906f7820
"
integrity sha512-
UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww
==
dependencies:
debug "2.6.9"
depd "~1.1.2"
...
...
@@ -4926,9 +4929,9 @@ send@0.17.1:
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "
~
1.
7.2
"
http-errors "1.
8.1
"
mime "1.6.0"
ms "2.1.
1
"
ms "2.1.
3
"
on-finished "~2.3.0"
range-parser "~1.2.1"
statuses "~1.5.0"
...
...
@@ -4940,25 +4943,25 @@ serialize-javascript@6.0.0:
dependencies:
randombytes "^2.1.0"
serve-static@1.14.
1
:
version "1.14.
1
"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.
1
.tgz#
666e636dc4f010f7ef29970a88a674320898b2f9
"
integrity sha512-
JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg
==
serve-static@1.14.
2
:
version "1.14.
2
"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.
2
.tgz#
722d6294b1d62626d41b43a013ece4598d292bfa
"
integrity sha512-
+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ
==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.17.
1
"
send "0.17.
2
"
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
setprototypeof@1.
1.1
:
version "1.
1.1
"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.
1.1
.tgz#
7e95acb24aa92f5885e0abef5ba131330d4ae683
"
integrity sha512-
JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OA
w==
setprototypeof@1.
2.0
:
version "1.
2.0
"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.
2.0
.tgz#
66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424
"
integrity sha512-
E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJp
w==
shallow-clone@^0.1.2:
version "0.1.2"
...
...
@@ -5374,10 +5377,10 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
toidentifier@1.0.
0
:
version "1.0.
0
"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.
0
.tgz#
7e1
be34
70f1e77948bc43d94a3c8f4d7752ba553
"
integrity sha512-
yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw
==
toidentifier@1.0.
1
:
version "1.0.
1
"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.
1
.tgz#
3
be34
321a88a820ed1bd80dfaa33e479fbb8dd35
"
integrity sha512-
o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA
==
tough-cookie@~2.5.0:
version "2.5.0"
...
...
@@ -5387,6 +5390,11 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
tree-kill@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
...
...
@@ -5464,7 +5472,7 @@ type-fest@^0.8.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
type-is@~1.6.17,
type-is@~1.6.18:
type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
...
...
@@ -5611,6 +5619,19 @@ wcwidth@^1.0.0:
dependencies:
defaults "^1.0.3"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
...
...
ts/webui/src/components/public-child/OpenRow.tsx
View file @
9392125e
...
...
@@ -14,6 +14,11 @@ import '../../static/style/overview/overview.scss';
import
'
../../static/style/copyParameter.scss
'
;
import
'
../../static/style/openRow.scss
'
;
/**
* netron URL must be synchronized with ts/nni_manager/rest_server/index.ts`.
* Remember to update it if the value is changed or this file is moved.
**/
interface
OpenRowProps
{
trialId
:
string
;
}
...
...
ts/webui/src/static/const.ts
View file @
9392125e
...
...
@@ -4,6 +4,13 @@ import { getPrefix } from './function';
const
METRIC_GROUP_UPDATE_THRESHOLD
=
100
;
const
METRIC_GROUP_UPDATE_SIZE
=
20
;
/**
* RESTAPI and DOWNLOAD_IP must be synchronized with:
* - nni/experiment/rest.py
* - ts/nni_manager/rest_server/index.ts
* Remember to update them if the values are changed or if this file is moved.
**/
const
prefix
=
getPrefix
();
const
RESTAPI
=
'
/api/v1/nni
'
;
const
MANAGER_IP
=
prefix
===
undefined
?
RESTAPI
:
`
${
prefix
}${
RESTAPI
}
`
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment