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