Commit cbf3b71f authored by Chris Hines's avatar Chris Hines
Browse files

major UI overhaul

parent 02910f23
Pipeline #7403 passed with stages
in 2 minutes and 55 seconds
......@@ -7,6 +7,7 @@ import { ShareconnectComponent } from './shareconnect/shareconnect.component';
import { JoblistComponent } from './joblist/joblist.component';
import {LoginComponent} from './login/login.component';
import {LogoutComponent} from './logout/logout.component';
import {SettingsComponent} from './settings/settings.component';
......@@ -18,6 +19,7 @@ const routes: Routes = [
//{ path: 'launch', component: JoblistComponent},
{ path: 'launch', component: LauncherComponent},
{ path: 'login', component: LoginComponent},
{ path: 'logout', component: LogoutComponent},
{ path: 'settings', component: SettingsComponent },
// { path: 'finishlaunch', component: LauncherComponent},
//{ path: 'cancellaunch', component: LauncherComponent},
......
......@@ -25,6 +25,7 @@ import { AuthorisationService } from './authorisation.service';
import { FlexLayoutModule } from '@angular/flex-layout';
import { FileExplorerModule } from './file-explorer/file-explorer.module';
import { MatMenuModule } from '@angular/material';
import { MatBadgeModule } from '@angular/material';
import { TesService} from './tes.service';
......@@ -51,6 +52,7 @@ import { ModaldialogComponent } from './modaldialog/modaldialog.component'
import {OverlayModule} from '@angular/cdk/overlay';
import { LoginComponent } from './login/login.component';
import { SettingsComponent } from './settings/settings.component';
import { LogoutComponent } from './logout/logout.component';
// import { FileExplorerModule } from './file-explorer/file-explorer.module';
......@@ -76,6 +78,7 @@ import { SettingsComponent } from './settings/settings.component';
ModaldialogComponent,
LoginComponent,
SettingsComponent,
LogoutComponent,
],
imports: [
BrowserModule,
......@@ -103,10 +106,11 @@ import { SettingsComponent } from './settings/settings.component';
MatProgressBarModule,
OverlayModule,
MatMenuModule,
MatBadgeModule,
],
entryComponents: [ LogoutdialogComponent, LaunchDialogComponent, ModaldialogComponent],
entryComponents: [ LogoutdialogComponent, ModaldialogComponent],
providers: [ StrudelappsService, ComputesitesService, TesService, SubmitAppService, MatDialog, AuthorisationService,BackendSelectionService],
bootstrap: [AppComponent]
})
......
......@@ -43,7 +43,6 @@ export class AuthorisationService {
private router: Router,
private backendSelectionService: BackendSelectionService,
private location: Location) {
console.log('created AuthorisationService');
// this.token = new BehaviorSubject<AuthToken>(new AuthToken('',''));
this.readyToNavigate = new Subject<[Boolean,string]>();
this.readyToNavigate.next([false,'']);
......@@ -77,7 +76,6 @@ export class AuthorisationService {
}
removeLocalAuthZ() {
localStorage.removeItem('localauthservers');
console.log('removed local computesites');
this.getSshAuthzServers();
}
......@@ -97,12 +95,9 @@ export class AuthorisationService {
var found: boolean;
for (let s of authzServers) {
found=false;
console.log('server fp',s.cafp)
for (let cert of agentContents) {
console.log('cert',cert);
if ('Signing CA' in cert) {
for (let ca of cert['Signing CA']) {
console.log('ca',ca);
if (ca == s.cafp) {
loggedin.push(s)
found=true;
......@@ -118,11 +113,6 @@ export class AuthorisationService {
loggedout.push(s)
}
}
console.log('updating which authz servers are logged in and out');
console.log('logged in');
console.log(loggedin);
console.log('logged out');
console.log(loggedout);
this.loggedOutAuthZ.next(loggedout);
this.loggedInAuthZ.next(loggedin);
}
......@@ -177,32 +167,25 @@ export class AuthorisationService {
}
public getCert(token: AuthToken) {
console.log('in getCert');
if (token.token === undefined || token.token === '' || token.token == null) {
console.log('no authtoken available, we wont be able to generate a cert');
console.log(token);
return
}
console.log("Generating key matter");
let starttime = new Date();
let newkeypair = keypair();
let publicKey = forge.pki.publicKeyFromPem(newkeypair.public);
let sshpub = forge.ssh.publicKeyToOpenSSH(publicKey, 'sv2@monash.edu');
let endtime = new Date();
console.log("generating new keys took", endtime.valueOf() - starttime.valueOf())
let headers = new HttpHeaders();
let options = { headers: headers, withCredentials: true};
let data = {'token': token.token, 'pubkey': sshpub, 'signing_url': token.sshauthzservice.sign};
console.log('posting to getcert',this.backendURI);
this.http.post<any>(this.backendURI+'/getcert',data, options)
.pipe(catchError(this.handleError([])))
.subscribe(resp => this.makeKeyCert(newkeypair.private, resp, token.sshauthzservice),
error => this.signingError(error,token.sshauthzservice));
console.log('getcert complete');
}
public getKeys(id?: Identity) {
......@@ -217,8 +200,6 @@ public getKeys(id?: Identity) {
let keyCert = new KeyCert()
keyCert.key = key;
keyCert.cert = resp['cert'];
console.log('in make KeyCert',resp);
console.log('updating keycert',keyCert);
var keys: KeyCert[] = [];
try{
keys = JSON.parse(sessionStorage.getItem('keys'));
......@@ -246,20 +227,18 @@ public getKeys(id?: Identity) {
this.statusMsg.next("A network error occured. Are you connected to the internet?")
}
this.statusMsg.next("Error querying ssh agent");
console.log(error);
}
public updateAgentContents() {
if (this.statusMsg !== undefined) {
this.statusMsg.next("Updating the list of available accounts");
};
console.log('querying ',this.backendURI+'/sshagent');
let headers = new HttpHeaders();
let options = { headers: headers, withCredentials: true};
var anyvar: any;
this.http.get<any>(this.backendURI+'/sshagent',options)
.pipe(catchError(this.handleError(anyvar)))
.subscribe(resp => { console.log('sshagent returned',resp); this.agentContents.next(resp); this.statusMsg.next("") },
.subscribe(resp => { this.agentContents.next(resp); this.statusMsg.next("") },
error => this.querySshAgentError(error));
// .subscribe(resp => this.computeSitesService.updateIdentities(resp),
// error => this.httperror(error))
......@@ -285,7 +264,6 @@ public getKeys(id?: Identity) {
public sshAdd(keyCert: KeyCert) {
console.log('in authorisation service sshAdd');
if (keyCert.key == undefined) {
return;
}
......@@ -305,12 +283,10 @@ public getKeys(id?: Identity) {
let redirect_uri = window.location.origin+this.locationStrategy.getBaseHref()+"sshauthz_callback";
let nonce=Math.random().toString(36).substring(2, 15)
console.log(this.location.path());
localStorage.setItem('authservice', JSON.stringify([authservice,nonce]));
localStorage.setItem('path', this.location.path());
console.log('login activated, pathname stored as',window.location.pathname);
if (authservice.scope == null) {
window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id);
} else {
......@@ -342,13 +318,11 @@ public getKeys(id?: Identity) {
// }
private httperror(error: any) {
console.log('authorsation service got an error');
this.statusMsg.next('There was an error logging in or generating crypto tokens');
console.error(error);
}
private signingError(error: any,sshauthzservice: SshAuthzServer) {
this.statusMsg.next('You don\'t appear to have an account on '+sshauthzservice.name);
console.log(error);
if (!(sshauthzservice.logout === null)) {
window.open(sshauthzservice.logout);
}
......@@ -359,8 +333,6 @@ public getKeys(id?: Identity) {
private handleError<T> (result?: T) {
return (error: any): Observable<T> => {
console.log(error)
console.log('handleError activated')
if (error.status == 500) {
return throwError("The backend server encountered and error. Please try again in a few minutes")
}
......
......@@ -22,10 +22,8 @@ export class BackendSelectionService {
private initApiServer() {
try {
this.apiserver.next(<APIServer>JSON.parse(localStorage.getItem('apiserver')));
console.log('apiserver set to localstorage value',this.apiserver.value);
} catch {
this.apiserver.next(<APIServer>environment.apiserver);
console.log('api server to set environement',this.apiserver.value);
}
if (this.apiserver.value == null) {
this.apiserver.next(<APIServer>environment.apiserver);
......@@ -33,14 +31,11 @@ export class BackendSelectionService {
}
private saveLastApiServer(s: APIServer) {
console.log('saving api server value to local storage')
localStorage.setItem('apiserver', JSON.stringify(s));
}
setApiServer(server: APIServer) {
console.log('calling setAPIServer');
this.apiserver.next(server);
console.log(this.apiserver);
this.saveLastApiServer(this.apiserver.value)
}
......
......@@ -39,7 +39,6 @@ export class ComputesitesService {
getStrudelApps(computesites: Computesite[]) {
for (let s of computesites) {
console.log('retrieving apps');
let headers = new HttpHeaders();
let options = { headers: headers, withCredentials: false};
if (s.appCatalogUri !== null) {
......@@ -109,7 +108,6 @@ export class ComputesitesService {
removeLocalComputeSites() {
localStorage.removeItem('localcomputesites');
console.log('removed local computesites');
this.getComputeSites();
}
......@@ -157,8 +155,6 @@ export class ComputesitesService {
var identities: Identity[] = [];
var appidentities: Identity[] = [];
var ftidentities: Identity[] = [];
console.log('compute sites value', this.computesites.value);
console.log('certs',certs);
for (cs of this.computesites.value) {
for (let i in certs) {
let principal = this.siteMatch(certs[i],cs);
......@@ -176,8 +172,6 @@ export class ComputesitesService {
}
this.identities.next(identities);
this.ftidentities.next(ftidentities);
console.log('setting ftidentities');
console.log(this.ftidentities.value);
this.appidentities.next(appidentities);
}
......
import {Computesite} from './computesite';
import {Computesite, Health} from './computesite';
import {Job} from './job';
import {BehaviorSubject} from 'rxjs';
// Identities are defined by a username on a computer, but rather than just
// DNS entry, there is extra info in the Computesite
export class Identity {
......@@ -6,15 +8,21 @@ export class Identity {
site: Computesite;
authservice: SshAuthzServer;
keyCerts: KeyCert[];
healthalerts: BehaviorSubject<Health[]>;
joblist: BehaviorSubject<Job[]>;
constructor( username: string, site: Computesite) {
this.username = username;
this.site = site;
this.keyCerts = [];
this.healthalerts = new BehaviorSubject<Health[]>([]);
this.joblist = new BehaviorSubject<Job[]>([]);
}
copy_skip_catalog(): Identity {
let id = new Identity(null,null);
id.username = this.username;
id.healthalerts = null;
id.joblist = null;
id.site = new Computesite();
id.site.url = this.site.url;
id.site.host = this.site.host;
......
<mat-card>
<mat-card-title>{{ jobdata.name }}</mat-card-title>
<mat-list-item>
<!--<mat-card-title>{{ jobdata.name }}</mat-card-title>-->
<div style="width: 100%">
<div *ngIf="jobdata.connectionState == 0">
<div gdAreas="name name name name|
<div gdAreas="name name name name|
status resources space buttons"
gdColumns="20% 60% auto 20%">
gdColumns="20% 60% auto 20%"
>
<div gdArea="name">
{{ jobdata.desc }}
{{ jobdata.name }} {{ jobdata.desc }}
</div>
<div gdArea="status">
{{ jobdata.state }}
......@@ -36,6 +38,7 @@
</div>
</div>
</div>
<!-- <table>
<tr>
<td width="100%">
......@@ -60,18 +63,31 @@
</td>
</tr>
</table> -->
</div>
<div *ngIf="jobdata.connectionState == 1">
Getting app parameters
<mat-progress-bar mode="determinate" value=30></mat-progress-bar>
</div>
<div *ngIf="jobdata.connectionState == 2">
Creating secure tunnels
<mat-progress-bar mode="determinate" value=60></mat-progress-bar>
<div *ngIf="jobdata.connectionState == 1">
<div gdArea="status">
Getting app parameters
</div>
<div gdArea="resources">
<mat-progress-bar mode="determinate" value=30></mat-progress-bar>
</div>
</div>
</div>
<div *ngIf="jobdata.connectionState == 3">
Determining correct URL
<mat-progress-bar mode="determinate" value=90></mat-progress-bar>
</div>
</mat-card>
<div *ngIf="jobdata.connectionState == 2">
<div gdArea="status">
Creating secure tunnels
</div>
<div gdArea="resources">
<mat-progress-bar mode="determinate" value=60></mat-progress-bar>
</div>
</div>
<div *ngIf="jobdata.connectionState == 3">
<div gdArea="status">
Determining correct URL
</div>
<div gdArea="resources">
<mat-progress-bar mode="determinate" value=90></mat-progress-bar>
</div>
</div>
</div>
</mat-list-item>
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
import { Job } from '../job';
import {TesService} from '../tes.service';
import { StrudelappsService } from '../strudelapps.service';
import { timer } from 'rxjs/observable/timer';
import { timer, Subscription } from 'rxjs';
import { repeat } from 'rxjs/operators';
@Component({
selector: 'app-job',
templateUrl: './job.component.html',
styleUrls: ['./job.component.css']
})
export class JobComponent implements OnInit {
export class JobComponent implements OnInit, OnDestroy {
@Input() jobdata: Job;
public available: Boolean;
public nocancel: Boolean;
public resources: string;
public timeremaining: string;
private updatesub: Subscription;
constructor(private tesService: TesService, private strudelAppsService: StrudelappsService) {
}
ngOnDestroy() {
this.updatesub.unsubscribe();
}
ngOnInit() {
this.tesService.joblist.subscribe(() => this.updateFields());
this.updatesub = timer(1000).pipe(repeat()).subscribe(() => this.updateFields());
}
updateFields() {
......
<mat-card style="width: 100%" style="box-sizing: border-box;">
<div *ngIf="(tesService.identitySubject | async) === null" >
Click login and select a provider or select one you've already logged in to.
</div>
<div *ngIf="(tesService.identitySubject | async) !== null" >
<div *ngFor="let h of tesService.cachetincidents | async">
<div *ngIf="h.stat == 'error'">
<mat-card style='width: 100%'>
<div class='health-warn'>
{{ h.msg }}
</div>
</mat-card>
</div>
</div>
<div *ngFor="let h of tesService.userhealth | async">
<div *ngIf="h.stat == 'error'">
<mat-card style='width: 100%'>
<div class='health-warn'>
{{ h.msg }}
</div>
</mat-card>
</div>
</div>
Jobs running as {{ (tesService.identitySubject | async).displayName() }}
<div *ngFor="let job of tesService.joblist | async">
<!--<div *ngIf="(identitySubject | async) === null" >
Click login and select a provider or select one you've already logged in to.
</div>-->
<div *ngIf="(identitySubject | async) !== null" >
<mat-list>
<div *ngFor="let job of ((identitySubject | async).joblist | async)">
<div *ngIf="job.state != 'Finished'">
<app-job [jobdata]=job></app-job>
</div>
<button mat-button (click)="refreshJobs()" style="width: 100%; text-align: right">Refresh Job list</button>
</div>
</div>
</mat-card>
</mat-list>
<button mat-button (click)="refreshJobs()" style="width: 100%; text-align: right">Refresh Job list</button>
<!-- <iframe src="http://localhost:5200" frameborder="0" style="width: 100%"></iframe> -->
</div>
import { Component, OnInit, Input, OnDestroy } from '@angular/core';
import {TesService} from '../tes.service';
import { Job } from '../job';
import { Observable } from 'rxjs/Observable';
import { delay } from 'rxjs/operators';
import { Subscription, interval } from 'rxjs';
import { Identity } from '../identity';
import { Renderer2} from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { BehaviorSubject, timer } from 'rxjs';
import { repeat } from 'rxjs/operators';
import { Strudelapp } from '../strudelapp';
import { TesService } from '../tes.service';
import { Job } from '../job';
import { Identity } from '../identity';
......@@ -17,21 +20,31 @@ import { BehaviorSubject } from 'rxjs';
})
export class JoblistComponent implements OnInit {
@Input() identity: Identity;
@Input() identitySubject: BehaviorSubject<Identity>;
@Input() appSubject: BehaviorSubject<Strudelapp>;
private sub: Subscription;
constructor(private tesService: TesService, private renderer: Renderer2) {
}
constructor(private tesService: TesService ) {
}
public ngOnInit(): void {
this.sub = timer(5000).pipe(repeat()).subscribe( () => this.getJobs() );
}
public getJobs(): void {
if (this.identitySubject.value !== null) {
this.tesService.getJobs(this.identitySubject.value);
}
}
public ngOnDestroy(): void {
if (this.sub !== undefined) {
this.sub.unsubscribe();
}
}
public refreshJobs(): void {
this.tesService.getJobs(this.tesService.identitySubject.value);
this.tesService.getJobs(this.identitySubject.value);
}
}
<mat-toolbar color="primary">
<mat-toolbar-row>
<button mat-icon-button ><mat-icon>menu</mat-icon></button>
<span>Strudel v2.0</span>
<span class="fill-horizontal-space"></span>
</mat-toolbar-row>
</mat-toolbar>
<mat-card>
Generating cryptographic tokens ... this should only take a few seconds
</mat-card>
<!--
<div gdAreas="appconfig appconfig |
batchconfig batchconfig |
cancel launch"
gdRows="auto auto auto">
<div gdArea="appconfig">
<iframe></iframe>
</div>
<div gdArea="batchconfig" style="width: 100%">
<iframe></iframe>
</div>
<div gdArea="launch">
<button mat-button (click)="launch()" enabled="submitAppService.readyToLaunch | async">Launch</button>
</div>
<div gdArea="cancel">
<button mat-button (click)="cancel()" >Cancel</button>
</div>
</div> -->
<div fxLayout="column" fxLayoutAlign="space-between stretch" style="height: 100%; width: 100%">
<div fxLayout="column" fxLayoutAlign="space-between stretch" style="width: 100%">
<!-- <div fxFlex style="background: blue">app config</div> -->
<iframe *ngIf="appconfigurl != null" fxGrow=10 fxFlex [src]="appconfigsafeurl" style="border: none"></iframe>
<iframe *ngIf="appconfigurl != null" [src]="batchcmdsafeurl" style="height: 1px; min-height: 0px; border: none" #batchbuilderiframe></iframe>
<!--<iframe *ngIf="appconfigurl != null" fxGrow=10 fxFlex [src]="appconfigsafeurl" style="border: none"></iframe>
<iframe *ngIf="appconfigurl != null" [src]="batchcmdsafeurl" style="height: 1px; min-height: 0px; border: none" #batchbuilderiframe></iframe>-->
<iframe *ngIf="appconfigurl == null" fxFlex [src]="batchcmdsafeurl" fxGrow=1 style="border: none" #batchbuilderiframe></iframe>
<div *ngIf="(appSubject | async ) !== null" style="width: 100%">
<iframe [src]="batchcmdsafeurl" style="border: none; border-style: none; border-width: 0px; width: 100%" [style.height]="height+'px'" #batchbuilderiframe></iframe>
<div fxLayout="row" fxLayoutAlign="space-around">
<button mat-button (click)="cancel()">Cancel</button><button mat-button (click)="launch()" #launchbtn>Launch</button>
<div fxLayout="row" fxLayoutAlign="space-around">
<button mat-button (click)="launch()" #launchbtn>Launch</button>
</div>
</div>
</div>
import { Component, OnInit, AfterViewInit, ViewChild, ElementRef, Inject } from '@angular/core';
import { Component, OnInit, ViewChild, ElementRef, Inject, Input } from '@angular/core';
import { DomSanitizer} from '@angular/platform-browser';
import { Renderer2 } from '@angular/core';
import { MatDialogRef,MAT_DIALOG_DATA } from '@angular/material';
import { SubmitAppService } from '../submit-app.service';
//import { MatDialogRef,MAT_DIALOG_DATA } from '@angular/material';
import { TesService } from '../tes.service';
import { timer, Subscription} from 'rxjs';
import { BehaviorSubject } from 'rxjs';
import { Identity } from '../identity';
import { Strudelapp } from '../strudelapp';
import { BatchInterface} from '../batchinterface';
@Component({
......@@ -12,6 +16,9 @@ import { timer, Subscription} from 'rxjs';
styleUrls: ['./launch-dialog.component.css']
})
export class LaunchDialogComponent implements OnInit {
@Input() identity: Identity;
@Input() appSubject: BehaviorSubject<Strudelapp>;
app: Strudelapp;
batchcmdurl: string;
appconfigurl: string;
batchcmdsafeurl: any;
......@@ -20,67 +27,64 @@ export class LaunchDialogComponent implements OnInit {
@ViewChild('launchbtn', { read: ElementRef, static: false }) launchbtn: ElementRef;
@ViewChild('batchbuilderiframe', { read: ElementRef, static: false }) batchbuilderiframe: ElementRef;
setFocus: Boolean;
sub: Subscription;
constructor( public dialogRef: MatDialogRef<LaunchDialogComponent>,
private domSanitizer: DomSanitizer,
private renderer: Renderer2,
private submitAppService: SubmitAppService) {
this.batchcmdurl = this.submitAppService.identity.value.site.url+"/"+this.submitAppService.app.value.name;
this.batchcmdsafeurl = this.domSanitizer.bypassSecurityTrustResourceUrl(this.batchcmdurl);
this.appconfigurl = this.submitAppService.app.value.url;
if (!(this.appconfigurl == null)) {
this.appconfigsafeurl = this.domSanitizer.bypassSecurityTrustResourceUrl(this.appconfigurl);