tes.service.ts 22.9 KB
Newer Older
1
import { Injectable, EventEmitter } from '@angular/core';
Chris Hines's avatar
Chris Hines committed
2
import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
3
import { Observable, Subject, BehaviorSubject, of, from, interval } from 'rxjs';
4
5
import { fromEvent, throwError, Subscription, merge } from 'rxjs';
import { catchError, map, tap, filter } from 'rxjs/operators';
Chris Hines's avatar
Chris Hines committed
6
import { Job } from './job';
7
import { AppAction, Strudelapp, StrudelappInstance } from './strudelapp';
Chris Hines's avatar
Chris Hines committed
8
import { Computesite, Health } from './computesite';
9
import { APIServer } from './apiserver';
Chris Hines's avatar
Chris Hines committed
10
import { Identity, AuthToken, KeyCert, SshAuthzServer } from './identity';
11
12
import { BatchInterface } from './batchinterface';
import { ComputesitesService } from './computesites.service';
13
import { StrudelappsService } from './strudelapps.service';
Chris Hines's avatar
Chris Hines committed
14
import { timer } from 'rxjs/observable/timer';
15
import { repeat, takeUntil, take } from 'rxjs/operators';
Chris Hines's avatar
Chris Hines committed
16
import { LocationStrategy, Location } from '@angular/common';
Chris Hines's avatar
Chris Hines committed
17
import { ActivatedRoute, Router } from '@angular/router';
Chris Hines's avatar
Chris Hines committed
18
19
import { ModaldialogComponent } from './modaldialog/modaldialog.component';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material';
Chris Hines's avatar
Chris Hines committed
20
21
import { AuthorisationService } from './authorisation.service';
import { environment } from '../environments/environment';
Chris Hines's avatar
Chris Hines committed
22
import { BackendSelectionService } from './backend-selection.service';
Chris Hines's avatar
Chris Hines committed
23

Chris Hines's avatar
Chris Hines committed
24
25
/** The TES service contains ways to start Tunnels, and Execute programs
Its also responsible for querying a compute site for running jobs */
Chris Hines's avatar
Chris Hines committed
26
27


28

