-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Drill-down for ECS tasks (#180)
Co-authored-by: Fabio Torchetti <fabbari@amazon.com>
- Loading branch information
1 parent
c1f5b50
commit 58676ee
Showing
6 changed files
with
417 additions
and
39 deletions.
There are no files selected for viewing
157 changes: 157 additions & 0 deletions
157
plugins/ecs/frontend/src/components/EcsDrawer/EcsDrawer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
/** | ||
* 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. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import React, { ChangeEvent, useState } from 'react'; | ||
|
||
import Drawer from '@material-ui/core/Drawer'; | ||
import Grid from '@material-ui/core/Grid'; | ||
import IconButton from '@material-ui/core/IconButton'; | ||
import Typography from '@material-ui/core/Typography'; | ||
import Button from '@material-ui/core/Button'; | ||
import { | ||
createStyles, | ||
makeStyles, | ||
Theme, | ||
withStyles, | ||
} from '@material-ui/core/styles'; | ||
import CloseIcon from '@material-ui/icons/Close'; | ||
|
||
const useDrawerContentStyles = makeStyles((_theme: Theme) => | ||
createStyles({ | ||
header: { | ||
display: 'flex', | ||
flexDirection: 'row', | ||
justifyContent: 'space-between', | ||
}, | ||
icon: { | ||
fontSize: 20, | ||
}, | ||
}), | ||
); | ||
|
||
interface EcsDrawerContentProps { | ||
close: () => void; | ||
title: string; | ||
header?: React.ReactNode; | ||
children?: React.ReactNode; | ||
} | ||
|
||
const EcsDrawerContent = ({ | ||
children, | ||
header, | ||
title, | ||
close, | ||
}: EcsDrawerContentProps) => { | ||
const classes = useDrawerContentStyles(); | ||
|
||
return ( | ||
<> | ||
<div className={classes.header}> | ||
<Grid container justifyContent="flex-start" alignItems="flex-start"> | ||
<Grid item xs={11}> | ||
<Typography variant="h5">{title}</Typography> | ||
</Grid> | ||
<Grid item xs={1}> | ||
<IconButton | ||
key="dismiss" | ||
title="Close the drawer" | ||
onClick={() => close()} | ||
color="inherit" | ||
> | ||
<CloseIcon className={classes.icon} /> | ||
</IconButton> | ||
</Grid> | ||
<Grid item xs={12}> | ||
{header} | ||
</Grid> | ||
</Grid> | ||
</div> | ||
<div>{children}</div> | ||
</> | ||
); | ||
}; | ||
|
||
export interface EcsDrawerProps { | ||
open?: boolean; | ||
title: string; | ||
label: React.ReactNode; | ||
drawerContentsHeader?: React.ReactNode; | ||
children?: React.ReactNode; | ||
} | ||
|
||
const useDrawerStyles = makeStyles((theme: Theme) => | ||
createStyles({ | ||
paper: { | ||
width: '50%', | ||
justifyContent: 'space-between', | ||
padding: theme.spacing(2.5), | ||
}, | ||
}), | ||
); | ||
|
||
const DrawerButton = withStyles({ | ||
root: { | ||
padding: '6px 5px', | ||
}, | ||
label: { | ||
textTransform: 'none', | ||
}, | ||
})(Button); | ||
|
||
export const EcsDrawer = ({ | ||
open, | ||
label, | ||
drawerContentsHeader, | ||
title, | ||
children, | ||
}: EcsDrawerProps) => { | ||
const classes = useDrawerStyles(); | ||
const [isOpen, setIsOpen] = useState<boolean>(open ?? false); | ||
|
||
const toggleDrawer = (e: ChangeEvent<{}>, newValue: boolean) => { | ||
e.stopPropagation(); | ||
setIsOpen(newValue); | ||
}; | ||
|
||
return ( | ||
<> | ||
<DrawerButton | ||
onClick={event => { | ||
setIsOpen(true); | ||
event.stopPropagation(); | ||
}} | ||
style={{ width: '100%', textAlign: 'left', justifyContent: 'left' }} | ||
> | ||
{label} | ||
</DrawerButton> | ||
<Drawer | ||
classes={{ | ||
paper: classes.paper, | ||
}} | ||
anchor="right" | ||
open={isOpen} | ||
onClose={(e: any) => toggleDrawer(e, false)} | ||
onClick={event => event.stopPropagation()} | ||
> | ||
{isOpen && ( | ||
<EcsDrawerContent | ||
header={drawerContentsHeader} | ||
title={title} | ||
children={children} | ||
close={() => setIsOpen(false)} | ||
/> | ||
)} | ||
</Drawer> | ||
</> | ||
); | ||
}; |
128 changes: 128 additions & 0 deletions
128
plugins/ecs/frontend/src/components/EcsDrawer/EcsTaskDrawer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/** | ||
* 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. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import { KeyValuePair, Task } from '@aws-sdk/client-ecs'; | ||
import Grid from '@material-ui/core/Grid'; | ||
import Typography from '@material-ui/core/Typography'; | ||
import React from 'react'; | ||
import { | ||
formatTime, | ||
getTaskDefinition, | ||
getTaskId, | ||
stringOrDefault, | ||
} from '../../util'; | ||
import { StructuredMetadataTable } from '@backstage/core-components'; | ||
import { TaskStatus } from '../EcsServices/EcsServices'; | ||
import { Box } from '@material-ui/core'; | ||
import { EcsContainer } from '../EcsServices/EcsContainer'; | ||
import { EcsDrawer } from './EcsDrawer'; | ||
|
||
const formatTaskOverview = (task: Task) => { | ||
return { | ||
ID: getTaskId(task.taskArn), | ||
lastStatus: <TaskStatus status={task.lastStatus} />, | ||
desiredStatus: <TaskStatus status={task.desiredStatus} />, | ||
createdAt: formatTime(task.createdAt), | ||
}; | ||
}; | ||
|
||
const formatTaskConfiguration = (task: Task) => { | ||
let attachmentDetails: KeyValuePair[] | undefined; | ||
|
||
if (task.attachments) { | ||
if (task.attachments.length > 0) { | ||
attachmentDetails = task.attachments[0].details; | ||
} | ||
} | ||
|
||
return { | ||
CPU: stringOrDefault(task.cpu), | ||
memory: stringOrDefault(task.memory), | ||
platformVersion: task.platformVersion, | ||
capacityProvider: stringOrDefault(task.capacityProviderName), | ||
launchType: stringOrDefault(task.launchType), | ||
containerInstanceID: stringOrDefault( | ||
task.containerInstanceArn?.split('/')[1], | ||
), | ||
taskDefinition: stringOrDefault(getTaskDefinition(task.taskDefinitionArn)), | ||
taskGroup: stringOrDefault(task.group), | ||
ENI_ID: stringOrDefault( | ||
attachmentDetails?.find(e => e.name === 'networkInterfaceId')?.value, | ||
), | ||
subnetID: stringOrDefault( | ||
attachmentDetails?.find(e => e.name === 'subnetId')?.value, | ||
), | ||
privateIPAddress: stringOrDefault( | ||
attachmentDetails?.find(e => e.name === 'privateIPv4Address')?.value, | ||
), | ||
}; | ||
}; | ||
|
||
export const EcsTaskDrawer = ({ task }: { task: Task }) => { | ||
if (!task) { | ||
return null; | ||
} | ||
|
||
const taskId = getTaskId(task.taskArn); | ||
|
||
return ( | ||
<EcsDrawer label={<div>{taskId}</div>} title={`Task ID ${taskId}`}> | ||
<Grid | ||
container | ||
direction="column" | ||
justifyContent="space-between" | ||
alignItems="flex-start" | ||
spacing={2} | ||
style={{ position: 'relative' }} | ||
> | ||
<Grid item xs={12} spacing={0}> | ||
<Typography> | ||
<Box component="span" fontWeight="fontWeightMedium"> | ||
Overview | ||
</Box> | ||
</Typography> | ||
</Grid> | ||
<Grid container item xs={12} spacing={0}> | ||
<StructuredMetadataTable metadata={formatTaskOverview(task)} /> | ||
</Grid> | ||
<Grid item xs={12} spacing={0}> | ||
<Typography> | ||
<Box component="span" fontWeight="fontWeightMedium"> | ||
Configuration | ||
</Box> | ||
</Typography> | ||
</Grid> | ||
<Grid container item xs={12} spacing={0}> | ||
<StructuredMetadataTable metadata={formatTaskConfiguration(task)} /> | ||
</Grid> | ||
<Grid item xs={12}> | ||
<Typography variant="h5">Containers</Typography> | ||
</Grid> | ||
{task.containers?.map(container => ( | ||
<> | ||
<Grid item xs={12} spacing={0}> | ||
<Typography> | ||
<Box component="span" fontWeight="fontWeightMedium"> | ||
{container.name} | ||
</Box> | ||
</Typography> | ||
</Grid> | ||
<Grid item xs={12}> | ||
<EcsContainer container={container} /> | ||
</Grid> | ||
</> | ||
))} | ||
</Grid> | ||
</EcsDrawer> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/** | ||
* 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. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
export * from './EcsDrawer'; |
52 changes: 52 additions & 0 deletions
52
plugins/ecs/frontend/src/components/EcsServices/EcsContainer.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/** | ||
* 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. | ||
* You may obtain a copy of the License at | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import { Container } from '@aws-sdk/client-ecs'; | ||
import { | ||
OverflowTooltip, | ||
StructuredMetadataTable, | ||
} from '@backstage/core-components'; | ||
import React from 'react'; | ||
import { TaskHealthStatus, TaskStatus } from '.'; | ||
import { Box } from '@material-ui/core'; | ||
import { stringOrDefault } from '../../util'; | ||
|
||
const overflowMaxWidth = '250px'; | ||
|
||
const formatContainer = (container: Container) => { | ||
return { | ||
ID: stringOrDefault(container.containerArn?.split('/')[3]), | ||
status: <TaskStatus status={container.lastStatus} />, | ||
healthStatus: <TaskHealthStatus status={container.healthStatus} />, | ||
CPU: stringOrDefault(container.cpu), | ||
memory: stringOrDefault(container.memoryReservation), | ||
imageURI: ( | ||
<Box maxWidth={overflowMaxWidth}> | ||
<OverflowTooltip text={stringOrDefault(container.image)} /> | ||
</Box> | ||
), | ||
imageDigest: ( | ||
<Box maxWidth={overflowMaxWidth}> | ||
<OverflowTooltip text={stringOrDefault(container.imageDigest)} /> | ||
</Box> | ||
), | ||
}; | ||
}; | ||
|
||
type EcsContainerProps = { | ||
container: Container; | ||
}; | ||
|
||
export const EcsContainer = ({ container }: EcsContainerProps) => { | ||
return <StructuredMetadataTable metadata={formatContainer(container)} />; | ||
}; |
Oops, something went wrong.