Line data Source code
1 : /*
2 : * This file is part of Cockpit.
3 : *
4 : * Copyright (C) 2022 Red Hat, Inc.
5 : *
6 : * Cockpit is free software; you can redistribute it and/or modify it
7 : * under the terms of the GNU Lesser General Public License as published by
8 : * the Free Software Foundation; either version 2.1 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * Cockpit is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public License
17 : * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 43 : import React from 'react';
21 :
22 : import { Icon } from '@patternfly/react-core';
23 : import { Button } from "@patternfly/react-core/dist/esm/components/Button";
24 : import { DescriptionList, DescriptionListDescription, DescriptionListGroup, DescriptionListTerm } from "@patternfly/react-core/dist/esm/components/DescriptionList";
25 : import { Flex, FlexItem } from "@patternfly/react-core/dist/esm/layouts/Flex";
26 : import { CheckCircleIcon, ErrorCircleOIcon } from "@patternfly/react-icons";
27 :
28 : import cockpit from 'cockpit';
29 : import { ListingTable } from "cockpit-components-table";
30 :
31 : import * as client from './client.js';
32 : import * as utils from './util.js';
33 :
34 43 : const _ = cockpit.gettext;
35 :
36 2 : const format_nanoseconds = (ns) => {
37 2 : const seconds = ns / 1000000000;
38 2 : return cockpit.format(cockpit.ngettext("$0 second", "$0 seconds", seconds), seconds);
39 2 : };
40 :
41 43 : const HealthcheckOnFailureActionText = {
42 43 : none: _("No action"),
43 43 : restart: _("Restart"),
44 43 : stop: _("Stop"),
45 43 : kill: _("Force stop"),
46 43 : };
47 :
48 2 : const ContainerHealthLogs = ({ con, container, onAddNotification, state }) => {
49 0 : const healthCheck = container.Config?.Healthcheck ?? container.Config?.Health ?? {}; // not-covered: only on old version
50 0 : const healthState = container.State?.Healthcheck ?? container.State?.Health ?? {}; // not-covered: only on old version
51 0 : const logs = [...(healthState.Log || [])].reverse(); // not-covered: Log should always exist, belt-and-suspenders
52 :
53 2 : return (
54 2 : <>
55 2 : <Flex alignItems={{ default: "alignItemsFlexStart" }}>
56 2 : <FlexItem grow={{ default: 'grow' }}>
57 2 : <DescriptionList isAutoFit id="container-details-healthcheck">
58 2 : <DescriptionListGroup>
59 2 : <DescriptionListTerm>{_("Status")}</DescriptionListTerm>
60 2 : <DescriptionListDescription>{state}</DescriptionListDescription>
61 2 : </DescriptionListGroup>
62 2 : <DescriptionListGroup>
63 2 : <DescriptionListTerm>{_("Command")}</DescriptionListTerm>
64 2 : <DescriptionListDescription>{utils.quote_cmdline(healthCheck.Test)}</DescriptionListDescription>
65 2 : </DescriptionListGroup>
66 2 : {healthCheck.Interval && <DescriptionListGroup>
67 2 : <DescriptionListTerm>{_("Interval")}</DescriptionListTerm>
68 2 : <DescriptionListDescription>{format_nanoseconds(healthCheck.Interval)}</DescriptionListDescription>
69 2 : </DescriptionListGroup>}
70 2 : {healthCheck.Retries && <DescriptionListGroup>
71 2 : <DescriptionListTerm>{_("Retries")}</DescriptionListTerm>
72 2 : <DescriptionListDescription>{healthCheck.Retries}</DescriptionListDescription>
73 2 : </DescriptionListGroup>}
74 2 : {healthCheck.StartPeriod && <DescriptionListGroup>
75 2 : <DescriptionListTerm>{_("Start period")}</DescriptionListTerm>
76 2 : <DescriptionListDescription>{format_nanoseconds(healthCheck.StartPeriod)}</DescriptionListDescription>
77 2 : </DescriptionListGroup>}
78 2 : {healthCheck.Timeout && <DescriptionListGroup>
79 2 : <DescriptionListTerm>{_("Timeout")}</DescriptionListTerm>
80 2 : <DescriptionListDescription>{format_nanoseconds(healthCheck.Timeout)}</DescriptionListDescription>
81 2 : </DescriptionListGroup>}
82 2 : {container.Config?.HealthcheckOnFailureAction && <DescriptionListGroup>
83 2 : <DescriptionListTerm>{_("When unhealthy")}</DescriptionListTerm>
84 2 : <DescriptionListDescription>{HealthcheckOnFailureActionText[container.Config.HealthcheckOnFailureAction]}</DescriptionListDescription>
85 2 : </DescriptionListGroup>}
86 2 : {healthState.FailingStreak && <DescriptionListGroup>
87 2 : <DescriptionListTerm>{_("Failing streak")}</DescriptionListTerm>
88 2 : <DescriptionListDescription>{healthState.FailingStreak}</DescriptionListDescription>
89 2 : </DescriptionListGroup>}
90 2 : </DescriptionList>
91 2 : </FlexItem>
92 2 : { container.State.Status === "running" &&
93 2 : <FlexItem>
94 2 : <Button variant="secondary" onClick={() => {
95 2 : client.runHealthcheck(con, container.Id)
96 0 : .catch(ex => {
97 0 : const error = cockpit.format(_("Failed to run health check on container $0"), container.Name); // not-covered: OS error
98 0 : onAddNotification({ type: 'danger', error, errorDetail: ex.message });
99 0 : });
100 2 : }}>
101 2 : {_("Run health check")}
102 2 : </Button>
103 2 : </FlexItem>}
104 2 : </Flex>
105 2 : <ListingTable aria-label={_("Logs")}
106 2 : className="health-logs"
107 2 : variant='compact'
108 2 : columns={[_("Last 5 runs"), _("Started at")]}
109 2 : rows={
110 2 : logs.map(log => {
111 2 : const id = "hc" + log.Start + container.Id;
112 2 : return {
113 0 : expandedContent: log.Output ? <pre>{log.Output}</pre> : null,
114 2 : columns: [
115 2 : {
116 2 : title: <Flex flexWrap={{ default: 'nowrap' }} spaceItems={{ default: 'spaceItemsSm' }} alignItems={{ default: 'alignItemsCenter' }}>
117 2 : {log.ExitCode === 0 ? <Icon status="success"><CheckCircleIcon className="green" /></Icon> : <Icon status="danger"><ErrorCircleOIcon className="red" /></Icon>}
118 2 : <span>{log.ExitCode === 0 ? _("Passed health run") : _("Failed health run")}</span>
119 2 : </Flex>
120 2 : },
121 2 : {
122 2 : title: <utils.RelativeTime time={log.Start} />
123 2 : }
124 2 : ],
125 2 : props: {
126 2 : key: id,
127 2 : "data-row-id": id,
128 2 : },
129 2 : };
130 2 : })
131 2 : } />
132 2 : </>
133 : );
134 2 : };
135 :
136 43 : export default ContainerHealthLogs;
|