diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.html index 88baf33ffc0..c9d98a5e648 100644 --- a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.html +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.html @@ -50,6 +50,15 @@ + + + + + + diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.spec.ts index 541e02e8f89..194bc336295 100644 --- a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.spec.ts +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.spec.ts @@ -20,6 +20,7 @@ let NamespaceServiceStub: Partial; KWABackendServiceStub = { getTrial: () => of([]), getTrialInfo: () => of(), + getTrialLogs: () => of(), }; NamespaceServiceStub = { diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.ts index 827f99befc5..b2412add206 100644 --- a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.ts +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.component.ts @@ -28,6 +28,8 @@ export class TrialDetailsComponent implements OnInit, OnDestroy { trialDetails: TrialK8s; experimentName: string; showTrialGraph: boolean = false; + trialLogs: string; + logsRequestError: string; // chart's options view = [700, 500]; @@ -139,6 +141,17 @@ export class TrialDetailsComponent implements OnInit, OnDestroy { this.startTrialPolling(); } }); + + this.backendService.getTrialLogs(this.trialName, this.namespace).subscribe( + logs => { + this.trialLogs = logs; + this.logsRequestError = ''; + }, + error => { + this.trialLogs = null; + this.logsRequestError = error; + }, + ); } private trialStatus(trial: TrialK8s): StatusEnum { diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.module.ts index a32cb7ef323..0c34d5b3889 100644 --- a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.module.ts +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-details.module.ts @@ -16,6 +16,7 @@ import { LoadingSpinnerModule, PanelModule, } from 'kubeflow'; +import { TrialLogsModule } from './trial-logs/trial-logs.module'; @NgModule({ declarations: [TrialDetailsComponent], @@ -34,6 +35,7 @@ import { PanelModule, TitleActionsToolbarModule, TrialYamlModule, + TrialLogsModule, ], exports: [TrialDetailsComponent], }) diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.html b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.html new file mode 100644 index 00000000000..a7ac44580f5 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.html @@ -0,0 +1,15 @@ + + + + Error: {{ logsRequestError }} + + + + + + diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.scss b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.scss new file mode 100644 index 00000000000..053f94c377f --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.scss @@ -0,0 +1,9 @@ +lib-panel { + display: block; + margin-top: 1rem; +} + +.logs-viewer { + margin-bottom: 2rem; + margin-top: 1rem; +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.spec.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.spec.ts new file mode 100644 index 00000000000..9a018027b10 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.spec.ts @@ -0,0 +1,36 @@ +import { CommonModule } from '@angular/common'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { + HeadingSubheadingRowModule, + KubeflowModule, + LogsViewerModule, +} from 'kubeflow'; + +import { TrialLogsComponent } from './trial-logs.component'; + +describe('TrialLogsComponent', () => { + let component: TrialLogsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TrialLogsComponent], + imports: [ + CommonModule, + KubeflowModule, + HeadingSubheadingRowModule, + LogsViewerModule, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TrialLogsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.ts new file mode 100644 index 00000000000..4c02ae69dbf --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.component.ts @@ -0,0 +1,22 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'app-trial-logs', + templateUrl: './trial-logs.component.html', + styleUrls: ['./trial-logs.component.scss'], +}) +export class TrialLogsComponent { + public logs: string[]; + + @Input() logsRequestError: string; + + @Input() + set trialLogs(trialLogs: string) { + if (!trialLogs) { + return; + } + + let arrayOfLogs = trialLogs.split('\n'); + this.logs = arrayOfLogs; + } +} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.module.ts b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.module.ts new file mode 100644 index 00000000000..ac570e04ab2 --- /dev/null +++ b/pkg/new-ui/v1beta1/frontend/src/app/pages/experiment-details/trials-table/trial-details/trial-logs/trial-logs.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { TrialLogsComponent } from './trial-logs.component'; +import { + HeadingSubheadingRowModule, + KubeflowModule, + LogsViewerModule, +} from 'kubeflow'; + +@NgModule({ + declarations: [TrialLogsComponent], + imports: [ + CommonModule, + KubeflowModule, + HeadingSubheadingRowModule, + LogsViewerModule, + ], + exports: [TrialLogsComponent], +}) +export class TrialLogsModule {} diff --git a/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.ts b/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.ts index 457defc12f2..bd6aab5c352 100644 --- a/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.ts +++ b/pkg/new-ui/v1beta1/frontend/src/app/services/backend.service.ts @@ -102,4 +102,52 @@ export class KWABackendService extends BackendService { .post(url, { postData: exp }) .pipe(catchError(error => this.parseError(error))); } + + getTrialLogs(name: string, namespace: string): Observable { + const url = `/katib/fetch_trial_logs/?trialName=${name}&namespace=${namespace}`; + + return this.http.get(url).pipe( + catchError(error => this.handleError(error, false)), + map((resp: any) => { + return resp; + }), + ); + } + + // ---------------------------Error Handling--------------------------------- + + // Override common service's getBackendErrorLog + // in order to properly show the message the backend has sent + public getBackendErrorLog(error: HttpErrorResponse): string { + if (error.error === null) { + return error.message; + } else { + // Show the message the backend has sent + return error.error; + } + } + + // Override common service's getErrorMessage + // in order to include the error.status in error message + public getErrorMessage( + error: HttpErrorResponse | ErrorEvent | string, + ): string { + if (typeof error === 'string') { + return error; + } + + if (error instanceof HttpErrorResponse) { + if (this.getBackendErrorLog(error) !== undefined) { + return `[${error.status}] ${this.getBackendErrorLog(error)}`; + } + + return `${error.status}: ${error.message}`; + } + + if (error instanceof ErrorEvent) { + return error.message; + } + + return `Unexpected error encountered`; + } }