// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance // with the License. A copy of the License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "LICENSE.txt" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES // OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and // limitations under the License. import {CloudFormationResourceStatus, Region} from '../../types/base' import {ClusterName, ClusterStatus} from '../../types/clusters' import {StackEvent, StackEvents} from '../../types/stackevents' import React from 'react' import {Link, useSearchParams} from 'react-router-dom' // Model import {DescribeCluster, GetClusterStackEvents} from '../../model' import { consoleDomain, getState, setState, clearState, useState, } from '../../store' import {useCollection} from '@cloudscape-design/collection-hooks' import {findFirst} from '../../util' // UI Elements import { Button, CollectionPreferences, Header, Pagination, SpaceBetween, Table, TextFilter, } from '@cloudscape-design/components' // Components import {StackEventStatusIndicator} from '../../components/Status' import DateView from '../../components/date/DateView' import Loading from '../../components/Loading' import EmptyState from '../../components/EmptyState' function EventStatus(stackEvent: StackEvent) { const {logicalResourceId, resourceStatus} = stackEvent const clusterName: ClusterName = useState(['app', 'clusters', 'selected']) const clusterPath = ['clusters', 'index', clusterName] let headNode = useState([...clusterPath, 'headNode']) const events: StackEvents = useState([ 'clusters', 'index', clusterName, 'stackevents', 'events', ]) let getHeadNode = (events: StackEvent[]) => { let event = findFirst( events, (e: StackEvent) => e.logicalResourceId === 'HeadNode', ) if (event) return {instanceId: event.physicalResourceId} } if ( logicalResourceId.startsWith('HeadNodeWaitCondition') && resourceStatus === CloudFormationResourceStatus.CreateFailed && !headNode ) { headNode = getHeadNode(events) } return ( {headNode && logicalResourceId.startsWith('HeadNodeWaitCondition') && resourceStatus === CloudFormationResourceStatus.CreateFailed && (
  Logs:{' '} cfn-init
)}
) } export default function ClusterStackEvents() { const clusterName: ClusterName = useState(['app', 'clusters', 'selected']) const events: StackEvents = useState([ 'clusters', 'index', clusterName, 'stackevents', 'events', ]) const columns = useState(['app', 'clusters', 'stackevents', 'columns']) || [ 'timestamp', 'logicalId', 'status', 'statusReason', ] const pageSize = useState(['app', 'clusters', 'stackevents', 'pageSize']) || 100 const defaultRegion = useState(['aws', 'region']) const region: Region = useState(['app', 'selectedRegion']) || defaultRegion let [searchParams, setSearchParams] = useSearchParams() const clusterPath = ['clusters', 'index', clusterName] const cluster = useState(clusterPath) let cfnHref = `${consoleDomain( region, )}/cloudformation/home?region=${region}#/stacks?filteringStatus=active&filteringText=${clusterName}` if (cluster) cfnHref = `${consoleDomain( region, )}/cloudformation/home?region=${region}#/stacks/events?filteringStatus=active&filteringText=${clusterName}&viewNested=true&hideStacks=false&stackId=${encodeURIComponent( cluster.cloudformationStackArn, )}` React.useEffect(() => { const clusterName: ClusterName = getState(['app', 'clusters', 'selected']) const clusterPath = ['clusters', 'index', clusterName] const cluster = getState(clusterPath) const headNode = getState([...clusterPath, 'headNode']) GetClusterStackEvents(clusterName) DescribeCluster(clusterName) let timerId: ReturnType | undefined = setInterval( () => { if (cluster.clusterStatus !== ClusterStatus.CreateInProgress) { clearInterval(timerId) timerId = undefined } else { if (!headNode) DescribeCluster(clusterName) GetClusterStackEvents(clusterName) } }, 5000, ) return () => { timerId && clearInterval(timerId) } }, []) const refreshStackEvents = () => { clearState(['clusters', 'index', clusterName, 'stackevents']) GetClusterStackEvents(clusterName) } const { items, actions, filteredItemsCount, collectionProps, filterProps, paginationProps, } = useCollection(events || [], { filtering: { empty: , noMatch: ( actions.setFiltering('')}> Clear filter } /> ), }, pagination: {pageSize: pageSize}, sorting: {}, selection: {}, }) React.useEffect(() => { filterProps.onChange({ detail: {filteringText: searchParams.get('filter') || ''}, }) // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchParams]) return events ? ( } > Events } resizableColumns wrapLines visibleColumns={columns} variant="container" columnDefinitions={[ { id: 'timestamp', header: 'Timestamp', cell: event => , sortingField: 'timestamp', }, { id: 'logicalId', header: 'Logical ID', cell: event => event.logicalResourceId, }, { id: 'status', header: 'Status', cell: event => , }, { id: 'statusReason', header: 'Status reason', cell: event => event.resourceStatusReason, }, ]} loading={events === null} items={items} loadingText="Loading Logs..." pagination={} filter={ { searchParams.set('filter', e.detail.filteringText) setSearchParams(searchParams) filterProps.onChange(e) }} countText={`Results: ${filteredItemsCount}`} filteringAriaLabel="Filter logs" /> } preferences={ { setState( ['app', 'clusters', 'stackevents', 'columns'], detail.visibleContent, ) setState( ['app', 'clusters', 'stackevents', 'pageSize'], detail.pageSize, ) }} title="Preferences" confirmLabel="Confirm" cancelLabel="Cancel" preferences={{ pageSize: pageSize, visibleContent: columns, }} pageSizePreference={{ title: 'Select page size', options: [ {value: 100, label: '100 Logs'}, {value: 250, label: '250 Logs'}, {value: 500, label: '500 Logs'}, ], }} visibleContentPreference={{ title: 'Select visible content', options: [ { label: 'Log columns', options: [ {id: 'timestamp', label: 'Timestamp'}, {id: 'logicalId', label: 'Logical ID'}, {id: 'status', label: 'Status'}, {id: 'statusReason', label: 'Status reason'}, ], }, ], }} /> } /> ) : ( ) }