1 import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
2 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
3 import { MultiClusterService } from '~/app/shared/api/multi-cluster.service';
4 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
5 import { Icons } from '~/app/shared/enum/icons.enum';
6 import { CdTableAction } from '~/app/shared/models/cd-table-action';
7 import { CdTableColumn } from '~/app/shared/models/cd-table-column';
8 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
9 import { ModalService } from '~/app/shared/services/modal.service';
10 import { MultiClusterFormComponent } from '../multi-cluster-form/multi-cluster-form.component';
11 import { TableComponent } from '~/app/shared/datatable/table/table.component';
12 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
13 import { Permissions } from '~/app/shared/models/permissions';
14 import { DeleteConfirmationModalComponent } from '~/app/shared/components/delete-confirmation-modal/delete-confirmation-modal.component';
15 import { NotificationService } from '~/app/shared/services/notification.service';
16 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
17 import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
18 import { MultiCluster } from '~/app/shared/models/multi-cluster';
19 import { ActivatedRoute, Router } from '@angular/router';
20 import { CookiesService } from '~/app/shared/services/cookie.service';
21 import { Observable, Subscription } from 'rxjs';
22 import { SettingsService } from '~/app/shared/api/settings.service';
23 import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
24 import { ListWithDetails } from '~/app/shared/classes/list-with-details.class';
27 selector: 'cd-multi-cluster-list',
28 templateUrl: './multi-cluster-list.component.html',
29 styleUrls: ['./multi-cluster-list.component.scss']
31 export class MultiClusterListComponent extends ListWithDetails implements OnInit, OnDestroy {
32 @ViewChild(TableComponent)
33 table: TableComponent;
34 @ViewChild('urlTpl', { static: true })
35 public urlTpl: TemplateRef<any>;
36 @ViewChild('durationTpl', { static: true })
37 durationTpl: TemplateRef<any>;
38 private subs = new Subscription();
39 permissions: Permissions;
40 tableActions: CdTableAction[];
41 clusterTokenStatus: object = {};
42 columns: Array<CdTableColumn> = [];
44 selection = new CdTableSelection();
45 bsModalRef: NgbModalRef;
46 clustersTokenMap: Map<string, string> = new Map<string, string>();
48 modalRef: NgbModalRef;
52 managedByConfig$: Observable<any>;
53 prometheusConnectionError: any[] = [];
56 private multiClusterService: MultiClusterService,
57 private router: Router,
58 public actionLabels: ActionLabelsI18n,
59 private notificationService: NotificationService,
60 private authStorageService: AuthStorageService,
61 private modalService: ModalService,
62 private cookieService: CookiesService,
63 private settingsService: SettingsService,
64 private cdsModalService: ModalCdsService,
65 private route: ActivatedRoute
72 name: this.actionLabels.CONNECT,
73 disable: (selection: CdTableSelection) => this.getDisable('connect', selection),
74 click: () => this.openRemoteClusterInfoModal('connect')
79 name: this.actionLabels.EDIT,
80 disable: (selection: CdTableSelection) => this.getDisable('edit', selection),
81 click: () => this.openRemoteClusterInfoModal('edit')
86 name: this.actionLabels.RECONNECT,
87 disable: (selection: CdTableSelection) => this.getDisable('reconnect', selection),
88 click: () => this.openRemoteClusterInfoModal('reconnect')
93 name: this.actionLabels.DISCONNECT,
94 disable: (selection: CdTableSelection) => this.getDisable('disconnect', selection),
95 click: () => this.openDeleteClusterModal()
98 this.permissions = this.authStorageService.getPermissions();
103 this.multiClusterService.subscribe((resp: object) => {
104 if (resp && resp['config']) {
105 this.hubUrl = resp['hub_url'];
106 this.currentUrl = resp['current_url'];
107 const clusterDetailsArray = Object.values(resp['config']).flat();
108 this.data = clusterDetailsArray;
109 this.checkClusterConnectionStatus();
110 this.data.forEach((cluster: any) => {
111 cluster['remainingTimeWithoutSeconds'] = 0;
112 if (cluster['ttl'] && cluster['ttl'] > 0) {
113 cluster['ttl'] = cluster['ttl'] * 1000;
114 cluster['remainingTimeWithoutSeconds'] = this.getRemainingTimeWithoutSeconds(
117 cluster['remainingDays'] = this.getRemainingDays(cluster['ttl']);
118 cluster['expiryDate'] = new Date(Date.now() + cluster['ttl']).toLocaleString();
127 prop: 'cluster_alias',
128 name: $localize`Alias`,
132 prop: 'cluster_connection_status',
133 name: $localize`Connection`,
135 cellTransformation: CellTemplate.badge,
136 customTemplateConfig: {
138 1: { value: 'DISCONNECTED', class: 'badge-danger' },
139 0: { value: 'CONNECTED', class: 'badge-success' },
140 2: { value: 'CHECKING..', class: 'badge-info' }
146 name: $localize`FSID`,
151 name: $localize`URL`,
153 cellTemplate: this.urlTpl
157 name: $localize`User`,
162 name: $localize`Token expires`,
164 cellTemplate: this.durationTpl
169 this.multiClusterService.subscribeClusterTokenStatus((resp: object) => {
170 this.clusterTokenStatus = resp;
171 this.checkClusterConnectionStatus();
175 this.managedByConfig$ = this.settingsService.getValues('MANAGED_BY_CLUSTERS');
178 ngOnDestroy(): void {
179 this.subs.unsubscribe();
182 getRemainingDays(time: number): number {
183 if (time === undefined || time == null) {
189 const toDays = 1000 * 60 * 60 * 24;
190 return Math.max(0, Math.floor(time / toDays));
193 getRemainingTimeWithoutSeconds(time: number): number {
194 return Math.floor(time / (1000 * 60)) * 60 * 1000;
197 checkClusterConnectionStatus() {
198 if (this.clusterTokenStatus && this.data) {
199 this.data.forEach((cluster: MultiCluster) => {
200 const clusterStatus = this.clusterTokenStatus[cluster.name];
201 if (clusterStatus !== undefined) {
202 cluster.cluster_connection_status = clusterStatus.status;
203 cluster.ttl = clusterStatus.time_left;
205 cluster.cluster_connection_status = 2;
207 if (cluster.cluster_alias === 'local-cluster') {
208 cluster.cluster_connection_status = 0;
214 openRemoteClusterInfoModal(action: string) {
215 const initialState = {
216 clustersData: this.data,
218 cluster: this.selection.first()
220 this.bsModalRef = this.modalService.show(MultiClusterFormComponent, initialState, {
223 this.bsModalRef.componentInstance.submitAction.subscribe(() => {
224 const currentRoute = this.router.url.split('?')[0];
225 this.multiClusterService.refreshMultiCluster(currentRoute);
226 this.checkClusterConnectionStatus();
227 this.multiClusterService.isClusterAdded(true);
231 openDeleteClusterModal() {
232 const cluster = this.selection.first();
233 this.modalRef = this.cdsModalService.show(DeleteConfirmationModalComponent, {
234 infoMessage: $localize`Please note that the data for the disconnected cluster will be visible for a duration of ~ 5 minutes. After this period, it will be automatically removed.`,
235 actionDescription: $localize`Disconnect`,
236 itemDescription: $localize`Cluster`,
237 itemNames: [cluster['cluster_alias'] + ' - ' + cluster['user']],
239 this.multiClusterService.deleteCluster(cluster['name'], cluster['user']).subscribe(() => {
240 this.cookieService.deleteToken(`${cluster['name']}-${cluster['user']}`);
241 this.multiClusterService.showPrometheusDelayMessage(true);
242 this.cdsModalService.dismissAll();
243 this.notificationService.show(
244 NotificationType.success,
245 $localize`Disconnected cluster '${cluster['cluster_alias']}'`
247 const currentRoute = this.router.url.split('?')[0];
248 this.multiClusterService.refreshMultiCluster(currentRoute);
253 getDisable(action: string, selection: CdTableSelection): string | boolean {
254 if (this.hubUrl !== this.currentUrl) {
255 return $localize`Please switch to the local-cluster to ${action} a remote cluster`;
257 if (!selection.hasSelection && action !== 'connect') {
258 return $localize`Please select one or more clusters to ${action}`;
260 if (selection.hasSingleSelection) {
261 const cluster = selection.first();
262 if (cluster['cluster_alias'] === 'local-cluster' && action !== 'connect') {
263 return $localize`Cannot ${action} local cluster`;
269 updateSelection(selection: CdTableSelection) {
270 this.selection = selection;
273 setExpandedRow(expandedRow: any) {
274 super.setExpandedRow(expandedRow);
275 this.router.navigate(['performance-details'], { relativeTo: this.route });
279 this.multiClusterService.refresh();
280 this.multiClusterService.refreshTokenStatus();