mirror of https://github.com/portainer/portainer
289 lines
8.4 KiB
TypeScript
289 lines
8.4 KiB
TypeScript
import { useRouter } from '@uirouter/react';
|
|
|
|
import * as notifications from '@/portainer/services/notifications';
|
|
import { useAuthorizations, Authorized } from '@/portainer/hooks/useUser';
|
|
import { Link } from '@/portainer/components/Link';
|
|
import { confirmContainerDeletion } from '@/portainer/services/modal.service/prompt';
|
|
import { setPortainerAgentTargetHeader } from '@/portainer/services/http-request.helper';
|
|
import type { ContainerId, DockerContainer } from '@/docker/containers/types';
|
|
import {
|
|
killContainer,
|
|
pauseContainer,
|
|
removeContainer,
|
|
restartContainer,
|
|
resumeContainer,
|
|
startContainer,
|
|
stopContainer,
|
|
} from '@/docker/containers/containers.service';
|
|
import type { EnvironmentId } from '@/portainer/environments/types';
|
|
import { ButtonGroup, Button } from '@/portainer/components/Button';
|
|
|
|
type ContainerServiceAction = (
|
|
endpointId: EnvironmentId,
|
|
containerId: ContainerId
|
|
) => Promise<void>;
|
|
|
|
interface Props {
|
|
selectedItems: DockerContainer[];
|
|
isAddActionVisible: boolean;
|
|
endpointId: EnvironmentId;
|
|
}
|
|
|
|
export function ContainersDatatableActions({
|
|
selectedItems,
|
|
isAddActionVisible,
|
|
endpointId,
|
|
}: Props) {
|
|
const selectedItemCount = selectedItems.length;
|
|
const hasPausedItemsSelected = selectedItems.some(
|
|
(item) => item.Status === 'paused'
|
|
);
|
|
const hasStoppedItemsSelected = selectedItems.some((item) =>
|
|
['stopped', 'created'].includes(item.Status)
|
|
);
|
|
const hasRunningItemsSelected = selectedItems.some((item) =>
|
|
['running', 'healthy', 'unhealthy', 'starting'].includes(item.Status)
|
|
);
|
|
|
|
const isAuthorized = useAuthorizations([
|
|
'DockerContainerStart',
|
|
'DockerContainerStop',
|
|
'DockerContainerKill',
|
|
'DockerContainerRestart',
|
|
'DockerContainerPause',
|
|
'DockerContainerUnpause',
|
|
'DockerContainerDelete',
|
|
'DockerContainerCreate',
|
|
]);
|
|
|
|
const router = useRouter();
|
|
|
|
if (!isAuthorized) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div className="actionBar">
|
|
<ButtonGroup>
|
|
<Authorized authorizations="DockerContainerStart">
|
|
<Button
|
|
color="success"
|
|
onClick={() => onStartClick(selectedItems)}
|
|
disabled={selectedItemCount === 0 || !hasStoppedItemsSelected}
|
|
>
|
|
<i className="fa fa-play space-right" aria-hidden="true" />
|
|
Start
|
|
</Button>
|
|
</Authorized>
|
|
|
|
<Authorized authorizations="DockerContainerStop">
|
|
<Button
|
|
color="danger"
|
|
onClick={() => onStopClick(selectedItems)}
|
|
disabled={selectedItemCount === 0 || !hasRunningItemsSelected}
|
|
>
|
|
<i className="fa fa-stop space-right" aria-hidden="true" />
|
|
Stop
|
|
</Button>
|
|
</Authorized>
|
|
|
|
<Authorized authorizations="DockerContainerKill">
|
|
<Button
|
|
color="danger"
|
|
onClick={() => onKillClick(selectedItems)}
|
|
disabled={selectedItemCount === 0}
|
|
>
|
|
<i className="fa fa-bomb space-right" aria-hidden="true" />
|
|
Kill
|
|
</Button>
|
|
</Authorized>
|
|
|
|
<Authorized authorizations="DockerContainerRestart">
|
|
<Button
|
|
onClick={() => onRestartClick(selectedItems)}
|
|
disabled={selectedItemCount === 0}
|
|
>
|
|
<i className="fa fa-sync space-right" aria-hidden="true" />
|
|
Restart
|
|
</Button>
|
|
</Authorized>
|
|
|
|
<Authorized authorizations="DockerContainerPause">
|
|
<Button
|
|
onClick={() => onPauseClick(selectedItems)}
|
|
disabled={selectedItemCount === 0 || !hasRunningItemsSelected}
|
|
>
|
|
<i className="fa fa-pause space-right" aria-hidden="true" />
|
|
Pause
|
|
</Button>
|
|
</Authorized>
|
|
|
|
<Authorized authorizations="DockerContainerUnpause">
|
|
<Button
|
|
onClick={() => onResumeClick(selectedItems)}
|
|
disabled={selectedItemCount === 0 || !hasPausedItemsSelected}
|
|
>
|
|
<i className="fa fa-play space-right" aria-hidden="true" />
|
|
Resume
|
|
</Button>
|
|
</Authorized>
|
|
|
|
<Authorized authorizations="DockerContainerDelete">
|
|
<Button
|
|
color="danger"
|
|
onClick={() => onRemoveClick(selectedItems)}
|
|
disabled={selectedItemCount === 0}
|
|
>
|
|
<i className="fa fa-trash-alt space-right" aria-hidden="true" />
|
|
Remove
|
|
</Button>
|
|
</Authorized>
|
|
</ButtonGroup>
|
|
|
|
{isAddActionVisible && (
|
|
<Authorized authorizations="DockerContainerCreate">
|
|
<Link to="docker.containers.new" className="space-left">
|
|
<Button>
|
|
<i className="fa fa-plus space-right" aria-hidden="true" />
|
|
Add container
|
|
</Button>
|
|
</Link>
|
|
</Authorized>
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
function onStartClick(selectedItems: DockerContainer[]) {
|
|
const successMessage = 'Container successfully started';
|
|
const errorMessage = 'Unable to start container';
|
|
executeActionOnContainerList(
|
|
selectedItems,
|
|
startContainer,
|
|
successMessage,
|
|
errorMessage
|
|
);
|
|
}
|
|
|
|
function onStopClick(selectedItems: DockerContainer[]) {
|
|
const successMessage = 'Container successfully stopped';
|
|
const errorMessage = 'Unable to stop container';
|
|
executeActionOnContainerList(
|
|
selectedItems,
|
|
stopContainer,
|
|
successMessage,
|
|
errorMessage
|
|
);
|
|
}
|
|
|
|
function onRestartClick(selectedItems: DockerContainer[]) {
|
|
const successMessage = 'Container successfully restarted';
|
|
const errorMessage = 'Unable to restart container';
|
|
executeActionOnContainerList(
|
|
selectedItems,
|
|
restartContainer,
|
|
successMessage,
|
|
errorMessage
|
|
);
|
|
}
|
|
|
|
function onKillClick(selectedItems: DockerContainer[]) {
|
|
const successMessage = 'Container successfully killed';
|
|
const errorMessage = 'Unable to kill container';
|
|
executeActionOnContainerList(
|
|
selectedItems,
|
|
killContainer,
|
|
successMessage,
|
|
errorMessage
|
|
);
|
|
}
|
|
|
|
function onPauseClick(selectedItems: DockerContainer[]) {
|
|
const successMessage = 'Container successfully paused';
|
|
const errorMessage = 'Unable to pause container';
|
|
executeActionOnContainerList(
|
|
selectedItems,
|
|
pauseContainer,
|
|
successMessage,
|
|
errorMessage
|
|
);
|
|
}
|
|
|
|
function onResumeClick(selectedItems: DockerContainer[]) {
|
|
const successMessage = 'Container successfully resumed';
|
|
const errorMessage = 'Unable to resume container';
|
|
executeActionOnContainerList(
|
|
selectedItems,
|
|
resumeContainer,
|
|
successMessage,
|
|
errorMessage
|
|
);
|
|
}
|
|
|
|
function onRemoveClick(selectedItems: DockerContainer[]) {
|
|
const isOneContainerRunning = selectedItems.some(
|
|
(container) => container.Status === 'running'
|
|
);
|
|
|
|
const runningTitle = isOneContainerRunning ? 'running' : '';
|
|
const title = `You are about to remove one or more ${runningTitle} containers.`;
|
|
|
|
confirmContainerDeletion(title, (result: string[]) => {
|
|
if (!result) {
|
|
return;
|
|
}
|
|
const cleanVolumes = !!result[0];
|
|
|
|
removeSelectedContainers(selectedItems, cleanVolumes);
|
|
});
|
|
}
|
|
|
|
async function executeActionOnContainerList(
|
|
containers: DockerContainer[],
|
|
action: ContainerServiceAction,
|
|
successMessage: string,
|
|
errorMessage: string
|
|
) {
|
|
for (let i = 0; i < containers.length; i += 1) {
|
|
const container = containers[i];
|
|
try {
|
|
setPortainerAgentTargetHeader(container.NodeName);
|
|
await action(endpointId, container.Id);
|
|
notifications.success(successMessage, container.Names[0]);
|
|
} catch (err) {
|
|
notifications.error(
|
|
'Failure',
|
|
err as Error,
|
|
`${errorMessage}:${container.Names[0]}`
|
|
);
|
|
}
|
|
}
|
|
|
|
router.stateService.reload();
|
|
}
|
|
|
|
async function removeSelectedContainers(
|
|
containers: DockerContainer[],
|
|
cleanVolumes: boolean
|
|
) {
|
|
for (let i = 0; i < containers.length; i += 1) {
|
|
const container = containers[i];
|
|
try {
|
|
setPortainerAgentTargetHeader(container.NodeName);
|
|
await removeContainer(endpointId, container, cleanVolumes);
|
|
notifications.success(
|
|
'Container successfully removed',
|
|
container.Names[0]
|
|
);
|
|
} catch (err) {
|
|
notifications.error(
|
|
'Failure',
|
|
err as Error,
|
|
'Unable to remove container'
|
|
);
|
|
}
|
|
}
|
|
|
|
router.stateService.reload();
|
|
}
|
|
}
|