mirror of https://github.com/portainer/portainer
fix(docker): remove word break in details [EE-4481] (#7996)
parent
fe8e834dbf
commit
d484a0eb64
|
@ -1,5 +1,5 @@
|
|||
import _ from 'lodash-es';
|
||||
import { trimSHA } from './utils';
|
||||
import { joinCommand, trimSHA } from './utils';
|
||||
|
||||
function includeString(text, values) {
|
||||
return values.some(function (val) {
|
||||
|
@ -191,12 +191,7 @@ angular
|
|||
};
|
||||
})
|
||||
.filter('command', function () {
|
||||
'use strict';
|
||||
return function (command) {
|
||||
if (command) {
|
||||
return command.join(' ');
|
||||
}
|
||||
};
|
||||
return joinCommand;
|
||||
})
|
||||
.filter('hideshasum', function () {
|
||||
'use strict';
|
||||
|
|
|
@ -9,3 +9,11 @@ export function trimSHA(imageName: string) {
|
|||
}
|
||||
return _.split(imageName, '@sha256')[0];
|
||||
}
|
||||
|
||||
export function joinCommand(command: null | Array<string> = []) {
|
||||
if (!command) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return command.join(' ');
|
||||
}
|
||||
|
|
|
@ -9,9 +9,13 @@ import { Gpu } from '@/react/docker/containers/CreateView/Gpu';
|
|||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
import { DockerfileDetails } from '@/react/docker/images/ItemView/DockerfileDetails';
|
||||
import { HealthStatus } from '@/react/docker/containers/ItemView/HealthStatus';
|
||||
|
||||
export const componentsModule = angular
|
||||
.module('portainer.docker.react.components', [])
|
||||
.component('dockerfileDetails', r2a(DockerfileDetails, ['image']))
|
||||
.component('dockerHealthStatus', r2a(HealthStatus, ['health']))
|
||||
.component(
|
||||
'containerQuickActions',
|
||||
r2a(withUIRouter(withCurrentUser(ContainerQuickActions)), [
|
||||
|
|
|
@ -162,40 +162,7 @@
|
|||
</access-control-panel>
|
||||
<!-- !access-control-panel -->
|
||||
|
||||
<div ng-if="container.State.Health" class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="fa-server" title-text="Container health"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Status</td>
|
||||
<td>
|
||||
<i
|
||||
ng-class="
|
||||
{ healthy: 'fa fa-heartbeat space-right green-icon', unhealthy: 'fa fa-heartbeat space-right red-icon', starting: 'fa fa-heartbeat space-right orange-icon' }[
|
||||
container.State.Health.Status
|
||||
]
|
||||
"
|
||||
></i>
|
||||
{{ container.State.Health.Status }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Failure count</td>
|
||||
<td>{{ container.State.Health.FailingStreak }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Last output</td>
|
||||
<td>{{ container.State.Health.Log[container.State.Health.Log.length - 1].Output }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
<docker-health-status ng-if="container.State.Health" health="container.State.Health"></docker-health-status>
|
||||
|
||||
<div class="row" authorization="DockerImageCreate">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
|
|
|
@ -162,58 +162,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
<rd-widget-header icon="list" feather-icon="true" title-text="Dockerfile details"></rd-widget-header>
|
||||
<rd-widget-body classes="no-padding">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>CMD</td>
|
||||
<td
|
||||
><code>{{ image.Command | command }}</code></td
|
||||
>
|
||||
</tr>
|
||||
<tr ng-if="image.Entrypoint">
|
||||
<td>ENTRYPOINT</td>
|
||||
<td
|
||||
><code>{{ image.Entrypoint | command }}</code></td
|
||||
>
|
||||
</tr>
|
||||
<tr ng-if="image.ExposedPorts.length > 0">
|
||||
<td>EXPOSE</td>
|
||||
<td>
|
||||
<span class="label label-default space-right" ng-repeat="port in image.ExposedPorts">
|
||||
{{ port }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="image.Volumes.length > 0">
|
||||
<td>VOLUME</td>
|
||||
<td>
|
||||
<span class="label label-default space-right" ng-repeat="volume in image.Volumes">
|
||||
{{ volume }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="image.Env.length > 0">
|
||||
<td>ENV</td>
|
||||
<td>
|
||||
<table class="table table-bordered table-condensed">
|
||||
<tr ng-repeat="var in image.Env">
|
||||
<td>{{ var|key: '=' }}</td>
|
||||
<td>{{ var|value: '=' }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</rd-widget-body>
|
||||
</rd-widget>
|
||||
</div>
|
||||
</div>
|
||||
<dockerfile-details ng-if="image" image="image"></dockerfile-details>
|
||||
|
||||
<div class="row" ng-if="history.length > 0">
|
||||
<div class="col-lg-12 col-md-12 col-xs-12">
|
||||
<rd-widget>
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
import clsx from 'clsx';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode;
|
||||
label: string;
|
||||
colClassName?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function DetailsRow({ label, children }: Props) {
|
||||
export function DetailsRow({
|
||||
label,
|
||||
children,
|
||||
colClassName,
|
||||
className,
|
||||
}: Props) {
|
||||
return (
|
||||
<tr>
|
||||
<td>{label}</td>
|
||||
{children && <td data-cy={`detailsTable-${label}Value`}>{children}</td>}
|
||||
<tr className={className}>
|
||||
<td className={clsx(colClassName, '!break-normal min-w-[150px]')}>
|
||||
{label}
|
||||
</td>
|
||||
{children && (
|
||||
<td className={colClassName} data-cy={`detailsTable-${label}Value`}>
|
||||
{children}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import { ComponentProps } from 'react';
|
||||
import { Server } from 'react-feather';
|
||||
|
||||
import { TableContainer, TableTitle } from '@@/datatables';
|
||||
import { DetailsTable } from '@@/DetailsTable';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { Health } from '../types/response';
|
||||
|
||||
const StatusMode: Record<
|
||||
Health['Status'],
|
||||
ComponentProps<typeof Icon>['mode']
|
||||
> = {
|
||||
healthy: 'success',
|
||||
unhealthy: 'danger',
|
||||
starting: 'warning',
|
||||
};
|
||||
|
||||
interface Props {
|
||||
health: Health;
|
||||
}
|
||||
|
||||
export function HealthStatus({ health }: Props) {
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-lg-12 col-md-12 col-xs-12">
|
||||
<TableContainer>
|
||||
<TableTitle label="Container health" icon={Server} />
|
||||
|
||||
<DetailsTable>
|
||||
<DetailsTable.Row label="Status">
|
||||
<div className="vertical-center">
|
||||
<Icon
|
||||
icon="fa fa-heartbeat"
|
||||
mode={StatusMode[health.Status]}
|
||||
className="space-right"
|
||||
/>
|
||||
{health.Status}
|
||||
</div>
|
||||
</DetailsTable.Row>
|
||||
|
||||
<DetailsTable.Row label="Failure count">
|
||||
{health.FailingStreak}
|
||||
</DetailsTable.Row>
|
||||
|
||||
<DetailsTable.Row label="Last output">
|
||||
{health.Log[health.Log.length - 1].Output}
|
||||
</DetailsTable.Row>
|
||||
</DetailsTable>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -70,6 +70,12 @@ interface MountPoint {
|
|||
Propagation: MountPropagation;
|
||||
}
|
||||
|
||||
export interface Health {
|
||||
Status: 'healthy' | 'unhealthy' | 'starting';
|
||||
FailingStreak: number;
|
||||
Log: Array<{ Output: string }>;
|
||||
}
|
||||
|
||||
export interface DockerContainerResponse {
|
||||
Id: string;
|
||||
Names: string[];
|
||||
|
@ -88,7 +94,6 @@ export interface DockerContainerResponse {
|
|||
};
|
||||
NetworkSettings?: SummaryNetworkSettings;
|
||||
Mounts: MountPoint[];
|
||||
|
||||
Portainer: PortainerMetadata;
|
||||
IsPortainer: boolean;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import { List } from 'react-feather';
|
||||
|
||||
import { joinCommand } from '@/docker/filters/utils';
|
||||
import { getPairKey, getPairValue } from '@/portainer/filters/filters';
|
||||
|
||||
import { TableContainer, TableTitle } from '@@/datatables';
|
||||
import { DetailsTable } from '@@/DetailsTable';
|
||||
|
||||
interface DockerImage {
|
||||
Command: Array<string>;
|
||||
Entrypoint: Array<string>;
|
||||
ExposedPorts: Array<number>;
|
||||
Volumes: Array<string>;
|
||||
Env: Array<string>;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
image: DockerImage;
|
||||
}
|
||||
|
||||
export function DockerfileDetails({ image }: Props) {
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="col-lg-12 col-md-12 col-xs-12">
|
||||
<TableContainer>
|
||||
<TableTitle label="Dockerfile details" icon={List} />
|
||||
<DetailsTable>
|
||||
<DetailsTable.Row label="CMD">
|
||||
<code>{joinCommand(image.Command)}</code>
|
||||
</DetailsTable.Row>
|
||||
|
||||
{image.Entrypoint && (
|
||||
<DetailsTable.Row label="ENTRYPOINT">
|
||||
<code>{joinCommand(image.Entrypoint)}</code>
|
||||
</DetailsTable.Row>
|
||||
)}
|
||||
|
||||
{image.ExposedPorts.length > 0 && (
|
||||
<DetailsTable.Row label="EXPOSE">
|
||||
{image.ExposedPorts.map((port, index) => (
|
||||
<span className="label label-default space-right" key={index}>
|
||||
{port}
|
||||
</span>
|
||||
))}
|
||||
</DetailsTable.Row>
|
||||
)}
|
||||
|
||||
{image.Volumes.length > 0 && (
|
||||
<DetailsTable.Row label="VOLUME">
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{image.Volumes.map((volume, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="label label-default space-right"
|
||||
ng-repeat="volume in image.Volumes"
|
||||
>
|
||||
{volume}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</DetailsTable.Row>
|
||||
)}
|
||||
|
||||
{image.Env.length > 0 && (
|
||||
<DetailsTable.Row label="ENV">
|
||||
<table className="table table-bordered table-condensed">
|
||||
<tbody>
|
||||
{image.Env.map((variable) => (
|
||||
<tr key={variable}>
|
||||
<td>{getPairKey(variable, '=')}</td>
|
||||
<td>{getPairValue(variable, '=')}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</DetailsTable.Row>
|
||||
)}
|
||||
</DetailsTable>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue