Skip to content

Commit 886e1a2

Browse files
Florian LappeFlorian Lappe
authored andcommitted
Merge branch 'development' of https://github.com/cloudiator/user-interface into development
2 parents 151c5ce + 35b6db7 commit 886e1a2

30 files changed

+505
-48
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
"rxjs": "^6.4.0",
4444
"sass-loader": "^7.1.0",
4545
"webpack": "^4.35.3",
46+
"xterm": "^4.1.0",
47+
"xterm-addon-attach": "^0.3.0",
48+
"xterm-addon-fit": "^0.2.1",
4649
"zone.js": "^0.8.29"
4750
},
4851
"devDependencies": {

src/app/app-dialog/app-dialog.module.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from './dialogs';
1010
import {DialogService} from './services/dialog.service';
1111
import {ToastService} from './services/toast.service';
12+
import { SshConsoleDialogComponent } from './dialogs/ssh-console-dialog/ssh-console-dialog.component';
1213

1314
/**
1415
* Main Module handling App DIalogs and Toasts.
@@ -19,7 +20,8 @@ import {ToastService} from './services/toast.service';
1920
ConfirmNewCloudDialogComponent,
2021
DeleteCloudDialogComponent,
2122
DeleteScheduleDialogComponent,
22-
ScheduleDiagnosticDialogComponent
23+
ScheduleDiagnosticDialogComponent,
24+
SshConsoleDialogComponent
2325
],
2426
imports: [
2527
OverlayModule
@@ -33,7 +35,8 @@ import {ToastService} from './services/toast.service';
3335
ConfirmNewCloudDialogComponent,
3436
DeleteCloudDialogComponent,
3537
DeleteScheduleDialogComponent,
36-
ScheduleDiagnosticDialogComponent
38+
ScheduleDiagnosticDialogComponent,
39+
SshConsoleDialogComponent
3740
]
3841
})
3942
export class AppDialogModule {

src/app/app-dialog/dialogs/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import {DeleteCloudDialogComponent} from './delete-cloud-dialog/delete-cloud-dia
33
import {DeleteScheduleDialogComponent} from './delete-schedule-dialog/delete-schedule-dialog.component';
44
import {ScheduleDiagnosticDialogComponent} from './schedule-diagnostic-dialog/schedule-diagnostic-dialog.component';
55
import {ToastComponent} from './toast/toast.component';
6+
import {SshConsoleDialogComponent} from './ssh-console-dialog/ssh-console-dialog.component';
67

78
export {
89
ConfirmNewCloudDialogComponent,
910
DeleteScheduleDialogComponent,
1011
DeleteCloudDialogComponent,
1112
ScheduleDiagnosticDialogComponent,
12-
ToastComponent
13+
ToastComponent,
14+
SshConsoleDialogComponent
1315
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<div class="modal is-active" (window:resize)="onResize()">
2+
<div class="modal-background"></div>
3+
<div class="modal-card">
4+
<header class="modal-card-head">
5+
<p class="modal-card-title">{{data.name}} SSH Terminal</p>
6+
<button class="delete" aria-label="close" (click)="onClose()"></button>
7+
</header>
8+
<section class="modal-card-body">
9+
<div #terminal class="terminal-container"></div>
10+
</section>
11+
<footer class="modal-card-foot">
12+
</footer>
13+
</div>
14+
</div>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
@import "~bulma/sass/utilities/mixins";
2+
3+
.modal-card {
4+
width: 90%;
5+
height: 90%;
6+
}
7+
8+
.modal-card-body {
9+
padding: 0;
10+
}
11+
12+
.terminal-container {
13+
width: 100%;
14+
height: 100%;
15+
background-color: black;
16+
}
17+
18+
.mobile-view {
19+
display: none;
20+
}
21+
22+
@include mobile {
23+
.modal {
24+
padding: 0;
25+
align-items: stretch;
26+
}
27+
28+
.modal-card {
29+
width: 100%;
30+
height: 100%;
31+
max-height: 100vh;
32+
margin: 0;
33+
}
34+
35+
.modal-card-head {
36+
border-radius: 0;
37+
}
38+
39+
.modal-card-foot {
40+
border-radius: 0;
41+
}
42+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
2+
3+
import {SshConsoleDialogComponent} from './ssh-console-dialog.component';
4+
import {AppDialogModule} from '../../app-dialog.module';
5+
import {DialogRef} from '../../model/dialogRef';
6+
import {DIALOG_DATA, DialogService} from '../../services/dialog.service';
7+
import {HttpClientTestingModule} from '@angular/common/http/testing';
8+
import {ApiModule} from 'cloudiator-rest-api';
9+
import {apiConfigFactory} from '../../../app.module';
10+
import {RootStoreModule} from '../../../root-store';
11+
import {ToastService} from '../../services/toast.service';
12+
13+
describe('SshConsoleDialogComponent', () => {
14+
let component: SshConsoleDialogComponent;
15+
let fixture: ComponentFixture<SshConsoleDialogComponent>;
16+
17+
beforeEach(async(() => {
18+
TestBed.configureTestingModule({
19+
declarations: [],
20+
imports: [
21+
RootStoreModule,
22+
HttpClientTestingModule,
23+
ApiModule.forRoot(apiConfigFactory),
24+
AppDialogModule
25+
],
26+
providers: [
27+
{provide: DialogRef, useVale: {}},
28+
{provide: DIALOG_DATA, useValue: {}}
29+
]
30+
})
31+
.compileComponents();
32+
}));
33+
34+
beforeEach(() => {
35+
fixture = TestBed.createComponent(SshConsoleDialogComponent);
36+
component = fixture.componentInstance;
37+
fixture.detectChanges();
38+
});
39+
40+
it('should create', () => {
41+
expect(component).toBeTruthy();
42+
});
43+
});
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import {Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild} from '@angular/core';
2+
import {DialogRef} from '../../model/dialogRef';
3+
import {DIALOG_DATA} from '../../services/dialog.service';
4+
import {AttachAddon} from 'xterm-addon-attach';
5+
import {FitAddon} from 'xterm-addon-fit';
6+
import {Terminal} from 'xterm';
7+
import {ToastService} from '../../services/toast.service';
8+
import {IpAddress, LoginCredential} from 'cloudiator-rest-api';
9+
import {SshService} from '../../../services/ssh.service';
10+
import {take} from 'rxjs/operators';
11+
import {ToastType} from '../../model/toast';
12+
13+
@Component({
14+
selector: 'app-ssh-console-dialog',
15+
templateUrl: './ssh-console-dialog.component.html',
16+
styleUrls: ['./ssh-console-dialog.component.scss']
17+
})
18+
export class SshConsoleDialogComponent implements OnInit, OnDestroy {
19+
20+
@ViewChild('terminal', {static: true}) terminal: ElementRef;
21+
22+
private socket: WebSocket;
23+
24+
private fitAddon = new FitAddon();
25+
26+
constructor(public dialogRef: DialogRef,
27+
@Inject(DIALOG_DATA) public data: { name: string, ipAddress: IpAddress, loginCredential: LoginCredential },
28+
private sshService: SshService,
29+
private toastService: ToastService) {
30+
}
31+
32+
ngOnInit() {
33+
// check if needed values exist
34+
if (!this.data.ipAddress || !this.data.loginCredential) {
35+
this.toastService.show({text: 'Could not connect to Virtual Machine', type: ToastType.DANGER});
36+
} else {
37+
this.sshService.connectTo(this.data.ipAddress, this.data.loginCredential)
38+
.pipe(take(1))
39+
.subscribe(socket => {
40+
this.socket = socket;
41+
42+
this.socket.onclose = ev => {
43+
console.log(ev);
44+
switch (ev.code) {
45+
case 4001:
46+
term.writeln('Authentication Error');
47+
break;
48+
case 4002:
49+
term.writeln('Machine closed connection');
50+
break;
51+
case 4003:
52+
term.writeln(ev.reason);
53+
break;
54+
case 4005:
55+
term.writeln('SSH Tunnel Error');
56+
break;
57+
default:
58+
term.writeln('Connection closed');
59+
}
60+
};
61+
62+
const attachAddon = new AttachAddon(this.socket);
63+
64+
const term = new Terminal();
65+
term.loadAddon(attachAddon);
66+
term.loadAddon(this.fitAddon);
67+
68+
term.open(this.terminal.nativeElement);
69+
70+
this.fitAddon.fit();
71+
});
72+
}
73+
}
74+
75+
ngOnDestroy(): void {
76+
if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {
77+
this.socket.close();
78+
}
79+
}
80+
81+
onClose() {
82+
this.dialogRef.close();
83+
}
84+
85+
onResize() {
86+
this.fitAddon.fit();
87+
}
88+
89+
}

src/app/app-routing.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const routes: Routes = [
2929
{path: 'editor', component: YamlEditorComponent, data: {graphs: false}, canActivate: [AuthGuard]},
3030
{path: 'editor/graphs', component: YamlEditorComponent, data: {graphs: true}, canActivate: [AuthGuard]},
3131

32-
{path: 'schedules', component: SchedulesOverviewComponent, runGuardsAndResolvers: 'always'},
32+
{path: 'schedules', component: SchedulesOverviewComponent, canActivate: [AuthGuard], runGuardsAndResolvers: 'always'},
3333

3434
{path: 'clouds', component: CloudOverviewComponent, canActivate: [AuthGuard]},
3535
{path: '', component: CloudOverviewComponent, canActivate: [AuthGuard]},

src/app/components/app/app.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ export class AppComponent implements OnInit, OnDestroy {
4949

5050
/**
5151
* Adds page reload warning if unsaved changes exist.
52+
* Not needed anymore as the app caches editor state now
5253
*/
5354
@HostListener('window:beforeunload')
5455
unloadNotification() {
55-
// ToDo: reactivate
5656
// return !this.editorHasUnsavedChanges;
5757
}
5858

src/app/components/clouds/cloud-card/cloud-card.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export class CloudCardComponent implements OnInit {
3030
case 'aws-ec2':
3131
return name === 'aws';
3232
case 'openstack4j':
33-
case 'openstack-nove':
33+
case 'openstack-nova':
3434
return name === 'openstack';
3535
case 'google-compute-engine':
3636
return name === 'gcp';

0 commit comments

Comments
 (0)