LCOV - code coverage report
Current view: top level - src - ContainerHealthLogs.jsx Coverage Total Hit
Test: cockpit-podman Lines: 91.8 % 98 90
Test Date: 2025-05-21 17:35:03

            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;
        

Generated by: LCOV version 2.0-1