diff --git a/src/app/kubernetes/support/abstract-watch.component.ts b/src/app/kubernetes/support/abstract-watch.component.ts index 5686b5b..b5c5c39 100644 --- a/src/app/kubernetes/support/abstract-watch.component.ts +++ b/src/app/kubernetes/support/abstract-watch.component.ts @@ -1,5 +1,5 @@ import {OnDestroy} from "@angular/core"; -import {Observable} from "rxjs"; +import {Observable, Subject, Subscriber, Subscription} from "rxjs"; import {NamespacedResourceService} from "../service/namespaced.resource.service"; import {KubernetesResource} from "../model/kubernetesresource.model"; import {enrichServiceWithRoute, Service, Services} from "../model/service.model"; @@ -24,10 +24,17 @@ import {ReplicaSetService} from "../service/replicaset.service"; * component has been used */ export class AbstractWatchComponent implements OnDestroy { - private listObservableCache: Map> = new Map>(); + public subjectCache: Map> = new Map>(); private watchCache: Map = new Map(); ngOnDestroy(): void { + for (let key in this.subjectCache) { + let subject = this.subjectCache[key]; + if (subject) { + subject.unsubscribe(); + } + } + this.subjectCache.clear(); for (let key in this.watchCache) { let watch = this.watchCache[key]; if (watch) { @@ -35,7 +42,6 @@ export class AbstractWatchComponent implements OnDestroy { } } this.watchCache.clear(); - this.listObservableCache.clear(); } protected listAndWatchServices(namespace: string, serviceService: ServiceService, routeService: RouteService): Observable { @@ -48,6 +54,8 @@ export class AbstractWatchComponent implements OnDestroy { listAndWatchDeployments(namespace: string, deploymentService: DeploymentService, deploymentConfigService: DeploymentConfigService, serviceService: ServiceService, routeService: RouteService): Observable { + const servicesObservable = this.listAndWatchServices(namespace, serviceService, routeService); + let deployments = Observable.combineLatest( this.listAndWatch(deploymentService, namespace, Deployment), this.listAndWatch(deploymentConfigService, namespace, DeploymentConfig), @@ -55,13 +63,15 @@ export class AbstractWatchComponent implements OnDestroy { ); let runtimeDeployments = Observable.combineLatest( deployments, - this.listAndWatchServices(namespace, serviceService, routeService), + servicesObservable, createDeploymentViews, ); return runtimeDeployments; } listAndWatchReplicas(namespace: string, replicaSetService: ReplicaSetService, replicationControllerService: ReplicationControllerService, serviceService: ServiceService, routeService: RouteService): Observable { + const servicesObservable = this.listAndWatchServices(namespace, serviceService, routeService); + let replicas = Observable.combineLatest( this.listAndWatch(replicaSetService, namespace, ReplicaSet), this.listAndWatch(replicationControllerService, namespace, ReplicationController), @@ -69,7 +79,7 @@ export class AbstractWatchComponent implements OnDestroy { ); let replicaViews = Observable.combineLatest( replicas, - this.listAndWatchServices(namespace, serviceService, routeService), + servicesObservable, createReplicaSetViews, ); return replicaViews; @@ -80,29 +90,34 @@ export class AbstractWatchComponent implements OnDestroy { service: NamespacedResourceService, namespace: string, type: { new (): T; } - ) { - return Observable.combineLatest( - this.getOrCreateList(service, namespace, type), - // We just emit an empty item if the watch fails - this.getOrCreateWatch(service, namespace, type).dataStream.catch(() => Observable.of(null)), - (list, msg) => this.combineListAndWatchEvent(list, msg, service, type, namespace), + ): Observable { + let key = namespace + "/" + type.name; + return this.getOrCreateSubject(key, () => + Observable.combineLatest( + //this.getOrCreateList(service, namespace, type), + service.list(namespace), + // We just emit an empty item if the watch fails + this.getOrCreateWatch(service, namespace, type) + .dataStream.catch(() => Observable.of(null)), + (list, msg) => this.combineListAndWatchEvent(list, msg, service, type, namespace), + ) ); } - protected getOrCreateList>( - service: NamespacedResourceService, - namespace: string, - type: { new (): T; } + protected getOrCreateSubject>( + key: string, + createObserverFn: () => Observable ): Observable { - let key = namespace + "/" + type.name; - let answer = this.listObservableCache[key]; + let answer = this.subjectCache[key]; if (!answer) { - answer = service.list(namespace); - this.listObservableCache[key] = answer; + let observable = createObserverFn(); + answer = new CachingSubject(observable); + this.subjectCache[key] = answer; } - return answer; + return answer.asObservable(); } + protected getOrCreateWatch>( service: NamespacedResourceService, namespace: string, @@ -192,6 +207,42 @@ export class AbstractWatchComponent implements OnDestroy { } } +/** + * Lets send the last value to any new subscriber before any new values. + * + * Unlike BehaviorSubject there is no need for an initial value. + * Unlike AsyncSubject we don't need to wait for complete before sending a next event + */ +export class CachingSubject extends Subject { + private _value: T; + private _hasValue = false; + private _subscription: Subscription; + + constructor(protected observable: Observable) { + super(); + this._subscription = observable.subscribe(this); + } + + unsubscribe(): void { + this._subscription.unsubscribe(); + super.unsubscribe(); + } + + next(value?: T): void { + this._value = value; + this._hasValue = true; + super.next(value); + } + + + protected _subscribe(subscriber: Subscriber): Subscription { + if (this._hasValue) { + subscriber.next(this._value); + } + return super._subscribe(subscriber); + } +} + /** * Lets create a new array instance to force an update event on insert or delete to lists */ diff --git a/src/app/kubernetes/ui/build/list-page/list-page.build.component.html b/src/app/kubernetes/ui/build/list-page/list-page.build.component.html index f0ebb9d..045bf67 100644 --- a/src/app/kubernetes/ui/build/list-page/list-page.build.component.html +++ b/src/app/kubernetes/ui/build/list-page/list-page.build.component.html @@ -1,4 +1,6 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/build/list/list.build.component.html b/src/app/kubernetes/ui/build/list/list.build.component.html index cbdbf81..782a57f 100644 --- a/src/app/kubernetes/ui/build/list/list.build.component.html +++ b/src/app/kubernetes/ui/build/list/list.build.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/buildconfig/list-page/list-page.buildconfig.component.html b/src/app/kubernetes/ui/buildconfig/list-page/list-page.buildconfig.component.html index efea369..8ea6d0d 100644 --- a/src/app/kubernetes/ui/buildconfig/list-page/list-page.buildconfig.component.html +++ b/src/app/kubernetes/ui/buildconfig/list-page/list-page.buildconfig.component.html @@ -1,4 +1,7 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/buildconfig/list/list.buildconfig.component.html b/src/app/kubernetes/ui/buildconfig/list/list.buildconfig.component.html index 1da5cf6..b0e6fb9 100644 --- a/src/app/kubernetes/ui/buildconfig/list/list.buildconfig.component.html +++ b/src/app/kubernetes/ui/buildconfig/list/list.buildconfig.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/configmap/list-page/list-page.configmap.component.html b/src/app/kubernetes/ui/configmap/list-page/list-page.configmap.component.html index 522347d..a29c786 100644 --- a/src/app/kubernetes/ui/configmap/list-page/list-page.configmap.component.html +++ b/src/app/kubernetes/ui/configmap/list-page/list-page.configmap.component.html @@ -1,4 +1,6 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/configmap/list/list.configmap.component.html b/src/app/kubernetes/ui/configmap/list/list.configmap.component.html index f322593..f5684c4 100644 --- a/src/app/kubernetes/ui/configmap/list/list.configmap.component.html +++ b/src/app/kubernetes/ui/configmap/list/list.configmap.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/deployment/list-page/list-page.deployment.component.html b/src/app/kubernetes/ui/deployment/list-page/list-page.deployment.component.html index 07e0e84..6f6df94 100644 --- a/src/app/kubernetes/ui/deployment/list-page/list-page.deployment.component.html +++ b/src/app/kubernetes/ui/deployment/list-page/list-page.deployment.component.html @@ -1,4 +1,6 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/deployment/list/list.deployment.component.html b/src/app/kubernetes/ui/deployment/list/list.deployment.component.html index b3b66e9..25c9054 100644 --- a/src/app/kubernetes/ui/deployment/list/list.deployment.component.html +++ b/src/app/kubernetes/ui/deployment/list/list.deployment.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/environment/list-page/list-page.environment.component.ts b/src/app/kubernetes/ui/environment/list-page/list-page.environment.component.ts index 8daf3bf..8367d4e 100644 --- a/src/app/kubernetes/ui/environment/list-page/list-page.environment.component.ts +++ b/src/app/kubernetes/ui/environment/list-page/list-page.environment.component.ts @@ -63,15 +63,39 @@ export class Kind { } export class KindNode { - title: Subject; - environment: Environment; - kind: Kind; - children: [ - { - loading: Observable, - data: ConnectableObservable, + children: LazyLoadingData[]; + + constructor(public kind: Kind, public environment: Environment, public title: Observable, protected loader: (KindNode) => LoadingData) { + this.children = [new LazyLoadingData(() => this.loader(this))]; + } +} + +export class LoadingData { + constructor(public loading: Observable, public data: Observable) { + } +} + +export class LazyLoadingData { + private _loadingData: LoadingData; + + constructor(protected loader: () => LoadingData) { + } + + get loading(): Observable { + return this.loadingData.loading; + } + + get data(): Observable { + return this.loadingData.data; + } + + protected get loadingData(): LoadingData { + if (!this._loadingData) { + this._loadingData = this.loader(); + //this._loadingData.data.connect(); } - ]; + return this._loadingData; + } } @Component({ @@ -134,54 +158,38 @@ export class EnvironmentListPageComponent extends AbstractWatchComponent impleme environment: environment, openshiftConsoleUrl: environmentOpenShiftConoleUrl(environment), kinds: KINDS.map(kind => { - // Give it a default title + let title = new BehaviorSubject(`${kind.name}s`); - let loading = new BehaviorSubject(true); - let data = this.getCachedList(kind.path, environment) - // Update the title with the number of objects - .distinctUntilChanged() - .map(arr => { - if (label) { - return arr.filter(val => { - // lets only filter resources with a space label - return !val.labels['space'] || val.labels['space'] === label; - }); - } else { + + let loader = () => { + console.log(`Now loading data for ${kind.name} and environment ${environment.name}`); + + let loading = new BehaviorSubject(true); + let data = this.getList(kind.path, environment) + .distinctUntilChanged() + .map(arr => { + if (label) { + arr = arr.filter(val => { + // lets only filter resources with a space label + return !val.labels['space'] || val.labels['space'] === label; + }); + } + loading.next(false); + + // TODO this seems to lock up the entire browser! :) + //title.next(`${arr.length} ${kind.name}${arr.length === 1 ? '' : 's'}`); return arr; - } - }) - .do(arr => title.next(`${arr.length} ${kind.name}${arr.length === 1 ? '' : 's'}`)) - .do(() => loading.next(false)) - .publishReplay(1); - return { - environment: environment, - kind: kind, - title: title, - children: [ - { - loading: loading, - data: data, - }, - ], - } as KindNode; + }); + return new LoadingData(loading, data); + }; + return new KindNode(kind, environment, title.asObservable(), loader); }), })), )) // Wait 200ms before publishing an empty value - it's probably not empty but it might be! - .debounce(arr => (arr.length > 0 ? Observable.interval(0) : Observable.interval(200))) + //.debounce(arr => (arr.length > 0 ? Observable.interval(0) : Observable.interval(200))) .do(() => this.loading.next(false)) .publish(); - // Now, connect all the data - // Note we don't do this inside main stream to allow the page to draw faster - this.environments.subscribe( - envs => envs.forEach( - env => env.kinds.forEach( - kind => kind.children.forEach( - child => child.data.connect(), - ), - ), - ), - ); this.environments.connect(); this.space.connect(); } @@ -194,21 +202,6 @@ export class EnvironmentListPageComponent extends AbstractWatchComponent impleme // TODO is there a way to disconnect from this.space / this.environments? } - - /** - * Lets cache the observables so that we don't requery the services each time we ask for the observables - */ - private getCachedList(kind: string, environment: Environment): Observable { - let namespace = environment.namespace.name; - let key = (namespace || "") + "/" + kind; - var answer = this.listCache[key]; - if (!answer) { - answer = this.getList(kind, environment); - this.listCache[key] = answer; - } - return answer; - } - private getList(kind: string, environment: Environment): Observable { let namespace = environment.namespace.name; switch (kind) { @@ -230,6 +223,7 @@ export class EnvironmentListPageComponent extends AbstractWatchComponent impleme } } + export function environmentOpenShiftConoleUrl(environment: Environment): string { let openshiftConsoleUrl = process.env.OPENSHIFT_CONSOLE_URL; let namespace = environment.namespaceName; diff --git a/src/app/kubernetes/ui/environment/list/list.environment.component.html b/src/app/kubernetes/ui/environment/list/list.environment.component.html index 7c14f0e..1c5a115 100644 --- a/src/app/kubernetes/ui/environment/list/list.environment.component.html +++ b/src/app/kubernetes/ui/environment/list/list.environment.component.html @@ -1,86 +1,92 @@ -
+
-

{{e.environment.name}}

-
-
- - -
-
- - - - - -
-
- - - - - -
-
- - - - - -
-
- - - - - -
-
- - - - - -
-
- - - - - -
-
- - - {{ node.data.title | async }} - - -
-
-
-
+
+
+ + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + {{ node.data.title | async }} + + +
+
+
+
+
+
diff --git a/src/app/kubernetes/ui/event/list-page/list-page.event.component.html b/src/app/kubernetes/ui/event/list-page/list-page.event.component.html index 0e18014..97254a4 100644 --- a/src/app/kubernetes/ui/event/list-page/list-page.event.component.html +++ b/src/app/kubernetes/ui/event/list-page/list-page.event.component.html @@ -1,4 +1,6 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/event/list/list.event.component.html b/src/app/kubernetes/ui/event/list/list.event.component.html index 8283ebc..e3877a0 100644 --- a/src/app/kubernetes/ui/event/list/list.event.component.html +++ b/src/app/kubernetes/ui/event/list/list.event.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/namespace/list-page/list-page.namespace.component.html b/src/app/kubernetes/ui/namespace/list-page/list-page.namespace.component.html index 8b97f4e..129a1ff 100644 --- a/src/app/kubernetes/ui/namespace/list-page/list-page.namespace.component.html +++ b/src/app/kubernetes/ui/namespace/list-page/list-page.namespace.component.html @@ -1,6 +1,8 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/namespace/list/list.namespace.component.html b/src/app/kubernetes/ui/namespace/list/list.namespace.component.html index 732f406..92aeb3d 100644 --- a/src/app/kubernetes/ui/namespace/list/list.namespace.component.html +++ b/src/app/kubernetes/ui/namespace/list/list.namespace.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/pipeline/full-history/full-history.pipeline.component.html b/src/app/kubernetes/ui/pipeline/full-history/full-history.pipeline.component.html index 6830c97..f2517a0 100644 --- a/src/app/kubernetes/ui/pipeline/full-history/full-history.pipeline.component.html +++ b/src/app/kubernetes/ui/pipeline/full-history/full-history.pipeline.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/pipeline/history/history.pipeline.component.html b/src/app/kubernetes/ui/pipeline/history/history.pipeline.component.html index 2cd7fdc..ae7919f 100644 --- a/src/app/kubernetes/ui/pipeline/history/history.pipeline.component.html +++ b/src/app/kubernetes/ui/pipeline/history/history.pipeline.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/pipeline/list-page/list-page.pipeline.component.html b/src/app/kubernetes/ui/pipeline/list-page/list-page.pipeline.component.html index 7b55c54..3edde07 100644 --- a/src/app/kubernetes/ui/pipeline/list-page/list-page.pipeline.component.html +++ b/src/app/kubernetes/ui/pipeline/list-page/list-page.pipeline.component.html @@ -1,4 +1,6 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/pipeline/list/list.pipeline.component.html b/src/app/kubernetes/ui/pipeline/list/list.pipeline.component.html index e988c3e..3e0d526 100644 --- a/src/app/kubernetes/ui/pipeline/list/list.pipeline.component.html +++ b/src/app/kubernetes/ui/pipeline/list/list.pipeline.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/pod/list-page/list-page.pod.component.html b/src/app/kubernetes/ui/pod/list-page/list-page.pod.component.html index bfcc685..4aec625 100644 --- a/src/app/kubernetes/ui/pod/list-page/list-page.pod.component.html +++ b/src/app/kubernetes/ui/pod/list-page/list-page.pod.component.html @@ -1,4 +1,7 @@
- + +
+ +
diff --git a/src/app/kubernetes/ui/pod/list/list.pod.component.html b/src/app/kubernetes/ui/pod/list/list.pod.component.html index 9009b33..696c30e 100644 --- a/src/app/kubernetes/ui/pod/list/list.pod.component.html +++ b/src/app/kubernetes/ui/pod/list/list.pod.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/replicaset/list-page/list-page.replicaset.component.html b/src/app/kubernetes/ui/replicaset/list-page/list-page.replicaset.component.html index 180d86b..29118a5 100644 --- a/src/app/kubernetes/ui/replicaset/list-page/list-page.replicaset.component.html +++ b/src/app/kubernetes/ui/replicaset/list-page/list-page.replicaset.component.html @@ -1,4 +1,7 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/replicaset/list/list.replicaset.component.html b/src/app/kubernetes/ui/replicaset/list/list.replicaset.component.html index 2f0797c..0ecf647 100644 --- a/src/app/kubernetes/ui/replicaset/list/list.replicaset.component.html +++ b/src/app/kubernetes/ui/replicaset/list/list.replicaset.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/service/list-page/list-page.service.component.html b/src/app/kubernetes/ui/service/list-page/list-page.service.component.html index b9e9ad0..615fa95 100644 --- a/src/app/kubernetes/ui/service/list-page/list-page.service.component.html +++ b/src/app/kubernetes/ui/service/list-page/list-page.service.component.html @@ -1,4 +1,6 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/service/list/list.service.component.html b/src/app/kubernetes/ui/service/list/list.service.component.html index 864cf73..c7da02b 100644 --- a/src/app/kubernetes/ui/service/list/list.service.component.html +++ b/src/app/kubernetes/ui/service/list/list.service.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/kubernetes/ui/space/list-page/list-page.space.component.html b/src/app/kubernetes/ui/space/list-page/list-page.space.component.html index fb7b1ed..93b9a30 100644 --- a/src/app/kubernetes/ui/space/list-page/list-page.space.component.html +++ b/src/app/kubernetes/ui/space/list-page/list-page.space.component.html @@ -1,6 +1,8 @@
- +
+ +
diff --git a/src/app/kubernetes/ui/space/list/list.space.component.html b/src/app/kubernetes/ui/space/list/list.space.component.html index 7b3509e..2327ef6 100644 --- a/src/app/kubernetes/ui/space/list/list.space.component.html +++ b/src/app/kubernetes/ui/space/list/list.space.component.html @@ -1,4 +1,4 @@ -
+