Add string constants and improve the default catch of statistics

1.  Move ephemeral strings to constants where appropriate
2.  Change the switch to a regex to catch the defaults in stats
3.  Renamed the websocket modules to match with the backend

Change-Id: I9e756bb046c78e6aef393adb83db2d22ebc6a585
This commit is contained in:
Schiefelbein, Andrew 2020-11-02 10:28:14 -06:00
parent 023252615e
commit c13a04669b
29 changed files with 472 additions and 398 deletions

View File

@ -16,10 +16,10 @@ import { Component, OnInit, ViewChild } from '@angular/core';
import { MatAccordion } from '@angular/material/expansion';
import { environment } from 'src/environments/environment';
import { IconService } from 'src/services/icon/icon.service';
import { WebsocketService } from 'src/services/websocket/websocket.service';
import { WsService } from 'src/services/ws/ws.service';
import { Dashboard, WsReceiver, WsMessage, WsConstants } from 'src/services/ws/ws.models';
import { Log } from 'src/services/log/log.service';
import { LogMessage } from 'src/services/log/log-message';
import { Dashboard, WSReceiver, WebsocketMessage } from 'src/services/websocket/websocket.models';
import { Nav } from './app.models';
import { AuthGuard } from 'src/services/auth-guard/auth-guard.service';
@ -29,12 +29,12 @@ import { AuthGuard } from 'src/services/auth-guard/auth-guard.service';
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, WSReceiver {
export class AppComponent implements OnInit, WsReceiver {
@ViewChild(MatAccordion) accordion: MatAccordion;
className = this.constructor.name;
type = 'ui';
component = 'any';
type = WsConstants.UI;
component = WsConstants.ANY;
currentYear: number;
version: string;
@ -79,30 +79,30 @@ export class AppComponent implements OnInit, WSReceiver {
}];
constructor(private iconService: IconService,
private websocketService: WebsocketService) {
private websocketService: WsService) {
this.currentYear = new Date().getFullYear();
this.version = environment.version;
this.websocketService.registerFunctions(this);
}
async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
this.websocketService.printIfToast(message);
} else {
switch (message.component) {
case 'log':
case WsConstants.LOG:
Log.Debug(new LogMessage('Log message received in app', this.className, message));
const panel = document.getElementById('logPanel');
panel.appendChild(document.createTextNode(message.message));
panel.appendChild(document.createElement('br'));
break;
case 'initialize':
case WsConstants.INITIALIZE:
Log.Debug(new LogMessage('Initialize message received in app', this.className, message));
if (message.hasOwnProperty('dashboards')) {
this.updateDashboards(message.dashboards);
}
break;
case 'keepalive':
case WsConstants.KEEPALIVE:
Log.Debug(new LogMessage('Keepalive message received in app', this.className, message));
break;
default:

View File

@ -18,9 +18,9 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { RouterModule } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { WebsocketService } from '../services/websocket/websocket.service';
import { WsService } from '../services/ws/ws.service';
import { ToastrModule } from 'ngx-toastr';
import { MonacoEditorModule, NgxMonacoEditorConfig } from 'ngx-monaco-editor';
import { MonacoEditorModule } from 'ngx-monaco-editor';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatIconModule } from '@angular/material/icon';
import { MatExpansionModule } from '@angular/material/expansion';
@ -67,7 +67,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
MatTooltipModule
],
declarations: [AppComponent, TaskComponent],
providers: [WebsocketService],
providers: [WsService],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})

View File

