From f2c0da0820cde09d574e7151262c40083309d58a Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 14 Feb 2020 14:03:30 +0100 Subject: [PATCH 01/33] Add initial implementation --- proxy.conf.json | 8 ++++++++ src/app/core/services/settings/settings.service.ts | 9 ++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/proxy.conf.json b/proxy.conf.json index dede5407dd..ee03bed1c7 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -3,5 +3,13 @@ "target": "https://dev.kubermatic.io", "secure": false, "changeOrigin": true + }, + "/": { + "target": "https://dev.kubermatic.io", + "secure": false, + "changeOrigin": true, + "ws": true, + "wss": true, + "logLevel": "debug" } } diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 88c4bcc0be..381a86533c 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -8,6 +8,7 @@ import {environment} from '../../../../environments/environment'; import {AppConfigService} from '../../../app-config.service'; import {AdminEntity, AdminSettings, ClusterTypeOptions} from '../../../shared/entity/AdminSettings'; import {Theme, UserSettings} from '../../../shared/entity/MemberEntity'; +import {webSocket, WebSocketSubject} from "rxjs/webSocket"; const DEFAULT_USER_SETTINGS: UserSettings = { itemsPerPage: 10, @@ -42,6 +43,9 @@ export class SettingsService { private _adminsRefresh$: Subject = new Subject(); private _refreshTimer$ = timer(0, this._appConfigService.getRefreshTimeBase() * 5); + myWebSocket: WebSocketSubject = webSocket(`ws://localhost/${this.restRoot}/ws/admin/settings`); + + constructor( private readonly _httpClient: HttpClient, private readonly _appConfigService: AppConfigService, private readonly _auth: Auth) {} @@ -103,9 +107,8 @@ export class SettingsService { } private _getAdminSettings(defaultOnError = false): Observable { - const url = `${this.restRoot}/admin/settings`; - const observable = this._httpClient.get(url); - return defaultOnError ? observable.pipe(catchError(() => of(DEFAULT_ADMIN_SETTINGS))) : observable; + const observable = this.myWebSocket.asObservable(); + return observable; } private _defaultAdminSettings(settings: AdminSettings): AdminSettings { From 84cfb4ab1015f2977938dc4bbfdc300facfcc1cf Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 14 Feb 2020 14:06:53 +0100 Subject: [PATCH 02/33] Update env files --- src/environments/environment.e2e.local.ts | 2 -- src/environments/environment.e2e.ts | 2 -- src/environments/environment.prod.ts | 2 -- src/environments/environment.ts | 2 -- 4 files changed, 8 deletions(-) diff --git a/src/environments/environment.e2e.local.ts b/src/environments/environment.e2e.local.ts index 0e50727ca6..6447136b80 100644 --- a/src/environments/environment.e2e.local.ts +++ b/src/environments/environment.e2e.local.ts @@ -6,8 +6,6 @@ export const environment = { customCSS: '../../assets/custom/style.css', refreshTimeBase: 1000, // Unit: ms restRoot: 'api/v1', - restRootV3: 'api/v3', - digitalOceanRestRoot: 'https://api.digitalocean.com/v2', oidcProviderUrl: 'http://dex.oauth:5556/dex/auth', oidcConnectorId: 'local', animations: false, diff --git a/src/environments/environment.e2e.ts b/src/environments/environment.e2e.ts index 2bcd4f99d2..fb1af9ccfd 100644 --- a/src/environments/environment.e2e.ts +++ b/src/environments/environment.e2e.ts @@ -6,8 +6,6 @@ export const environment = { customCSS: '../../assets/custom/style.css', refreshTimeBase: 1000, // Unit: ms restRoot: 'api/v1', - restRootV3: 'api/v3', - digitalOceanRestRoot: 'https://api.digitalocean.com/v2', oidcProviderUrl: 'https://dev.kubermatic.io/dex/auth', oidcConnectorId: 'local', animations: false, diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 3ae28b20dc..7e51146b7c 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -6,8 +6,6 @@ export const environment = { customCSS: '../assets/custom/style.css', refreshTimeBase: 1000, // Unit: ms restRoot: '/api/v1', - restRootV3: '/api/v3', - digitalOceanRestRoot: 'https://api.digitalocean.com/v2', oidcProviderUrl: window.location.protocol + '//' + window.location.host + '/dex/auth', oidcConnectorId: null, animations: true, diff --git a/src/environments/environment.ts b/src/environments/environment.ts index bb1146c3a0..c7ad222995 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -11,8 +11,6 @@ export const environment = { customCSS: '../../assets/custom/style.css', refreshTimeBase: 1000, // Unit: ms restRoot: 'api/v1', - restRootV3: 'api/v3', - digitalOceanRestRoot: 'https://api.digitalocean.com/v2', oidcProviderUrl: 'https://dev.kubermatic.io/dex/auth', oidcConnectorId: null, animations: true, From d5b4f1774dd491b3ef08996efdfbfc21bed2a645 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 17 Feb 2020 12:35:51 +0100 Subject: [PATCH 03/33] Update proxy configs --- proxy-local.conf.json | 5 +++-- proxy.conf.json | 9 +-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/proxy-local.conf.json b/proxy-local.conf.json index ea2209ec0f..12536dc2b5 100644 --- a/proxy-local.conf.json +++ b/proxy-local.conf.json @@ -1,7 +1,8 @@ { - "/api/**": { + "/": { "target": "http://localhost:8080", "secure": false, - "changeOrigin": true + "changeOrigin": true, + "ws": true } } diff --git a/proxy.conf.json b/proxy.conf.json index ee03bed1c7..a39216bead 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -1,15 +1,8 @@ { - "/api/**": { - "target": "https://dev.kubermatic.io", - "secure": false, - "changeOrigin": true - }, "/": { "target": "https://dev.kubermatic.io", "secure": false, "changeOrigin": true, - "ws": true, - "wss": true, - "logLevel": "debug" + "ws": true } } From 4328d3bbe1b21e46ad53d387dadcfe8e0377f132 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 17 Feb 2020 15:14:01 +0100 Subject: [PATCH 04/33] Remove redundant refresh --- .../services/settings/settings.service.ts | 31 +++++++------------ .../admin/admin-settings.component.ts | 3 +- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 381a86533c..85a2649ef5 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -38,17 +38,16 @@ export class SettingsService { private _userSettings$: Observable; private _userSettingsRefresh$: Subject = new Subject(); private _adminSettings$: Observable; - private _adminSettingsRefresh$: Subject = new Subject(); + private _adminSettingsWebSocket: WebSocketSubject = webSocket(`ws://localhost/${this.restRoot}/ws/admin/settings`); private _admins$: Observable; private _adminsRefresh$: Subject = new Subject(); private _refreshTimer$ = timer(0, this._appConfigService.getRefreshTimeBase() * 5); - myWebSocket: WebSocketSubject = webSocket(`ws://localhost/${this.restRoot}/ws/admin/settings`); - constructor( - private readonly _httpClient: HttpClient, private readonly _appConfigService: AppConfigService, - private readonly _auth: Auth) {} + private readonly _httpClient: HttpClient, private readonly _appConfigService: AppConfigService, + private readonly _auth: Auth) { + } get userSettings(): Observable { if (!this._userSettings$) { @@ -91,13 +90,9 @@ export class SettingsService { get adminSettings(): Observable { if (!this._adminSettings$) { - this._adminSettings$ = - merge(this._refreshTimer$, this._adminSettingsRefresh$) - .pipe(switchMap( - () => - iif(() => this._auth.authenticated(), this._getAdminSettings(true), of(DEFAULT_ADMIN_SETTINGS)))) - .pipe(map(settings => this._defaultAdminSettings(settings))) - .pipe(shareReplay({refCount: true, bufferSize: 1})); + this._adminSettings$ = iif(() => this._auth.authenticated(), this._getAdminSettings(true), of(DEFAULT_ADMIN_SETTINGS)) + .pipe(map(settings => this._defaultAdminSettings(settings))) + .pipe(shareReplay({refCount: true, bufferSize: 1})); } return this._adminSettings$; } @@ -107,8 +102,8 @@ export class SettingsService { } private _getAdminSettings(defaultOnError = false): Observable { - const observable = this.myWebSocket.asObservable(); - return observable; + const observable = this._adminSettingsWebSocket.asObservable(); + return defaultOnError ? observable.pipe(catchError(() => of(DEFAULT_ADMIN_SETTINGS))) : observable; } private _defaultAdminSettings(settings: AdminSettings): AdminSettings { @@ -123,10 +118,6 @@ export class SettingsService { return settings; } - refreshAdminSettings(): void { - this._adminSettingsRefresh$.next(); - } - patchAdminSettings(patch: any): Observable { const url = `${this.restRoot}/admin/settings`; return this._httpClient.patch(url, patch); @@ -135,8 +126,8 @@ export class SettingsService { get admins(): Observable { if (!this._admins$) { this._admins$ = merge(this._refreshTimer$, this._adminsRefresh$) - .pipe(switchMap(() => this._getAdmins())) - .pipe(shareReplay({refCount: true, bufferSize: 1})); + .pipe(switchMap(() => this._getAdmins())) + .pipe(shareReplay({refCount: true, bufferSize: 1})); } return this._admins$; } diff --git a/src/app/settings/admin/admin-settings.component.ts b/src/app/settings/admin/admin-settings.component.ts index b98a2bb51b..fffee3712d 100644 --- a/src/app/settings/admin/admin-settings.component.ts +++ b/src/app/settings/admin/admin-settings.component.ts @@ -65,12 +65,11 @@ export class AdminSettingsComponent implements OnInit, OnChanges, OnDestroy { } }); - this._settingsChange.pipe(debounceTime(1000)) + this._settingsChange.pipe(debounceTime(500)) .pipe(takeUntil(this._unsubscribe)) .pipe(switchMap(() => this._settingsService.patchAdminSettings(this._getPatch()))) .subscribe(settings => { this._applySettings(settings); - this._settingsService.refreshAdminSettings(); }); this._settingsService.userSettings.pipe(takeUntil(this._unsubscribe)).subscribe(settings => { From 020f11920ecfa6417652c91b9ed83bce1d79f9c9 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 17 Feb 2020 15:40:25 +0100 Subject: [PATCH 05/33] Fix observable --- src/app/core/services/settings/settings.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 85a2649ef5..949c610f46 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -91,8 +91,7 @@ export class SettingsService { get adminSettings(): Observable { if (!this._adminSettings$) { this._adminSettings$ = iif(() => this._auth.authenticated(), this._getAdminSettings(true), of(DEFAULT_ADMIN_SETTINGS)) - .pipe(map(settings => this._defaultAdminSettings(settings))) - .pipe(shareReplay({refCount: true, bufferSize: 1})); + .pipe(map(settings => this._defaultAdminSettings(settings))); } return this._adminSettings$; } From 1ae80ca3e99ca140f7e115f02d4dbc834d3e9bad Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 18 Feb 2020 12:08:35 +0100 Subject: [PATCH 06/33] Update websocket root --- src/app/core/services/settings/settings.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 949c610f46..7ca00968b5 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -34,16 +34,16 @@ const DEFAULT_ADMIN_SETTINGS: AdminSettings = { @Injectable() export class SettingsService { - private readonly restRoot: string = environment.restRoot; + private readonly restRoot = environment.restRoot; + private readonly wsRoot = `ws://${window.location.host}/${this.restRoot}/ws`; private _userSettings$: Observable; private _userSettingsRefresh$: Subject = new Subject(); private _adminSettings$: Observable; - private _adminSettingsWebSocket: WebSocketSubject = webSocket(`ws://localhost/${this.restRoot}/ws/admin/settings`); + private _adminSettingsWebSocket: WebSocketSubject = webSocket(`${this.wsRoot}/admin/settings`); private _admins$: Observable; private _adminsRefresh$: Subject = new Subject(); private _refreshTimer$ = timer(0, this._appConfigService.getRefreshTimeBase() * 5); - constructor( private readonly _httpClient: HttpClient, private readonly _appConfigService: AppConfigService, private readonly _auth: Auth) { From e92e9e186e3004924bc509539cc50bcfddc6377f Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 18 Feb 2020 12:31:51 +0100 Subject: [PATCH 07/33] Separate websocket proxy config --- proxy.conf.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proxy.conf.json b/proxy.conf.json index a39216bead..20d8e2d9f4 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -2,6 +2,11 @@ "/": { "target": "https://dev.kubermatic.io", "secure": false, + "changeOrigin": true + }, + "ws://*": { + "target": "ws://dev.kubermatic.io", + "secure": false, "changeOrigin": true, "ws": true } From 5ae9e6e48f9f33a3836b3a18241a527a73048e46 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 18 Feb 2020 12:50:00 +0100 Subject: [PATCH 08/33] Fix formatting --- .../services/settings/settings.service.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 7ca00968b5..516367727d 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -2,13 +2,13 @@ import {HttpClient} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {iif, merge, Observable, of, Subject, timer} from 'rxjs'; import {catchError, map, shareReplay, switchMap} from 'rxjs/operators'; +import {webSocket, WebSocketSubject} from 'rxjs/webSocket'; import {Auth} from '..'; import {environment} from '../../../../environments/environment'; import {AppConfigService} from '../../../app-config.service'; import {AdminEntity, AdminSettings, ClusterTypeOptions} from '../../../shared/entity/AdminSettings'; import {Theme, UserSettings} from '../../../shared/entity/MemberEntity'; -import {webSocket, WebSocketSubject} from "rxjs/webSocket"; const DEFAULT_USER_SETTINGS: UserSettings = { itemsPerPage: 10, @@ -35,7 +35,8 @@ const DEFAULT_ADMIN_SETTINGS: AdminSettings = { @Injectable() export class SettingsService { private readonly restRoot = environment.restRoot; - private readonly wsRoot = `ws://${window.location.host}/${this.restRoot}/ws`; + private readonly wsProtocol = window.location.protocol.replace('http', 'ws'); + private readonly wsRoot = `${this.wsProtocol}//${window.location.host}/${this.restRoot}/ws`; private _userSettings$: Observable; private _userSettingsRefresh$: Subject = new Subject(); private _adminSettings$: Observable; @@ -45,9 +46,8 @@ export class SettingsService { private _refreshTimer$ = timer(0, this._appConfigService.getRefreshTimeBase() * 5); constructor( - private readonly _httpClient: HttpClient, private readonly _appConfigService: AppConfigService, - private readonly _auth: Auth) { - } + private readonly _httpClient: HttpClient, private readonly _appConfigService: AppConfigService, + private readonly _auth: Auth) {} get userSettings(): Observable { if (!this._userSettings$) { @@ -90,8 +90,9 @@ export class SettingsService { get adminSettings(): Observable { if (!this._adminSettings$) { - this._adminSettings$ = iif(() => this._auth.authenticated(), this._getAdminSettings(true), of(DEFAULT_ADMIN_SETTINGS)) - .pipe(map(settings => this._defaultAdminSettings(settings))); + this._adminSettings$ = + iif(() => this._auth.authenticated(), this._getAdminSettings(true), of(DEFAULT_ADMIN_SETTINGS)) + .pipe(map(settings => this._defaultAdminSettings(settings))); } return this._adminSettings$; } @@ -125,8 +126,8 @@ export class SettingsService { get admins(): Observable { if (!this._admins$) { this._admins$ = merge(this._refreshTimer$, this._adminsRefresh$) - .pipe(switchMap(() => this._getAdmins())) - .pipe(shareReplay({refCount: true, bufferSize: 1})); + .pipe(switchMap(() => this._getAdmins())) + .pipe(shareReplay({refCount: true, bufferSize: 1})); } return this._admins$; } From 5e0aac6d1983c2e30469472f6ce488c4f95aa59a Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 18 Feb 2020 13:45:47 +0100 Subject: [PATCH 09/33] Update proxy configs --- proxy.conf.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/proxy.conf.json b/proxy.conf.json index 20d8e2d9f4..a39216bead 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -2,11 +2,6 @@ "/": { "target": "https://dev.kubermatic.io", "secure": false, - "changeOrigin": true - }, - "ws://*": { - "target": "ws://dev.kubermatic.io", - "secure": false, "changeOrigin": true, "ws": true } From f1c21f3f2c37d496420d4606c96be5e2eed3c7f8 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 18 Feb 2020 13:59:27 +0100 Subject: [PATCH 10/33] Use JS instead of JSON for proxy config --- angular.json | 6 +++--- proxy-local.conf.js | 19 +++++++++++++++++++ proxy-local.conf.json | 8 -------- proxy.conf.js | 19 +++++++++++++++++++ proxy.conf.json | 8 -------- 5 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 proxy-local.conf.js delete mode 100644 proxy-local.conf.json create mode 100644 proxy.conf.js delete mode 100644 proxy.conf.json diff --git a/angular.json b/angular.json index 3af2bb41f8..1267e22a76 100644 --- a/angular.json +++ b/angular.json @@ -105,7 +105,7 @@ "builder": "@angular-devkit/build-angular:dev-server", "options": { "browserTarget": "kubermatic:build", - "proxyConfig": "./proxy.conf.json", + "proxyConfig": "./proxy.conf.js", "port": 8000, "hmrWarning": false }, @@ -114,7 +114,7 @@ "browserTarget": "kubermatic:build:production" }, "local": { - "proxyConfig": "./proxy-local.conf.json" + "proxyConfig": "./proxy-local.conf.js" }, "e2e": { "hmr": false, @@ -123,7 +123,7 @@ "e2e-local": { "hmr": false, "browserTarget": "kubermatic:build:e2e-local", - "proxyConfig": "./proxy-local.conf.json" + "proxyConfig": "./proxy-local.conf.js" } } }, diff --git a/proxy-local.conf.js b/proxy-local.conf.js new file mode 100644 index 0000000000..bc8898a52a --- /dev/null +++ b/proxy-local.conf.js @@ -0,0 +1,19 @@ +// In current setup every call will be going through the proxy. +// +// More information: +// - https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md +// - https://github.com/chimurai/http-proxy-middleware#http-proxy-options + +const PROXY_CONFIG = [ + { + context: [ + "/", + ], + target: "http://localhost:8080", + changeOrigin: true, + secure: false, + ws: true, + } +]; + +module.exports = PROXY_CONFIG; diff --git a/proxy-local.conf.json b/proxy-local.conf.json deleted file mode 100644 index 12536dc2b5..0000000000 --- a/proxy-local.conf.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "/": { - "target": "http://localhost:8080", - "secure": false, - "changeOrigin": true, - "ws": true - } -} diff --git a/proxy.conf.js b/proxy.conf.js new file mode 100644 index 0000000000..bdee3da39e --- /dev/null +++ b/proxy.conf.js @@ -0,0 +1,19 @@ +// In current setup every call will be going through the proxy. +// +// More information: +// - https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md +// - https://github.com/chimurai/http-proxy-middleware#http-proxy-options + +const PROXY_CONFIG = [ + { + context: [ + "/", + ], + target: "https://dev.kubermatic.io", + changeOrigin: true, + secure: false, + ws: true, + } +]; + +module.exports = PROXY_CONFIG; diff --git a/proxy.conf.json b/proxy.conf.json deleted file mode 100644 index a39216bead..0000000000 --- a/proxy.conf.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "/": { - "target": "https://dev.kubermatic.io", - "secure": false, - "changeOrigin": true, - "ws": true - } -} From 03be9e5b0217dd54bcecba0a2ab544958b253ee1 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 18 Feb 2020 14:23:33 +0100 Subject: [PATCH 11/33] Record e2e --- cypress.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress.json b/cypress.json index 68a83549b0..95b7667726 100644 --- a/cypress.json +++ b/cypress.json @@ -9,5 +9,5 @@ "viewportHeight": 1080, "viewportWidth": 1920, "projectId": "mdsuba", - "video": false + "video": true } diff --git a/package.json b/package.json index 5010af8fcf..be28b89a8a 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "test": "jest", "test:ci": "jest --coverage -i", "test:watch": "jest --watch", - "cy": "cypress run --record false --browser chrome", + "cy": "cypress run --record true --browser chrome", "e2e": "start-server-and-test start:e2e http-get://localhost:8000 cy", "e2e:local": "start-server-and-test start:e2e-local http-get://localhost:8000 cy", "check": "npm run check:ts && npm run check:scss && npm run check:licenses", From 844bfb6d29e6c2db80a7b2161a10c98b49c7d332 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 18 Feb 2020 15:12:57 +0100 Subject: [PATCH 12/33] Retry websocket connections --- src/app/core/services/settings/settings.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 516367727d..e59b2a6fae 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -1,10 +1,10 @@ import {HttpClient} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {iif, merge, Observable, of, Subject, timer} from 'rxjs'; -import {catchError, map, shareReplay, switchMap} from 'rxjs/operators'; +import {catchError, delay, map, retryWhen, shareReplay, switchMap, tap} from 'rxjs/operators'; import {webSocket, WebSocketSubject} from 'rxjs/webSocket'; -import {Auth} from '..'; +import {Auth, NotificationService} from '..'; import {environment} from '../../../../environments/environment'; import {AppConfigService} from '../../../app-config.service'; import {AdminEntity, AdminSettings, ClusterTypeOptions} from '../../../shared/entity/AdminSettings'; @@ -47,7 +47,7 @@ export class SettingsService { constructor( private readonly _httpClient: HttpClient, private readonly _appConfigService: AppConfigService, - private readonly _auth: Auth) {} + private readonly _auth: Auth, private readonly _notificationService: NotificationService) {} get userSettings(): Observable { if (!this._userSettings$) { @@ -102,7 +102,7 @@ export class SettingsService { } private _getAdminSettings(defaultOnError = false): Observable { - const observable = this._adminSettingsWebSocket.asObservable(); + const observable = this._adminSettingsWebSocket.asObservable().pipe(retryWhen(errors => errors.pipe(tap(err => this._notificationService.error(err)), delay(1000)))); return defaultOnError ? observable.pipe(catchError(() => of(DEFAULT_ADMIN_SETTINGS))) : observable; } From 53158134a7267a0d325e8043db4394c9c4a9a6d6 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 18 Feb 2020 16:10:48 +0100 Subject: [PATCH 13/33] Handle error --- src/app/settings/admin/admin-settings.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/settings/admin/admin-settings.component.ts b/src/app/settings/admin/admin-settings.component.ts index fffee3712d..b529e90913 100644 --- a/src/app/settings/admin/admin-settings.component.ts +++ b/src/app/settings/admin/admin-settings.component.ts @@ -63,6 +63,8 @@ export class AdminSettingsComponent implements OnInit, OnChanges, OnDestroy { } this._applySettings(settings); } + }, error => { + this._notificationService.error(error); }); this._settingsChange.pipe(debounceTime(500)) From d6d0fae984ce25c722828cf37ba36ed816df23ca Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 18 Feb 2020 16:19:48 +0100 Subject: [PATCH 14/33] Fix formatting --- .../services/settings/settings.service.ts | 3 ++- .../admin/admin-settings.component.ts | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index e59b2a6fae..3d04d8d901 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -102,7 +102,8 @@ export class SettingsService { } private _getAdminSettings(defaultOnError = false): Observable { - const observable = this._adminSettingsWebSocket.asObservable().pipe(retryWhen(errors => errors.pipe(tap(err => this._notificationService.error(err)), delay(1000)))); + const observable = this._adminSettingsWebSocket.asObservable().pipe( + retryWhen(errors => errors.pipe(tap(err => this._notificationService.error(err)), delay(1000)))); return defaultOnError ? observable.pipe(catchError(() => of(DEFAULT_ADMIN_SETTINGS))) : observable; } diff --git a/src/app/settings/admin/admin-settings.component.ts b/src/app/settings/admin/admin-settings.component.ts index b529e90913..6ea7e1f70d 100644 --- a/src/app/settings/admin/admin-settings.component.ts +++ b/src/app/settings/admin/admin-settings.component.ts @@ -56,16 +56,19 @@ export class AdminSettingsComponent implements OnInit, OnChanges, OnDestroy { this.dataSource.data = this.admins; }); - this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)).subscribe(settings => { - if (!_.isEqual(settings, this.apiSettings)) { - if (this.apiSettings) { - this._notificationService.success('Successfully applied external settings update'); - } - this._applySettings(settings); - } - }, error => { - this._notificationService.error(error); - }); + this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)) + .subscribe( + settings => { + if (!_.isEqual(settings, this.apiSettings)) { + if (this.apiSettings) { + this._notificationService.success('Successfully applied external settings update'); + } + this._applySettings(settings); + } + }, + error => { + this._notificationService.error(error); + }); this._settingsChange.pipe(debounceTime(500)) .pipe(takeUntil(this._unsubscribe)) From be92b32d3dfb18240f3925573951da592c83d2b6 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 19 Feb 2020 11:53:41 +0100 Subject: [PATCH 15/33] Cleanup CI scripts --- .prow.yaml | 6 ++-- Makefile | 3 -- hack/e2e/run_ci_e2e_test.sh | 57 ------------------------------------- 3 files changed, 3 insertions(+), 63 deletions(-) delete mode 100755 hack/e2e/run_ci_e2e_test.sh diff --git a/.prow.yaml b/.prow.yaml index d6ffe15b29..9d6bd07ea1 100644 --- a/.prow.yaml +++ b/.prow.yaml @@ -1,5 +1,5 @@ presubmits: -- name: pull-dashboard-headless +- name: pull-dashboard-test-headless always_run: true decorate: true clone_uri: "ssh://git@github.com/kubermatic/dashboard-v2.git" @@ -24,7 +24,7 @@ presubmits: name: kubermatic-codecov key: token -- name: pull-dashboard-e2e +- name: pull-dashboard-test-e2e always_run: true decorate: true clone_uri: "ssh://git@github.com/kubermatic/dashboard-v2.git" @@ -48,7 +48,7 @@ presubmits: - image: quay.io/kubermatic/e2e-kind-cypress:v1.1.1 command: - make - - run-e2e-ci-v2 + - run-e2e-ci securityContext: privileged: true resources: diff --git a/Makefile b/Makefile index c0eca90509..c6a62edcf8 100644 --- a/Makefile +++ b/Makefile @@ -32,9 +32,6 @@ test-headless: install ./hack/upload-coverage.sh run-e2e-ci: install - ./hack/e2e/run_ci_e2e_test.sh - -run-e2e-ci-v2: install ./hack/e2e/ci-e2e.sh dist: install diff --git a/hack/e2e/run_ci_e2e_test.sh b/hack/e2e/run_ci_e2e_test.sh deleted file mode 100755 index 10c91ab0fd..0000000000 --- a/hack/e2e/run_ci_e2e_test.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -CONTROLLER_IMAGE="quay.io/kubermatic/cluster-exposer:v2.0.0" - -if [[ -z ${JOB_NAME} ]]; then - echo "This script should only be running in a CI environment." - exit 0 -fi - -if [[ -z ${PROW_JOB_ID} ]]; then - echo "Build id env variable has to be set." - exit 0 -fi - -export CYPRESS_KUBERMATIC_DEX_DEV_E2E_USERNAME="roxy@loodse.com" -export CYPRESS_KUBERMATIC_DEX_DEV_E2E_USERNAME_2="roxy2@loodse.com" -export CYPRESS_KUBERMATIC_DEX_DEV_E2E_PASSWORD="password" - -export CYPRESS_RECORD_KEY=7859bcb8-1d2a-4d56-b7f5-ca70b93f944c - -function cleanup { - kubectl delete service -l "prow.k8s.io/id=$PROW_JOB_ID" - - # Kill all descendant processes - pkill -P $$ -} -trap cleanup EXIT - -# Set docker config -echo $IMAGE_PULL_SECRET_DATA | base64 -d > /config.json - -sed 's/localhost/localhost dex.oauth/' < /etc/hosts > /hosts -cat /hosts > /etc/hosts - -# Start docker daemon -dockerd > /dev/null 2> /dev/null & - -# Wait for it to start -while (! docker stats --no-stream ); do - # Docker takes a few seconds to initialize - echo "Waiting for Docker..." - sleep 1 -done - -# Load kind image -docker load --input /kindest.tar - -deploy.sh -DOCKER_CONFIG=/ docker run --name controller -d -v /root/.kube/config:/inner -v /etc/kubeconfig/kubeconfig:/outer --network host --privileged ${CONTROLLER_IMAGE} --kubeconfig-inner "/inner" --kubeconfig-outer "/outer" --namespace "default" --build-id "$PROW_JOB_ID" -docker logs -f controller & - -expose.sh - -npm run versioninfo -WAIT_ON_TIMEOUT=600000 npm run e2e:local From c401e694212507bb4cc774087bff99fe51727848 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 19 Feb 2020 11:58:46 +0100 Subject: [PATCH 16/33] Revert local proxy config --- proxy-local.conf.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proxy-local.conf.js b/proxy-local.conf.js index bc8898a52a..6fdc57805f 100644 --- a/proxy-local.conf.js +++ b/proxy-local.conf.js @@ -7,12 +7,11 @@ const PROXY_CONFIG = [ { context: [ - "/", + "/api/**", ], target: "http://localhost:8080", changeOrigin: true, secure: false, - ws: true, } ]; From 8795f2fafa5c66fae7ec4953238689d9f831cdc1 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 19 Feb 2020 15:00:16 +0100 Subject: [PATCH 17/33] Further adjustments --- proxy-local.conf.js | 9 +++++++++ .../cluster-details/cluster-details.component.html | 2 +- src/app/core/services/settings/settings.service.ts | 12 +++++++----- src/app/wizard/wizard.component.ts | 6 ++++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/proxy-local.conf.js b/proxy-local.conf.js index 6fdc57805f..0e8cad8097 100644 --- a/proxy-local.conf.js +++ b/proxy-local.conf.js @@ -12,6 +12,15 @@ const PROXY_CONFIG = [ target: "http://localhost:8080", changeOrigin: true, secure: false, + }, + { + context: [ + "ws://*" + ], + target: "ws://localhost:8080", + changeOrigin: true, + secure: false, + ws: true, } ]; diff --git a/src/app/cluster/cluster-details/cluster-details.component.html b/src/app/cluster/cluster-details/cluster-details.component.html index 3259741c96..12bc6b7c8a 100644 --- a/src/app/cluster/cluster-details/cluster-details.component.html +++ b/src/app/cluster/cluster-details/cluster-details.component.html @@ -42,7 +42,7 @@ target="_blank" mat-flat-button [disabled]="!isClusterRunning || (!isEditEnabled() && isOpenshiftCluster())" - *ngIf="(settings.adminSettings | async).enableDashboard"> + *ngIf="(settings.adminSettings | async)?.enableDashboard"> {{getConnectName()}} diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 3d04d8d901..67adfe633a 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -4,7 +4,7 @@ import {iif, merge, Observable, of, Subject, timer} from 'rxjs'; import {catchError, delay, map, retryWhen, shareReplay, switchMap, tap} from 'rxjs/operators'; import {webSocket, WebSocketSubject} from 'rxjs/webSocket'; -import {Auth, NotificationService} from '..'; +import {Auth} from '..'; import {environment} from '../../../../environments/environment'; import {AppConfigService} from '../../../app-config.service'; import {AdminEntity, AdminSettings, ClusterTypeOptions} from '../../../shared/entity/AdminSettings'; @@ -47,7 +47,7 @@ export class SettingsService { constructor( private readonly _httpClient: HttpClient, private readonly _appConfigService: AppConfigService, - private readonly _auth: Auth, private readonly _notificationService: NotificationService) {} + private readonly _auth: Auth) {} get userSettings(): Observable { if (!this._userSettings$) { @@ -102,9 +102,11 @@ export class SettingsService { } private _getAdminSettings(defaultOnError = false): Observable { - const observable = this._adminSettingsWebSocket.asObservable().pipe( - retryWhen(errors => errors.pipe(tap(err => this._notificationService.error(err)), delay(1000)))); - return defaultOnError ? observable.pipe(catchError(() => of(DEFAULT_ADMIN_SETTINGS))) : observable; + return this._adminSettingsWebSocket.asObservable().pipe( + retryWhen(errors => errors.pipe(tap(err => console.error(err), delay(1000)))) + ); + // shareReplay(1), + // return defaultOnError ? observable.pipe(catchError(() => of(DEFAULT_ADMIN_SETTINGS))) : observable; } private _defaultAdminSettings(settings: AdminSettings): AdminSettings { diff --git a/src/app/wizard/wizard.component.ts b/src/app/wizard/wizard.component.ts index 52d2a36763..dcd5150671 100644 --- a/src/app/wizard/wizard.component.ts +++ b/src/app/wizard/wizard.component.ts @@ -61,8 +61,14 @@ export class WizardComponent implements OnInit, OnDestroy { this._settingsService.adminSettings.pipe(first()).subscribe( settings => this.addNodeData.count = settings.defaultNodeCount); + console.log("Asd") this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)).subscribe(settings => { this.settings = settings; + console.log("123") + + }, error => { + console.log("ddd") + console.log(error) }); this.updateSteps(); From 6dd5e7633b4e6b35c3c83e9e06b3f230d6e27276 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 19 Feb 2020 15:58:18 +0100 Subject: [PATCH 18/33] Further adjustments --- proxy-local.conf.js | 7 ++++--- src/app/wizard/wizard.component.ts | 11 ++--------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/proxy-local.conf.js b/proxy-local.conf.js index 0e8cad8097..3c106fe1c7 100644 --- a/proxy-local.conf.js +++ b/proxy-local.conf.js @@ -7,7 +7,7 @@ const PROXY_CONFIG = [ { context: [ - "/api/**", + "/api/v1/**", ], target: "http://localhost:8080", changeOrigin: true, @@ -15,12 +15,13 @@ const PROXY_CONFIG = [ }, { context: [ - "ws://*" + "api/v1/ws/**" ], - target: "ws://localhost:8080", + target: "http://localhost:8080", changeOrigin: true, secure: false, ws: true, + logLevel: 'debug', } ]; diff --git a/src/app/wizard/wizard.component.ts b/src/app/wizard/wizard.component.ts index dcd5150671..9e0ddf7af8 100644 --- a/src/app/wizard/wizard.component.ts +++ b/src/app/wizard/wizard.component.ts @@ -61,15 +61,8 @@ export class WizardComponent implements OnInit, OnDestroy { this._settingsService.adminSettings.pipe(first()).subscribe( settings => this.addNodeData.count = settings.defaultNodeCount); - console.log("Asd") - this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)).subscribe(settings => { - this.settings = settings; - console.log("123") - - }, error => { - console.log("ddd") - console.log(error) - }); + this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)).subscribe( + settings => this.settings = settings); this.updateSteps(); this._projectService.selectedProject.pipe(takeUntil(this._unsubscribe)) From f95fd09f38faac0c81a0fa18386d0e37fa291659 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 19 Feb 2020 17:27:25 +0100 Subject: [PATCH 19/33] Further adjustments --- proxy-local.conf.js | 10 ---------- src/app/core/services/settings/settings.service.ts | 9 ++++++--- src/app/settings/admin/admin-settings.component.ts | 3 --- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/proxy-local.conf.js b/proxy-local.conf.js index 3c106fe1c7..3dd1745472 100644 --- a/proxy-local.conf.js +++ b/proxy-local.conf.js @@ -13,16 +13,6 @@ const PROXY_CONFIG = [ changeOrigin: true, secure: false, }, - { - context: [ - "api/v1/ws/**" - ], - target: "http://localhost:8080", - changeOrigin: true, - secure: false, - ws: true, - logLevel: 'debug', - } ]; module.exports = PROXY_CONFIG; diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 67adfe633a..ce8b614ac7 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -103,10 +103,13 @@ export class SettingsService { private _getAdminSettings(defaultOnError = false): Observable { return this._adminSettingsWebSocket.asObservable().pipe( - retryWhen(errors => errors.pipe(tap(err => console.error(err), delay(1000)))) + tap(() => retryWhen(errors => errors.pipe(delay(2000)))), + catchError(() => of(DEFAULT_ADMIN_SETTINGS)) ); - // shareReplay(1), - // return defaultOnError ? observable.pipe(catchError(() => of(DEFAULT_ADMIN_SETTINGS))) : observable; + // TODO shareReplay(1), + // TODO retryWhen doesnt work with catchError? + // TODO add my own defaulting, i.e. additional subject that will keep settings + // TODO fix proxy in local setup } private _defaultAdminSettings(settings: AdminSettings): AdminSettings { diff --git a/src/app/settings/admin/admin-settings.component.ts b/src/app/settings/admin/admin-settings.component.ts index 6ea7e1f70d..936460eab7 100644 --- a/src/app/settings/admin/admin-settings.component.ts +++ b/src/app/settings/admin/admin-settings.component.ts @@ -65,9 +65,6 @@ export class AdminSettingsComponent implements OnInit, OnChanges, OnDestroy { } this._applySettings(settings); } - }, - error => { - this._notificationService.error(error); }); this._settingsChange.pipe(debounceTime(500)) From d7261e2b5c4ce080cc41e3dd1acec669aeeecaef Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Wed, 19 Feb 2020 17:58:17 +0100 Subject: [PATCH 20/33] Add some comments --- src/app/core/services/settings/settings.service.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index ce8b614ac7..1e56e72054 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -1,7 +1,7 @@ import {HttpClient} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {iif, merge, Observable, of, Subject, timer} from 'rxjs'; -import {catchError, delay, map, retryWhen, shareReplay, switchMap, tap} from 'rxjs/operators'; +import {catchError, map, shareReplay, switchMap} from 'rxjs/operators'; import {webSocket, WebSocketSubject} from 'rxjs/webSocket'; import {Auth} from '..'; @@ -103,12 +103,16 @@ export class SettingsService { private _getAdminSettings(defaultOnError = false): Observable { return this._adminSettingsWebSocket.asObservable().pipe( - tap(() => retryWhen(errors => errors.pipe(delay(2000)))), - catchError(() => of(DEFAULT_ADMIN_SETTINGS)) + //tap(() => retryWhen(errors => errors.pipe(delay(2000)))), + catchError(() => of(DEFAULT_ADMIN_SETTINGS)), + shareReplay() ); - // TODO shareReplay(1), - // TODO retryWhen doesnt work with catchError? // TODO add my own defaulting, i.e. additional subject that will keep settings + // - subject with settings, initialized with defaults + // - while it is subscribed for the first time watch is initialized in the bg (retries all time) + // - watch is sending the data to the settings subject + // user will have defaults from the begininng, all the errors will be hidden by the first watch + // we might add some notification that ws connection is not done/done // TODO fix proxy in local setup } From c437ae6188807b348c65b3648571f1a62c1fed8a Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 20 Feb 2020 10:18:18 +0100 Subject: [PATCH 21/33] Add defaulting --- proxy-local.conf.js | 23 ++++++--- proxy.conf.js | 22 +++++--- .../services/settings/settings.service.ts | 51 +++++++++---------- 3 files changed, 54 insertions(+), 42 deletions(-) diff --git a/proxy-local.conf.js b/proxy-local.conf.js index 3dd1745472..c86f868f69 100644 --- a/proxy-local.conf.js +++ b/proxy-local.conf.js @@ -1,18 +1,27 @@ -// In current setup every call will be going through the proxy. -// -// More information: -// - https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md -// - https://github.com/chimurai/http-proxy-middleware#http-proxy-options - +// TODO Fix: +// https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md +// https://github.com/chimurai/http-proxy-middleware#http-proxy-options const PROXY_CONFIG = [ { context: [ - "/api/v1/**", + "/api/**", ], target: "http://localhost:8080", changeOrigin: true, secure: false, }, + { + context: [ + "ws://**", + "wss://**", + ], + target: "http://localhost:8080", + prependPath: false, + changeOrigin: true, + secure: false, + ws: true, + logLevel: "debug", + } ]; module.exports = PROXY_CONFIG; diff --git a/proxy.conf.js b/proxy.conf.js index bdee3da39e..31b550e95e 100644 --- a/proxy.conf.js +++ b/proxy.conf.js @@ -1,18 +1,26 @@ -// In current setup every call will be going through the proxy. -// -// More information: -// - https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md -// - https://github.com/chimurai/http-proxy-middleware#http-proxy-options - +// TODO Fix: +// https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md +// https://github.com/chimurai/http-proxy-middleware#http-proxy-options const PROXY_CONFIG = [ { context: [ - "/", + "/api/**", + ], + target: "https://dev.kubermatic.io", + changeOrigin: true, + secure: false, + }, + { + context: [ + "ws://**", + "wss://**", ], target: "https://dev.kubermatic.io", + prependPath: false, changeOrigin: true, secure: false, ws: true, + logLevel: "debug", } ]; diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 1e56e72054..937be23c55 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -1,14 +1,14 @@ import {HttpClient} from '@angular/common/http'; import {Injectable} from '@angular/core'; -import {iif, merge, Observable, of, Subject, timer} from 'rxjs'; -import {catchError, map, shareReplay, switchMap} from 'rxjs/operators'; -import {webSocket, WebSocketSubject} from 'rxjs/webSocket'; +import {BehaviorSubject, iif, merge, Observable, of, Subject, timer} from 'rxjs'; +import {catchError, delay, map, retryWhen, shareReplay, switchMap, tap} from 'rxjs/operators'; import {Auth} from '..'; import {environment} from '../../../../environments/environment'; import {AppConfigService} from '../../../app-config.service'; import {AdminEntity, AdminSettings, ClusterTypeOptions} from '../../../shared/entity/AdminSettings'; import {Theme, UserSettings} from '../../../shared/entity/MemberEntity'; +import {webSocket, WebSocketSubject} from "rxjs/webSocket"; const DEFAULT_USER_SETTINGS: UserSettings = { itemsPerPage: 10, @@ -32,17 +32,20 @@ const DEFAULT_ADMIN_SETTINGS: AdminSettings = { enableOIDCKubeconfig: false, }; -@Injectable() +@Injectable({ + providedIn: 'root', +}) export class SettingsService { private readonly restRoot = environment.restRoot; private readonly wsProtocol = window.location.protocol.replace('http', 'ws'); private readonly wsRoot = `${this.wsProtocol}//${window.location.host}/${this.restRoot}/ws`; private _userSettings$: Observable; - private _userSettingsRefresh$: Subject = new Subject(); - private _adminSettings$: Observable; - private _adminSettingsWebSocket: WebSocketSubject = webSocket(`${this.wsRoot}/admin/settings`); + private _userSettingsRefresh$ = new Subject(); + private readonly _adminSettings = new BehaviorSubject(DEFAULT_ADMIN_SETTINGS); + private readonly _adminSettingsWS: WebSocketSubject = webSocket(`${this.wsRoot}/admin/settings`); + private _adminSettingsWatch: Observable; private _admins$: Observable; - private _adminsRefresh$: Subject = new Subject(); + private _adminsRefresh$ = new Subject(); private _refreshTimer$ = timer(0, this._appConfigService.getRefreshTimeBase() * 5); constructor( @@ -89,33 +92,25 @@ export class SettingsService { } get adminSettings(): Observable { - if (!this._adminSettings$) { - this._adminSettings$ = - iif(() => this._auth.authenticated(), this._getAdminSettings(true), of(DEFAULT_ADMIN_SETTINGS)) - .pipe(map(settings => this._defaultAdminSettings(settings))); + // Subscribe to websocket and proxy all the settings updated coming from the API to the subject that is + // exposed in this method. Thanks to that it is possible to have default value and retry mechanism that + // will run in the background if connection will fail. Subscription to the API should happen only once. + // Behavior subject is used internally to always emit last value when subscription happens. + if (!this._adminSettingsWatch) { + this._adminSettingsWatch = + iif(() => this._auth.authenticated(), this._adminSettingsWS.asObservable().pipe( + retryWhen(errors => errors.pipe(delay(3000)))), of(DEFAULT_ADMIN_SETTINGS)); + this._adminSettingsWatch.subscribe(adminSettings => this._adminSettings.next( + this._defaultAdminSettings(adminSettings))); } - return this._adminSettings$; + + return this._adminSettings; } get defaultAdminSettings(): AdminSettings { return DEFAULT_ADMIN_SETTINGS; } - private _getAdminSettings(defaultOnError = false): Observable { - return this._adminSettingsWebSocket.asObservable().pipe( - //tap(() => retryWhen(errors => errors.pipe(delay(2000)))), - catchError(() => of(DEFAULT_ADMIN_SETTINGS)), - shareReplay() - ); - // TODO add my own defaulting, i.e. additional subject that will keep settings - // - subject with settings, initialized with defaults - // - while it is subscribed for the first time watch is initialized in the bg (retries all time) - // - watch is sending the data to the settings subject - // user will have defaults from the begininng, all the errors will be hidden by the first watch - // we might add some notification that ws connection is not done/done - // TODO fix proxy in local setup - } - private _defaultAdminSettings(settings: AdminSettings): AdminSettings { if (!settings) { return DEFAULT_ADMIN_SETTINGS; From e5295483edd1aa5505fda406d6a186d30eda9561 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 20 Feb 2020 10:19:16 +0100 Subject: [PATCH 22/33] Fix imports --- src/app/core/services/settings/settings.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 937be23c55..c3e6c7d051 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -1,7 +1,7 @@ import {HttpClient} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {BehaviorSubject, iif, merge, Observable, of, Subject, timer} from 'rxjs'; -import {catchError, delay, map, retryWhen, shareReplay, switchMap, tap} from 'rxjs/operators'; +import {catchError, delay, map, retryWhen, shareReplay, switchMap} from 'rxjs/operators'; import {Auth} from '..'; import {environment} from '../../../../environments/environment'; From c65a0f547c51736837cdaef7a3b1dc4ee82ea27b Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 20 Feb 2020 10:46:11 +0100 Subject: [PATCH 23/33] Organize code --- .../services/settings/settings.service.ts | 21 +++++++++---------- .../admin/admin-settings.component.ts | 18 +++++++--------- src/app/wizard/wizard.component.ts | 4 ++-- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index c3e6c7d051..1d198abf23 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -2,13 +2,13 @@ import {HttpClient} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {BehaviorSubject, iif, merge, Observable, of, Subject, timer} from 'rxjs'; import {catchError, delay, map, retryWhen, shareReplay, switchMap} from 'rxjs/operators'; +import {webSocket} from 'rxjs/webSocket'; import {Auth} from '..'; import {environment} from '../../../../environments/environment'; import {AppConfigService} from '../../../app-config.service'; import {AdminEntity, AdminSettings, ClusterTypeOptions} from '../../../shared/entity/AdminSettings'; import {Theme, UserSettings} from '../../../shared/entity/MemberEntity'; -import {webSocket, WebSocketSubject} from "rxjs/webSocket"; const DEFAULT_USER_SETTINGS: UserSettings = { itemsPerPage: 10, @@ -41,9 +41,8 @@ export class SettingsService { private readonly wsRoot = `${this.wsProtocol}//${window.location.host}/${this.restRoot}/ws`; private _userSettings$: Observable; private _userSettingsRefresh$ = new Subject(); - private readonly _adminSettings = new BehaviorSubject(DEFAULT_ADMIN_SETTINGS); - private readonly _adminSettingsWS: WebSocketSubject = webSocket(`${this.wsRoot}/admin/settings`); - private _adminSettingsWatch: Observable; + private readonly _adminSettings$ = new BehaviorSubject(DEFAULT_ADMIN_SETTINGS); + private _adminSettingsWatch$: Observable; private _admins$: Observable; private _adminsRefresh$ = new Subject(); private _refreshTimer$ = timer(0, this._appConfigService.getRefreshTimeBase() * 5); @@ -96,15 +95,15 @@ export class SettingsService { // exposed in this method. Thanks to that it is possible to have default value and retry mechanism that // will run in the background if connection will fail. Subscription to the API should happen only once. // Behavior subject is used internally to always emit last value when subscription happens. - if (!this._adminSettingsWatch) { - this._adminSettingsWatch = - iif(() => this._auth.authenticated(), this._adminSettingsWS.asObservable().pipe( - retryWhen(errors => errors.pipe(delay(3000)))), of(DEFAULT_ADMIN_SETTINGS)); - this._adminSettingsWatch.subscribe(adminSettings => this._adminSettings.next( - this._defaultAdminSettings(adminSettings))); + if (!this._adminSettingsWatch$) { + const webSocket$ = webSocket(`${this.wsRoot}/admin/settings`) + .asObservable() + .pipe(retryWhen(errors => errors.pipe(delay(1500)))); + this._adminSettingsWatch$ = iif(() => this._auth.authenticated(), webSocket$, of(DEFAULT_ADMIN_SETTINGS)); + this._adminSettingsWatch$.subscribe(settings => this._adminSettings$.next(this._defaultAdminSettings(settings))); } - return this._adminSettings; + return this._adminSettings$; } get defaultAdminSettings(): AdminSettings { diff --git a/src/app/settings/admin/admin-settings.component.ts b/src/app/settings/admin/admin-settings.component.ts index 936460eab7..fffee3712d 100644 --- a/src/app/settings/admin/admin-settings.component.ts +++ b/src/app/settings/admin/admin-settings.component.ts @@ -56,16 +56,14 @@ export class AdminSettingsComponent implements OnInit, OnChanges, OnDestroy { this.dataSource.data = this.admins; }); - this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)) - .subscribe( - settings => { - if (!_.isEqual(settings, this.apiSettings)) { - if (this.apiSettings) { - this._notificationService.success('Successfully applied external settings update'); - } - this._applySettings(settings); - } - }); + this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)).subscribe(settings => { + if (!_.isEqual(settings, this.apiSettings)) { + if (this.apiSettings) { + this._notificationService.success('Successfully applied external settings update'); + } + this._applySettings(settings); + } + }); this._settingsChange.pipe(debounceTime(500)) .pipe(takeUntil(this._unsubscribe)) diff --git a/src/app/wizard/wizard.component.ts b/src/app/wizard/wizard.component.ts index 9e0ddf7af8..58fcd0f1fc 100644 --- a/src/app/wizard/wizard.component.ts +++ b/src/app/wizard/wizard.component.ts @@ -61,8 +61,8 @@ export class WizardComponent implements OnInit, OnDestroy { this._settingsService.adminSettings.pipe(first()).subscribe( settings => this.addNodeData.count = settings.defaultNodeCount); - this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)).subscribe( - settings => this.settings = settings); + this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)) + .subscribe(settings => this.settings = settings); this.updateSteps(); this._projectService.selectedProject.pipe(takeUntil(this._unsubscribe)) From 99f8fcfd39e31fca34b3aeaf853b99594b07f6f2 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 20 Feb 2020 10:48:03 +0100 Subject: [PATCH 24/33] Turn off video recording --- cypress.json | 2 +- package.json | 2 +- proxy-local.conf.js | 3 --- proxy.conf.js | 3 --- 4 files changed, 2 insertions(+), 8 deletions(-) diff --git a/cypress.json b/cypress.json index 95b7667726..68a83549b0 100644 --- a/cypress.json +++ b/cypress.json @@ -9,5 +9,5 @@ "viewportHeight": 1080, "viewportWidth": 1920, "projectId": "mdsuba", - "video": true + "video": false } diff --git a/package.json b/package.json index be28b89a8a..5010af8fcf 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "test": "jest", "test:ci": "jest --coverage -i", "test:watch": "jest --watch", - "cy": "cypress run --record true --browser chrome", + "cy": "cypress run --record false --browser chrome", "e2e": "start-server-and-test start:e2e http-get://localhost:8000 cy", "e2e:local": "start-server-and-test start:e2e-local http-get://localhost:8000 cy", "check": "npm run check:ts && npm run check:scss && npm run check:licenses", diff --git a/proxy-local.conf.js b/proxy-local.conf.js index c86f868f69..bdb40c8a0d 100644 --- a/proxy-local.conf.js +++ b/proxy-local.conf.js @@ -1,6 +1,3 @@ -// TODO Fix: -// https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md -// https://github.com/chimurai/http-proxy-middleware#http-proxy-options const PROXY_CONFIG = [ { context: [ diff --git a/proxy.conf.js b/proxy.conf.js index 31b550e95e..6bda30822a 100644 --- a/proxy.conf.js +++ b/proxy.conf.js @@ -1,6 +1,3 @@ -// TODO Fix: -// https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md -// https://github.com/chimurai/http-proxy-middleware#http-proxy-options const PROXY_CONFIG = [ { context: [ From 4b76b7963cad1936e44a1074f71c73e3b764502e Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 20 Feb 2020 11:15:47 +0100 Subject: [PATCH 25/33] Log websocket connection errors in the console --- src/app/core/services/settings/settings.service.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 1d198abf23..43d3108784 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -1,7 +1,7 @@ import {HttpClient} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {BehaviorSubject, iif, merge, Observable, of, Subject, timer} from 'rxjs'; -import {catchError, delay, map, retryWhen, shareReplay, switchMap} from 'rxjs/operators'; +import {catchError, delay, map, retryWhen, shareReplay, switchMap, tap} from 'rxjs/operators'; import {webSocket} from 'rxjs/webSocket'; import {Auth} from '..'; @@ -96,9 +96,13 @@ export class SettingsService { // will run in the background if connection will fail. Subscription to the API should happen only once. // Behavior subject is used internally to always emit last value when subscription happens. if (!this._adminSettingsWatch$) { - const webSocket$ = webSocket(`${this.wsRoot}/admin/settings`) - .asObservable() - .pipe(retryWhen(errors => errors.pipe(delay(1500)))); + const webSocket$ = + webSocket(`${this.wsRoot}/admin/settings`) + .asObservable() + .pipe(retryWhen( + // Display error in the console for debugging purposes, otherwise it would be ignored. + // tslint:disable-next-line:no-console + errors => errors.pipe(tap(console.error), delay(this._appConfigService.getRefreshTimeBase() * 3)))); this._adminSettingsWatch$ = iif(() => this._auth.authenticated(), webSocket$, of(DEFAULT_ADMIN_SETTINGS)); this._adminSettingsWatch$.subscribe(settings => this._adminSettings$.next(this._defaultAdminSettings(settings))); } From 0a02f4dde427afc223dfa66038d3c7c3ca0c5b1c Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 20 Feb 2020 11:44:26 +0100 Subject: [PATCH 26/33] Fix comments --- src/app/core/services/settings/settings.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 43d3108784..2e122e8b51 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -91,7 +91,7 @@ export class SettingsService { } get adminSettings(): Observable { - // Subscribe to websocket and proxy all the settings updated coming from the API to the subject that is + // Subscribe to websocket and proxy all the settings updates coming from the API to the subject that is // exposed in this method. Thanks to that it is possible to have default value and retry mechanism that // will run in the background if connection will fail. Subscription to the API should happen only once. // Behavior subject is used internally to always emit last value when subscription happens. From 02a936a40dbf06b867ce2bc6dfed6457c9a7a3d1 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Thu, 20 Feb 2020 15:58:15 +0100 Subject: [PATCH 27/33] Rebuild custom link form on changes --- .../custom-links-form.component.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/app/settings/admin/custom-link-form/custom-links-form.component.ts b/src/app/settings/admin/custom-link-form/custom-links-form.component.ts index 58e8ca484f..4ead2f01b6 100644 --- a/src/app/settings/admin/custom-link-form/custom-links-form.component.ts +++ b/src/app/settings/admin/custom-link-form/custom-links-form.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core'; import {AbstractControl, FormArray, FormBuilder, FormGroup, Validators} from '@angular/forms'; import * as _ from 'lodash'; @@ -9,7 +9,7 @@ import {CustomLink, CustomLinkLocation} from '../../../shared/utils/custom-link- templateUrl: './custom-links-form.component.html', styleUrls: ['./custom-links-form.component.scss'], }) -export class CustomLinksFormComponent implements OnInit { +export class CustomLinksFormComponent implements OnInit, OnChanges { @Input() customLinks: CustomLink[] = []; @Output() customLinksChange = new EventEmitter(); @Input() apiCustomLinks: CustomLink[] = []; @@ -22,9 +22,19 @@ export class CustomLinksFormComponent implements OnInit { } ngOnInit(): void { + this._buildForm(); + } + + ngOnChanges(changes: SimpleChanges): void { + if(changes.customLinks.currentValue !== changes.customLinks.previousValue) { + this._buildForm(); + } + } + + private _buildForm(): void { this.form = this._formBuilder.group({customLinks: this._formBuilder.array([])}); this.customLinks.forEach( - customLink => this._addCustomLink(customLink.label, customLink.url, customLink.icon, customLink.location)); + customLink => this._addCustomLink(customLink.label, customLink.url, customLink.icon, customLink.location)); this._addCustomLink(); } From 362f8161d616880e66971434427a3511f0eece42 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 21 Feb 2020 12:33:38 +0100 Subject: [PATCH 28/33] Fix local proxy --- proxy-local.conf.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/proxy-local.conf.js b/proxy-local.conf.js index bdb40c8a0d..11a5d5e090 100644 --- a/proxy-local.conf.js +++ b/proxy-local.conf.js @@ -6,18 +6,7 @@ const PROXY_CONFIG = [ target: "http://localhost:8080", changeOrigin: true, secure: false, - }, - { - context: [ - "ws://**", - "wss://**", - ], - target: "http://localhost:8080", - prependPath: false, - changeOrigin: true, - secure: false, ws: true, - logLevel: "debug", } ]; From d67c2708d5380a0400d358a4ea32310c9a0b5e9a Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 21 Feb 2020 12:42:32 +0100 Subject: [PATCH 29/33] Fix proxy --- proxy.conf.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/proxy.conf.js b/proxy.conf.js index 6bda30822a..da07663306 100644 --- a/proxy.conf.js +++ b/proxy.conf.js @@ -6,18 +6,7 @@ const PROXY_CONFIG = [ target: "https://dev.kubermatic.io", changeOrigin: true, secure: false, - }, - { - context: [ - "ws://**", - "wss://**", - ], - target: "https://dev.kubermatic.io", - prependPath: false, - changeOrigin: true, - secure: false, ws: true, - logLevel: "debug", } ]; From 41e2b1ae9bb830720c4c47d32fa1e26bbbcd090a Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Fri, 21 Feb 2020 16:45:37 +0100 Subject: [PATCH 30/33] Format files --- .../admin/custom-link-form/custom-links-form.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/settings/admin/custom-link-form/custom-links-form.component.ts b/src/app/settings/admin/custom-link-form/custom-links-form.component.ts index 4ead2f01b6..adc67b1255 100644 --- a/src/app/settings/admin/custom-link-form/custom-links-form.component.ts +++ b/src/app/settings/admin/custom-link-form/custom-links-form.component.ts @@ -26,7 +26,7 @@ export class CustomLinksFormComponent implements OnInit, OnChanges { } ngOnChanges(changes: SimpleChanges): void { - if(changes.customLinks.currentValue !== changes.customLinks.previousValue) { + if (changes.customLinks.currentValue !== changes.customLinks.previousValue) { this._buildForm(); } } @@ -34,7 +34,7 @@ export class CustomLinksFormComponent implements OnInit, OnChanges { private _buildForm(): void { this.form = this._formBuilder.group({customLinks: this._formBuilder.array([])}); this.customLinks.forEach( - customLink => this._addCustomLink(customLink.label, customLink.url, customLink.icon, customLink.location)); + customLink => this._addCustomLink(customLink.label, customLink.url, customLink.icon, customLink.location)); this._addCustomLink(); } From c2e2401b0ca6b283b21a2f4270ff9f5359d5a6bc Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 25 Feb 2020 11:45:11 +0100 Subject: [PATCH 31/33] Overwrite origin header --- proxy.conf.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proxy.conf.js b/proxy.conf.js index da07663306..ff893e812f 100644 --- a/proxy.conf.js +++ b/proxy.conf.js @@ -5,6 +5,9 @@ const PROXY_CONFIG = [ ], target: "https://dev.kubermatic.io", changeOrigin: true, + headers: { + 'Origin': 'https://dev.kubermatic.io', + }, secure: false, ws: true, } From b5d9b4728bb849a0133801275b9a8bb183f9829a Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 25 Feb 2020 15:37:01 +0100 Subject: [PATCH 32/33] Log ws errors in debug mode only --- src/app/core/services/settings/settings.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/services/settings/settings.service.ts b/src/app/core/services/settings/settings.service.ts index 2e122e8b51..33e0cdd843 100644 --- a/src/app/core/services/settings/settings.service.ts +++ b/src/app/core/services/settings/settings.service.ts @@ -102,7 +102,7 @@ export class SettingsService { .pipe(retryWhen( // Display error in the console for debugging purposes, otherwise it would be ignored. // tslint:disable-next-line:no-console - errors => errors.pipe(tap(console.error), delay(this._appConfigService.getRefreshTimeBase() * 3)))); + errors => errors.pipe(tap(console.debug), delay(this._appConfigService.getRefreshTimeBase() * 3)))); this._adminSettingsWatch$ = iif(() => this._auth.authenticated(), webSocket$, of(DEFAULT_ADMIN_SETTINGS)); this._adminSettingsWatch$.subscribe(settings => this._adminSettings$.next(this._defaultAdminSettings(settings))); } From d72d6bad88a32607d914f1a1e871791f9c458ee6 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Tue, 25 Feb 2020 15:45:52 +0100 Subject: [PATCH 33/33] Fix notification --- src/app/settings/admin/admin-settings.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/settings/admin/admin-settings.component.ts b/src/app/settings/admin/admin-settings.component.ts index fffee3712d..c7013eae0f 100644 --- a/src/app/settings/admin/admin-settings.component.ts +++ b/src/app/settings/admin/admin-settings.component.ts @@ -58,7 +58,7 @@ export class AdminSettingsComponent implements OnInit, OnChanges, OnDestroy { this._settingsService.adminSettings.pipe(takeUntil(this._unsubscribe)).subscribe(settings => { if (!_.isEqual(settings, this.apiSettings)) { - if (this.apiSettings) { + if (this._shouldDisplayUpdateNotification()) { this._notificationService.success('Successfully applied external settings update'); } this._applySettings(settings); @@ -87,6 +87,10 @@ export class AdminSettingsComponent implements OnInit, OnChanges, OnDestroy { this._unsubscribe.complete(); } + private _shouldDisplayUpdateNotification(): boolean { + return this.apiSettings && this.apiSettings !== this._settingsService.defaultAdminSettings; + } + private _applySettings(settings: AdminSettings): void { this.apiSettings = settings; this.settings = _.cloneDeep(this.apiSettings);