Chris Hines's avatar
Chris Hines committed
29
30
31
@Injectable({
  providedIn: 'root',
})
Chris Hines's avatar
Chris Hines committed
32
export class TesService {
33
34
35
36
public Base: string;
//public Base=environment.tesurl;
//private twsproxy = environment.twsurl;
private twsproxy: string;
Chris Hines's avatar
Chris Hines committed
37
// public Base='http://localhost:5000';
38
public statusMsg: BehaviorSubject<any>;
Chris Hines's avatar
Chris Hines committed
39
public jobs: any[];
Chris Hines's avatar
Chris Hines committed
40
// public joblist: BehaviorSubject<{ [id: string ]: Job[]}>;
Chris Hines's avatar
Chris Hines committed
41
42
//public joblist: BehaviorSubject<Job[]>;
//public userhealth: BehaviorSubject<Health[]>;
Chris Hines's avatar
Chris Hines committed
43

Chris Hines's avatar
Chris Hines committed
44
private timerSubscription: any;
Chris Hines's avatar
Chris Hines committed
45
private appwindow: any;
46
47
public apiserver: BehaviorSubject<APIServer>;
public apiservers: BehaviorSubject<APIServer[]>;
48
private updateJobSub: Subscription;
Chris Hines's avatar
Chris Hines committed
49
50
private updateUserHealthSub: Subscription;
private cachetincidents: BehaviorSubject<Health[]>;
51
private nextUpdate: Subscription;
52
private cancelRequests$: Subject<boolean>;
53
public identitySubject: BehaviorSubject<Identity>;
Chris Hines's avatar
Chris Hines committed
54
public appSubject: BehaviorSubject<Strudelapp>;
Chris Hines's avatar
Chris Hines committed
55
private openapps: any[];
56
private authdone$: Subject<boolean>;
57

58
// public batchinterface: {[id: string] : BatchInterface};
Chris Hines's avatar
Chris Hines committed
59

60
  constructor(private http: HttpClient,
Chris Hines's avatar
Chris Hines committed
61
              public dialog: MatDialog,
Chris Hines's avatar
Chris Hines committed
62
63
              private computesitesService: ComputesitesService,
              private authorisationService: AuthorisationService,
Chris Hines's avatar
Chris Hines committed
64
              private strudelappsService: StrudelappsService,
Chris Hines's avatar
Chris Hines committed
65
              private backendSelectionService: BackendSelectionService,
Chris Hines's avatar
Chris Hines committed
66
              private location: Location ) {
Chris Hines's avatar
Chris Hines committed
67
68

    // this.joblist = new BehaviorSubject<{[id: string]: Job[]}>({});
Chris Hines's avatar
Chris Hines committed
69
70
71
    //this.joblist = new BehaviorSubject<Job[]>([]);
    //this.cachetincidents = new BehaviorSubject<Health[]>([]);
    //this.userhealth = new BehaviorSubject<Health[]>([{'stat':'ok','msg':''}])
Chris Hines's avatar
Chris Hines committed
72
    this.apiserver = new BehaviorSubject<APIServer>(null);
73
    this.apiservers = new BehaviorSubject<APIServer[]>([]);
74
    this.cancelRequests$ = new Subject<boolean>();
75
    this.authdone$ = new Subject<boolean>();
Chris Hines's avatar
Chris Hines committed
76
    this.openapps = [];
Chris Hines's avatar
Chris Hines committed
77
78
    //this.identitySubject = new BehaviorSubject<Identity>(null);
    //this.appSubject = new BehaviorSubject<Strudelapp>(null);
Chris Hines's avatar
Chris Hines committed
79

80
      this.backendSelectionService.apiserver.subscribe( (value) => { if (value != null) {this.twsproxy = value.tws ; this.Base = value.tes }});
Chris Hines's avatar
Chris Hines committed
81
    timer(500).pipe(repeat()).subscribe(() => this.checkWindows());
82
    // this.batchinterface = {};
Chris Hines's avatar
Chris Hines committed
83
    // this.computesitesService.identities.subscribe(identities => this.startPolling(identities));
Chris Hines's avatar
Chris Hines committed
84
85
 }

Chris Hines's avatar
Chris Hines committed
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public checkWindows() {
  var app: any;
  this.openapps.forEach( (app,index) => {
    if (app.window.closed) {
      if (app.job.state == 'RUNNING') {
        let dialogRef = this.dialog.open(ModaldialogComponent, {
          width: '600px',
          data: app.job,
        });
        //dialogRef.afterClosed().subscribe((job) => {if (job !== null) { console.log('terminate'); console.log(job);}})
        dialogRef.afterClosed().subscribe((job) => {if (job !== null) { this.cancel(job)}});
      }
      this.openapps.splice(index,1);
    }
  })
}

Chris Hines's avatar
Chris Hines committed
103
104
105
public setStatusMsg(statusMsg: BehaviorSubject<any>) {
  this.statusMsg = statusMsg;
}
106

Chris Hines's avatar
Chris Hines committed
107
 private buildParams(app: Strudelapp, identity: Identity, batchinterface: BatchInterface, appinst?: any): string {
108
   let params = new URLSearchParams();
Chris Hines's avatar
Chris Hines committed
109
110
111
   let id = identity.copy_skip_catalog();
   id.site.appCatalog = null;

Chris Hines's avatar
Chris Hines committed
112
113
114
   if (appinst!== null)  {
     params.set('appinstance',JSON.stringify(appinst));
   }
115
116
   params.set('app',JSON.stringify(app));
   params.set('interface',JSON.stringify(batchinterface));
Chris Hines's avatar
Chris Hines committed
117
   params.set('identity',JSON.stringify(id));
118
   return params.toString();
Chris Hines's avatar
Chris Hines committed
119
120
 }

121

122
 updateJoblist(resp, identity: Identity) {
123
124
125
126
127
128
129
130
     // resp contains a javascript represnetiation of a list of jobs
     // We want to update the joblist BUT we don't want to create new Job objects
     // instead we want to reuse existing job objects removing any which are no longer valid
     // and adding any new ones. We also want the list sorted from largest jobid to smallest (oldest job)
     // The sort is lexographic since sometimes jobids are a string rather than a number
   
   var joblist: Job[] = []
   var jobquery: Job[] = <Job[]>resp;
Chris Hines's avatar
Chris Hines committed
131
   var lastjoblist: Job[] = identity.joblist.value;
132
133
   var qjobids: any[] = [];
   var jobids: any[] = [];
Chris Hines's avatar
Chris Hines committed
134
135
136
   var j: Job;
   var newjob: Job;
   var idx: number;
137
138
139
140
141
142
143

   for (j of jobquery) {
       qjobids.push(j.jobid);
   }
   for (j of lastjoblist) {
       if (qjobids.indexOf(j.jobid) != -1) {
           if (jobids.indexOf(j.jobid) == -1) {
Chris Hines's avatar
Chris Hines committed
144
145
146
147
148
149
               idx = qjobids.indexOf(j.jobid)
               newjob = jobquery[idx]
               // These values in the job may change, but we need to keep using the old object
               j.state  = newjob.state;
               j.endtime = newjob.endtime;
               j.batch_host = newjob.batch_host;
150
151
152
153
               joblist.push(j);
               jobids.push(j.jobid);
           }
       }
154
   }
155
156
157
158
159
160
161
162
   for (j of jobquery) {
       if (jobids.indexOf(j.jobid) == -1) {
           joblist.push(j);
           jobids.push(j.jobid);
       }
   }
       
   for (j of joblist) {
163
164
     if (j.app === undefined || j.app == null) {
       j.app = this.strudelappsService.getApp(j.appname,identity.site.appCatalog.value);
165
166
167
168
169
170
171
172
173
     }
     if (j.identity == undefined) {
        j.identity = identity;
     }
     if (j.connectionState == undefined) {
         j.connectionState = 0;
     }
   }
   joblist = joblist.sort((a,b) =>  (a.jobid < b.jobid)? 1:-1);
Chris Hines's avatar
Chris Hines committed
174
   identity.joblist.next(joblist);
Chris Hines's avatar
Chris Hines committed
175
   //this.statusMsg.next(null);
176
177
 }

Chris Hines's avatar
Chris Hines committed
178
179
 private getBatchInterfaceError(error: any) {
   console.error(error);
180
181
 }

182
183


Chris Hines's avatar
Chris Hines committed
184
185
 private getCachetIncidentsError(error: any, identity: Identity) {
   return this.getJobsError(error, identity)
Chris Hines's avatar
Chris Hines committed
186
 }
187

Chris Hines's avatar
Chris Hines committed
188
189
 
