From e3ea109745ac6ec967f898ae3fc17d40fd42aaf7 Mon Sep 17 00:00:00 2001 From: Chris Hines <chris.hines@monash.edu> Date: Thu, 14 Jun 2018 16:19:46 +1000 Subject: [PATCH] more source code --- src/app/.job.ts.swp | Bin 0 -> 12288 bytes src/app/app-routing.module.ts | 20 ++ src/app/computesite.ts | 4 + src/app/computesites.service.spec.ts | 15 ++ src/app/computesites.service.ts | 15 ++ src/app/job.ts | 8 + src/app/job/job.component.css | 0 src/app/job/job.component.html | 25 ++ src/app/job/job.component.spec.ts | 25 ++ src/app/job/job.component.ts | 42 ++++ src/app/joblist/joblist.component.css | 0 src/app/joblist/joblist.component.html | 6 + src/app/joblist/joblist.component.spec.ts | 25 ++ src/app/joblist/joblist.component.ts | 49 ++++ src/app/launcher/dialog-placeholder.html | 3 + src/app/launcher/launcher.component.css | 0 src/app/launcher/launcher.component.html | 18 ++ src/app/launcher/launcher.component.spec.ts | 25 ++ src/app/launcher/launcher.component.ts | 82 +++++++ src/app/mock-compute-site.ts | 6 + src/app/mock-strudel-app.ts | 64 ++++++ .../siteselection/siteselection.component.css | 0 .../siteselection.component.html | 13 ++ .../siteselection.component.spec.ts | 25 ++ .../siteselection/siteselection.component.ts | 29 +++ src/app/strudelapp.ts | 8 + src/app/strudelapps.service.spec.ts | 15 ++ src/app/strudelapps.service.ts | 26 +++ src/app/tes.service.spec.ts | 15 ++ src/app/tes.service.ts | 217 ++++++++++++++++++ src/app/teserrors/teserrors.component.css | 0 src/app/teserrors/teserrors.component.html | 3 + src/app/teserrors/teserrors.component.spec.ts | 25 ++ src/app/teserrors/teserrors.component.ts | 23 ++ .../tokenextractor.component.css | 0 .../tokenextractor.component.html | 3 + .../tokenextractor.component.spec.ts | 25 ++ .../tokenextractor.component.ts | 23 ++ 38 files changed, 882 insertions(+) create mode 100644 src/app/.job.ts.swp create mode 100644 src/app/app-routing.module.ts create mode 100644 src/app/computesite.ts create mode 100644 src/app/computesites.service.spec.ts create mode 100644 src/app/computesites.service.ts create mode 100644 src/app/job.ts create mode 100644 src/app/job/job.component.css create mode 100644 src/app/job/job.component.html create mode 100644 src/app/job/job.component.spec.ts create mode 100644 src/app/job/job.component.ts create mode 100644 src/app/joblist/joblist.component.css create mode 100644 src/app/joblist/joblist.component.html create mode 100644 src/app/joblist/joblist.component.spec.ts create mode 100644 src/app/joblist/joblist.component.ts create mode 100644 src/app/launcher/dialog-placeholder.html create mode 100644 src/app/launcher/launcher.component.css create mode 100644 src/app/launcher/launcher.component.html create mode 100644 src/app/launcher/launcher.component.spec.ts create mode 100644 src/app/launcher/launcher.component.ts create mode 100644 src/app/mock-compute-site.ts create mode 100644 src/app/mock-strudel-app.ts create mode 100644 src/app/siteselection/siteselection.component.css create mode 100644 src/app/siteselection/siteselection.component.html create mode 100644 src/app/siteselection/siteselection.component.spec.ts create mode 100644 src/app/siteselection/siteselection.component.ts create mode 100644 src/app/strudelapp.ts create mode 100644 src/app/strudelapps.service.spec.ts create mode 100644 src/app/strudelapps.service.ts create mode 100644 src/app/tes.service.spec.ts create mode 100644 src/app/tes.service.ts create mode 100644 src/app/teserrors/teserrors.component.css create mode 100644 src/app/teserrors/teserrors.component.html create mode 100644 src/app/teserrors/teserrors.component.spec.ts create mode 100644 src/app/teserrors/teserrors.component.ts create mode 100644 src/app/tokenextractor/tokenextractor.component.css create mode 100644 src/app/tokenextractor/tokenextractor.component.html create mode 100644 src/app/tokenextractor/tokenextractor.component.spec.ts create mode 100644 src/app/tokenextractor/tokenextractor.component.ts diff --git a/src/app/.job.ts.swp b/src/app/.job.ts.swp new file mode 100644 index 0000000000000000000000000000000000000000..06708e136886eebf98fa2da91c3168aa23a1e71e GIT binary patch literal 12288 zcmeI%!AiqG5P;#S;87GkdRgn)rq)YopFqK5J$OqttBtft%x*;x^*KC>&*2kz@a7|U ztG<HUHiB3wy+;0pVc5)&&3uqUNYp$zJXY;SQ~cTx`FwakeXW1U%eIK_C#kWM!s0ay zZGUmcta>-QVW6|v1oo;G*g^+ko(C6M)No52w9Fy&Ab`NUz{s8LwbyzFt^Mh%zFXf> zTbsT4KCU2u00IagfB*srAb`NX6>xb??)`Kt<%FwcuKmj8mV^KT2q1s}0tg_000Iag zfB*s&6bNIH%XN`QACv$8-{1dZzkl=b?BmJDv|hpacw__+KmY**5I_I{1Q0*~0R$=` za4X8Md=w25t*i^3>G)J6>3R2OB{o+7SxDlJN=L)U6kRdbc~&^32cfm<D2vpMd;vkd BK~w+$ literal 0 HcmV?d00001 diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts new file mode 100644 index 0000000..9ffa6d8 --- /dev/null +++ b/src/app/app-routing.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { LauncherComponent } from './launcher/launcher.component'; +import { TokenextractorComponent } from './tokenextractor/tokenextractor.component'; + + +const routes: Routes = [ + { path: '', redirectTo: 'launch', pathMatch: 'full'}, + { path: 'launch', component: LauncherComponent}, + { path: 'sshauthz_callback', component: TokenextractorComponent} + +]; + +@NgModule({ + imports: [ + RouterModule.forRoot(routes) + ], + exports: [ RouterModule ], +}) +export class AppRoutingModule { } diff --git a/src/app/computesite.ts b/src/app/computesite.ts new file mode 100644 index 0000000..807c020 --- /dev/null +++ b/src/app/computesite.ts @@ -0,0 +1,4 @@ +export class Computesite { + url: string; + name: string; +} diff --git a/src/app/computesites.service.spec.ts b/src/app/computesites.service.spec.ts new file mode 100644 index 0000000..3d6b411 --- /dev/null +++ b/src/app/computesites.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { ComputesitesService } from './computesites.service'; + +describe('ComputesitesService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ComputesitesService] + }); + }); + + it('should be created', inject([ComputesitesService], (service: ComputesitesService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/computesites.service.ts b/src/app/computesites.service.ts new file mode 100644 index 0000000..0a92e89 --- /dev/null +++ b/src/app/computesites.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@angular/core'; +import { Computesite } from './computesite'; +import { COMPUTESITES } from './mock-compute-site'; + +@Injectable() +export class ComputesitesService { + + constructor() { } + + getComputeSites(): Computesite[] { + return COMPUTESITES; + } + + +} diff --git a/src/app/job.ts b/src/app/job.ts new file mode 100644 index 0000000..2e26957 --- /dev/null +++ b/src/app/job.ts @@ -0,0 +1,8 @@ +export class Job { + public name: string; + public jobid: string; + public desc: string; + public state: string; + public time: number; + public batch_host: string; +} diff --git a/src/app/job/job.component.css b/src/app/job/job.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/job/job.component.html b/src/app/job/job.component.html new file mode 100644 index 0000000..6846660 --- /dev/null +++ b/src/app/job/job.component.html @@ -0,0 +1,25 @@ +<mat-card> + <mat-card-title>{{ jobdata.name }}</mat-card-title> + <table> + <tr> + <td width="1000%"> + {{ jobdata.desc }} + </td> + <td width="20%"> + {{ jobdata.state }} + </td> + <td withdt="10%"> + <button mat-button (click)="onCancel()"> + Cancel + </button> + </td> + <td width="10%"> + <div *ngIf="available"> + <button mat-button (click)="onConnect()" [disabled]="busy"> + Connect + </button> + </div> + </td> + </tr> +</table> +</mat-card> diff --git a/src/app/job/job.component.spec.ts b/src/app/job/job.component.spec.ts new file mode 100644 index 0000000..713b2f8 --- /dev/null +++ b/src/app/job/job.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { JobComponent } from './job.component'; + +describe('JobComponent', () => { + let component: JobComponent; + let fixture: ComponentFixture<JobComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ JobComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(JobComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/job/job.component.ts b/src/app/job/job.component.ts new file mode 100644 index 0000000..aac09fe --- /dev/null +++ b/src/app/job/job.component.ts @@ -0,0 +1,42 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Job } from '../job'; +import {TesService} from '../tes.service'; +import { StrudelappsService } from '../strudelapps.service'; + +@Component({ + selector: 'app-job', + templateUrl: './job.component.html', + styleUrls: ['./job.component.css'] +}) +export class JobComponent implements OnInit { + + @Input() jobdata: Job; + private available: Boolean; + private busy: Boolean; + constructor(private tesService: TesService, private strudelAppsService: StrudelappsService) { + + +} + + ngOnInit() { + if (this.jobdata.state == "RUNNING") { + this.available = true; + } else { + this.available = false; + } + this.tesService.busy.subscribe(busy => this.busy = busy); + } + + onCancel() { + this.tesService.cancel(this.jobdata.jobid); + } + + onConnect() { + console.log('attempting connect'); + // Before connecting we must resolve what type of app we are connecting to + let app = this.strudelAppsService.getApp(this.jobdata.name); + console.log('app definition is ',app); + this.tesService.connect(this.jobdata.jobid,this.jobdata.batch_host,app); + } + +} diff --git a/src/app/joblist/joblist.component.css b/src/app/joblist/joblist.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/joblist/joblist.component.html b/src/app/joblist/joblist.component.html new file mode 100644 index 0000000..552ce46 --- /dev/null +++ b/src/app/joblist/joblist.component.html @@ -0,0 +1,6 @@ +<div *ngIf="jobs.lenght == 0"> + You don't appear to have any jobs +</div> +<div *ngFor="let job of jobs"> + <app-job [jobdata]=job></app-job> +</div> diff --git a/src/app/joblist/joblist.component.spec.ts b/src/app/joblist/joblist.component.spec.ts new file mode 100644 index 0000000..d40e3a9 --- /dev/null +++ b/src/app/joblist/joblist.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { JoblistComponent } from './joblist.component'; + +describe('JoblistComponent', () => { + let component: JoblistComponent; + let fixture: ComponentFixture<JoblistComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ JoblistComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(JoblistComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/joblist/joblist.component.ts b/src/app/joblist/joblist.component.ts new file mode 100644 index 0000000..0c85dac --- /dev/null +++ b/src/app/joblist/joblist.component.ts @@ -0,0 +1,49 @@ +import { Component, OnInit } from '@angular/core'; +import {TesService} from '../tes.service'; +import { Job } from '../job'; +import { Observable } from 'rxjs/Observable'; + + + +@Component({ + selector: 'app-joblist', + templateUrl: './joblist.component.html', + styleUrls: ['./joblist.component.css'] +}) +export class JoblistComponent implements OnInit { + private jobs: any[] = []; + private displayedColumns = ['id']; + private jobsSubscription: any; + + constructor(private tesService: TesService,) { } + + ngOnInit() { + + this.jobsSubscription = this.tesService.joblist.subscribe(jobs => this.jobs = jobs); + console.log("jobs subscribed by joblist component"); + } + + public ngOnDestroy(): void { + if (this.jobsSubscription) { + this.jobsSubscription.unsubscribe(); + } + + } + + updateJobs(jobs) { + this.jobs = jobs; + console.log("job data received by the joblist component"); + console.log(this.jobs); + } + + login(){ + //Wire this up to the tes login component to get an ssh cert on the tes backend + //This will also tell the TES which site we are using + return; + } + + logout() { + return; + } + +} diff --git a/src/app/launcher/dialog-placeholder.html b/src/app/launcher/dialog-placeholder.html new file mode 100644 index 0000000..b7ce2f3 --- /dev/null +++ b/src/app/launcher/dialog-placeholder.html @@ -0,0 +1,3 @@ +<p> + {{ data.msg }} +</p> diff --git a/src/app/launcher/launcher.component.css b/src/app/launcher/launcher.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/launcher/launcher.component.html b/src/app/launcher/launcher.component.html new file mode 100644 index 0000000..38c925b --- /dev/null +++ b/src/app/launcher/launcher.component.html @@ -0,0 +1,18 @@ + +<div *ngIf="authorised"> +<mat-form-field> + <mat-select placeholder="Application" [(value)]=app> + <mat-option *ngFor="let a of strudelapps" [value]=a> + {{a.name}} + </mat-option> + </mat-select> +</mat-form-field> +<button mat-button (click)=configureResources()>Configure Resources</button> +<button mat-button (click)=configureApp() [disabled]="!app">Configure App</button> +<button mat-button (click)=submitApp() [disabled]="!app">Start</button> + +<app-joblist></app-joblist> + + + +</div> diff --git a/src/app/launcher/launcher.component.spec.ts b/src/app/launcher/launcher.component.spec.ts new file mode 100644 index 0000000..a840baf --- /dev/null +++ b/src/app/launcher/launcher.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LauncherComponent } from './launcher.component'; + +describe('LauncherComponent', () => { + let component: LauncherComponent; + let fixture: ComponentFixture<LauncherComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LauncherComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LauncherComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/launcher/launcher.component.ts b/src/app/launcher/launcher.component.ts new file mode 100644 index 0000000..deaf186 --- /dev/null +++ b/src/app/launcher/launcher.component.ts @@ -0,0 +1,82 @@ +import { Component, OnInit, NgModule, Inject } from '@angular/core'; +import {Strudelapp} from '../strudelapp'; +import { StrudelappsService } from '../strudelapps.service'; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material'; +import { TesService } from '../tes.service'; + + + +@Component({ + selector: 'app-launcher', + templateUrl: './launcher.component.html', + styleUrls: ['./launcher.component.css'] +}) +export class LauncherComponent implements OnInit { + private strudelapps: Strudelapp[]; + + private app: Strudelapp; + private authorised: boolean; + + constructor(private strudelappsService: StrudelappsService, public dialog: MatDialog, private tesService: TesService) { } + + ngOnInit() { + this.loadStrudelapps(); + setTimeout( () => { this.tesService.authorised.subscribe(auth => this.authorised = auth); }); + } + + + configureApp() { + console.log('configuring app for',this.app.name); + this.openDialog('If the application takes any parameters, this will open a new window where you can configure them') + } + + configureResources() { + this.openDialog('This will open a new window to configure resource reqests') + } + + submitApp() { + console.log('submitting app for',this.app.name); + this.tesService.submit(this.app); + // this.openDialog('This is where we should start the app'); + } + + appselect(a: any) { + console.log('appselect') + this.app = a; + } + + loadStrudelapps() { + this.strudelapps = this.strudelappsService.getStrudelapps() + } + + + openDialog(msg: string ): void { + let dialogRef = this.dialog.open(DialogPlaceholder, { + width: '250px', + height: '400px', + data: { 'msg': msg } + }); + + dialogRef.afterClosed().subscribe(result => { + console.log('The dialog was closed'); + }); + } + + +} + +@Component({ + selector: 'dialog-placeholder', + templateUrl: 'dialog-placeholder.html', +}) +export class DialogPlaceholder { + + constructor( + public dialogRef: MatDialogRef<DialogPlaceholder>, + @Inject(MAT_DIALOG_DATA) public data: any) { } + + onNoClick(): void { + this.dialogRef.close(); + } + +} diff --git a/src/app/mock-compute-site.ts b/src/app/mock-compute-site.ts new file mode 100644 index 0000000..29078a9 --- /dev/null +++ b/src/app/mock-compute-site.ts @@ -0,0 +1,6 @@ +import { Computesite } from './computesite'; + +export const COMPUTESITES: Computesite[] = [ + { url: 'http://localhost:8080/config', name: 'M3' }, + {url: '', name: 'Other' }, +]; diff --git a/src/app/mock-strudel-app.ts b/src/app/mock-strudel-app.ts new file mode 100644 index 0000000..5045b19 --- /dev/null +++ b/src/app/mock-strudel-app.ts @@ -0,0 +1,64 @@ +import { Strudelapp } from './strudelapp'; + +const tmuxscript = `#!/bin/bash +#SBATCH -J tmux +tmux new-session -d -s $SLURM_JOB_NAME bash +# determine the process id of the tmux server +pid=$( /bin/ps x | /bin/grep -i "[t]mux new-session -d -s" | sed 's/^\ *//' | cut -f 1 -d " " ) +ps x +# Sleep until the tmux server exits +while [ -e /proc/$pid ]; do sleep 5; done +` + +const jupyterscript = `#!/bin/bash +#SBATCH -J Jupyter +/projects/pMOSP/chines/jupyter-venv/bin/jupyter-notebook +` + +const stddesktop = `#!/bin/bash +#SBATCH --job-name=desktop +# --exclusive allows the job to consume all resources on the node reguardless of how many cpus/gpus/memory/etc +#SBATCH --exclusive +#SBATCH --nodes=1 +#SBATCH --ntasks=3 +#SBATCH --cpus-per-task=1 +#SBATCH --gres=gpu:K1:1 +#SBATCH --partition=m3f +#SBATCH --mem=10288M + +#module purge +module load tigervnc/1.8.0 +#module list +export PATH=$PATH:$XMASSIVESERVICEPATH/bin +export XDG_CONFIG_DIRS=$XMASSIVESERVICEPATH/xdg_config +export XDG_DATA_DIRS=$XMASSIVESERVICEPATH/xdg_data:/usr/share:/usr/local/share +echo " Starting VNC..." +#Remove old pid files that should not exist due to vncserver not being shut down cleanly last time +#rm -f ~/.vnc/$HOSTNAME:1.pid +#vncserver -xstartup $XSTARTUP #:1 so we can start multiple VNC servers +vncserver -xstartup /usr/local/desktop/services/massive-std/xstartup/xstartup +xinit /usr/bin/xterm -- -sharevts -novtswitch -config xorg.conf & +#vncserver +while true; +do + sleep 30; +done` + +const vncviewer = { 'cmd': ['python','vncviewer.py','--password','{password}','--host','localhost','--port','{localtunnelport}'], 'redir':'?password={password}'} + +const tmuxclient = {'cmd': ['/usr/bin/gnome-terminal', '--','ssh','-t', + 'localhost','-p','{localtunnelport}','-o', + 'StrictHostkeyChecking=No','/bin/tmux a {sessionname}'], + 'redir': null} + + +const jupyterclient = {'cmd': null, 'redir': '?token={token}'} + +export const STRUDELAPPS: Strudelapp[] = [ + { url: null, name: 'tmux', startscript: tmuxscript, + paramscmd: '/home/chines/smuxparams.py', client: tmuxclient, localbind: false }, + { url: null, name: 'Jupyter Notebook', startscript: jupyterscript, + paramscmd: '/home/chines/jupyter_params.py', client: jupyterclient, localbind: true }, + { url: null, name: 'Standard Desktop', startscript: stddesktop, + paramscmd: '/home/chines/desktop_params.py', client: vncviewer, localbind: false }, +]; diff --git a/src/app/siteselection/siteselection.component.css b/src/app/siteselection/siteselection.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/siteselection/siteselection.component.html b/src/app/siteselection/siteselection.component.html new file mode 100644 index 0000000..06dbdec --- /dev/null +++ b/src/app/siteselection/siteselection.component.html @@ -0,0 +1,13 @@ + + <div> + <mat-form-field> + <mat-select placeholder="Site" [(value)]=siteurl> + <mat-option *ngFor="let site of computesites" [value]=site.url> + {{site.name}} + </mat-option> + </mat-select> + </mat-form-field> + <div *ngIf="siteurl"> + <!--<button mat-button (click)=configureResources()>Configure Resources</button>--> + </div> +</div> diff --git a/src/app/siteselection/siteselection.component.spec.ts b/src/app/siteselection/siteselection.component.spec.ts new file mode 100644 index 0000000..d77e666 --- /dev/null +++ b/src/app/siteselection/siteselection.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SiteselectionComponent } from './siteselection.component'; + +describe('SiteselectionComponent', () => { + let component: SiteselectionComponent; + let fixture: ComponentFixture<SiteselectionComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SiteselectionComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SiteselectionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/siteselection/siteselection.component.ts b/src/app/siteselection/siteselection.component.ts new file mode 100644 index 0000000..bfb204f --- /dev/null +++ b/src/app/siteselection/siteselection.component.ts @@ -0,0 +1,29 @@ +import { Component, OnInit } from '@angular/core'; +import { Computesite } from '../computesite' +import { ComputesitesService} from '../computesites.service'; + +@Component({ + selector: 'app-siteselection', + templateUrl: './siteselection.component.html', + styleUrls: ['./siteselection.component.css'] +}) +export class SiteselectionComponent implements OnInit { + private computesites: Computesite[]; + private siteurl: String; + constructor(private computesiteSerivce: ComputesitesService) { } + + ngOnInit() { + this.loadComputesites(); + } + configureResources() { + console.log('configuring resources for',this.siteurl); + } + siteselect(a: any) { + console.log('siteselect') + } + + loadComputesites() { + this.computesites = this.computesiteSerivce.getComputeSites() + } + +} diff --git a/src/app/strudelapp.ts b/src/app/strudelapp.ts new file mode 100644 index 0000000..b5214d8 --- /dev/null +++ b/src/app/strudelapp.ts @@ -0,0 +1,8 @@ +export class Strudelapp { + url: string; + name: string; + startscript: string; + paramscmd: string; + client: {cmd: string[], redir: string}; + localbind: boolean; +} diff --git a/src/app/strudelapps.service.spec.ts b/src/app/strudelapps.service.spec.ts new file mode 100644 index 0000000..b374223 --- /dev/null +++ b/src/app/strudelapps.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { StrudelappsService } from './strudelapps.service'; + +describe('StrudelappsService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [StrudelappsService] + }); + }); + + it('should be created', inject([StrudelappsService], (service: StrudelappsService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/strudelapps.service.ts b/src/app/strudelapps.service.ts new file mode 100644 index 0000000..8b767a3 --- /dev/null +++ b/src/app/strudelapps.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@angular/core'; +import { Strudelapp } from './strudelapp'; +import { STRUDELAPPS } from './mock-strudel-app'; + +@Injectable() +export class StrudelappsService { + + constructor() { } + getStrudelapps(): Strudelapp[] { + return STRUDELAPPS; + } + + getApp(name: string) { + if (name.match(/mux/)) { + return STRUDELAPPS[0]; + } + if (name.match(/upyter/)) { + return STRUDELAPPS[1]; + } + if (name.match(/desktop/)) { + return STRUDELAPPS[2]; + } + return STRUDELAPPS[0]; + } + +} diff --git a/src/app/tes.service.spec.ts b/src/app/tes.service.spec.ts new file mode 100644 index 0000000..df976ab --- /dev/null +++ b/src/app/tes.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { TesService } from './tes.service'; + +describe('TesService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [TesService] + }); + }); + + it('should be created', inject([TesService], (service: TesService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/app/tes.service.ts b/src/app/tes.service.ts new file mode 100644 index 0000000..0f44f50 --- /dev/null +++ b/src/app/tes.service.ts @@ -0,0 +1,217 @@ +import { Injectable } from '@angular/core'; +import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; +import { Observable } from 'rxjs/Observable'; +import { of } from 'rxjs/observable/of'; +import { catchError, map, tap } from 'rxjs/operators'; +import { Job } from './job'; +import {BehaviorSubject} from 'rxjs/BehaviorSubject'; +import { Strudelapp } from './strudelapp'; +import { timer } from 'rxjs/observable/timer'; +import { repeat } from 'rxjs/operators'; +// import { keypair } from 'keypair'; +import * as keypair from 'keypair'; +import * as forge from "node-forge"; +// import keypair = require('keypair'); +// import forge = require('node-forge'); + + +@Injectable() +export class TesService { +private Base='http://localhost:5000' +public authorised: BehaviorSubject<boolean>; +public tesSelected: BehaviorSubject<boolean>; +public errMsg: BehaviorSubject<any>; +public jobs: any[]; +public busy: BehaviorSubject<boolean> ; +public testingAuth: BehaviorSubject<boolean>; +public joblist: BehaviorSubject<Job[]>; +private timerSubscription: any; +private token: string; +private signing_endpoint: string; +private sshauthzbase: string; +private sshauthz: string; +private client_id: string; +private keypair: any; +private ssh: any; +private sshcert: any; + + + + constructor(private http: HttpClient) { + this.errMsg = new BehaviorSubject<any>(''); + this.authorised = new BehaviorSubject<boolean>(false); + this.tesSelected = new BehaviorSubject<boolean>(true); + this.testingAuth = new BehaviorSubject<boolean>(false); + this.busy = new BehaviorSubject<boolean>(false); + this.joblist = new BehaviorSubject<Job[]>([]); + this.timerSubscription = null; + this.token = null; + this.sshauthzbase = "https://autht.massive.org.au/hpcid/"; + this.sshauthz = this.sshauthzbase + 'oauth/authorize'; + this.signing_endpoint = this.sshauthzbase + 'api/v1/sign_key'; + this.client_id = "86c06039-e589-4b39-9d1f-9eca431be18f"; + + + } + + getJobs() { + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + this.errMsg.next(null); + this.http.get<Job[]>(this.Base+'/stat',options) + .pipe(catchError(this.handleError('getJobs',[]))) + .subscribe(resp => this.joblist.next(resp)); + } + + submit(app: Strudelapp) { + console.log("In tes submit url "+this.Base+'/submit'); + console.log("starting app"+app.name); + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + this.errMsg.next(null); + this.busy.next(true); + this.http.post<any>(this.Base+'/appsetup',app, options) + .pipe(catchError(this.handleError('submit',[]))) + .subscribe(resp => this.http.post<any>(this.Base+'/submit',{}, options) + .pipe(catchError(this.handleError('submit',[]))) + .subscribe(resp => this.busy.next(false))); + } + + cancel(jobid: string) { + console.log("In tes cancel"); + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + this.errMsg.next(null); + let data = {}; + this.http.delete<any>(this.Base+'/cancel/'+jobid, options) + .pipe(catchError(this.handleError('cancel',[]))) + .subscribe(resp => this.submitted(resp)); + } + + submitted(resp: any ) { + this.busy.next(false); + } + + getCert(frag: any) { + // Given a URL fragment, extract the authentication token, and use it to get + // an SSH certificate. SSH cert to be used on subsequent calls to the TES + console.log('in tesservice getCert'); + let match = frag.match(/access_token\=([\S\s]*?)[&|$]/); + if (match == null) { + this.errMsg.next("No access token: It looks like your login to SSHAuthZ didn't work."); + this.testingAuth.next(false); + this.authorised.next(false); + } + this.authorised.next(true); + console.log(match); + this.token = match[1]; + console.log(this.token); + // this.token = frag; + console.log('tes stored the token'+this.token); + + // let data = {'token': this.token, 'sshauthz_endpoint': this.signing_endpoint} + // console.log(data); + this.keypair = keypair(); + let publicKey = forge.pki.publicKeyFromPem(this.keypair.public); + this.ssh = forge.ssh.publicKeyToOpenSSH(publicKey, 'sv2@monash.edu'); + + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + + let data = {'token': this.token, 'pubkey': this.ssh, 'signing_url': this.signing_endpoint}; + + this.busy.next(true); + console.log('posting to getcert',this.Base); + this.http.post<any>(this.Base+'/getcert',data, options) + .pipe(catchError(this.handleError('getCert',[]))) + .subscribe(resp => this.storeCert(resp)); +} + +private storeCert(resp) { + console.log(resp); + this.busy.next(false); + this.authorised.next(true); + this.testingAuth.next(false); + + console.log(resp['cert']); + this.sshcert = resp['cert']; + if (!(this.timerSubscription === null)) { + this.timerSubscription.unsubscribe() + } + console.log('creating timer to poll for jobs'); + this.timerSubscription = timer(5000).pipe(repeat()).subscribe(() => this.getJobs()); + this.getJobs(); + +} + +public login() { + + + let redirect_uri = window.location.origin+"/sshauthz_callback"; + let state="asdfzxcv"; + window.location.assign(this.sshauthz+"?response_type=token&redirect_uri="+redirect_uri+"&state="+state+"&client_id="+this.client_id); +} + +// public testAuth() { +// if (this.sshcert === null) { +// this.authorised.next(false); +// } else { +// this.authorised.next(true); +// } +// this.testingAuth.next(false); +// // this.tesSelected.next(true); +// // this.Base = 'http://localhost:5000' +// // this.errMsg.next(null); +// // +// // let headers = new HttpHeaders(); +// // this.testingAuth.next(true); +// // let options = { headers: headers, withCredentials: true}; +// // this.http.get(this.Base+'/testauth',options).pipe(catchError(this.handleError('submit',[]))) +// // .subscribe(resp => this.testAuthComplete(resp)); +// } + +public logout(): Boolean { + this.token = null; + this.sshcert = null; + this.errMsg.next(null); + this.authorised.next(false); + if (!(this.timerSubscription === null)) { + this.timerSubscription.unsubscribe() + } + return true; +} +public connect(jobid: string,exechost: string,app: Strudelapp) { + this.errMsg.next(null); + let headers = new HttpHeaders(); + let options = { headers: headers, withCredentials: true}; + this.busy.next(true); + this.http.post<any>(this.Base+'/appsetup',app, options) + .pipe(catchError(this.handleError('submit',[]))) + .subscribe(resp => { window.open(this.Base+"/connect/"+jobid+"/"+exechost); this.busy.next(false)}); +} + +public getErrmsgSubject(): BehaviorSubject<any> { + return this.errMsg; +} + + +private handleError<T> (operation = 'operation', result?: T) { + return (error: any): Observable<T> => { + // TODO: send the error to remote logging infrastructure + console.error(error); // log to console instead + this.authorised.next(false); + this.token = null; + this.sshcert = null; + + if ( operation == 'getJobs') { + this.errMsg.next("Hmm, that didn't work. If you're using a local connection, please make sure Strudel-TES is running."); + } + if (operation == 'submit') { + this.errMsg.next("Hmm, I couldn't submit that job") + } + return of(result as T); + }; +} + + +} diff --git a/src/app/teserrors/teserrors.component.css b/src/app/teserrors/teserrors.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/teserrors/teserrors.component.html b/src/app/teserrors/teserrors.component.html new file mode 100644 index 0000000..8b4f737 --- /dev/null +++ b/src/app/teserrors/teserrors.component.html @@ -0,0 +1,3 @@ +<p> +{{ errmsg }} +</p> diff --git a/src/app/teserrors/teserrors.component.spec.ts b/src/app/teserrors/teserrors.component.spec.ts new file mode 100644 index 0000000..4a31e33 --- /dev/null +++ b/src/app/teserrors/teserrors.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TeserrorsComponent } from './teserrors.component'; + +describe('TeserrorsComponent', () => { + let component: TeserrorsComponent; + let fixture: ComponentFixture<TeserrorsComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ TeserrorsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TeserrorsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/teserrors/teserrors.component.ts b/src/app/teserrors/teserrors.component.ts new file mode 100644 index 0000000..89a3a56 --- /dev/null +++ b/src/app/teserrors/teserrors.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit } from '@angular/core'; +import { TesService } from '../tes.service'; + +@Component({ + selector: 'app-teserrors', + templateUrl: './teserrors.component.html', + styleUrls: ['./teserrors.component.css'] +}) +export class TeserrorsComponent implements OnInit { + + private errmsg: string; + + constructor(private tesService: TesService,) { } + + ngOnInit() { + this.tesService.getErrmsgSubject().subscribe( msg => this.getNewMessage(msg)); + } + + getNewMessage(msg) { + this.errmsg = msg; + } + +} diff --git a/src/app/tokenextractor/tokenextractor.component.css b/src/app/tokenextractor/tokenextractor.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/tokenextractor/tokenextractor.component.html b/src/app/tokenextractor/tokenextractor.component.html new file mode 100644 index 0000000..171e239 --- /dev/null +++ b/src/app/tokenextractor/tokenextractor.component.html @@ -0,0 +1,3 @@ +<p> + tokenextractor works! +</p> diff --git a/src/app/tokenextractor/tokenextractor.component.spec.ts b/src/app/tokenextractor/tokenextractor.component.spec.ts new file mode 100644 index 0000000..2853267 --- /dev/null +++ b/src/app/tokenextractor/tokenextractor.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TokenextractorComponent } from './tokenextractor.component'; + +describe('TokenextractorComponent', () => { + let component: TokenextractorComponent; + let fixture: ComponentFixture<TokenextractorComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ TokenextractorComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TokenextractorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/tokenextractor/tokenextractor.component.ts b/src/app/tokenextractor/tokenextractor.component.ts new file mode 100644 index 0000000..7366e36 --- /dev/null +++ b/src/app/tokenextractor/tokenextractor.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Router, NavigationStart } from '@angular/router'; + +import { TesService } from '../tes.service'; + + +@Component({ + selector: 'app-tokenextractor', + templateUrl: './tokenextractor.component.html', + styleUrls: ['./tokenextractor.component.css'] +}) +export class TokenextractorComponent implements OnInit { + + constructor( private route: ActivatedRoute, private router: Router, private tesService: TesService ) { + } + + ngOnInit() { + this.route.fragment.subscribe( frag => this.tesService.getCert(frag)); + this.router.navigate(['/']); + } + +} -- GitLab