Skip to content

Commit cedce4b

Browse files
authored
Add cluster name to snapshot list and detail view (#7695)
* Add cluster name to snapshot list and details Signed-off-by: Mathias Petermann <[email protected]> * Avoid race condition when fetching clustername for backup Signed-off-by: Mathias Petermann <[email protected]> * Improve clustername display in snapshot list Signed-off-by: Mathias Petermann <[email protected]> * Add clusterName to restore list Signed-off-by: Mathias Petermann <[email protected]> * Add clusterName to automatic backup list Signed-off-by: Mathias Petermann <[email protected]> * avoid linear search for clustersnames, avoid nested subscribed, rely on forkJoins instead Signed-off-by: Mathias Petermann <[email protected]> * use dashes if no clustername was found Signed-off-by: Mathias Petermann <[email protected]> --------- Signed-off-by: Mathias Petermann <[email protected]>
1 parent a42ded1 commit cedce4b

File tree

10 files changed

+149
-59
lines changed

10 files changed

+149
-59
lines changed

modules/web/src/app/backup/details/automatic-backup/component.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {Component, OnDestroy, OnInit} from '@angular/core';
1616
import {MatDialog, MatDialogConfig} from '@angular/material/dialog';
1717
import {ActivatedRoute, Router} from '@angular/router';
1818
import {getBackupHealthStatus, HealthStatus} from '@app/shared/utils/health-status';
19+
import {ClusterService} from '@core/services/cluster';
1920
import {BackupService} from '@core/services/backup';
2021
import {ProjectService} from '@core/services/project';
2122
import {UserService} from '@core/services/user';
@@ -27,8 +28,9 @@ import {Project} from '@shared/entity/project';
2728
import {GroupConfig} from '@shared/model/Config';
2829
import {MemberUtils, Permission} from '@shared/utils/member';
2930
import _ from 'lodash';
30-
import {Subject} from 'rxjs';
31-
import {filter, map, switchMap, take, takeUntil} from 'rxjs/operators';
31+
import {forkJoin, of, Subject} from 'rxjs';
32+
import {filter, switchMap, take, takeUntil} from 'rxjs/operators';
33+
import {Cluster} from '@app/shared/entity/cluster';
3234

3335
@Component({
3436
selector: 'km-automatic-backup-details',
@@ -43,7 +45,7 @@ export class AutomaticBackupDetailsComponent implements OnInit, OnDestroy {
4345
selectedProject = {} as Project;
4446
isInitialized = false;
4547
backup: EtcdBackupConfig = {} as EtcdBackupConfig;
46-
48+
cluster: Cluster;
4749
get canDelete(): boolean {
4850
return MemberUtils.hasPermission(this._user, this._currentGroupConfig, View.Backups, Permission.Delete);
4951
}
@@ -57,6 +59,7 @@ export class AutomaticBackupDetailsComponent implements OnInit, OnDestroy {
5759
}
5860

5961
constructor(
62+
private readonly _clusterService: ClusterService,
6063
private readonly _backupService: BackupService,
6164
private readonly _projectService: ProjectService,
6265
private readonly _userService: UserService,
@@ -74,20 +77,23 @@ export class AutomaticBackupDetailsComponent implements OnInit, OnDestroy {
7477

7578
this._projectService.selectedProject
7679
.pipe(
77-
switchMap(project => {
78-
this.selectedProject = project;
79-
return this._userService.getCurrentUserGroup(project.id);
80-
})
80+
switchMap(project =>
81+
forkJoin({
82+
userGroup: this._userService.getCurrentUserGroup(project.id).pipe(take(1)),
83+
backups: this._backupService.list(project.id, false).pipe(take(1)),
84+
clusters: this._clusterService.clusters(project.id, false).pipe(take(1)),
85+
project: of(project),
86+
})
87+
)
8188
)
8289
.pipe(takeUntil(this._unsubscribe))
83-
.subscribe(userGroup => (this._currentGroupConfig = this._userService.getCurrentUserGroupConfig(userGroup)));
90+
.subscribe(({userGroup, backups, clusters, project}) => {
91+
this.selectedProject = project;
92+
this._currentGroupConfig = this._userService.getCurrentUserGroupConfig(userGroup);
93+
94+
this.backup = backups.find(backup => backup.id === this._route.snapshot.params.backupID);
95+
this.cluster = clusters.find(cluster => cluster.id === this.backup.spec?.clusterId);
8496

85-
this._projectService.selectedProject
86-
.pipe(switchMap(project => this._backupService.list(project.id)))
87-
.pipe(map(backups => backups.find(backup => backup.id === this._route.snapshot.params.backupID)))
88-
.pipe(takeUntil(this._unsubscribe))
89-
.subscribe(backup => {
90-
this.backup = backup;
9197
this.isInitialized = true;
9298
});
9399
}

modules/web/src/app/backup/details/automatic-backup/template.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@
5454
</div>
5555
</mat-card-header>
5656
<mat-card-content fxLayout="row">
57+
<km-property>
58+
<div key>Cluster Name</div>
59+
<div value>{{cluster?.name || '-'}}</div>
60+
</km-property>
5761
<km-property>
5862
<div key>Cluster ID</div>
5963
<div value>{{backup.spec?.clusterId}}</div>

modules/web/src/app/backup/details/snapshot/component.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
DeleteSnapshotDialogComponent,
2020
DeleteSnapshotDialogConfig,
2121
} from '@app/backup/list/snapshot/delete-dialog/component';
22+
import {ClusterService} from '@core/services/cluster';
2223
import {BackupService} from '@core/services/backup';
2324
import {ProjectService} from '@core/services/project';
2425
import {UserService} from '@core/services/user';
@@ -28,9 +29,10 @@ import {Member} from '@shared/entity/member';
2829
import {Project} from '@shared/entity/project';
2930
import {GroupConfig} from '@shared/model/Config';
3031
import {MemberUtils, Permission} from '@shared/utils/member';
31-
import {Subject} from 'rxjs';
32-
import {filter, map, switchMap, take, takeUntil} from 'rxjs/operators';
32+
import {forkJoin, of, Subject} from 'rxjs';
33+
import {filter, switchMap, take, takeUntil} from 'rxjs/operators';
3334
import {getBackupHealthStatus, HealthStatus} from '@shared/utils/health-status';
35+
import {Cluster} from '@app/shared/entity/cluster';
3436

3537
@Component({
3638
selector: 'km-snapshot-details',
@@ -45,7 +47,7 @@ export class SnapshotDetailsComponent implements OnInit, OnDestroy {
4547
selectedProject = {} as Project;
4648
isInitialized = false;
4749
backup: EtcdBackupConfig = {} as EtcdBackupConfig;
48-
50+
cluster: Cluster;
4951
get canDelete(): boolean {
5052
return MemberUtils.hasPermission(this._user, this._currentGroupConfig, View.Backups, Permission.Delete);
5153
}
@@ -59,6 +61,7 @@ export class SnapshotDetailsComponent implements OnInit, OnDestroy {
5961
}
6062

6163
constructor(
64+
private readonly _clusterService: ClusterService,
6265
private readonly _backupService: BackupService,
6366
private readonly _projectService: ProjectService,
6467
private readonly _userService: UserService,
@@ -76,20 +79,23 @@ export class SnapshotDetailsComponent implements OnInit, OnDestroy {
7679

7780
this._projectService.selectedProject
7881
.pipe(
79-
switchMap(project => {
80-
this.selectedProject = project;
81-
return this._userService.getCurrentUserGroup(project.id);
82-
})
82+
switchMap(project =>
83+
forkJoin({
84+
userGroup: this._userService.getCurrentUserGroup(project.id).pipe(take(1)),
85+
backups: this._backupService.list(project.id, true).pipe(take(1)),
86+
clusters: this._clusterService.clusters(project.id, false).pipe(take(1)),
87+
project: of(project),
88+
})
89+
)
8390
)
8491
.pipe(takeUntil(this._unsubscribe))
85-
.subscribe(userGroup => (this._currentGroupConfig = this._userService.getCurrentUserGroupConfig(userGroup)));
92+
.subscribe(({userGroup, backups, clusters, project}) => {
93+
this.selectedProject = project;
94+
this._currentGroupConfig = this._userService.getCurrentUserGroupConfig(userGroup);
95+
96+
this.backup = backups.find(backup => backup.id === this._route.snapshot.params.backupID);
97+
this.cluster = clusters.find(cluster => cluster.id === this.backup.spec?.clusterId);
8698

87-
this._projectService.selectedProject
88-
.pipe(switchMap(project => this._backupService.list(project.id, true)))
89-
.pipe(map(backups => backups.find(backup => backup.id === this._route.snapshot.params.backupID)))
90-
.pipe(takeUntil(this._unsubscribe))
91-
.subscribe(backup => {
92-
this.backup = backup;
9399
this.isInitialized = true;
94100
});
95101
}

modules/web/src/app/backup/details/snapshot/template.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
</mat-card-title>
4848
</mat-card-header>
4949
<mat-card-content fxLayout="row">
50+
<km-property>
51+
<div key>Cluster Name</div>
52+
<div value>{{cluster?.name || '-'}}</div>
53+
</km-property>
5054
<km-property>
5155
<div key>Cluster ID</div>
5256
<div value>{{backup.spec?.clusterId}}</div>

modules/web/src/app/backup/list/automatic-backup/component.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import {
2121
AddAutomaticBackupDialogComponent,
2222
AddAutomaticBackupDialogConfig,
2323
} from '@app/backup/list/automatic-backup/add-dialog/component';
24+
import {Cluster} from '@app/shared/entity/cluster';
2425
import {BackupService} from '@core/services/backup';
26+
import {ClusterService} from '@core/services/cluster';
2527
import {NotificationService} from '@core/services/notification';
2628
import {ProjectService} from '@core/services/project';
2729
import {UserService} from '@core/services/user';
@@ -34,7 +36,7 @@ import {GroupConfig} from '@shared/model/Config';
3436
import {HealthStatus, getBackupHealthStatus} from '@shared/utils/health-status';
3537
import {MemberUtils, Permission} from '@shared/utils/member';
3638
import _ from 'lodash';
37-
import {Subject} from 'rxjs';
39+
import {forkJoin, of, Subject} from 'rxjs';
3840
import {filter, switchMap, take, takeUntil} from 'rxjs/operators';
3941

4042
@Component({
@@ -49,6 +51,7 @@ export class AutomaticBackupListComponent implements OnInit, OnDestroy {
4951
private _currentGroupConfig: GroupConfig;
5052
private _selectedProject = {} as Project;
5153
private _backups = [];
54+
private _clusters = new Map<string, Cluster>();
5255
dataSource = new MatTableDataSource<EtcdBackupConfig>();
5356
isInitialized = true;
5457

@@ -57,7 +60,7 @@ export class AutomaticBackupListComponent implements OnInit, OnDestroy {
5760
}
5861

5962
get columns(): string[] {
60-
return ['status', 'name', 'cluster', 'destination', 'schedule', 'keep', 'created', 'actions'];
63+
return ['status', 'name', 'clusterName', 'cluster', 'destination', 'schedule', 'keep', 'created', 'actions'];
6164
}
6265

6366
get isEmpty(): boolean {
@@ -82,6 +85,7 @@ export class AutomaticBackupListComponent implements OnInit, OnDestroy {
8285

8386
constructor(
8487
private readonly _backupService: BackupService,
88+
private readonly _clusterService: ClusterService,
8589
private readonly _projectService: ProjectService,
8690
private readonly _userService: UserService,
8791
private readonly _matDialog: MatDialog,
@@ -101,20 +105,24 @@ export class AutomaticBackupListComponent implements OnInit, OnDestroy {
101105

102106
this._projectService.selectedProject
103107
.pipe(
104-
switchMap(project => {
105-
this._selectedProject = project;
106-
return this._userService.getCurrentUserGroup(project.id);
107-
})
108+
switchMap(project =>
109+
forkJoin({
110+
userGroup: this._userService.getCurrentUserGroup(project.id).pipe(take(1)),
111+
backups: this._backupService.list(project.id).pipe(take(1)),
112+
clusters: this._clusterService.clusters(project.id, false).pipe(take(1)),
113+
project: of(project),
114+
})
115+
)
108116
)
109117
.pipe(takeUntil(this._unsubscribe))
110-
.subscribe(userGroup => (this._currentGroupConfig = this._userService.getCurrentUserGroupConfig(userGroup)));
118+
.subscribe(({userGroup, backups, clusters, project}) => {
119+
this._selectedProject = project;
120+
this._currentGroupConfig = this._userService.getCurrentUserGroupConfig(userGroup);
111121

112-
this._projectService.selectedProject
113-
.pipe(switchMap(project => this._backupService.list(project.id)))
114-
.pipe(takeUntil(this._unsubscribe))
115-
.subscribe(backups => {
116122
this._backups = backups;
117123
this.dataSource.data = this._backups;
124+
125+
clusters.forEach(cluster => this._clusters.set(cluster.id, cluster));
118126
});
119127
}
120128

@@ -136,6 +144,10 @@ export class AutomaticBackupListComponent implements OnInit, OnDestroy {
136144
return getBackupHealthStatus(backup, condition);
137145
}
138146

147+
getClusterName(backup: EtcdBackupConfig): string {
148+
return this._clusters.get(backup.spec.clusterId)?.name ?? '-';
149+
}
150+
139151
delete(backup: EtcdBackupConfig): void {
140152
const config: MatDialogConfig = {
141153
data: {

modules/web/src/app/backup/list/automatic-backup/template.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@
6161
</td>
6262
</ng-container>
6363

64+
<ng-container matColumnDef="clusterName">
65+
<th mat-header-cell
66+
*matHeaderCellDef
67+
class="km-header-cell p-15">Cluster Name
68+
</th>
69+
<td mat-cell
70+
*matCellDef="let element">
71+
<span>{{getClusterName(element)}}</span>
72+
</td>
73+
</ng-container>
74+
6475
<ng-container matColumnDef="cluster">
6576
<th mat-header-cell
6677
*matHeaderCellDef

modules/web/src/app/backup/list/restore/component.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import {Component, OnDestroy, OnInit, TrackByFunction, ViewChild} from '@angular
1616
import {MatDialog, MatDialogConfig} from '@angular/material/dialog';
1717
import {MatPaginator} from '@angular/material/paginator';
1818
import {MatTableDataSource} from '@angular/material/table';
19+
import {Cluster} from '@app/shared/entity/cluster';
1920
import {BackupService} from '@core/services/backup';
21+
import {ClusterService} from '@core/services/cluster';
2022
import {NotificationService} from '@core/services/notification';
2123
import {ProjectService} from '@core/services/project';
2224
import {UserService} from '@core/services/user';
@@ -28,7 +30,7 @@ import {Project} from '@shared/entity/project';
2830
import {GroupConfig} from '@shared/model/Config';
2931
import {MemberUtils, Permission} from '@shared/utils/member';
3032
import _ from 'lodash';
31-
import {Subject} from 'rxjs';
33+
import {forkJoin, Subject} from 'rxjs';
3234
import {filter, switchMap, take, takeUntil, tap} from 'rxjs/operators';
3335

3436
@Component({
@@ -44,6 +46,7 @@ export class RestoreListComponent implements OnInit, OnDestroy {
4446
private _currentGroupConfig: GroupConfig;
4547
private _selectedProject = {} as Project;
4648
private _restores = [];
49+
private _clusters = new Map<string, Cluster>();
4750
dataSource = new MatTableDataSource<EtcdRestore>();
4851
isInitialized = true;
4952

@@ -52,7 +55,7 @@ export class RestoreListComponent implements OnInit, OnDestroy {
5255
}
5356

5457
get columns(): string[] {
55-
return ['name', 'phase', 'clusterID', 'backupName', 'destination', 'actions'];
58+
return ['name', 'phase', 'clusterName', 'clusterID', 'backupName', 'destination', 'actions'];
5659
}
5760

5861
get isEmpty(): boolean {
@@ -69,6 +72,7 @@ export class RestoreListComponent implements OnInit, OnDestroy {
6972

7073
constructor(
7174
private readonly _backupService: BackupService,
75+
private readonly _clusterService: ClusterService,
7276
private readonly _projectService: ProjectService,
7377
private readonly _userService: UserService,
7478
private readonly _matDialog: MatDialog,
@@ -91,16 +95,21 @@ export class RestoreListComponent implements OnInit, OnDestroy {
9195
.subscribe(_ => this._onChange.next());
9296

9397
this._onChange
94-
.pipe(switchMap(_ => this._userService.getCurrentUserGroup(this._selectedProject.id).pipe(take(1))))
95-
.pipe(takeUntil(this._unsubscribe))
96-
.subscribe(userGroup => (this._currentGroupConfig = this._userService.getCurrentUserGroupConfig(userGroup)));
97-
98-
this._onChange
99-
.pipe(switchMap(_ => this._backupService.restoreList(this._selectedProject.id)))
98+
.pipe(
99+
switchMap(_ =>
100+
forkJoin({
101+
userGroup: this._userService.getCurrentUserGroup(this._selectedProject.id).pipe(take(1)),
102+
restores: this._backupService.restoreList(this._selectedProject.id).pipe(take(1)),
103+
clusters: this._clusterService.clusters(this._selectedProject.id, false).pipe(take(1)),
104+
})
105+
)
106+
)
100107
.pipe(takeUntil(this._unsubscribe))
101-
.subscribe(restores => {
108+
.subscribe(({userGroup, restores, clusters}) => {
109+
this._currentGroupConfig = this._userService.getCurrentUserGroupConfig(userGroup);
102110
this._restores = restores;
103111
this.dataSource.data = this._restores;
112+
clusters.forEach(cluster => this._clusters.set(cluster.id, cluster));
104113
});
105114
}
106115

@@ -109,6 +118,10 @@ export class RestoreListComponent implements OnInit, OnDestroy {
109118
this._unsubscribe.complete();
110119
}
111120

121+
getClusterName(restore: EtcdRestore): string {
122+
return this._clusters.get(restore.spec.clusterId)?.name ?? '-';
123+
}
124+
112125
delete(restore: EtcdRestore): void {
113126
const config: MatDialogConfig = {
114127
data: {

modules/web/src/app/backup/list/restore/template.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@
5151
</td>
5252
</ng-container>
5353

54+
<ng-container matColumnDef="clusterName">
55+
<th mat-header-cell
56+
*matHeaderCellDef
57+
class="km-header-cell p-15">Cluster Name
58+
</th>
59+
<td mat-cell
60+
*matCellDef="let element">
61+
<span>{{getClusterName(element)}}</span>
62+
</td>
63+
</ng-container>
64+
5465
<ng-container matColumnDef="clusterID">
5566
<th mat-header-cell
5667
*matHeaderCellDef

0 commit comments

Comments
 (0)