 public cancelHealthRequests() {
190
    this.cancelRequests$.next(true);
Chris Hines's avatar
Chris Hines committed
191
192
 }

Chris Hines's avatar
Chris Hines committed
193
 private getUserHealthError(error: any, identity: Identity) {
194
   identity.accountalerts.next([]);
Chris Hines's avatar
Chris Hines committed
195
196
   this.statusMsg.next("There was an error checking your user account");
   //return this.getJobsError(error,identity)
Chris Hines's avatar
Chris Hines committed
197
198
 }

Chris Hines's avatar
Chris Hines committed
199
200
 private getJobsError(error: any, identity: Identity) {
   identity.joblist.next([]);
Chris Hines's avatar
Chris Hines committed
201
   if (error.status == 0) {
Chris Hines's avatar
Chris Hines committed
202
     this.statusMsg.next("A network error occurred. Please try again later");
Chris Hines's avatar
Chris Hines committed
203
     return
204
   }
205
   console.error(error);
206
   if (error.status == 401) {
Chris Hines's avatar
Chris Hines committed
207
     this.statusMsg.next("Login expired. Please log in again.");
208
     this.authorisationService.updateAgentContents();
Chris Hines's avatar
Chris Hines committed
209
     return
210
   }
211
212
213
214
215
216
   if (error.status == 400) {
       if (error.error !== undefined && error.error.message !== undefined) {
        this.statusMsg.next(error.error.message);
       }
       return
   }
Chris Hines's avatar
Chris Hines committed
217
218
 }

Chris Hines's avatar
Chris Hines committed
219
220
221
222
 getJobs(identity: Identity) {
  let headers = new HttpHeaders();
  let options = { headers: headers, withCredentials: true};
  // remove from the job list any jobs for identities that we don't know about
Chris Hines's avatar
Chris Hines committed
223
  let oldjobs = identity.joblist.value;
224
225
226
  let bi = new BatchInterface();
  bi.cancelcmd = identity.site.cancelcmd;
  bi.statcmd = identity.site.statcmd;
227
228
229
230
231
  let params = new URLSearchParams();
  params.set('statcmd',JSON.stringify(identity.site.statcmd));
  params.set('host',JSON.stringify(identity.site.host));
  params.set('username',JSON.stringify(identity.username));
  
232
  this.updateJobSub = this.http.get<Job[]>(this.Base+'/stat'+'?'+params.toString(),options)
233
                .subscribe(resp => this.updateJoblist(resp, identity),
Chris Hines's avatar
Chris Hines committed
234
                           error => this.getJobsError(error, identity));
Chris Hines's avatar
Chris Hines committed
235
236
}

Chris Hines's avatar
Chris Hines committed
237
238
239
240
241
242
243
244
245
getUserHealth(identity: Identity)  {
  let headers = new HttpHeaders();
  let options = { headers: headers, withCredentials: true};
  // remove from the job list any jobs for identities that we don't know about
  let params = new URLSearchParams();
  if (identity === undefined || identity === null) {
    return
  }
  if (identity.site.userhealth === undefined) {
246
    identity.accountalerts.next([]);
Chris Hines's avatar
Chris Hines committed
247
248
249
250
251
252
253
    return
  }
  params.set('statcmd',JSON.stringify(identity.site.userhealth));
  params.set('host',JSON.stringify(identity.site.host));
  params.set('username',JSON.stringify(identity.username));
  
  this.updateUserHealthSub = this.http.get<Health[]>(this.Base+'/stat'+'?'+params.toString(),options)
254
    .pipe(takeUntil(this.cancelRequests$))
Chris Hines's avatar
Chris Hines committed
255
256
257
258
    .subscribe(resp => this.addUserHealth(identity,resp), error => this.getUserHealthError(error,identity));
}

getHealthAlerts(identity: Identity) {
259
260
  //identity.accountalerts.next(null);
  //identity.systemalerts.next([]);
Chris Hines's avatar
Chris Hines committed
261
262
  this.getCachetIncidents(identity);
  this.getUserHealth(identity);
Chris Hines's avatar
Chris Hines committed
263
264
265
266
267
268
269
270
271
}

getCachetIncidents(identity: Identity) {
  let headers = new HttpHeaders();
  let options = { headers: headers, withCredentials: false};
  // remove from the job list any jobs for identities that we don't know about
  let params = new URLSearchParams();

  if (identity.site.cacheturis === undefined ||  identity.site.cacheturis.length == 0) {
272
    identity.systemalerts.next([]);
Chris Hines's avatar
Chris Hines committed
273
274
275
276
    return
  }
  for (let uri of identity.site.cacheturis) {
    this.http.get(uri,options)
277
      .pipe(takeUntil(this.cancelRequests$))
Chris Hines's avatar
Chris Hines committed
278
      .subscribe(resp => this.addCachetIncidents(identity,resp), error => this.getCachetIncidentsError(error,identity));
Chris Hines's avatar
Chris Hines committed
279
280
281
  }
}

Chris Hines's avatar
Chris Hines committed
282
addCachetIncidents(identity,resp) {
283
284
  console.log('got an sa response');
  /*let ci = identity.systemalerts.value;
285
286
  if (ci == null) {
    ci = []
287
288
  }*/
  let ci = [];
Chris Hines's avatar
Chris Hines committed
289
290
291
292
293
294
295
296
297
  for (let i of resp.data) {
    if (i.status == 3 || i.status == 4) {
      continue;
    }
    let h = new Health();
    h.stat = 'error';
    h.msg = i.message;
    ci.push(h);
  }
298
  identity.systemalerts.next(ci);
299
300
301
  console.log('update system alerts for ',identity.displayName());
  console.log(identity.systemalerts.value);
  console.log(identity.systemalerts);
Chris Hines's avatar
Chris Hines committed
302
303
304
}

addUserHealth(identity,resp) {
305
  /*let ci = identity.accountalerts.value;
306
307
  if (ci == null) {
    ci = []
308
309
  }*/
  let ci = []
Chris Hines's avatar
Chris Hines committed
310
  for (let i of resp) {
Chris Hines's avatar
Chris Hines committed
311
312
313
    let h = new Health();
    h.stat = i.stat;
    h.msg = i.message;
314
315
316
    if (i.title != undefined) {
      h.title = i.title;
    }
Chris Hines's avatar
Chris Hines committed
317
318
319
    if (i.type != undefined) {
      h.type = i.type;
      h.data = i.data;
Chris Hines's avatar
Chris Hines committed
320
    }
Chris Hines's avatar
Chris Hines committed
321
    ci.push(h);
Chris Hines's avatar
Chris Hines committed
322
  }
323
  console.log('update account alerts for ',identity.displayName());
324
  identity.accountalerts.next(ci);
325
326
  console.log(identity.accountalerts.value);
  console.log(identity.accountalerts);
Chris Hines's avatar
Chris Hines committed
327
328
329
330
}



331
 public getconfig(app: Strudelapp, identity: Identity): Observable<any> {
Chris Hines's avatar
Chris Hines committed
332
333
334
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
   return this.http.get<any>(identity.site.url+'getconfig/'+app.name,options)
Chris Hines's avatar
Chris Hines committed
335
                                        // .pipe(catchError(this.handleError))
Chris Hines's avatar
Chris Hines committed
336
337
}

Chris Hines's avatar
Chris Hines committed
338
339
 submissionError(error: any) {
   if (error.status != 0) {
340
341
342
343
344
       if ('error' in error && 'message' in error.error) {
         this.statusMsg.next(error.error.message);
       } else {
         this.statusMsg.next('Job submission failed');
       }
Chris Hines's avatar
Chris Hines committed
345
346
   }
 }
347
348
349
350
351
352
353
354
355
356
 cancelError(error: any) {
     console.log('error canceling job',error);
   if (error.status != 0) {
       if ('error' in error && 'message' in error.error) {
         this.statusMsg.next(error.error.message);
       } else {
         this.statusMsg.next('Job submission failed');
       }
   }
 }
Chris Hines's avatar
Chris Hines committed
357
358
359

