import React from 'react'; import { Dropdown, MenuItem } from 'react-bootstrap'; import type { Application, IManifest, IMoniker, IRouterInjectedProps } from '@spinnaker/core'; import { AccountTag, CollapsibleSection, ConsoleOutputLink, CopyToClipboard, InstanceDetailsHeader, InstanceLinks, InstanceReader, ManifestReader, RecentHistoryService, robotToHuman, SETTINGS, timestamp, useModal, withRouter, } from '@spinnaker/core'; import type { IKubernetesInstance } from '../../interfaces'; import { findKubernetesInstanceIdentifier } from './kubernetesInstanceDetails.utils'; import { AnnotationCustomSections } from '../../manifest/AnnotationCustomSections'; import { ManifestLabels } from '../../manifest/ManifestLabels'; import { ManifestQos } from '../../manifest/ManifestQos'; import { ManifestResources } from '../../manifest/ManifestResources'; import { ManifestArtifact } from '../../manifest/artifact/ManifestArtifact'; import { DeleteModal } from '../../manifest/delete/DeleteModal'; import { KubernetesManifestCommandBuilder } from '../../manifest/manifestCommandBuilder.service'; import { ManifestCondition } from '../../manifest/status/ManifestCondition'; import { ManifestWizard } from '../../manifest/wizard/ManifestWizard'; import { ManifestEvents } from '../../pipelines/stages/deployManifest/manifestStatus/ManifestEvents'; interface IInstanceFromStateParams { instanceId: string; } export interface IKubernetesInstanceDetailsProps { $stateParams?: { instanceId?: string; }; app: Application; autoClose?: () => void; environment?: string; instance?: IInstanceFromStateParams; moniker?: IMoniker; } interface IConsoleOutputInstance { account: string; region: string; id: string; provider: string; } interface IKubernetesInstanceDetailsState { instance?: IKubernetesInstance; loading: boolean; manifest?: IKubernetesInstanceManifest; } interface IKubernetesInstanceManifest extends IManifest { metrics?: any; } export interface IKubernetesInstanceActionsProps { app: Application; instance: IKubernetesInstance; manifest: IManifest; } export class KubernetesInstanceDetailsComponent extends React.Component< IKubernetesInstanceDetailsProps & IRouterInjectedProps, IKubernetesInstanceDetailsState > { protected readonly renderActions: boolean = true; protected readonly instanceLinksPlacement: 'afterInformation' | 'afterSections' = 'afterSections'; public state: IKubernetesInstanceDetailsState = { loading: true, }; private isUnmounted = false; private appReady = false; private loadRequestId = 0; private unsubscribeFromRefresh: () => void; public componentDidMount(): void { this.props.app .ready() .then(() => { if (this.isUnmounted) { return; } this.appReady = true; this.loadInstance(); this.unsubscribeFromRefresh = this.props.app.onRefresh(this.loadInstance); }) .catch(this.autoClose); } public componentDidUpdate(prevProps: IKubernetesInstanceDetailsProps): void { if (this.appReady && this.getInstanceId(prevProps) !== this.getInstanceId(this.props)) { this.loadInstance(); } } public componentWillUnmount(): void { this.isUnmounted = true; if (this.unsubscribeFromRefresh) { this.unsubscribeFromRefresh(); } } private getInstanceId = (props: IKubernetesInstanceDetailsProps): string => props.instance?.instanceId || props.$stateParams?.instanceId; private loadInstance = (): void => { const { app } = this.props; const instanceId = this.getInstanceId(this.props); const requestId = ++this.loadRequestId; const identifier = findKubernetesInstanceIdentifier(app, instanceId, (extraData) => RecentHistoryService.addExtraDataToLatest('instances', extraData), ); if (!identifier) { this.autoClose(); return; } this.setState({ loading: true }); Promise.all([ InstanceReader.getInstanceDetails(identifier.account, identifier.namespace, identifier.name).then( (instanceDetails: IKubernetesInstance) => ({ ...instanceDetails, id: identifier.id, name: identifier.name, provider: 'kubernetes', }), ), ManifestReader.getManifest(identifier.account, identifier.namespace, identifier.name).then( (manifest) => manifest as IKubernetesInstanceManifest, ), ]) .then(([instance, manifest]) => { if (this.isCurrentLoad(requestId)) { this.setState({ instance, manifest, loading: false }); } }) .catch(() => { if (this.isCurrentLoad(requestId)) { this.autoClose(); } }); }; private isCurrentLoad(requestId: number): boolean { return !this.isUnmounted && requestId === this.loadRequestId; } private autoClose = (): void => { if (this.isUnmounted) { return; } if (this.props.autoClose) { this.props.autoClose(); return; } this.props.stateService.params.allowModalToStayOpen = true; this.props.stateService.go('^', null, { location: 'replace' }); }; private buildConsoleOutputInstance(instance: IKubernetesInstance): IConsoleOutputInstance { return { account: instance.account, region: instance.zone || instance.namespace, id: instance.humanReadableName || instance.name, provider: instance.provider || instance.cloudProvider || 'kubernetes', }; } public render(): JSX.Element { const { app } = this.props; const { instance, loading, manifest } = this.state; if (loading || !instance || !manifest) { return (