From 5fedc1c7d6bc49112f0a9369b5a4d445e731fa31 Mon Sep 17 00:00:00 2001 From: Simon Kobyda Date: Thu, 13 Jul 2023 12:53:48 +0200 Subject: [PATCH] Validate of container name is already in use Fixes #609 --- src/ImageRunModal.jsx | 37 +++++++++++++++++++++++++++++++------ src/client.js | 2 ++ test/check-application | 2 ++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/ImageRunModal.jsx b/src/ImageRunModal.jsx index b875db75c..5f8730b8f 100644 --- a/src/ImageRunModal.jsx +++ b/src/ImageRunModal.jsx @@ -2,6 +2,7 @@ import React from 'react'; import { Button } from "@patternfly/react-core/dist/esm/components/Button"; import { Checkbox } from "@patternfly/react-core/dist/esm/components/Checkbox"; import { Form, FormGroup } from "@patternfly/react-core/dist/esm/components/Form"; +import { FormHelper } from "cockpit-components-form-helper.jsx"; import { FormSelect, FormSelectOption } from "@patternfly/react-core/dist/esm/components/FormSelect"; import { Grid, GridItem } from "@patternfly/react-core/dist/esm/layouts/Grid"; import { Modal } from "@patternfly/react-core/dist/esm/components/Modal"; @@ -258,7 +259,7 @@ export class ImageRunModal extends React.Component { }; async onCreateClicked(runImage = false) { - if (!this.validateForm()) + if (!await this.validateForm()) return; const Dialogs = this.props.dialogs; @@ -591,8 +592,17 @@ export class ImageRunModal extends React.Component { return Object.keys(validationFailed).length == 0; }; - validateForm = () => { - const { publish, volumes, env } = this.state; + async validateContainerName(containerName) { + try { + await client.containerExists(this.isSystem(), containerName); + } catch (error) { + return; + } + return _("Name already in use"); + } + + async validateForm() { + const { publish, volumes, env, containerName } = this.state; const validationFailed = { }; const publishValidation = publish.map(a => { @@ -626,10 +636,15 @@ export class ImageRunModal extends React.Component { if (envValidation.some(entry => Object.keys(entry).length > 0)) validationFailed.env = envValidation; + const containerNameValidation = await this.validateContainerName(containerName); + + if (containerNameValidation) + validationFailed.containerName = containerNameValidation; + this.setState({ validationFailed }); return this.isFormValid(validationFailed); - }; + } /* Updates a validation object of the whole dynamic list's form (e.g. the whole port-mapping form) * @@ -704,12 +719,22 @@ export class ImageRunModal extends React.Component { const defaultBody = (
{this.state.dialogError && } - + this.onValueChanged('containerName', value)} /> + onChange={(_, value) => { + utils.validationClear(dialogValues.validationFailed, "containerName", (value) => this.onValueChanged("validationFailed", value)); + utils.validationDebounce(async () => { + const delta = await this.validateContainerName(value); + if (delta) + this.onValueChanged("validationFailed", { ...dialogValues.validationFailed, containerName: delta }); + }); + this.onValueChanged('containerName', value); + }} /> + {_("Details")}} className="pf-v5-c-form pf-m-horizontal"> diff --git a/src/client.js b/src/client.js index 572056243..41df29d27 100644 --- a/src/client.js +++ b/src/client.js @@ -179,3 +179,5 @@ export const pruneUnusedImages = system => podmanJson("libpod/images/prune?all=t export const imageHistory = (system, id) => podmanJson(`libpod/images/${id}/history`, "GET", {}, system); export const imageExists = (system, id) => podmanCall("libpod/images/" + id + "/exists", "GET", {}, system); + +export const containerExists = (system, id) => podmanCall("libpod/containers/" + id + "/exists", "GET", {}, system); diff --git a/test/check-application b/test/check-application index 6ddd6d707..c3bfaee80 100755 --- a/test/check-application +++ b/test/check-application @@ -2281,6 +2281,8 @@ class TestApplication(testlib.MachineCase): b.click(f'#containers-images tbody tr:contains("{IMG_BUSYBOX}") .ct-container-create') b.wait_visible('div.pf-v5-c-modal-box header:contains("Create container")') + validateField("#image-name-group", "registry", "Name already in use") + # Switch to Integration tab b.click("#pf-tab-1-create-image-dialog-tab-integration")