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 (
); } const manifestBody = manifest.manifest; const statusConditions = manifestBody?.status?.conditions || []; return (
{this.renderActions && (
)}
{this.renderInformationSection(instance, manifest)} {this.renderInstanceLinks('afterInformation', instance)} {statusConditions.map((condition: any, index: number) => (
))}
{manifest.artifacts?.length > 0 && (
    {manifest.artifacts.map((artifact, index) => (
  • ))}
)} {this.renderInstanceLinks('afterSections', instance)}
); } private renderInstanceLinks( placement: 'afterInformation' | 'afterSections', instance: IKubernetesInstance, ): JSX.Element | null { const { app, environment } = this.props; if (this.instanceLinksPlacement !== placement) { return null; } return ( ); } private renderInformationSection(instance: IKubernetesInstance, manifest: IManifest): JSX.Element { const manifestBody = manifest.manifest; return (
Created
{timestamp(instance.createdTime)}
Account
Namespace
{instance.namespace}
Kind
{instance.kind}
QOS Class
Logs
); } } export const KubernetesInstanceDetails = withRouter(KubernetesInstanceDetailsComponent); export function KubernetesInstanceActions({ app, instance, manifest }: IKubernetesInstanceActionsProps) { const deleteModal = useModal(); if (!SETTINGS.kubernetesAdHocInfraWritesEnabled) { return null; } const editInstance = (): void => { KubernetesManifestCommandBuilder.buildNewManifestCommand( app, manifest.manifest, instance.moniker, instance.account, ).then((builtCommand) => { ManifestWizard.show({ title: 'Edit Manifest', application: app, command: builtCommand }); }); }; return ( <> {robotToHuman(instance.kind)} Actions Delete Edit ); }