LCOV - code coverage report
Current view: top level - src - ContainerCommitModal.jsx Coverage Total Hit
Test: cockpit-podman Lines: 99.2 % 130 129
Test Date: 2025-05-21 17:35:03

            Line data    Source code
       1           43 : import React, { useState } from 'react';
       2              : 
       3              : import { Button } from "@patternfly/react-core/dist/esm/components/Button";
       4              : import { Checkbox } from "@patternfly/react-core/dist/esm/components/Checkbox";
       5              : import { Form, FormGroup } from "@patternfly/react-core/dist/esm/components/Form";
       6              : import { TextInput } from "@patternfly/react-core/dist/esm/components/TextInput";
       7              : import {
       8              :     Modal
       9              : } from '@patternfly/react-core/dist/esm/deprecated/components/Modal';
      10              : import { FormHelper } from 'cockpit-components-form-helper.jsx';
      11              : import { useDialogs } from "dialogs.jsx";
      12              : import { fmt_to_fragments } from 'utils.jsx';
      13              : 
      14              : import cockpit from 'cockpit';
      15              : 
      16              : import { ErrorNotification } from './Notification.jsx';
      17              : import * as client from './client.js';
      18              : import * as utils from './util.js';
      19              : 
      20           43 : const _ = cockpit.gettext;
      21              : 
      22            2 : const ContainerCommitModal = ({ con, container, localImages }) => {
      23            2 :     const Dialogs = useDialogs();
      24              : 
      25            2 :     const [imageName, setImageName] = useState("");
      26            2 :     const [tag, setTag] = useState("");
      27            2 :     const [author, setAuthor] = useState("");
      28            2 :     const [command, setCommand] = useState(utils.quote_cmdline(container.Config.Cmd));
      29            2 :     const [pause, setPause] = useState(false);
      30            2 :     const [useDocker, setUseDocker] = useState(false);
      31              : 
      32            2 :     const [dialogError, setDialogError] = useState("");
      33            2 :     const [dialogErrorDetail, setDialogErrorDetail] = useState("");
      34            2 :     const [commitInProgress, setCommitInProgress] = useState(false);
      35            2 :     const [nameError, setNameError] = useState("");
      36              : 
      37            2 :     const handleCommit = (force) => {
      38            2 :         if (!force && !imageName) {
      39            2 :             setNameError(_("Image name is required"));
      40            2 :             return;
      41            2 :         }
      42              : 
      43            2 :         let full_name = imageName + ":" + (tag !== "" ? tag : "latest");
      44            2 :         if (full_name.indexOf("/") < 0)
      45            2 :             full_name = "localhost/" + full_name;
      46              : 
      47            2 :         if (!force && localImages?.some(image => image.uid === container.uid && image.Name === full_name)) {
      48            2 :             setNameError(_("Image name is not unique"));
      49            2 :             return;
      50            2 :         }
      51              : 
      52            2 :         function quote(word) {
      53            2 :             word = word.replace(/"/g, '\\"');
      54            2 :             return '"' + word + '"';
      55            2 :         }
      56              : 
      57            2 :         const commitData = {};
      58            2 :         commitData.container = container.Id;
      59            2 :         commitData.repo = imageName;
      60            2 :         commitData.author = author;
      61            2 :         commitData.pause = pause;
      62              : 
      63            2 :         if (useDocker)
      64            2 :             commitData.format = 'docker';
      65              : 
      66            2 :         if (tag)
      67            2 :             commitData.tag = tag;
      68              : 
      69            2 :         commitData.changes = [];
      70            2 :         if (command.trim() !== "") {
      71            2 :             let cmdData = "";
      72            2 :             const words = utils.unquote_cmdline(command.trim());
      73            2 :             const cmdStr = words.map(quote).join(", ");
      74            2 :             cmdData = "CMD [" + cmdStr + "]";
      75            2 :             commitData.changes.push(cmdData);
      76            2 :         }
      77              : 
      78            2 :         setCommitInProgress(true);
      79            2 :         setNameError("");
      80            2 :         setDialogError("");
      81            2 :         setDialogErrorDetail("");
      82            2 :         client.commitContainer(con, commitData)
      83            2 :                 .then(() => Dialogs.close())
      84            2 :                 .catch(ex => {
      85            2 :                     setDialogError(cockpit.format(_("Failed to commit container $0"), container.Name));
      86            2 :                     setDialogErrorDetail(cockpit.format("$0: $1", ex.message, ex.reason));
      87            2 :                     setCommitInProgress(false);
      88            2 :                 });
      89            2 :     };
      90              : 
      91            2 :     const commitContent = (
      92            2 :         <Form isHorizontal>
      93            0 :             {dialogError && <ErrorNotification errorMessage={dialogError} errorDetail={dialogErrorDetail} onDismiss={() => setDialogError("")} />}
      94            2 :             <FormGroup fieldId="commit-dialog-image-name" label={_("New image name")}>
      95            2 :                 <TextInput id="commit-dialog-image-name"
      96            2 :                            value={imageName}
      97            2 :                            validated={nameError ? "error" : "default"}
      98            2 :                            onChange={(_, value) => { setNameError(""); setImageName(value) }} />
      99            2 :                 <FormHelper fieldId="commit-dialog-image-name" helperTextInvalid={nameError} />
     100            2 :             </FormGroup>
     101              : 
     102            2 :             <FormGroup fieldId="commit-dialog-image-tag" label={_("Tag")}>
     103            2 :                 <TextInput id="commit-dialog-image-tag"
     104            2 :                            placeholder="latest" // Do not translate
     105            2 :                            value={tag}
     106            2 :                            onChange={(_, value) => { setNameError(""); setTag(value) }} />
     107            2 :             </FormGroup>
     108              : 
     109            2 :             <FormGroup fieldId="commit-dialog-author" label={_("Author")}>
     110            2 :                 <TextInput id="commit-dialog-author"
     111            2 :                            placeholder={_("Example, Your Name <yourname@example.com>")}
     112            2 :                            value={author}
     113            2 :                            onChange={(_, value) => setAuthor(value)} />
     114            2 :             </FormGroup>
     115              : 
     116            2 :             <FormGroup fieldId="commit-dialog-command" label={_("Command")}>
     117            2 :                 <TextInput id="commit-dialog-command"
     118            2 :                            value={command}
     119            2 :                            onChange={(_, value) => setCommand(value)} />
     120            2 :             </FormGroup>
     121              : 
     122            2 :             <FormGroup fieldId="commit-dialog-pause" label={_("Options")} isStack hasNoPaddingTop>
     123            2 :                 <Checkbox id="commit-dialog-pause"
     124            2 :                           isChecked={pause}
     125            2 :                           onChange={(_, val) => setPause(val)}
     126            2 :                           label={_("Pause container when creating image")} />
     127            2 :                 <Checkbox id="commit-dialog-docker"
     128            2 :                           isChecked={useDocker}
     129            2 :                           onChange={(_, val) => setUseDocker(val)}
     130            2 :                           description={_("Docker format is useful when sharing the image with Docker or Moby Engine")}
     131            2 :                           label={_("Use legacy Docker format")} />
     132            2 :             </FormGroup>
     133            2 :         </Form>
     134              :     );
     135              : 
     136            2 :     return (
     137            2 :         <Modal isOpen
     138            2 :                  showClose={false}
     139            2 :                  position="top" variant="medium"
     140            2 :                  title={_("Commit container")}
     141            2 :                  description={fmt_to_fragments(_("Create a new image based on the current state of the $0 container."), <b>{container.Name}</b>)}
     142            2 :                  footer={<>
     143            2 :                      <Button variant="primary"
     144            2 :                              className="btn-ctr-commit"
     145            2 :                              isLoading={commitInProgress && !nameError}
     146            2 :                              isDisabled={commitInProgress || nameError}
     147            2 :                              onClick={() => handleCommit(false)}>
     148            2 :                          {_("Commit")}
     149            2 :                      </Button>
     150            2 :                      {nameError && <Button variant="warning"
     151            2 :                              className="btn-ctr-commit-force"
     152            2 :                              isLoading={commitInProgress}
     153            2 :                              isDisabled={commitInProgress}
     154            2 :                              onClick={() => handleCommit(true)}>
     155            2 :                          {_("Force commit")}
     156            2 :                      </Button>}
     157            2 :                      <Button variant="link"
     158            2 :                              className="btn-ctr-cancel-commit"
     159            2 :                              isDisabled={commitInProgress}
     160            2 :                              onClick={Dialogs.close}>
     161            2 :                          {_("Cancel")}
     162            2 :                      </Button>
     163            2 :                  </>}
     164              :         >
     165            2 :             {commitContent}
     166            2 :         </Modal>
     167              :     );
     168            2 : };
     169              : 
     170           43 : export default ContainerCommitModal;
        

Generated by: LCOV version 2.0-1