Skip to content

Commit

Permalink
kwa(front): Support all namespaces
Browse files Browse the repository at this point in the history
Add support for all-namespaces in KWA.

Signed-off-by: Elena Zioga <[email protected]>
  • Loading branch information
elenzio9 committed Feb 20, 2023
1 parent 1429d61 commit 318b35c
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
numberToExponential,
transformStringResponses,
} from '../../shared/utils';
import { getDeleteDialogConfig } from '../experiments/delete-modal-config';
import { generateDeleteConfig } from '../experiments/delete-modal-config';
import { ExperimentK8s } from '../../models/experiment.k8s.model';

@Component({
Expand Down Expand Up @@ -134,7 +134,7 @@ export class ExperimentDetailsComponent implements OnInit, OnDestroy {
}

private deleteExperiment(name: string, namespace: string) {
const deleteDialogConfig = getDeleteDialogConfig(name, namespace);
const deleteDialogConfig = generateDeleteConfig(name);
const ref = this.confirmDialog.open(name, deleteDialogConfig);

const delSub = ref.componentInstance.applying$.subscribe(applying => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import {
ActionIconValue,
TableConfig,
DateTimeValue,
TemplateValue,
ChipsListValue,
ComponentValue,
LinkValue,
LinkType,
Expand All @@ -16,11 +14,11 @@ import {
parseSucceededTrials,
parseRunningTrials,
parseFailedTrials,
parseTotalTrials,
} from './utils';
import { ExperimentOptimalTrialComponent } from './optimal-trial/experiment-optimal-trial.component';

export const experimentsTableConfig: TableConfig = {
dynamicNamespaceColumn: true,
columns: [
{
matHeaderCellDef: 'Status',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { DialogConfig } from 'kubeflow';

// --- Configs for the Confirm Dialogs ---
export function getDeleteDialogConfig(
name: string,
namespace: string,
): DialogConfig {
export function generateDeleteConfig(name: string): DialogConfig {
return {
title: `Delete experiment`,
message: `Are you sure you want to delete ${name} experiment from namespace ${namespace}?`,
accept: 'DELETE',
title: $localize`Delete experiment ${name}?`,
message: $localize`You cannot undo this action. Are you sure you want to delete this experiment?`,
accept: $localize`DELETE`,
confirmColor: 'warn',
cancel: 'CANCEL',
cancel: $localize`CANCEL`,
error: '',
applying: 'DELETING',
applying: $localize`DELETING`,
width: '600px',
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ConfirmDialogService,
NamespaceService,
KubeflowModule,
PollerService,
} from 'kubeflow';
import { KWABackendService } from 'src/app/services/backend.service';
import { RouterTestingModule } from '@angular/router/testing';
Expand Down Expand Up @@ -46,6 +47,7 @@ describe('ExperimentsComponent', () => {
{ provide: KWABackendService, useValue: KWABackendServiceStub },
{ provide: ConfirmDialogService, useValue: {} },
{ provide: NamespaceService, useValue: NamespaceServiceStub },
{ provide: PollerService, useValue: {} },
],
}).compileComponents();
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { environment } from '@app/environment';
import { Subscription } from 'rxjs';
import isEqual from 'lodash-es/isEqual';
import {
ConfirmDialogService,
DIALOG_RESP,
ExponentialBackoff,
NamespaceService,
ActionEvent,
DashboardState,
ToolbarButton,
PollerService,
} from 'kubeflow';

import { KWABackendService } from 'src/app/services/backend.service';
Expand All @@ -18,7 +17,7 @@ import {
ExperimentsProcessed,
} from '../../models/experiment.model';
import { experimentsTableConfig } from './config';
import { getDeleteDialogConfig } from './delete-modal-config';
import { generateDeleteConfig } from './delete-modal-config';
import { Router } from '@angular/router';

@Component({
Expand All @@ -27,50 +26,67 @@ import { Router } from '@angular/router';
styleUrls: ['./experiments.component.scss'],
})
export class ExperimentsComponent implements OnInit, OnDestroy {
experiments: ExperimentsProcessed = [];
currNamespace: string;
config = experimentsTableConfig;
env = environment;
dashboardDisconnectedState = DashboardState.Disconnected;

private subs = new Subscription();
private poller: ExponentialBackoff;
nsSub = new Subscription();
pollSub = new Subscription();

private rawData: Experiment[] = [];
currNamespace: string | string[];
config = experimentsTableConfig;
experiments: ExperimentsProcessed = [];

buttons: ToolbarButton[] = [
new ToolbarButton({
text: `New Experiment`,
icon: 'add',
stroked: true,
fn: () => {
this.router.navigate(['/new']);
},
}),
];
dashboardDisconnectedState = DashboardState.Disconnected;

private newExperimentButton = new ToolbarButton({
text: $localize`New Experiment`,
icon: 'add',
stroked: true,
fn: () => {
this.router.navigate(['/new']);
},
});

buttons: ToolbarButton[] = [this.newExperimentButton];

constructor(
private backend: KWABackendService,
private confirmDialog: ConfirmDialogService,
private router: Router,
public ns: NamespaceService,
public poller: PollerService,
) {}

ngOnInit() {
this.startExperimentsPolling();

ngOnInit(): void {
// Reset the poller whenever the selected namespace changes
this.subs.add(
this.ns.getSelectedNamespace().subscribe(nameSpace => {
this.currNamespace = nameSpace;
this.poller.reset();
}),
);
this.nsSub = this.ns.getSelectedNamespace2().subscribe(ns => {
this.currNamespace = ns;
this.poll(ns);
this.newExperimentButton.namespaceChanged(ns, $localize`Experiment`);
});
}

ngOnDestroy() {
this.nsSub.unsubscribe();
this.pollSub.unsubscribe();
}

ngOnDestroy(): void {
this.subs.unsubscribe();
this.poller.stop();
public poll(ns: string | string[]) {
this.pollSub.unsubscribe();
this.experiments = [];

const request = this.backend.getExperiments(ns);

this.pollSub = this.poller.exponential(request).subscribe(experiments => {
this.experiments = experiments.map(row => {
return {
...row,
link: {
text: row.name,
url: `/experiment/${this.currNamespace}/${row.name}`,
},
};
});
});
}

trackByFn(index: number, experiment: Experiment) {
Expand All @@ -81,76 +97,38 @@ export class ExperimentsComponent implements OnInit, OnDestroy {
const exp = a.data as Experiment;
switch (a.action) {
case 'delete':
this.onDeleteExperiment(exp.name);
this.deleteClicked(exp);
break;
}
}

onDeleteExperiment(name: string) {
const deleteDialogConfig = getDeleteDialogConfig(name, this.currNamespace);
const ref = this.confirmDialog.open(name, deleteDialogConfig);
deleteClicked(exp: Experiment) {
const deleteDialogConfig = generateDeleteConfig(exp.name);
const ref = this.confirmDialog.open(exp.name, deleteDialogConfig);

const delSub = ref.componentInstance.applying$.subscribe(applying => {
if (!applying) {
return;
}

// Close the open dialog only if the DELETE request succeeded
this.backend.deleteExperiment(name, this.currNamespace).subscribe({
next: _ => {
this.poller.reset();
this.backend.deleteExperiment(exp.name, exp.namespace).subscribe(
res => {
ref.close(DIALOG_RESP.ACCEPT);
},
error: err => {
const errorMsg = err;
deleteDialogConfig.error = errorMsg;
err => {
deleteDialogConfig.error = err;
ref.componentInstance.applying$.next(false);
},
});
);

// DELETE request has succeeded
ref.afterClosed().subscribe(res => {
delSub.unsubscribe();
if (res !== DIALOG_RESP.ACCEPT) {
return;
}

this.poller.reset();
});
});
}

private startExperimentsPolling() {
this.poller = new ExponentialBackoff({ interval: 1000, retries: 3 });

// Poll for new data and reset the poller if different data is found
this.subs.add(
this.poller.start().subscribe(() => {
if (!this.currNamespace) {
return;
}

this.backend
.getExperiments(this.currNamespace)
.subscribe(experiments => {
if (isEqual(this.rawData, experiments)) {
return;
}

this.experiments = experiments.map(row => {
return {
...row,
link: {
text: row.name,
url: `/experiment/${row.namespace}/${row.name}`,
},
};
});

this.rawData = experiments;
this.poller.reset();
});
}),
);
}
}
17 changes: 16 additions & 1 deletion pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class KWABackendService extends BackendService {
return throwError(msg);
}

getExperiments(namespace: string): Observable<Experiments> {
getExperimentsSingleNamespace(namespace: string): Observable<Experiments> {
// If the route doesn't end in a "/"" then the backend will return a 301 to
// the url ending with "/".
const url = `/katib/fetch_experiments/?namespace=${namespace}`;
Expand All @@ -51,6 +51,21 @@ export class KWABackendService extends BackendService {
);
}

getExperimentsAllNamespaces(namespaces: string[]): Observable<Experiments> {
return this.getObjectsAllNamespaces(
this.getExperimentsSingleNamespace.bind(this),
namespaces,
);
}

getExperiments(ns: string | string[]): Observable<Experiments> {
if (Array.isArray(ns)) {
return this.getExperimentsAllNamespaces(ns);
}

return this.getExperimentsSingleNamespace(ns);
}

getExperimentTrialsInfo(name: string, namespace: string): Observable<any> {
const url = `/katib/fetch_hp_job_info/?experimentName=${name}&namespace=${namespace}`;

Expand Down

0 comments on commit 318b35c

Please sign in to comment.