 buildBody(app: Strudelapp, appparams?: string) {
   return JSON.stringify({'app': app, 'appparams': appparams});
Chris Hines's avatar
Chris Hines committed
360
361
 }

362
 submit(app: Strudelapp, identity: Identity, batchinterface: BatchInterface, appparams?: any) {
Chris Hines's avatar
Chris Hines committed
363
364
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
365
   this.statusMsg.next('Submitting job');
Chris Hines's avatar
Chris Hines committed
366
   let paramstr = this.buildParams(app,identity,batchinterface);
Chris Hines's avatar
Chris Hines committed
367
   // let body = this.buildBody(app,appparams)
368
369
370
371
372
373
374
   let keys = JSON.stringify(this.authorisationService.getKeys());
   let loggedin = JSON.stringify(this.authorisationService.loggedInAuthZ.value);
   let ids = [];
     for (let id of this.computesitesService.ftidentities.value) {
         ids.push(id.copy_skip_catalog())
     }
   let body = {'app': app, 'appparams': appparams, 'keys': keys, 'ids': JSON.stringify(JSON.stringify(ids))}
Chris Hines's avatar
Chris Hines committed
375
   this.http.post<any>(this.Base+'/submit'+'?'+paramstr, body, options)
376
                                          .subscribe(resp => {
Chris Hines's avatar
Chris Hines committed
377
378
379
                                                              this.statusMsg.next(null)
                                                            },
                                                    error => this.submissionError(error));
Chris Hines's avatar
Chris Hines committed
380
381
 }

Chris Hines's avatar
Chris Hines committed
382
383
 submitted(resp: any, identity: Identity ) {
   this.getJobs(identity);
384
385
 }

386
 cancel(job: Job) {
Chris Hines's avatar
Chris Hines committed
387
388
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
Chris Hines's avatar
Chris Hines committed
389
   // this.statusMsg.next(null);
Chris Hines's avatar
Chris Hines committed
390
   let data = {};
391
392
393
394
   let bi = new BatchInterface();
   bi.statcmd = job.identity.site.statcmd;
   bi.cancelcmd = job.identity.site.cancelcmd;
   let paramstr = this.buildParams(job.app,job.identity,bi);
395
   this.http.delete<any>(this.Base+'/cancel/'+job.jobid+'?'+paramstr, options)
396
397
         .subscribe(resp => this.submitted(resp,job.identity),
                    error => this.cancelError(error));
Chris Hines's avatar
Chris Hines committed
398
399
400
401
402
403
 }

 public watchAppwindow(appwindow, dialogRef) {
   if (appwindow.closed) {
     dialogRef.close();
   }
Chris Hines's avatar
Chris Hines committed
404
405
 }

406
 public getAppInstance(job: Job, action: AppAction) {
Chris Hines's avatar
Chris Hines committed
407
408
409
   let username = job.identity.username;
   let loginhost = job.identity.site.host;
   let batchhost = job.batch_host;
Chris Hines's avatar
Chris Hines committed
410
   let jobid = job.jobid;
Chris Hines's avatar
Chris Hines committed
411
   let params = new URLSearchParams;
412
413
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
414
   params.set('cmd',JSON.stringify(action.paramscmd));
Chris Hines's avatar
Chris Hines committed
415
416
   let paramstr = params.toString();
   job.connectionState = 1;
Chris Hines's avatar
Chris Hines committed
417
   this.http.get<string>(this.Base+'/appinstance/'+username+'/'+loginhost+'/'+batchhost+'/'+jobid+'?'+paramstr, options)
Chris Hines's avatar
Chris Hines committed
418
                // .pipe(catchError(this.handleError))
419
     .subscribe(resp =>  { console.log('ran',action.paramscmd,'got',resp); job.appinst = resp; if (action.client != null) { this.createTunnel(job, action) } else { job.connectionState = 0} },
420
             error => { this.handleAppInstanceError(job,error) })
Chris Hines's avatar
Chris Hines committed
421
422
423
424
425
426
427
428
   // let paramstr = this.buildParams(job.app,job.identity,this.batchinterface[job.identity.repr()]);
   // let headers = new HttpHeaders();
   // let options = { headers: headers, withCredentials: true};
   // this.http.get<any>(this.Base+'/getappinst/'+paramstr,options)
   //                                      .pipe(catchError(this.handleError))
   //                                      .subscribe(resp =>  { job.appinst = <StrudelappInstance>resp; this.openAppWindow(job);});
 }

429
 public createTunnel(job: Job, action: AppAction) {
Chris Hines's avatar
Chris Hines committed
430
431
432
433
434
435
   let username = job.identity.username;
   let loginhost = job.identity.site.host;
   let batchhost = job.batch_host;
   let params = new URLSearchParams;
   let headers = new HttpHeaders();
   job.connectionState = 2;
436
437
438
439
440

     //    let sleepDuration = 20;
     //var now = new Date().getTime();
     //while(new Date().getTime() < now + sleepDuration){ /* do nothing */ }

Chris Hines's avatar
Chris Hines committed
441
   let options = { headers: headers, withCredentials: true};
442
   //params.set('cmd',JSON.stringify(action.paramscmd));
Chris Hines's avatar
Chris Hines committed
443
444
   let paramstr = params.toString();
   this.http.post<string>(this.Base+'/createtunnel/'+username+'/'+loginhost+'/'+batchhost+'?'+paramstr, job.appinst, options)
445
     .subscribe(() =>  { this.getAppUrl(job, action) }, error => {this.handleTunnelError(job,error)} )
Chris Hines's avatar
Chris Hines committed
446
 }
Chris Hines's avatar
Chris Hines committed
447

448
 public getAppUrl(job: Job, action: AppAction) {
Chris Hines's avatar
Chris Hines committed
449
450
451
   let params = new URLSearchParams;
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
452
453
   let pseudoapp = {'client':{'redir':action.client.redir}};
   params.set('app',JSON.stringify(pseudoapp));
Chris Hines's avatar
Chris Hines committed
454
455
456
457
458
   params.set('appinst',JSON.stringify(job.appinst));
   let paramstr = params.toString();
   job.connectionState = 3;
   this.http.get<string>(this.Base+'/appurl?'+paramstr,options)
                .pipe(catchError(this.handleError))
459
                .subscribe(resp => {  job.connectionState = 0; this.openAppWindow(resp,job,action)});
Chris Hines's avatar
Chris Hines committed
460
461
462
463


 }

464
465
 public getInterface(job: Job, action: AppAction) {
   this.getAppInstance(job, action);
Chris Hines's avatar
Chris Hines committed
466
467
 }

468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
 private windowLoaded(window: any, location: string): boolean {
   console.log('in windowloaded testing');
   console.log(window.location);
   try {
     if (window.location.toString() == location) {
       console.log('location match');
       return true;
     }
     console.log('location not match');
     return false;
   } catch {
     return true;
   }
 }

483
 public openAppWindow(url: any, job: Job, action: AppAction) {
484
485
     var re = /^https:\/\/([a-z0-9\.-]+)\/?/;
     let twshost = this.twsproxy.replace(re,"$1");
486
     console.log('in openappwindow url is ',url);
487
     let windowloc = url.replace(/\{twsproxy\}/g,this.twsproxy).replace(/twshost/g,twshost);
488
489
490
491
492
493
     console.log('window loc is',windowloc);
     var authwindow = null;
   
     if (action.client.usebasicauth) {
       let authwindowloc = windowloc.replace(/^https:\/\//,'https://'+job.appinst.username+':'+job.appinst.password+'@');
       authwindow = window.open(authwindowloc);
494
495
496
497
       this.authdone$.pipe(take(1)).subscribe( () => { authwindow.close() ; this.finishAppWindow(windowloc,job) });
       interval(500).pipe(takeUntil(this.authdone$),filter((v) => this.windowLoaded(authwindow, authwindowloc))).subscribe( () => this.authdone$.next(true));
     } else {
       this.finishAppWindow(windowloc,job);
498
     }
499
 }
Chris Hines's avatar
Chris Hines committed
500

501
   public finishAppWindow(windowloc: any, job:Job) {
Chris Hines's avatar
Chris Hines committed
502
503
     let appwindow = window.open(windowloc);
     if (appwindow == null) {
504
505
506
         this.statusMsg.next('It looks like a window failed to open. Please check your popup blocker settings (Strudel 2 needs to be able to open a window to your application');
         return;
     }
Chris Hines's avatar
Chris Hines committed
507
508
     if (appwindow.closed) {
       return
509
     }
Chris Hines's avatar
Chris Hines committed
510
     this.openapps.push({'window':appwindow,'job':job})
Chris Hines's avatar
Chris Hines committed
511
512
 }

513
 public connect(job: Job, action: AppAction, appinst?: any) {
Chris Hines's avatar
Chris Hines committed
514
515
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
516
517
518
   console.log('called connect');
   console.log(action);
   console.log(job);
519
   this.getInterface(job,action); // getInterface will subsequently called getAppInstance, which will call createTunnel, which will openAppWindow
Chris Hines's avatar
Chris Hines committed
520
 }
Chris Hines's avatar
Chris Hines committed
521

Chris Hines's avatar
Chris Hines committed
522
523
524
525



public postAgentData(keyCert: KeyCert) {
526
527
528
529
  let headers = new HttpHeaders();
  let options = { headers: headers, withCredentials: true};

  this.statusMsg.next("Authorising ...")
Chris Hines's avatar
Chris Hines committed
530
  let data = {'key': keyCert.key, 'cert': keyCert.cert};
Chris Hines's avatar
Chris Hines committed
531
  return this.http.post<any>(this.Base+'/sshagent',data,options)
Chris Hines's avatar
Chris Hines committed
532
                      .pipe(catchError(this.handleError))
Chris Hines's avatar
Chris Hines committed
533

534
535
}

Chris Hines's avatar
Chris Hines committed
536
public postMkDir(id: Identity, path: string, name: string ) {
537
538
  let headers = new HttpHeaders();
  let options = { headers: headers, withCredentials: true};
Chris Hines's avatar
Chris Hines committed
539
540
541
542
  let params = new URLSearchParams;
  params.set('identity',JSON.stringify(id));
  params.set('path',JSON.stringify(path));

543
  return this.http.post<any>(this.Base+'/mkdir?'+params.toString(),name, options);
544
545
}

Chris Hines's avatar
Chris Hines committed
546
public getSftpData(id: Identity, path: string, cd: string ) {
547
548
  let headers = new HttpHeaders();
  let options = { headers: headers, withCredentials: true};
Chris Hines's avatar
Chris Hines committed
549
550
551
552
  let params = new URLSearchParams;
  params.set('identity',JSON.stringify(id));
  params.set('path',JSON.stringify(path));
  params.set('cd',JSON.stringify(cd));
553

Chris Hines's avatar
Chris Hines committed
554
  return this.http.get<any>(this.Base+'/ls?'+params.toString(),options)
555
                      // .pipe(catchError(this.handleError))
556

Chris Hines's avatar
Chris Hines committed
557
558
}

Chris Hines's avatar
Chris Hines committed
559
560


561
562
public getstatusMsgSubject(): BehaviorSubject<any> {
  return this.statusMsg;
Chris Hines's avatar
Chris Hines committed
563
564
}

Chris Hines's avatar
Chris Hines committed
565
566
567
568
569
private httperror(errorstr: string) {
  var re = /login expired/gi;
  let searchresult = errorstr.search(re);
  if (searchresult != -1) {
    this.statusMsg.next("Some authentication tokens have expired, you may need to log in again");
570
    console.error(errorstr);
Chris Hines's avatar
Chris Hines committed
571
    this.authorisationService.updateAgentContents();
Chris Hines's avatar
Chris Hines committed
572
  }
573
  console.error(errorstr);
Chris Hines's avatar
Chris Hines committed
574
}
Chris Hines's avatar
Chris Hines committed
575

Chris Hines's avatar
Chris Hines committed
576
577
578
579
580
581
582
583
584
// private networkError(error: HttpErrorResponse) {
//   if (error.error instanceof ErrorEvent) {
//     console.log('network error contacting TES backend');
//   } else {
//     return error;
//   }
// }


585
586
587
588
589
590
591
592
593
594
private handleTunnelError(job: Job, error: any) {
  job.connectionState=0;
  if (error.stats == 500) {
    this.statusMsg.next(error.error.message);
    return;
  }
  this.statusMsg.next('Failed to create a tunnel. Please try again');
  return;
}

595
private handleAppInstanceError(job: Job, error: any) {
Chris Hines's avatar
Chris Hines committed
596
    console.log(error);
597
    job.connectionState=0;
598
599
600
601
    if (error.status == 0) {
      this.statusMsg.next('It appears we had a timeout learning about your '+job.name+'. Its possible '+job.identity.site.name+' is experiencing issues');
      return;
    }
Chris Hines's avatar
Chris Hines committed
602
603
    if (error.error !== undefined && error.error.message !== undefined) {
        this.statusMsg.next(error.error.message);
Chris Hines's avatar
Chris Hines committed
604
605
    } else if (error.error !== undefined && error.error.error !== undefined) {
      this.statusMsg.next(error.error.error.msg);
606
607
608
609
610
    } else {
        this.statusMsg.next(error);
    }
}

Chris Hines's avatar
Chris Hines committed
611
private handleError(error: HttpErrorResponse) {
612
613
  console.error('in handleError');
  console.error(error);
Chris Hines's avatar
Chris Hines committed
614
615
616
617
618
619
620
621
  if (error.error instanceof ErrorEvent) {
    // A client-side or network error occurred. Handle it accordingly.
    return throwError("Hmm, that didn't work. If you're using a local connection, please make sure Strudel-TES is running.");
  } else {
    // Not sure if this code works correctly. It should update identities in case the error is
    // that the user isn't allowed to run the job

    var re = /identity/gi;
Chris Hines's avatar
Chris Hines committed
622
623
624
625
626
627
628
629
630
    if (error.error.message != undefined) {
      let searchresult = error.error.message.search(re);
      if (searchresult != -1) {
        // this.getIdentities();
        return throwError('login expired, refreshing');
      }
      return throwError(error.error.message)
    } else {
      return throwError('An error occured but I\'m not sure exactly what. Please try again latter or contact the sys admins');
Chris Hines's avatar
Chris Hines committed
631
    }
Chris Hines's avatar
Chris Hines committed
632
633
634
    // this.statusMsg.next("There was an error submitting that job. The backend gave me the message: " + error.error.message);

  }
Chris Hines's avatar
Chris Hines committed
635
}
Chris Hines's avatar
Chris Hines committed
636
637
638



Chris Hines's avatar
Chris Hines committed
639
}