@ -13,10 +13,10 @@
*/
import { Component, OnInit, ViewChild } from '@angular/core';
import { WebsocketService } from '../../../services/websocket/websocket.service';
import { WebsocketMessage, WSReceiver } from '../../../services/websocket/websocket.models';
import { Log } from '../../../services/log/log.service';
import { LogMessage } from '../../../services/log/log-message';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsReceiver, WsConstants } from 'src/services/ws/ws.models';
import { Log } from 'src/services/log/log.service';
import { LogMessage } from 'src/services/log/log-message';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
@ -29,11 +29,10 @@ import { NodeData, PhaseData } from './baremetal.models';
styleUrls: ['./baremetal.component.css']
})
export class BaremetalComponent implements WSReceiver, OnInit {
export class BaremetalComponent implements WsReceiver, OnInit {
className = this.constructor.name;
// TODO (aschiefe): extract these strings to constants
type = 'ctl';
component = 'baremetal';
type = WsConstants.CTL;
component = WsConstants.BAREMETAL;
nodeColumns: string[] = ['select', 'name', 'id', 'bmcAddress'];
nodeDataSource: MatTableDataSource<NodeData> = new MatTableDataSource();
@ -47,16 +46,16 @@ export class BaremetalComponent implements WSReceiver, OnInit {
@ViewChild('phaseTableSort', { static: false }) phaseSort: MatSort;
@ViewChild('phasePaginator', { static: false }) phasePaginator: MatPaginator;
constructor(private websocketService: WebsocketService) {
constructor(private websocketService: WsService) {
this.websocketService.registerFunctions(this);
}
async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
this.websocketService.printIfToast(message);
} else {
switch (message.subComponent) {
case 'getDefaults':
case WsConstants.GET_DEFAULTS:
this.pushData(message.data);
break;
default:
@ -67,7 +66,7 @@ export class BaremetalComponent implements WSReceiver, OnInit {
}
ngOnInit(): void {
const message = new WebsocketMessage(this.type, this.component, 'getDefaults');
const message = new WsMessage(this.type, this.component, WsConstants.GET_DEFAULTS);
Log.Debug(new LogMessage('Attempting to ask for node data', this.className, message));
this.websocketService.sendMessage(message);
}
@ -178,7 +177,7 @@ export class BaremetalComponent implements WSReceiver, OnInit {
// retrieve the targets to run the action against
// create the websocket message & fire the request to the backend
const message = new WebsocketMessage(this.type, this.component, subComponent);
const message = new WsMessage(this.type, this.component, subComponent);
const displaying = (document.getElementById('displaySelect') as HTMLInputElement).value;
const targets: string[] = new Array();
if (displaying === 'node') {

View File

@ -13,10 +13,10 @@
*/
import { Component } from '@angular/core';
import { WebsocketService } from '../../../services/websocket/websocket.service';
import { WebsocketMessage, WSReceiver } from '../../../services/websocket/websocket.models';
import { Log } from '../../../services/log/log.service';
import { LogMessage } from '../../../services/log/log-message';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsReceiver, WsConstants } from 'src/services/ws/ws.models';
import { Log } from 'src/services/log/log.service';
import { LogMessage } from 'src/services/log/log-message';
@Component({
selector: 'app-cluster',
@ -24,18 +24,17 @@ import { LogMessage } from '../../../services/log/log-message';
styleUrls: ['./cluster.component.css']
})
export class ClusterComponent implements WSReceiver {
export class ClusterComponent implements WsReceiver {
className = this.constructor.name;
// TODO (aschiefe): extract these strings to constants
type = 'ctl';
component = 'cluster';
type = WsConstants.CTL;
component = WsConstants.CLUSTER;
constructor(private websocketService: WebsocketService) {
constructor(private websocketService: WsService) {
this.websocketService.registerFunctions(this);
}
async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
this.websocketService.printIfToast(message);
} else {
switch (message.subComponent) {

View File

@ -14,9 +14,9 @@
import { Component, OnInit, Input } from '@angular/core';
import { Context, ContextOptions } from '../config.models';
import { WebsocketService } from '../../../../services/websocket/websocket.service';
import { WsService } from 'src/services/ws/ws.service';
import { FormControl } from '@angular/forms';
import { WebsocketMessage } from 'src/services/websocket/websocket.models';
import { WsMessage, WsConstants } from 'src/services/ws/ws.models';
@Component({
selector: 'app-config-context',
@ -25,8 +25,8 @@ import { WebsocketMessage } from 'src/services/websocket/websocket.models';
})
export class ConfigContextComponent implements OnInit {
@Input() context: Context;
type = 'ctl';
component = 'config';
type = WsConstants.CTL;
component = WsConstants.CONFIG;
locked = true;
@ -38,7 +38,7 @@ export class ConfigContextComponent implements OnInit {
controlsArray = [this.name, this.contextKubeconf, this.manifest, this.managementConfiguration, this.encryptionConfig];
constructor(private websocketService: WebsocketService) {}
constructor(private websocketService: WsService) {}
ngOnInit(): void {
this.name.setValue(this.context.name);
@ -68,7 +68,7 @@ export class ConfigContextComponent implements OnInit {
EncryptionConfig: this.encryptionConfig.value,
};
const msg = new WebsocketMessage(this.type, this.component, 'setContext');
const msg = new WsMessage(this.type, this.component, WsConstants.SET_CONTEXT);
msg.data = JSON.parse(JSON.stringify(opts));
msg.name = this.name.value;
@ -77,7 +77,7 @@ export class ConfigContextComponent implements OnInit {
}
useContext(name: string): void {
const msg = new WebsocketMessage(this.type, this.component, 'useContext');
const msg = new WsMessage(this.type, this.component, WsConstants.USE_CONTEXT);
msg.name = name;
this.websocketService.sendMessage(msg);
}

View File

@ -15,8 +15,8 @@
import { Component, OnInit, Input } from '@angular/core';
import { FormControl } from '@angular/forms';
import { EncryptionConfig, EncryptionConfigOptions } from '../config.models';
import { WebsocketService } from '../../../../services/websocket/websocket.service';
import { WebsocketMessage } from '../../../../services/websocket/websocket.models';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsConstants } from 'src/services/ws/ws.models';
@Component({
selector: 'app-config-encryption',
@ -25,8 +25,8 @@ import { WebsocketMessage } from '../../../../services/websocket/websocket.model
})
export class ConfigEncryptionComponent implements OnInit {
@Input() config: EncryptionConfig;
type = 'ctl';
component = 'config';
type = WsConstants.CTL;
component = WsConstants.CONFIG;
locked = true;
name = new FormControl({value: '', disabled: true});
@ -37,7 +37,7 @@ export class ConfigEncryptionComponent implements OnInit {
controlsArray = [this.encryptionKeyPath, this.decryptionKeyPath, this.keySecretName, this.keySecretNamespace];
constructor(private websocketService: WebsocketService) {}
constructor(private websocketService: WsService) {}
ngOnInit(): void {
this.name.setValue(this.config.name);
@ -68,7 +68,7 @@ export class ConfigEncryptionComponent implements OnInit {
KeySecretNamespace: this.keySecretNamespace.value,
};
const msg = new WebsocketMessage(this.type, this.component, 'setEncryptionConfig');
const msg = new WsMessage(this.type, this.component, WsConstants.SET_ENCRYPTION_CONFIG);
msg.data = JSON.parse(JSON.stringify(opts));
msg.name = this.name.value;

View File

@ -14,8 +14,8 @@
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { WebsocketService } from '../../../../services/websocket/websocket.service';
import { WebsocketMessage } from '../../../../services/websocket/websocket.models';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsConstants } from 'src/services/ws/ws.models';
@Component({
selector: 'app-config-init',
@ -23,22 +23,22 @@ import { WebsocketMessage } from '../../../../services/websocket/websocket.model
styleUrls: ['./config-init.component.css']
})
export class ConfigInitComponent {
type = 'ctl';
component = 'config';
type = WsConstants.CTL;
component = WsConstants.CONFIG;
initValue = new FormControl('');
specifyValue = new FormControl('');
constructor(private websocketService: WebsocketService) {}
constructor(private websocketService: WsService) {}
initAirshipConfig(): void {
const msg = new WebsocketMessage(this.type, this.component, 'init');
const msg = new WsMessage(this.type, this.component, WsConstants.INIT);
msg.message = this.initValue.value;
this.websocketService.sendMessage(msg);
}
setAirshipConfig(): void {
const msg = new WebsocketMessage(this.type, this.component, 'setAirshipConfig');
const msg = new WsMessage(this.type, this.component, WsConstants.SET_AIRSHIP_CONFIG);
msg.message = this.specifyValue.value;
this.websocketService.sendMessage(msg);
}

View File

@ -15,8 +15,8 @@
import { Component, Input, OnInit } from '@angular/core';
import { ManagementConfig } from '../config.models';
import { FormControl, Validators } from '@angular/forms';
import { WebsocketService } from 'src/services/websocket/websocket.service';
import { WebsocketMessage } from 'src/services/websocket/websocket.models';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsConstants } from 'src/services/ws/ws.models';
@Component({
selector: 'app-config-management',
@ -25,8 +25,8 @@ import { WebsocketMessage } from 'src/services/websocket/websocket.models';
})
export class ConfigManagementComponent implements OnInit {
@Input() config: ManagementConfig;
msgType = 'ctl';
component = 'config';
msgType = WsConstants.CTL;
component = WsConstants.CONFIG;
locked = true;
@ -39,7 +39,7 @@ export class ConfigManagementComponent implements OnInit {
controlsArray = [this.name, this.insecure, this.systemRebootDelay, this.systemActionRetries, this.type, this.useproxy];
constructor(private websocketService: WebsocketService) { }
constructor(private websocketService: WsService) { }
ngOnInit(): void {
this.name.setValue(this.config.name);
@ -63,7 +63,7 @@ export class ConfigManagementComponent implements OnInit {
}
setManagementConfig(): void {
const msg = new WebsocketMessage(this.msgType, this.component, 'setManagementConfig');
const msg = new WsMessage(this.msgType, this.component, WsConstants.SET_MANAGEMENT_CONFIG);
msg.name = this.name.value;
const cfg: ManagementConfig = {

View File

@ -15,8 +15,8 @@
import { Component, Input, OnInit } from '@angular/core';
import { Manifest, ManifestOptions, Repository } from '../config.models';
import { FormControl } from '@angular/forms';
import { WebsocketService } from 'src/services/websocket/websocket.service';
import { WebsocketMessage } from 'src/services/websocket/websocket.models';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsConstants } from 'src/services/ws/ws.models';
@Component({
@ -27,8 +27,8 @@ import { WebsocketMessage } from 'src/services/websocket/websocket.models';
export class ConfigManifestComponent implements OnInit {
@Input() manifest: Manifest;
type = 'ctl';
component = 'config';
type = WsConstants.CTL;
component = WsConstants.CONFIG;
locked = true;
Name = new FormControl({value: '', disabled: true});
@ -59,7 +59,7 @@ export class ConfigManifestComponent implements OnInit {
this.MetadataPath
];
constructor(private websocketService: WebsocketService) { }
constructor(private websocketService: WsService) { }
ngOnInit(): void {
this.Name.setValue(this.manifest.name);
@ -97,7 +97,7 @@ export class ConfigManifestComponent implements OnInit {
}
setManifest(): void {
const msg = new WebsocketMessage(this.type, this.component, 'setManifest');
const msg = new WsMessage(this.type, this.component, WsConstants.SET_MANIFEST);
msg.name = this.manifest.name;
// TODO(mfuller): since "Force" and "IsPhase" can only be set by passing in

View File

@ -13,22 +13,21 @@
*/
import { Component, OnInit } from '@angular/core';
import { WebsocketService } from '../../../services/websocket/websocket.service';
import { WebsocketMessage, WSReceiver } from '../../../services/websocket/websocket.models';
import { Log } from '../../../services/log/log.service';
import { LogMessage } from '../../../services/log/log-message';
import { Log } from 'src/services/log/log.service';
import { LogMessage } from 'src/services/log/log-message';
import { Context, ManagementConfig, Manifest, EncryptionConfig } from './config.models';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsReceiver, WsConstants } from 'src/services/ws/ws.models';
@Component({
selector: 'app-bare-metal',
templateUrl: './config.component.html',
})
export class ConfigComponent implements WSReceiver, OnInit {
export class ConfigComponent implements WsReceiver, OnInit {
className = this.constructor.name;
// TODO (aschiefe): extract these strings to constants
type = 'ctl';
component = 'config';
type = WsConstants.CTL;
component = WsConstants.CONFIG;
airshipConfigPath: string;
@ -38,7 +37,7 @@ export class ConfigComponent implements WSReceiver, OnInit {
managementConfigs: ManagementConfig[] = [];
encryptionConfigs: EncryptionConfig[] = [];
constructor(private websocketService: WebsocketService) {
constructor(private websocketService: WsService) {
this.websocketService.registerFunctions(this);
}
@ -46,50 +45,50 @@ export class ConfigComponent implements WSReceiver, OnInit {
this.getConfig();
}
async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
this.websocketService.printIfToast(message);
} else {
switch (message.subComponent) {
case 'init':
case WsConstants.INIT:
this.websocketService.printIfToast(message);
this.getConfig();
break;
case 'setAirshipConfig':
case WsConstants.SET_AIRSHIP_CONFIG:
this.websocketService.printIfToast(message);
this.getConfig();
break;
case 'getAirshipConfigPath':
case WsConstants.GET_AIRSHIP_CONFIG_PATH:
this.airshipConfigPath = message.message;
break;
case 'getCurrentContext':
case WsConstants.GET_CURRENT_CONTEXT:
this.currentContext = message.message;
break;
case 'getContexts':
case WsConstants.GET_CONTEXTS:
Object.assign(this.contexts, message.data);
break;
case 'getManifests':
case WsConstants.GET_MANIFESTS:
Object.assign(this.manifests, message.data);
break;
case 'getEncryptionConfigs':
case WsConstants.GET_ENCRYPTION_CONFIGS:
Object.assign(this.encryptionConfigs, message.data);
break;
case 'getManagementConfigs':
case WsConstants.GET_MANAGEMENT_CONFIGS:
Object.assign(this.managementConfigs, message.data);
break;
case 'useContext':
case WsConstants.USE_CONTEXT:
this.getCurrentContext();
break;
case 'setContext':
case WsConstants.SET_CONTEXT:
this.websocketService.printIfToast(message);
break;
case 'setEncryptionConfig':
case WsConstants.SET_ENCRYPTION_CONFIG:
this.websocketService.printIfToast(message);
break;
case 'setManifest':
case WsConstants.SET_MANIFEST:
this.websocketService.printIfToast(message);
break;
case 'setManagementConfig':
case WsConstants.SET_MANAGEMENT_CONFIG:
this.websocketService.printIfToast(message);
break;
default:
@ -109,38 +108,38 @@ export class ConfigComponent implements WSReceiver, OnInit {
}
getAirshipConfigPath(): void {
this.websocketService.sendMessage(new WebsocketMessage(
this.websocketService.sendMessage(new WsMessage(
this.type, this.component, 'getAirshipConfigPath')
);
}
getCurrentContext(): void {
this.websocketService.sendMessage(new WebsocketMessage(
this.type, this.component, 'getCurrentContext')
this.websocketService.sendMessage(new WsMessage(
this.type, this.component, WsConstants.GET_CURRENT_CONTEXT)
);
}
getContexts(): void {
this.websocketService.sendMessage(new WebsocketMessage(
this.type, this.component, 'getContexts')
this.websocketService.sendMessage(new WsMessage(
this.type, this.component, WsConstants.GET_CONTEXTS)
);
}
getManifests(): void {
this.websocketService.sendMessage(new WebsocketMessage(
this.type, this.component, 'getManifests')
this.websocketService.sendMessage(new WsMessage(
this.type, this.component, WsConstants.GET_MANIFESTS)
);
}
getEncryptionConfigs(): void {
this.websocketService.sendMessage(new WebsocketMessage(
this.type, this.component, 'getEncryptionConfigs')
this.websocketService.sendMessage(new WsMessage(
this.type, this.component, WsConstants.GET_ENCRYPTION_CONFIGS)
);
}
getManagementConfigs(): void {
this.websocketService.sendMessage(new WebsocketMessage(
this.type, this.component, 'getManagementConfigs')
this.websocketService.sendMessage(new WsMessage(
this.type, this.component, WsConstants.GET_MANAGEMENT_CONFIGS)
);
}
}

View File

@ -21,35 +21,35 @@ import { DocumentComponent } from './document/document.component';
import { ImageComponent } from './image/image.component';
import { PhaseComponent } from './phase/phase.component';
import { SecretComponent } from './secret/secret.component';
import { WsConstants } from 'src/services/ws/ws.models';
import { AuthGuard } from 'src/services/auth-guard/auth-guard.service';
const routes: Routes = [{
path: 'baremetal',
path: WsConstants.BAREMETAL,
canActivate: [AuthGuard],
component: BaremetalComponent
}, {
path: 'cluster',
path: WsConstants.CLUSTER,
canActivate: [AuthGuard],
component: ClusterComponent,
}, {
path: 'config',
path: WsConstants.CONFIG,
canActivate: [AuthGuard],
component: ConfigComponent,
}, {
path: 'documents',
path: WsConstants.DOCUMENTS,
canActivate: [AuthGuard],
component: DocumentComponent,
}, {
path: 'image',
path: WsConstants.IMAGE,
canActivate: [AuthGuard],
component: ImageComponent,
}, {
path: 'phase',
path: WsConstants.PHASE,
canActivate: [AuthGuard],
component: PhaseComponent,
}, {
path: 'secret',
path: WsConstants.SECRET,
canActivate: [AuthGuard],
component: SecretComponent,
}];

View File

@ -12,11 +12,11 @@
# limitations under the License.
*/
import {Component} from '@angular/core';
import {WebsocketService} from '../../../services/websocket/websocket.service';
import {WebsocketMessage, WSReceiver} from '../../../services/websocket/websocket.models';
import {Log} from '../../../services/log/log.service';
import {LogMessage} from '../../../services/log/log-message';
import { Component } from '@angular/core';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsReceiver, WsConstants } from 'src/services/ws/ws.models';
import { Log } from 'src/services/log/log.service';
import { LogMessage } from 'src/services/log/log-message';
@Component({
selector: 'app-document',
@ -24,24 +24,23 @@ import {LogMessage} from '../../../services/log/log-message';
styleUrls: ['./document.component.css']
})
export class DocumentComponent implements WSReceiver {
export class DocumentComponent implements WsReceiver {
className = this.constructor.name;
statusMsg: string;
type = 'ctl';
component = 'document';
type = WsConstants.CTL;
component = WsConstants.DOCUMENT;
activeLink = 'overview';
constructor(private websocketService: WebsocketService) {
constructor(private websocketService: WsService) {
this.websocketService.registerFunctions(this);
}
public async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
public async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
this.websocketService.printIfToast(message);
} else {
switch (message.subComponent) {
case 'pull':
case WsConstants.PULL:
this.statusMsg = 'Document pull was a ' + message.message;
const button = (document.getElementById('DocPullBtn') as HTMLInputElement);
button.removeAttribute('disabled');
@ -55,7 +54,7 @@ export class DocumentComponent implements WSReceiver {
documentPull(): void {
this.statusMsg = '';
this.websocketService.sendMessage(new WebsocketMessage(this.type, this.component, 'pull'));
this.websocketService.sendMessage(new WsMessage(this.type, this.component, WsConstants.PULL));
const button = (document.getElementById('DocPullBtn') as HTMLInputElement);
button.setAttribute('disabled', 'disabled');
}

View File

@ -13,8 +13,8 @@
*/
import { Component } from '@angular/core';
import { WebsocketService } from 'src/services/websocket/websocket.service';
import { WebsocketMessage, WSReceiver } from 'src/services/websocket/websocket.models';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsReceiver, WsConstants } from 'src/services/ws/ws.models';
import { Log } from 'src/services/log/log.service';
import { LogMessage } from 'src/services/log/log-message';
@ -24,19 +24,18 @@ import { LogMessage } from 'src/services/log/log-message';
styleUrls: ['./image.component.css']
})
export class ImageComponent implements WSReceiver {
export class ImageComponent implements WsReceiver {
className = this.constructor.name;
// TODO (aschiefe): extract these strings to constants
type = 'ctl';
component = 'image';
type = WsConstants.CTL;
component = WsConstants.IMAGE;
statusMsg: string;
constructor(private websocketService: WebsocketService) {
constructor(private websocketService: WsService) {
this.websocketService.registerFunctions(this);
}
async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
this.websocketService.printIfToast(message);
} else {
// TODO (aschiefe): determine what should be notifications and what should be 86ed
@ -45,6 +44,6 @@ export class ImageComponent implements WSReceiver {
}
generateIso(): void {
this.websocketService.sendMessage(new WebsocketMessage(this.type, this.component, 'generate'));
this.websocketService.sendMessage(new WsMessage(this.type, this.component, WsConstants.GENERATE));
}
}

View File

@ -12,12 +12,12 @@
# limitations under the License.
*/
import {Component, OnInit} from '@angular/core';
import {MatDialogRef} from '@angular/material/dialog';
import {KustomNode} from '../phase.models';
import {WebsocketMessage} from '../../../../services/websocket/websocket.models';
import {WebsocketService} from '../../../../services/websocket/websocket.service';
import {FormControl, FormGroup} from '@angular/forms';
import { Component, OnInit } from '@angular/core';
import { MatDialogRef } from '@angular/material/dialog';
import { KustomNode } from '../phase.models';
import { WsMessage, WsConstants } from 'src/services/ws/ws.models';
import { WsService } from 'src/services/ws/ws.service';
import { FormControl, FormGroup } from '@angular/forms';
@Component({
selector: 'app-phase-viewer',
@ -50,7 +50,7 @@ export class PhaseViewerComponent implements OnInit {
constructor(
public dialogRef: MatDialogRef<PhaseViewerComponent>,
private websocketService: WebsocketService) {}
private websocketService: WsService) {}
ngOnInit(): void {
this.bundleYaml = this.yaml;
@ -67,20 +67,20 @@ export class PhaseViewerComponent implements OnInit {
setModel(val: string): void {
switch (val) {
case 'bundle':
case WsConstants.BUNDLE:
this.yaml = this.bundleYaml;
break;
case 'executor':
case WsConstants.EXECUTOR:
this.yaml = this.executorYaml;
break;
case 'details':
case WsConstants.DETAILS:
this.yaml = this.phaseDetails;
break;
}
}
getDocumentsBySelector(selector: string): void {
const msg = new WebsocketMessage('ctl', 'phase', 'getDocumentsBySelector');
const msg = new WsMessage(WsConstants.CTL, WsConstants.PHASE, WsConstants.GET_DOCUMENT_BY_SELECTOR);
msg.message = selector;
msg.id = this.id;
this.websocketService.sendMessage(msg);
@ -88,7 +88,7 @@ export class PhaseViewerComponent implements OnInit {
getYaml(id: string): void {
this.yaml = null;
const msg = new WebsocketMessage('ctl', 'phase', 'getYaml');
const msg = new WsMessage(WsConstants.CTL, WsConstants.PHASE, WsConstants.GET_YAML);
msg.id = id;
msg.message = 'rendered';
this.websocketService.sendMessage(msg);

View File

@ -12,19 +12,19 @@
# limitations under the License.
*/
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {PhaseComponent} from './phase.component';
import {MatTabsModule} from '@angular/material/tabs';
import {MatTreeModule} from '@angular/material/tree';
import {MatButtonModule} from '@angular/material/button';
import {MatButtonToggleModule} from '@angular/material/button-toggle';
import {MatIconModule} from '@angular/material/icon';
import {MonacoEditorModule} from 'ngx-monaco-editor';
import {FormsModule} from '@angular/forms';
import {ToastrModule} from 'ngx-toastr';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MatCardModule} from '@angular/material/card';
import {MatProgressBarModule } from '@angular/material/progress-bar';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PhaseComponent } from './phase.component';
import { MatTabsModule } from '@angular/material/tabs';
import { MatTreeModule } from '@angular/material/tree';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatIconModule } from '@angular/material/icon';
import { MonacoEditorModule } from 'ngx-monaco-editor';
import { FormsModule } from '@angular/forms';
import { ToastrModule } from 'ngx-toastr';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatCardModule } from '@angular/material/card';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatDialogModule } from '@angular/material/dialog';
import { MatMenuModule } from '@angular/material/menu';

View File

@ -12,17 +12,17 @@
# limitations under the License.
*/
import {Component} from '@angular/core';
import {WebsocketService} from '../../../services/websocket/websocket.service';
import {WebsocketMessage, WSReceiver} from '../../../services/websocket/websocket.models';
import {Log} from '../../../services/log/log.service';
import {LogMessage} from '../../../services/log/log-message';
import {KustomNode, RunOptions} from './phase.models';
import {NestedTreeControl} from '@angular/cdk/tree';
import {MatTreeNestedDataSource} from '@angular/material/tree';
import { Component } from '@angular/core';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsReceiver, WsConstants } from 'src/services/ws/ws.models';
import { Log } from 'src/services/log/log.service';
import { LogMessage } from 'src/services/log/log-message';
import { KustomNode, RunOptions } from './phase.models';
import { NestedTreeControl } from '@angular/cdk/tree';
import { MatTreeNestedDataSource } from '@angular/material/tree';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { PhaseViewerComponent } from './phase-viewer/phase-viewer.component';
import {PhaseRunnerComponent} from './phase-runner/phase-runner.component';
import { PhaseRunnerComponent } from './phase-runner/phase-runner.component';
@Component({
selector: 'app-phase',
@ -30,7 +30,7 @@ import {PhaseRunnerComponent} from './phase-runner/phase-runner.component';
styleUrls: ['./phase.component.css']
})
export class PhaseComponent implements WSReceiver {
export class PhaseComponent implements WsReceiver {
className = this.constructor.name;
statusMsg: string;
loading: boolean;
@ -39,8 +39,8 @@ export class PhaseComponent implements WSReceiver {
phaseViewerRef: MatDialogRef<PhaseViewerComponent, any>;
phaseRunnerRef: MatDialogRef<PhaseRunnerComponent, any>;
type = 'ctl';
component = 'phase';
type = WsConstants.CTL;
component = WsConstants.PHASE;
activeLink = 'overview';
targetPath: string;
@ -66,46 +66,46 @@ export class PhaseComponent implements WSReceiver {
});
}
constructor(private websocketService: WebsocketService, public dialog: MatDialog) {
constructor(private websocketService: WsService, public dialog: MatDialog) {
this.websocketService.registerFunctions(this);
this.getTarget();
this.getPhaseTree(); // load the source first
}
public async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
public async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
this.websocketService.printIfToast(message);
this.loading = false;
if (message.subComponent === 'run') {
if (message.subComponent === WsConstants.RUN) {
this.toggleNode(message.id);
}
} else {
switch (message.subComponent) {
case 'getTarget':
case WsConstants.GET_TARGET:
this.targetPath = message.message;
break;
case 'getPhaseTree':
case WsConstants.GET_PHASE_TREE:
this.handleGetPhaseTree(message.data);
break;
case 'getPhase':
case WsConstants.GET_PHASE:
this.handleGetPhase(message);
break;
case 'getYaml':
case WsConstants.GET_YAML:
this.handleGetYaml(message);
break;
case 'getDocumentsBySelector':
case WsConstants.GET_DOCUMENT_BY_SELECTOR:
this.handleGetDocumentsBySelector(message);
break;
case 'getExecutorDoc':
case WsConstants.GET_EXECUTOR_DOC:
this.handleGetExecutorDoc(message);
break;
case 'yamlWrite':
case WsConstants.YAML_WRITE:
this.handleYamlWrite(message);
break;
case 'validatePhase':
case WsConstants.VALIDATE_PHASE:
this.handleValidatePhase(message);
break;
case 'run':
case WsConstants.RUN:
this.handleRunPhase(message);
break;
default:
@ -115,11 +115,11 @@ export class PhaseComponent implements WSReceiver {
}
}
handleValidatePhase(message: WebsocketMessage): void {
handleValidatePhase(message: WsMessage): void {
this.websocketService.printIfToast(message);
}
handleRunPhase(message: WebsocketMessage): void {
handleRunPhase(message: WsMessage): void {
this.toggleNode(message.id);
}
@ -129,7 +129,7 @@ export class PhaseComponent implements WSReceiver {
this.dataSource.data = this.phaseTree;
}
handleGetPhase(message: WebsocketMessage): void {
handleGetPhase(message: WsMessage): void {
this.loading = false;
let yaml = '';
if (message.yaml !== '' && message.yaml !== undefined) {
@ -138,16 +138,16 @@ export class PhaseComponent implements WSReceiver {
this.phaseViewerRef = this.openPhaseDialog(message.id, message.name, message.details, yaml);
}
handleGetExecutorDoc(message: WebsocketMessage): void {
handleGetExecutorDoc(message: WsMessage): void {
this.phaseViewerRef.componentInstance.executorYaml = atob(message.yaml);
}
handleGetDocumentsBySelector(message: WebsocketMessage): void {
handleGetDocumentsBySelector(message: WsMessage): void {
this.phaseViewerRef.componentInstance.loading = false;
Object.assign(this.phaseViewerRef.componentInstance.results, message.data);
}
handleGetYaml(message: WebsocketMessage): void {
handleGetYaml(message: WsMessage): void {
if (message.message === 'rendered') {
this.phaseViewerRef.componentInstance.yaml = atob(message.yaml);
} else {
@ -158,7 +158,7 @@ export class PhaseComponent implements WSReceiver {
}
}
handleYamlWrite(message: WebsocketMessage): void {
handleYamlWrite(message: WsMessage): void {
this.changeEditorContents((message.yaml));
this.setTitle(message.name);
this.currentDocId = message.id;
@ -176,7 +176,7 @@ export class PhaseComponent implements WSReceiver {
}
saveYaml(): void {
const websocketMessage = this.newMessage('yamlWrite');
const websocketMessage = this.newMessage(WsConstants.YAML_WRITE);
websocketMessage.id = this.currentDocId;
websocketMessage.name = this.editorTitle;
websocketMessage.yaml = btoa(this.code);
@ -185,7 +185,7 @@ export class PhaseComponent implements WSReceiver {
getPhaseTree(): void {
this.loading = true;
const websocketMessage = this.newMessage('getPhaseTree');
const websocketMessage = this.newMessage(WsConstants.GET_PHASE_TREE);
this.websocketService.sendMessage(websocketMessage);
}
@ -229,29 +229,23 @@ export class PhaseComponent implements WSReceiver {
}
getPhaseDetails(id: object): void {
const msg = this.newMessage('getPhaseDetails');
msg.id = JSON.stringify(id);
this.websocketService.sendMessage(msg);
}
getPhase(id: object): void {
this.loading = true;
const msg = this.newMessage('getPhase');
const msg = this.newMessage(WsConstants.GET_PHASE);
msg.id = JSON.stringify(id);
this.websocketService.sendMessage(msg);
}
getYaml(id: string): void {
this.code = null;
const msg = this.newMessage('getYaml');
const msg = this.newMessage(WsConstants.GET_YAML);
msg.id = id;
msg.message = 'source';
this.websocketService.sendMessage(msg);
}
getExecutorDoc(id: object): void {
const msg = this.newMessage('getExecutorDoc');
const msg = this.newMessage(WsConstants.GET_DOCUMENT_BY_SELECTOR);
msg.id = JSON.stringify(id);
this.websocketService.sendMessage(msg);
}
@ -262,7 +256,7 @@ export class PhaseComponent implements WSReceiver {
}
getTarget(): void {
const websocketMessage = this.newMessage('getTarget');
const websocketMessage = this.newMessage(WsConstants.GET_TARGET);
this.websocketService.sendMessage(websocketMessage);
}
@ -270,7 +264,7 @@ export class PhaseComponent implements WSReceiver {
// before actually running the phase
runPhase(node: KustomNode, opts: RunOptions): void {
node.running = true;
const msg = this.newMessage('run');
const msg = this.newMessage(WsConstants.RUN);
msg.id = JSON.stringify(node.phaseId);
if (opts !== undefined) {
msg.data = JSON.parse(JSON.stringify(opts));
@ -279,13 +273,13 @@ export class PhaseComponent implements WSReceiver {
}
validatePhase(id: object): void {
const msg = this.newMessage('validatePhase');
const msg = this.newMessage(WsConstants.VALIDATE_PHASE);
msg.id = JSON.stringify(id);
this.websocketService.sendMessage(msg);
}
newMessage(subComponent: string): WebsocketMessage {
return new WebsocketMessage(this.type, this.component, subComponent);
newMessage(subComponent: string): WsMessage {
return new WsMessage(this.type, this.component, subComponent);
}
findNode(node: KustomNode, id: string): KustomNode {

View File

@ -13,13 +13,13 @@
*/
import { Component, OnInit, ViewChild } from '@angular/core';
import { WebsocketService } from 'src/services/websocket/websocket.service';
import { WebsocketMessage, WSReceiver } from 'src/services/websocket/websocket.models';
import { WsService } from 'src/services/ws/ws.service';
import { WsMessage, WsReceiver, WsConstants } from 'src/services/ws/ws.models';
import { Log } from 'src/services/log/log.service';
import { LogMessage } from 'src/services/log/log-message';
import { MatStepper } from '@angular/material/stepper';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import {STEPPER_GLOBAL_OPTIONS, StepperSelectionEvent } from '@angular/cdk/stepper';
import { STEPPER_GLOBAL_OPTIONS, StepperSelectionEvent } from '@angular/cdk/stepper';
@Component({
selector: 'app-secret',
@ -30,11 +30,10 @@ import {STEPPER_GLOBAL_OPTIONS, StepperSelectionEvent } from '@angular/cdk/stepp
}]
})
export class SecretComponent implements WSReceiver, OnInit {
export class SecretComponent implements WsReceiver, OnInit {
className = this.constructor.name;
// TODO (aschiefe): extract these strings to constants
type = 'ctl';
component = 'secret';
type = WsConstants.CTL;
component = WsConstants.SECRET;
// form groups, these control the stepper and the validators for the inputs
decryptSrcFG: FormGroup;
@ -45,7 +44,7 @@ export class SecretComponent implements WSReceiver, OnInit {
@ViewChild('decryptStepper', { static: false }) decryptStepper: MatStepper;
@ViewChild('encryptStepper', { static: false }) encryptStepper: MatStepper;
constructor(private websocketService: WebsocketService, private formBuilder: FormBuilder) {
constructor(private websocketService: WsService, private formBuilder: FormBuilder) {
this.websocketService.registerFunctions(this);
}
@ -64,12 +63,12 @@ export class SecretComponent implements WSReceiver, OnInit {
});
}
async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
this.websocketService.printIfToast(message);
} else {
switch (message.subComponent) {
case 'generate':
case WsConstants.GENERATE:
document.getElementById('GenerateOutputDiv').innerHTML = message.message;
break;
default:
@ -80,21 +79,21 @@ export class SecretComponent implements WSReceiver, OnInit {
}
decrypt(): void {
const message = new WebsocketMessage(this.type, this.component, 'decrypt');
const message = new WsMessage(this.type, this.component, WsConstants.DECRYPT);
message.message = 'Decrypt is currently not implemented in CTL';
this.websocketService.printIfToast(message);
this.decryptStepper.reset();
}
encrypt(): void {
const message = new WebsocketMessage(this.type, this.component, 'encrypt');
const message = new WsMessage(this.type, this.component, WsConstants.ENCRYPT);
message.message = 'Encrypt is currently not implemented in CTL';
this.websocketService.printIfToast(message);
this.encryptStepper.reset();
}
generateSecret(): void {
const message = new WebsocketMessage(this.type, this.component, 'generate');
const message = new WsMessage(this.type, this.component, WsConstants.GENERATE);
Log.Debug(new LogMessage('Attempting to generate secret', this.className, message));
this.websocketService.sendMessage(message);
}

View File

@ -13,20 +13,20 @@
*/
import { Component, OnInit } from '@angular/core';
import { WebsocketService } from 'src/services/websocket/websocket.service';
import { WSReceiver, WebsocketMessage, Authentication } from 'src/services/websocket/websocket.models';
import { WsService } from 'src/services/ws/ws.service';
import { WsReceiver, WsMessage, Authentication, WsConstants } from 'src/services/ws/ws.models';
@Component({
styleUrls: ['login.component.css'],
templateUrl: 'login.component.html',
})
export class LoginComponent implements WSReceiver, OnInit {
export class LoginComponent implements WsReceiver, OnInit {
className = this.constructor.name;
type = 'ui'; // needed to have the websocket service in the constructor
component = 'login'; // needed to have the websocket service in the constructor
type = WsConstants.UI;
component = WsConstants.LOGIN;
constructor(private websocketService: WebsocketService) { }
constructor(private websocketService: WsService) { }
ngOnInit(): void {
// bind the enter key to the submit button on the page
@ -41,13 +41,13 @@ export class LoginComponent implements WSReceiver, OnInit {
// This will always throw an error but should never be called because we did not register a receiver
// The auth guard will take care of the auth messages since it's dealing with the tokens
receiver(message: WebsocketMessage): Promise<void> {
receiver(message: WsMessage): Promise<void> {
throw new Error('Method not implemented.');
}
// formSubmit sends the auth request to the backend
public formSubmit(id, passwd): void {
const message = new WebsocketMessage(this.type, 'auth', 'authenticate');
const message = new WsMessage(this.type, WsConstants.AUTH, WsConstants.AUTHENTICATE);
message.authentication = new Authentication(id, passwd);
this.websocketService.sendMessage(message);
}

View File

@ -13,8 +13,8 @@
*/
import { Component } from '@angular/core';
import { WebsocketService } from '../../services/websocket/websocket.service';
import { WSReceiver, WebsocketMessage } from '../../services/websocket/websocket.models';
import { WsService } from '../../services/ws/ws.service';
import { WsReceiver, WsMessage, WsConstants } from '../../services/ws/ws.models';
import { Task, Progress } from './task.models';
import { Log } from '../../services/log/log.service';
import { LogMessage } from '../../services/log/log-message';
@ -24,31 +24,31 @@ import { LogMessage } from '../../services/log/log-message';
templateUrl: './task.component.html',
styleUrls: ['./task.component.css']
})
export class TaskComponent implements WSReceiver {
export class TaskComponent implements WsReceiver {
className = this.constructor.name;
type = 'ui';
component = 'task';
type = WsConstants.UI;
component = WsConstants.TASK;
message: string;
tasks: Task[] = [];
isOpen = false;
constructor(private websocketService: WebsocketService) {
constructor(private websocketService: WsService) {
this.websocketService.registerFunctions(this);
}
public async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
public async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
this.websocketService.printIfToast(message);
} else {
switch (message.subComponent) {
case 'taskStart':
case WsConstants.TASK_START:
this.handleTaskStart(message);
break;
case 'taskUpdate':
case WsConstants.TASK_UPDATE:
this.handleTaskUpdate(message);
break;
case 'taskEnd':
case WsConstants.TASK_END:
this.handleTaskEnd(message);
break;
default:
@ -58,15 +58,15 @@ export class TaskComponent implements WSReceiver {
}
}
handleTaskStart(message: WebsocketMessage): void {
handleTaskStart(message: WsMessage): void {
this.addTask(message);
const msg = new WebsocketMessage(this.type, this.component, message.subComponent);
const msg = new WsMessage(this.type, this.component, message.subComponent);
msg.message = `${message.name} added to Running Tasks`;
msg.sessionID = message.sessionID;
this.websocketService.printIfToast(msg);
}
handleTaskUpdate(message: WebsocketMessage): void {
handleTaskUpdate(message: WsMessage): void {
const task = this.findTask(message.id);
if (task !== null) {
Object.assign(task.progress, message.data);
@ -75,20 +75,20 @@ export class TaskComponent implements WSReceiver {
task.progress.message = task.progress.errors.toString();
}
} else {
const msg = new WebsocketMessage(this.type, this.component, message.subComponent);
const msg = new WsMessage(this.type, this.component, message.subComponent);
msg.sessionID = message.sessionID;
msg.message = `Task with id ${message.id} not found`;
this.websocketService.printIfToast(msg);
}
}
handleTaskEnd(message: WebsocketMessage): void {
handleTaskEnd(message: WsMessage): void {
const task = this.findTask(message.id);
if (task !== null) {
Object.assign(task.progress, message.data);
task.running = false;
} else {
const msg = new WebsocketMessage(this.type, this.component, message.subComponent);
const msg = new WsMessage(this.type, this.component, message.subComponent);
msg.sessionID = message.sessionID;
msg.message = `Task with id ${message.id} not found`;
this.websocketService.printIfToast(msg);
@ -103,7 +103,7 @@ export class TaskComponent implements WSReceiver {
}
}
addTask(message: WebsocketMessage): void {
addTask(message: WsMessage): void {
const p = new Progress();
Object.assign(p, message.data);

View File

@ -16,14 +16,14 @@ import { Injectable } from '@angular/core';
import { Router, CanActivate, Event as RouterEvent, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router';
import { Log } from 'src/services/log/log.service';
import { LogMessage } from 'src/services/log/log-message';
import { WebsocketService } from 'src/services/websocket/websocket.service';
import { WSReceiver, WebsocketMessage } from 'src/services/websocket/websocket.models';
import { WsService } from 'src/services/ws/ws.service';
import { WsReceiver, WsMessage, WsConstants } from 'src/services/ws/ws.models';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements WSReceiver, CanActivate {
export class AuthGuard implements WsReceiver, CanActivate {
// static router for those who may need it, I'm looking at your app components
public static router: Router;
@ -31,14 +31,14 @@ export class AuthGuard implements WSReceiver, CanActivate {
private loading = false;
private sendToLogin = false;
type = 'ui';
component = 'auth';
type = WsConstants.UI;
component = WsConstants.AUTH;
// Called by the logout link at the top right of the page
public static logout(): void {
// blank out the object storage so we can't get re authenticate
WebsocketService.token = undefined;
WebsocketService.refreshToken = undefined;
WsService.token = undefined;
WsService.refreshToken = undefined;
// blank out the local storage so we can't get re authenticate
localStorage.removeItem('airshipUI-token');
@ -60,7 +60,7 @@ export class AuthGuard implements WSReceiver, CanActivate {
}
}
constructor(private websocketService: WebsocketService, private router: Router) {
constructor(private websocketService: WsService, private router: Router) {
// create a static router so other components can access it if needs be
AuthGuard.router = router;
@ -71,13 +71,13 @@ export class AuthGuard implements WSReceiver, CanActivate {
});
}
async receiver(message: WebsocketMessage): Promise<void> {
if (message.hasOwnProperty('error')) {
async receiver(message: WsMessage): Promise<void> {
if (message.hasOwnProperty(WsConstants.ERROR)) {
Log.Error(new LogMessage('Error received in AuthGuard', this.className, message));
AuthGuard.logout();
} else {
switch (message.subComponent) {
case 'approved':
case WsConstants.APPROVED:
this.setToken(message.token, false);
Log.Debug(new LogMessage('Auth approved received', this.className, message));
// redirect to / only when on /login otherwise leave the path where it was before the auth attempt
@ -86,11 +86,11 @@ export class AuthGuard implements WSReceiver, CanActivate {
this.router.navigate(['/']);
}
break;
case 'denied':
case WsConstants.DENIED:
AuthGuard.logout();
Log.Debug(new LogMessage('Auth denied received', this.className, message));
break;
case 'refresh':
case WsConstants.REFRESH:
this.setToken(message.refreshToken, true);
Log.Debug(new LogMessage('Auth token refresh received', this.className, message));
break;
@ -159,41 +159,41 @@ export class AuthGuard implements WSReceiver, CanActivate {
const token = JSON.parse(tokenString);
if (token !== null) {
if (token.hasOwnProperty('token')) {
WebsocketService.token = token.token;
WsService.token = token.token;
}
}
}
// the UI frontend is not the decider, the back end is. If this token is good we continue, if it's not we stop
private validateToken(): boolean {
if (WebsocketService.token === undefined) { this.getStoredToken(); }
if (WsService.token === undefined) { this.getStoredToken(); }
// even after all this it's possible to have nothing. I started with nothing and still have most of it left
if (WebsocketService.token !== undefined) {
const message = new WebsocketMessage(this.type, this.component, 'validate');
message.token = WebsocketService.token;
if (WsService.token !== undefined) {
const message = new WsMessage(this.type, this.component, WsConstants.VALIDATE);
message.token = WsService.token;
// if we have a refresh token we also need to include that in the validity check
if (WebsocketService.refreshToken !== undefined) {
message.refreshToken = WebsocketService.refreshToken;
if (WsService.refreshToken !== undefined) {
message.refreshToken = WsService.refreshToken;
}
this.websocketService.sendMessage(message);
}
return WebsocketService.token !== undefined;
return WsService.token !== undefined;
}
// store the token locally so we can be authenticated between runs
private setToken(token, isRefresh): void {
// set the token for auth check going forward
if (isRefresh) {
WebsocketService.refreshToken = token;
WsService.refreshToken = token;
} else {
WebsocketService.token = token;
WsService.token = token;
// set the token locally to have a login till browser exits
const json: any = { token: WebsocketService.token };
const json: any = { token: WsService.token };
localStorage.setItem('airshipUI-token', JSON.stringify(json));
}
}

View File

@ -12,15 +12,15 @@
# limitations under the License.
*/
import { WebsocketMessage } from '../websocket/websocket.models';
import { WsMessage } from '../ws/ws.models';
export class LogMessage {
// the holy trinity of the websocket messages, a triumvirate if you will, which is how all are routed
message: string;
className: string;
logMessage: string | WebsocketMessage;
logMessage: string | WsMessage;
constructor(message?: string | undefined, className?: string | undefined, logMessage?: string | WebsocketMessage | undefined) {
constructor(message?: string | undefined, className?: string | undefined, logMessage?: string | WsMessage | undefined) {
this.message = message;
this.className = className;
this.logMessage = logMessage;

View File

@ -1,73 +0,0 @@
/*
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*/
export interface WSReceiver {
// the holy trinity of the websocket messages, a triumvirate if you will, which is how all are routed
type: string;
component: string;
// This is the method which will need to be implemented in the component to handle the messages
receiver(message: WebsocketMessage): Promise<void>;
}
// WebsocketMessage is the structure for the json that is used to talk to the backend
export class WebsocketMessage {
sessionID: string;
type: string;
component: string;
subComponent: string;
timestamp: number;
dashboards: Dashboard[];
error: string;
html: string;
name: string;
details: string;
id: string;
isAuthenticated: boolean;
message: string;
token: string;
refreshToken: string;
data: JSON;
yaml: string;
actionType: string;
targets: string[];
authentication: Authentication;
// this constructor looks like this in case anyone decides they want just a raw message with no data predefined
// or an easy way to specify the defaults
constructor(type?: string | null, component?: string | null, subComponent?: string | null) {
this.type = type;
this.component = component;
this.subComponent = subComponent;
}
}
// Dashboard has the urls of the links that will pop out new dashboard tabs on the left hand side
export class Dashboard {
name: string;
baseURL: string;
path: string;
isProxied: boolean;
}
// AuthMessage is used to send and auth request and hold the token if it's authenticated
export class Authentication {
id: string;
password: string;
constructor(id?: string | null, password?: string | null) {
this.id = id;
this.password = password;
}
}

View File

@ -0,0 +1,140 @@
/*
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*/
export interface WsReceiver {
// the holy trinity of the websocket messages, a triumvirate if you will, which is how all are routed
type: string;
component: string;
// This is the method which will need to be implemented in the component to handle the messages
receiver(message: WsMessage): Promise<void>;
}
// WebsocketMessage is the structure for the json that is used to talk to the backend
export class WsMessage {
sessionID: string;
type: string;
component: string;
subComponent: string;
timestamp: number;
dashboards: Dashboard[];
error: string;
html: string;
name: string;
details: string;
id: string;
isAuthenticated: boolean;
message: string;
token: string;
refreshToken: string;
data: JSON;
yaml: string;
actionType: string;
targets: string[];
authentication: Authentication;
// this constructor looks like this in case anyone decides they want just a raw message with no data predefined
// or an easy way to specify the defaults
constructor(type?: string | null, component?: string | null, subComponent?: string | null) {
this.type = type;
this.component = component;
this.subComponent = subComponent;
}
}
export class WsConstants {
// CTL constants
public static readonly BAREMETAL = 'baremetal';
public static readonly BUNDLE = 'bundle';
public static readonly CTL = 'ctl';
public static readonly CLUSTER = 'cluster';
public static readonly CONFIG = 'config';
public static readonly DECRYPT = 'decrypt';
public static readonly DETAILS = 'details';
public static readonly DOCUMENT = 'document';
public static readonly DOCUMENTS = 'documents';
public static readonly ENCRYPT = 'encrypt';
public static readonly ERROR = 'error';
public static readonly EXECUTOR = 'executor';
public static readonly GET_DEFAULTS = 'getDefaults';
public static readonly GENERATE = 'generate';
public static readonly IMAGE = 'image';
public static readonly INIT = 'init';
public static readonly PHASE = 'phase';
public static readonly PULL = 'pull';
public static readonly RUN = 'run';
public static readonly SECRET = 'secret';
public static readonly VALIDATE_PHASE = 'validatePhase';
public static readonly YAML_WRITE = 'yamlWrite';
public static readonly GET_DOCUMENT_BY_SELECTOR = 'getDocumentsBySelector';
public static readonly GET_EXECUTOR_DOC = 'getExecutorDoc';
public static readonly GET_PHASE = 'getPhase';
public static readonly GET_PHASE_TREE = 'getPhaseTree';
public static readonly GET_TARGET = 'getTarget';
public static readonly GET_YAML = 'getYaml';
public static readonly GET_AIRSHIP_CONFIG_PATH = 'getAirshipConfigPath';
public static readonly GET_CURRENT_CONTEXT = 'getCurrentContext';
public static readonly GET_CONTEXTS = 'getContexts';
public static readonly GET_ENCRYPTION_CONFIGS = 'getEncryptionConfigs';
public static readonly GET_MANIFESTS = 'getManifests';
public static readonly GET_MANAGEMENT_CONFIGS = 'getManagementConfigs';
public static readonly SET_AIRSHIP_CONFIG = 'setAirshipConfig';
public static readonly SET_CONTEXT = 'setContext';
public static readonly SET_MANIFEST = 'setManifest';
public static readonly SET_MANAGEMENT_CONFIG = 'setManagementConfig';
public static readonly SET_ENCRYPTION_CONFIG = 'setEncryptionConfig';
public static readonly USE_CONTEXT = 'useContext';
// UI constants
public static readonly ANY = 'any';
public static readonly APPROVED = 'approved';
public static readonly AUTH = 'auth';
public static readonly AUTHENTICATE = 'authenticate';
public static readonly DENIED = 'denied';
public static readonly INITIALIZE = 'initialize';
public static readonly KEEPALIVE = 'keepalive';
public static readonly LOG = 'log';
public static readonly LOGIN = 'login';
public static readonly REFRESH = 'refresh';
public static readonly UI = 'ui';
public static readonly VALIDATE = 'validate';
public static readonly TASK = 'task';
public static readonly TASK_END = 'taskEnd';
public static readonly TASK_START = 'taskStart';
public static readonly TASK_UPDATE = 'taskUpdate';
}
// Dashboard has the urls of the links that will pop out new dashboard tabs on the left hand side
export class Dashboard {
name: string;
baseURL: string;
path: string;
isProxied: boolean;
}
// AuthMessage is used to send and auth request and hold the token if it's authenticated
export class Authentication {
id: string;
password: string;
constructor(id?: string | null, password?: string | null) {
this.id = id;
this.password = password;
}
}

View File

@ -13,11 +13,11 @@
*/
import { TestBed } from '@angular/core/testing';
import { WebsocketService } from './websocket.service';
import { WsService } from './ws.service';
import { ToastrModule } from 'ngx-toastr';
describe('WebsocketService', () => {
let service: WebsocketService;
describe('WsService', () => {
let service: WsService;
beforeEach(() => {
TestBed.configureTestingModule({
@ -25,7 +25,7 @@ describe('WebsocketService', () => {
ToastrModule.forRoot(),
]
});
service = TestBed.inject(WebsocketService);
service = TestBed.inject(WsService);
});
it('should be created', () => {

View File

@ -13,7 +13,7 @@
*/
import { Injectable, OnDestroy } from '@angular/core';
import { WebsocketMessage, WSReceiver, Authentication } from './websocket.models';
import { WsMessage, WsReceiver } from './ws.models';
import { ToastrService } from 'ngx-toastr';
import 'reflect-metadata';
@ -21,7 +21,7 @@ import 'reflect-metadata';
providedIn: 'root'
})
export class WebsocketService implements OnDestroy {
export class WsService implements OnDestroy {
// to avoid circular includes this has to go here
public static token: string;
public static refreshToken: string;
@ -34,12 +34,12 @@ export class WebsocketService implements OnDestroy {
// functionMap is how we know where to send the direct messages
// the structure of this map is: type -> component -> receiver
private functionMap = new Map<string, Map<string, WSReceiver>>();
private functionMap = new Map<string, Map<string, WsReceiver>>();
// messageToObject unmarshalls the incoming message into a WebsocketMessage object
private static messageToObject(incomingMessage: string): WebsocketMessage {
private static messageToObject(incomingMessage: string): WsMessage {
const json = JSON.parse(incomingMessage);
const obj = new WebsocketMessage();
const obj = new WsMessage();
Object.assign(obj, json);
return obj;
}
@ -56,12 +56,12 @@ export class WebsocketService implements OnDestroy {
}
// sendMessage will relay a WebsocketMessage to the go backend
public async sendMessage(message: WebsocketMessage): Promise<void> {
public async sendMessage(message: WsMessage): Promise<void> {
try {
message.sessionID = this.sessionID;
message.timestamp = new Date().getTime();
if (WebsocketService.token !== undefined) { message.token = WebsocketService.token; }
if (WebsocketService.refreshToken !== undefined) { message.refreshToken = WebsocketService.refreshToken; }
if (WsService.token !== undefined) { message.token = WsService.token; }
if (WsService.refreshToken !== undefined) { message.refreshToken = WsService.refreshToken; }
// TODO (aschiefe): determine if this debug statement is a good thing (tm)
// Log.Debug(new LogMessage('Sending WebSocket Message', this.className, message));
this.ws.send(JSON.stringify(message));
@ -85,7 +85,7 @@ export class WebsocketService implements OnDestroy {
this.ws = new WebSocket('wss://localhost:10443/ws');
this.ws.onmessage = (event) => {
this.messageHandler(WebsocketService.messageToObject(event.data));
this.messageHandler(WsService.messageToObject(event.data));
};
this.ws.onerror = (event) => {
@ -165,7 +165,7 @@ export class WebsocketService implements OnDestroy {
}
// Takes the WebsocketMessage and iterates through the function map to send a directed message when it shows up
private async messageHandler(message: WebsocketMessage): Promise<void> {
private async messageHandler(message: WsMessage): Promise<void> {
if (this.sessionID === undefined && message.hasOwnProperty('sessionID')) {
this.sessionID = message.sessionID;
}
@ -196,26 +196,26 @@ export class WebsocketService implements OnDestroy {
window.clearTimeout(this.keepAliveTimeout);
window.clearInterval(this.keepAliveTimeout);
if (this.ws !== undefined && this.ws !== null && this.ws.readyState !== this.ws.CLOSED) {
this.sendMessage(new WebsocketMessage('ui', 'keepalive', null));
this.sendMessage(new WsMessage('ui', 'keepalive', null));
}
this.keepAliveTimeout = setTimeout(() => { this.keepAlive(); }, 60000);
}
// registerFunctions is a is called out of the target's constructor so it can auto populate the function map
public registerFunctions(target: WSReceiver): void {
public registerFunctions(target: WsReceiver): void {
const type = target.type;
const component = target.component;
if (this.functionMap.hasOwnProperty(type)) {
this.functionMap[type][component] = target;
} else {
const components = new Map<string, WSReceiver>();
const components = new Map<string, WsReceiver>();
components[component] = target;
this.functionMap[type] = components;
}
}
// printIfToast puts up the toast popup message on the UI
printIfToast(message: WebsocketMessage): void {
printIfToast(message: WsMessage): void {
if (message.error !== undefined && message.error !== null) {
this.toastrService.error(message.error);
} else {

View File

@ -29,7 +29,14 @@ import (
)
const (
keySize = 4096 // 4k key
// keypair details
keySize = 4096 // 4k key
privateKeyType = "RSA PRIVATE KEY"
publicKeyType = "CERTIFICATE"
// certificate request details
cn = "localhost" // common name
o = "Airship UI" // organization
)
// GeneratePrivateKey will a pem encoded private key and an rsa private key object
@ -42,7 +49,7 @@ func GeneratePrivateKey() ([]byte, *rsa.PrivateKey, error) {
buf := &bytes.Buffer{}
err = pem.Encode(buf, &pem.Block{
Type: "RSA PRIVATE KEY",
Type: privateKeyType,
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
})
if err != nil {
@ -63,7 +70,7 @@ func GeneratePublicKey(privateKey *rsa.PrivateKey) ([]byte, error) {
buf := &bytes.Buffer{}
err = pem.Encode(buf, &pem.Block{
Type: "CERTIFICATE",
Type: publicKeyType,
Bytes: derCert,
})
if err != nil {
@ -77,8 +84,8 @@ func generateCSR() x509.Certificate {
return x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: "localhost",
Organization: []string{"Airship UI"},
CommonName: cn,
Organization: []string{o},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(1, 0, 0),

View File

@ -17,6 +17,7 @@ package statistics
import (
"database/sql"
"os"
"regexp"
"strings"
"sync"
"time"
@ -38,6 +39,8 @@ type Transaction struct {
}
var (
doNotRecordRegex = regexp.MustCompile(`(?i)^get|^init$`) // the default gets we don't care about
writeMutex sync.Mutex
db *sql.DB
tables = []string{"baremetal", "cluster", "config", "document", "image", "phase", "secret"}
@ -173,13 +176,7 @@ func isRecordable(request configs.WsMessage) bool {
}
// don't record default get data events
switch request.SubComponent {
case configs.GetTarget,
configs.GetDefaults,
configs.GetPhaseTree,
configs.GetPhase,
configs.GetYaml,
configs.GetDocumentsBySelector:
if doNotRecordRegex.MatchString(string(request.SubComponent)) {
recordable = false
}

View File

@ -30,6 +30,12 @@ import (
// TODO: use a private key for this instead of a phrase
var jwtKey = []byte("airshipUI_JWT_key")
const (
username = "username"
password = "password"
expiration = "exp"
)
// The UI will either request authentication or validation, handle those situations here
func handleAuth(_ *string, request configs.WsMessage) configs.WsMessage {
response := configs.WsMessage{
@ -98,7 +104,7 @@ func validateToken(request configs.WsMessage) (*string, error) {
// extract the claim from the token
if claim, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
// extract the user from the claim
if user, ok := claim["username"].(string); ok {
if user, ok := claim[username].(string); ok {
// test to see if we need to sent a refresh token
go testForRefresh(claim, request)
return &user, nil
@ -132,9 +138,9 @@ func createToken(id string, passwd string) (*string, error) {
// set some claims
claims := make(jwt.MapClaims)
claims["username"] = id
claims["password"] = passwd
claims["exp"] = time.Now().Add(time.Hour * 1).Unix()
claims[username] = id
claims[password] = passwd
claims[expiration] = time.Now().Add(time.Hour * 1).Unix()
// create the token
jwtClaim := jwt.New(jwt.SigningMethodHS256)
@ -149,7 +155,7 @@ func createToken(id string, passwd string) (*string, error) {
func testForRefresh(claim jwt.MapClaims, request configs.WsMessage) {
// for some reason the exp is stored as an float and not an int in the claim conversion
// so we do a little dance and cast some floats to ints and everyone goes on with their lives
if exp, ok := claim["exp"].(float64); ok {
if exp, ok := claim[expiration].(float64); ok {
if int64(exp) < time.Now().Add(time.Minute*15).Unix() {
createRefreshToken(claim, request)
}
@ -159,7 +165,7 @@ func testForRefresh(claim jwt.MapClaims, request configs.WsMessage) {
// createRefreshToken will create an oauth2 refresh token based on the timeout on the UI
func createRefreshToken(claim jwt.MapClaims, request configs.WsMessage) {
// add the new expiration to the claim
claim["exp"] = time.Now().Add(time.Hour * 1).Unix()
claim[expiration] = time.Now().Add(time.Hour * 1).Unix()
// create the token
jwtClaim := jwt.New(jwt.SigningMethodHS256)

View File

@ -27,6 +27,14 @@ import (
// map of proxy targets which will be used based on the request
var proxyMap = map[string]*url.URL{}
const (
host = "Host"
xForwardHost = "X-Forwarded-Host"
xForwardFor = "X-Forwarded-For"
tcp = "tcp"
localhost0 = "localhost:0"
)
type transport struct {
http.RoundTripper
}
@ -73,25 +81,26 @@ func handleProxy(response http.ResponseWriter, request *http.Request) {
request.URL.Host = target.Host
request.URL.Scheme = target.Scheme
host := request.Header.Get("Host")
request.Header.Set("X-Forwarded-Host", host)
request.Header.Set("X-Forwarded-For", host)
host := request.Header.Get(host)
request.Header.Set(xForwardHost, host)
request.Header.Set(xForwardFor, host)
proxy.ServeHTTP(response, request)
}
func getRandomPort() (string, error) {
func getRandomPort() (*string, error) {
// get a random port for the proxy
listener, err := net.Listen("tcp", "localhost:0")
listener, err := net.Listen(tcp, localhost0)
if err != nil {
return "", err
return nil, err
}
// close the port so we can start the proxy
defer listener.Close()
// get the string of the port
return listener.Addr().String(), nil
port := listener.Addr().String()
return &port, nil
}
// proxyServer will proxy dashboard connections allowing us to inject headers
@ -124,18 +133,18 @@ func startProxies() {
}
// set the target for the proxied request to the original url
proxyMap[port] = target
proxyMap[*port] = target
// set the target for the link in the ui to the proxy address
dashboard.BaseURL = "http://" + port
dashboard.BaseURL = "http://" + *port
// kick off proxy
log.Debugf("Attempting to start proxy for %s on: %s\n", dashboard.Name, port)
log.Debugf("Attempting to start proxy for %s on: %s\n", dashboard.Name, *port)
// set the dashboard from this point on to go to the proxy
configs.UIConfig.Dashboards[index] = dashboard
// and away we go.........
go proxyServer(port)
go proxyServer(*port)
}
}