You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by ji...@apache.org on 2021/01/02 14:32:31 UTC
[submarine] branch master updated: SUBMARINE-693. Sync the status
of notebook on workbench web
This is an automated email from the ASF dual-hosted git repository.
jiwq pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git
The following commit(s) were added to refs/heads/master by this push:
new 2aaecb8 SUBMARINE-693. Sync the status of notebook on workbench web
2aaecb8 is described below
commit 2aaecb89be66d26f042649df76c970f1d7f257c4
Author: kobe860219 <ko...@gmail.com>
AuthorDate: Mon Dec 28 13:44:12 2020 +0800
SUBMARINE-693. Sync the status of notebook on workbench web
### What is this PR for?
This improvement make workbench web could sync status of notebooks. It's make UI more friendly for users.
The pr is related of [SUBMARINE-675](https://github.com/apache/submarine/commit/d104080a926d30bcc45ccd740f9489dd7365d0b5)
### What type of PR is it?
[Improvement]
### Todos
* [ ] - Task
### What is the Jira issue?
[SUBMARINE-693](https://issues.apache.org/jira/browse/SUBMARINE-693)
### How should this be tested?
[Travis CI](https://travis-ci.org/github/kobe860219/submarine/builds/751380815)
### Screenshots (if appropriate)
![Sync status of notebook on web](https://user-images.githubusercontent.com/48027290/103102477-2ada9800-4657-11eb-8f4d-97df393bf1fa.gif)
### Questions:
* Does the licenses files need update? No
* Is there breaking changes for older versions? No
* Does this needs documentation? No
Author: kobe860219 <ko...@gmail.com>
Closes #482 from kobe860219/SUBMARINE-693 and squashes the following commits:
0f60967 [kobe860219] SUBMARINE-693. Sync the status of notebook on workbench web
593d8ac [kobe860219] SUBMARINE-693. Sync the status of notebook on workbench web
95d0fc9 [kobe860219] SUBMARINE-693. Sync the status of notebook on workbench web
fc15452 [kobe860219] SUBMARINE-693. Sync the status of notebook on workbench web
0ca618b [kobe860219] SUBMARINE-693. Sync the status of notebook on workbench web
787ce20 [kobe860219] SUBMARINE-693. Sync the status of notebook on workbench web
---
.../workbench/notebook/notebook.component.html | 30 +++++-
.../pages/workbench/notebook/notebook.component.ts | 44 +++++++--
.../workbench-web/src/app/services/polling.ts | 105 +++++++++++++++++++++
3 files changed, 170 insertions(+), 9 deletions(-)
diff --git a/submarine-workbench/workbench-web/src/app/pages/workbench/notebook/notebook.component.html b/submarine-workbench/workbench-web/src/app/pages/workbench/notebook/notebook.component.html
index 36f7ef5..9e7d814 100644
--- a/submarine-workbench/workbench-web/src/app/pages/workbench/notebook/notebook.component.html
+++ b/submarine-workbench/workbench-web/src/app/pages/workbench/notebook/notebook.component.html
@@ -71,6 +71,7 @@
<nz-table id="notebookListTable" style="padding-top: 5px;" #basicTable [nzData]="allNotebookList">
<thead>
<tr>
+ <th></th>
<th>Name</th>
<th>Environment</th>
<th>Docker Image</th>
@@ -81,16 +82,39 @@
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data; let i = index">
- <td>
+ <td *ngIf="data.status === 'running'">
+ <i
+ nz-icon
+ [nzType]="'check-circle'"
+ [nzTheme]="'twotone'"
+ [nzTwotoneColor]="'#52c41a'"
+ style="color: #08c; font-size: 24px;">
+ </i>
+ </td>
+ <td *ngIf="data.status !== 'running'">
+ <i
+ nz-icon
+ nzType="loading"
+ nzTheme="outline"
+ style="color: #08c; font-size: 24px;">
+ </i>
+ </td>
+ <td *ngIf="data.status === 'running'">
<a href="{{ data.url }}" target="_blank">{{ data.name }}</a>
</td>
+ <td *ngIf="data.status !== 'running'">
+ {{ data.name }}
+ </td>
<td>{{ data.spec.environment.name }}</td>
<td>{{ data.spec.environment.dockerImage }}</td>
<td>
{{ data.spec.spec.resources }}
</td>
- <td>{{ data.status }}</td>
- <td><a id="delete{{ i }}" (click)="deleteNotebook(data.notebookId)">Delete</a></td>
+ <td>
+ <a (click)=showReason(data.reason)>{{ data.status }}</a>
+ </td>
+ <td>
+ <a id="delete{{ i }}" (click)="deleteNotebook(data.notebookId)">Delete</a></td>
</tr>
</tbody>
</nz-table>
diff --git a/submarine-workbench/workbench-web/src/app/pages/workbench/notebook/notebook.component.ts b/submarine-workbench/workbench-web/src/app/pages/workbench/notebook/notebook.component.ts
index b271c88..e0d1533 100644
--- a/submarine-workbench/workbench-web/src/app/pages/workbench/notebook/notebook.component.ts
+++ b/submarine-workbench/workbench-web/src/app/pages/workbench/notebook/notebook.component.ts
@@ -25,6 +25,10 @@ import { EnvironmentService } from '@submarine/services/environment.service';
import { ExperimentValidatorService } from '@submarine/services/experiment.validator.service';
import { UserService } from '@submarine/services/user.service';
import { nullSafeIsEquivalent } from '@angular/compiler/src/output/output_ast';
+import { Subscription } from 'rxjs';
+import { ExponentialBackoff } from '@submarine/services/polling';
+import { isEqual } from "lodash";
+import { NzNotificationService } from 'ng-zorro-antd/notification';
@Component({
selector: 'submarine-notebook',
@@ -50,22 +54,40 @@ export class NotebookComponent implements OnInit {
isVisible = false;
MEMORY_UNITS = ['M', 'Gi'];
+ // User Information
userId;
+ // Sync //
+ // Subscription
+ subscriptions = new Subscription();
+ // Poller
+ poller: ExponentialBackoff;
+
constructor(
private notebookService: NotebookService,
private nzMessageService: NzMessageService,
private environmentService: EnvironmentService,
private experimentValidatorService: ExperimentValidatorService,
private userService: UserService,
- private fb: FormBuilder
+ private fb: FormBuilder,
+ private nzNotificationService: NzNotificationService
) {}
ngOnInit() {
- this.userService.fetchUserInfo().subscribe((res) => {
- this.userId = res.id;
- this.fetchNotebookList(this.userId);
+ this.poller = new ExponentialBackoff({ interval: 1000, retries: 3 });
+ const resourcesSub = this.poller.start().subscribe(() => {
+ this.userService.fetchUserInfo().subscribe((res) => {
+ this.userId = res.id;
+ this.notebookService.fetchNotebookList(this.userId).subscribe(resources => {
+ if (!isEqual(this.allNotebookList, resources)) {
+ this.allNotebookList = resources;
+ this.poller.reset();
+ }
+ });
+ });
});
+
+ this.subscriptions.add(resourcesSub);
this.notebookForm = this.fb.group({
notebookName: [null, [
@@ -82,6 +104,10 @@ export class NotebookComponent implements OnInit {
this.fetchEnvList();
}
+ ngOnDestroy() {
+ this.subscriptions.unsubscribe();
+ }
+
// Get all environment
fetchEnvList() {
this.environmentService.fetchEnvironmentList().subscribe((list) => {
@@ -99,6 +125,7 @@ export class NotebookComponent implements OnInit {
fetchNotebookList(id: string) {
this.notebookService.fetchNotebookList(id).subscribe((list) => {
this.allNotebookList = list;
+ console.log(this.allNotebookList);
});
}
@@ -139,7 +166,6 @@ export class NotebookComponent implements OnInit {
deleteNotebook(id: string) {
this.notebookService.deleteNotebook(id).subscribe(
() => {
- this.nzMessageService.success('Delete Notebook Successfully!');
this.updateNotebookTable(this.userId);
},
(err) => {
@@ -283,12 +309,18 @@ export class NotebookComponent implements OnInit {
});
},
complete: () => {
- this.nzMessageService.success('Notebook creation succeeds');
this.isVisible = false;
}
});
}
+ showReason(reason: string) {
+ this.nzNotificationService.blank(
+ 'Notebook Status',
+ reason
+ );
+ }
+
// TODO(kobe860219): Make a notebook run
runNotebook() {}
diff --git a/submarine-workbench/workbench-web/src/app/services/polling.ts b/submarine-workbench/workbench-web/src/app/services/polling.ts
new file mode 100644
index 0000000..1a9a4a4
--- /dev/null
+++ b/submarine-workbench/workbench-web/src/app/services/polling.ts
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+ import { Subscription, Subject, interval, timer } from "rxjs";
+
+ export interface BackoffConfig {
+ retries?: number;
+ interval?: number;
+ maxInterval?: number;
+ }
+
+ const defaultConfig: BackoffConfig = {
+ retries: 1,
+ interval: 1000,
+ maxInterval: 16000
+ }
+
+ export class ExponentialBackoff {
+ private retries: number;
+ private interval: number;
+ private maxInterval: number;
+
+ private scheduler: Subscription;
+ private poller: Subject<number>;
+ private n: number;
+
+ private remainingTries: number;
+ private currInterval: number;
+
+ constructor(config: BackoffConfig = defaultConfig) {
+ const conf = { ...defaultConfig, ...config };
+
+ this.retries = conf.retries;
+ this.interval = conf.interval;
+ this.maxInterval = conf.maxInterval;
+
+ this.poller = new Subject<number>();
+
+ this.n = 0;
+ this.remainingTries = this.retries + 1;
+ this.currInterval = this.interval;
+ }
+
+ public start() {
+ if(this.scheduler) {
+ this.scheduler.unsubscribe();
+ }
+
+ this.scheduler = timer(0, this.interval).subscribe(() => {
+ this.iterate();
+ });
+
+ return this.poller;
+ }
+
+ private iterate() {
+ this.n++;
+ this.poller.next(this.n);
+
+ this.scheduler.unsubscribe();
+ this.remainingTries--;
+ if (this.remainingTries === 0) {
+ this.remainingTries = this.retries;
+ this.currInterval = Math.min(this.currInterval * 2, this.maxInterval);
+ }
+
+ this.scheduler = interval(this.currInterval).subscribe(() => {
+ this.iterate();
+ });
+ }
+
+ public reset() {
+ this.n = 0;
+ this.currInterval = this.interval;
+ this.remainingTries = this.retries + 1;
+
+ this.start();
+ }
+
+ public stop() {
+ if (this.scheduler) {
+ this.scheduler.unsubscribe();
+ }
+ }
+
+ public getPoller() {
+ return this.poller;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org