@@ -16,11 +16,11 @@ import * as express from 'express';
16
16
import { Application , static as StaticHandler } from 'express' ;
17
17
import * as proxy from 'http-proxy-middleware' ;
18
18
19
- import { IConfigs } from './configs' ;
19
+ import { UIConfigs } from './configs' ;
20
20
import { getAddress } from './utils' ;
21
21
import { getBuildMetadata , getHealthzEndpoint , getHealthzHandler } from './handlers/healthz' ;
22
22
import { getArtifactsHandler } from './handlers/artifacts' ;
23
- import { getCreateTensorboardHandler , getGetTensorboardHandler } from './handlers/tensorboard' ;
23
+ import { getCreateTensorboardHandler , getTensorboardHandler , deleteTensorboardHandler } from './handlers/tensorboard' ;
24
24
import { getPodLogsHandler } from './handlers/pod-logs' ;
25
25
import { clusterNameHandler , projectIdHandler } from './handlers/core' ;
26
26
import { getAllowCustomVisualizationsHandler } from './handlers/vis' ;
@@ -40,72 +40,101 @@ function getRegisterHandler(app: Application, basePath: string) {
40
40
} ;
41
41
}
42
42
43
+ /**
44
+ * UIServer wraps around a express application to:
45
+ * - proxy requests to ml-pipeline api server
46
+ * - retrieve artifacts from the various backend
47
+ * - create and retrieve new viewer instances
48
+ * - serve static front-end resources (i.e. react app)
49
+ */
43
50
export class UIServer {
44
51
app : Application ;
45
52
httpServer ?: Server ;
46
53
47
- constructor ( public readonly options : IConfigs ) {
54
+ constructor ( public readonly options : UIConfigs ) {
48
55
this . app = createUIServer ( options ) ;
49
56
}
50
57
58
+ /**
59
+ * Starts the http server.
60
+ * @param port optionally overwrite the provided port to listen to.
61
+ */
51
62
start ( port ?: number | string ) {
63
+ if ( ! ! this . httpServer ) {
64
+ throw new Error ( 'UIServer already started.' ) ;
65
+ }
52
66
port = port || this . options . server . port ;
53
67
this . httpServer = this . app . listen ( port , ( ) => {
54
68
console . log ( 'Server listening at http://localhost:' + port ) ;
55
69
} ) ;
56
70
return this . httpServer ;
57
71
}
58
72
73
+ /**
74
+ * Stops the http server.
75
+ */
59
76
close ( ) {
60
- return this . httpServer && this . httpServer . close ( ) ;
77
+ if ( ! ! this . httpServer ) {
78
+ this . httpServer . close ( ) ;
79
+ }
80
+ this . httpServer = undefined ;
81
+ return this ;
61
82
}
62
83
}
63
84
64
- function createUIServer ( options : IConfigs ) {
85
+ function createUIServer ( options : UIConfigs ) {
65
86
const currDir = path . resolve ( __dirname ) ;
66
87
const basePath = options . server . basePath ;
67
- const apiVersion = options . server . apiVersion ;
88
+ const apiVersionPrefix = options . server . apiVersionPrefix ;
68
89
const apiServerAddress = getAddress ( options . pipeline ) ;
69
90
const envoyServiceAddress = getAddress ( options . metadata . envoyService ) ;
70
91
71
92
const app : Application = express ( ) ;
72
93
const registerHandler = getRegisterHandler ( app , basePath ) ;
73
94
74
- app . use ( function ( req , _ , next ) {
95
+ /** log to stdout */
96
+ app . use ( ( req , _ , next ) => {
75
97
console . info ( req . method + ' ' + req . originalUrl ) ;
76
98
next ( ) ;
77
99
} ) ;
78
100
101
+ /** Healthz */
79
102
registerHandler (
80
103
app . get ,
81
- `/${ apiVersion } /healthz` ,
104
+ `/${ apiVersionPrefix } /healthz` ,
82
105
getHealthzHandler ( {
83
- healthzEndpoint : getHealthzEndpoint ( apiServerAddress , options . server . apiVersion ) ,
106
+ healthzEndpoint : getHealthzEndpoint ( apiServerAddress , apiVersionPrefix ) ,
84
107
healthzStats : getBuildMetadata ( currDir ) ,
85
108
} ) ,
86
109
) ;
87
110
111
+ /** Artifact */
88
112
registerHandler ( app . get , '/artifacts/get' , getArtifactsHandler ( options . artifacts ) ) ;
89
113
90
- registerHandler ( app . get , '/apps/tensorboard' , getGetTensorboardHandler ( ) ) ;
114
+ /** Tensorboard viewer */
115
+ registerHandler ( app . get , '/apps/tensorboard' , getTensorboardHandler ) ;
116
+ registerHandler ( app . delete , '/apps/tensorboard' , deleteTensorboardHandler ) ;
91
117
registerHandler (
92
118
app . post ,
93
119
'/apps/tensorboard' ,
94
120
getCreateTensorboardHandler ( options . viewer . tensorboard . podTemplateSpec ) ,
95
121
) ;
96
122
123
+ /** Pod logs */
97
124
registerHandler ( app . get , '/k8s/pod/logs' , getPodLogsHandler ( options . argo , options . artifacts ) ) ;
98
125
126
+ /** Cluster (GKS only) */
99
127
registerHandler ( app . get , '/system/cluster-name' , clusterNameHandler ) ;
100
128
registerHandler ( app . get , '/system/project-id' , projectIdHandler ) ;
101
129
130
+ /** Visualization */
102
131
registerHandler (
103
132
app . get ,
104
133
'/visualizations/allowed' ,
105
134
getAllowCustomVisualizationsHandler ( options . visualizations . allowCustomVisualizations ) ,
106
135
) ;
107
136
108
- // Proxy metadata requests to the Envoy instance which will handle routing to the metadata gRPC server
137
+ /** Proxy metadata requests to the Envoy instance which will handle routing to the metadata gRPC server */
109
138
app . all (
110
139
'/ml_metadata.*' ,
111
140
proxy ( {
@@ -119,11 +148,12 @@ function createUIServer(options: IConfigs) {
119
148
120
149
// Order matters here, since both handlers can match any proxied request with a referer,
121
150
// and we prioritize the basepath-friendly handler
122
- proxyMiddleware ( app , `${ basePath } /${ apiVersion } ` ) ;
123
- proxyMiddleware ( app , `/${ apiVersion } ` ) ;
151
+ proxyMiddleware ( app , `${ basePath } /${ apiVersionPrefix } ` ) ;
152
+ proxyMiddleware ( app , `/${ apiVersionPrefix } ` ) ;
124
153
154
+ /** Proxy to ml-pipeline api server */
125
155
app . all (
126
- `/${ apiVersion } /*` ,
156
+ `/${ apiVersionPrefix } /*` ,
127
157
proxy ( {
128
158
changeOrigin : true ,
129
159
onProxyReq : proxyReq => {
@@ -132,29 +162,33 @@ function createUIServer(options: IConfigs) {
132
162
target : apiServerAddress ,
133
163
} ) ,
134
164
) ;
135
-
136
165
app . all (
137
- `${ basePath } /${ apiVersion } /*` ,
166
+ `${ basePath } /${ apiVersionPrefix } /*` ,
138
167
proxy ( {
139
168
changeOrigin : true ,
140
169
onProxyReq : proxyReq => {
141
170
console . log ( 'Proxied request: ' , ( proxyReq as any ) . path ) ;
142
171
} ,
143
- pathRewrite : path =>
144
- path . startsWith ( basePath ) ? path . substr ( basePath . length , path . length ) : path ,
172
+ pathRewrite : pathStr =>
173
+ pathStr . startsWith ( basePath ) ? pathStr . substr ( basePath . length , pathStr . length ) : pathStr ,
145
174
target : apiServerAddress ,
146
175
} ) ,
147
176
) ;
148
177
149
- // These pathes can be matched by static handler. Putting them before it to
150
- // override behavior for index html.
178
+ /**
179
+ * Modify index.html.
180
+ * These pathes can be matched by static handler. Putting them before it to
181
+ * override behavior for index html.
182
+ */
151
183
const indexHtmlHandler = getIndexHTMLHandler ( options . server ) ;
152
184
registerHandler ( app . get , '/' , indexHtmlHandler ) ;
153
185
registerHandler ( app . get , '/index.html' , indexHtmlHandler ) ;
154
186
187
+ /** Static resource (i.e. react app) */
155
188
app . use ( basePath , StaticHandler ( options . server . staticDir ) ) ;
156
189
app . use ( StaticHandler ( options . server . staticDir ) ) ;
157
190
191
+ /** Fallback to index.html */
158
192
app . get ( '*' , indexHtmlHandler ) ;
159
193
160
194
return app ;
0 commit comments