diff --git a/src/ImageSearchModal.jsx b/src/ImageSearchModal.jsx index 749c938da..b789151b1 100644 --- a/src/ImageSearchModal.jsx +++ b/src/ImageSearchModal.jsx @@ -1,8 +1,8 @@ import React from 'react'; import { ListGroup, ListGroupItem, Modal } from 'patternfly-react'; -import { Button, InputGroup, InputGroupText } from '@patternfly/react-core'; -import { SearchIcon } from '@patternfly/react-icons'; +import { Button, InputGroup } from '@patternfly/react-core'; +import * as Select from '../lib/cockpit-components-select.jsx'; import { ErrorNotification } from './Notification.jsx'; import cockpit from 'cockpit'; import rest from './rest.js'; @@ -22,6 +22,7 @@ export class ImageSearchModal extends React.Component { searchInProgress: false, searchFinished: false, isSystem: props.systemServiceAvailable, + registry: "", }; this.onDownloadClicked = this.onDownloadClicked.bind(this); this.onItemSelected = this.onItemSelected.bind(this); @@ -72,12 +73,16 @@ export class ImageSearchModal extends React.Component { this.setState({ searchInProgress: true }); this.activeConnection = rest.connect(client.getAddress(this.state.isSystem), this.state.isSystem); + + const rr = this.state.registry; + const registry = rr.length < 1 || rr[rr.length - 1] === "/" ? rr : rr + "/"; + const options = { method: "GET", path: "/v1.12/libpod/images/search", body: "", params: { - term: this.state.imageIdentifier, + term: registry + this.state.imageIdentifier, }, }; this.activeConnection.call(options) @@ -136,17 +141,37 @@ export class ImageSearchModal extends React.Component { } - - - - this.onValueChanged('imageIdentifier', e.target.value)} /> +
+ + this.onValueChanged('imageIdentifier', e.target.value)} /> + +
+ + this.setState({ registry: value }, () => this.onSearchTriggered(false)) + }> + + {_("All registries")} + + {(this.props.registries.search || []).map(r => { + return + {r} + ; + }) + } + +
+
{this.state.searchInProgress &&
} diff --git a/src/Images.jsx b/src/Images.jsx index d1ba391d6..e2b0c0a1f 100644 --- a/src/Images.jsx +++ b/src/Images.jsx @@ -261,6 +261,7 @@ class Images extends React.Component { close={() => this.setState({ showSearchImageModal: false })} downloadImage={this.downloadImage} user={this.props.user} + registries={this.props.registries} userServiceAvailable={this.props.userServiceAvailable} systemServiceAvailable={this.props.systemServiceAvailable} /> } {this.state.imageDownloadInProgress &&
{_("Pulling")} {this.state.imageDownloadInProgress}...
} diff --git a/src/app.jsx b/src/app.jsx index a92ccdfc8..bfaf1c121 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -324,7 +324,11 @@ class Application extends React.Component { init(system) { client.getInfo(system) .then(reply => { - this.setState({ [system ? "systemServiceAvailable" : "userServiceAvailable"]: true, version: reply.version.Version }); + this.setState({ + [system ? "systemServiceAvailable" : "userServiceAvailable"]: true, + version: reply.version.Version, + registries: reply.registries, + }); this.updateImagesAfterEvent(system); this.updateContainersAfterEvent(system, true); client.streamEvents(system, @@ -514,6 +518,7 @@ class Application extends React.Component { user={permission.user || _("user")} userServiceAvailable={this.state.userServiceAvailable} systemServiceAvailable={this.state.systemServiceAvailable} + registries={this.state.registries} />; const containerList = /etc/containers/registries.conf && systemctl stop podman.service".format(REGISTRIES_CONF)) - # Push busybox image to the local registry + # Push busybox image to the local registries self.execute(True, "podman tag busybox localhost:5000/my-busybox && podman push localhost:5000/my-busybox") + self.execute(True, "podman tag busybox localhost:6000/my-busybox && podman push localhost:6000/my-busybox") # Untag busybox image which duplicates the image we are about to download self.execute(True, "podman rmi -f busybox") @@ -433,6 +435,7 @@ class TestApplication(testlib.MachineCase): def fillDialog(self): # Search for image specified with self.imageName and self.imageTag b.click("#{0}".format(self.user)) + b.set_val('#registry-select', "localhost:5000") # HACK: Sometimes the value is not shown fully. FIXME b.set_input_text("#search-image-dialog-name", self.imageName, value_check=False) if self.imageTag: @@ -468,7 +471,7 @@ class TestApplication(testlib.MachineCase): checkImage(b, "localhost:5000/{0}:{1}".format(self.imageName, self.imageTag or "latest"), "system" if self.user == "system" else "admin") # Find out this image ID - self.imageSha = execute(self.user == "system", "podman inspect --format '{{{{.Id}}}}' {0}:{1}".format(self.imageName, self.imageTag or "latest")).strip() + self.imageSha = execute(self.user == "system", "podman inspect --format '{{{{.Id}}}}' localhost:5000/{0}:{1}".format(self.imageName, self.imageTag or "latest")).strip() return self @@ -505,6 +508,19 @@ class TestApplication(testlib.MachineCase): self.login_and_go("/podman", authorized=True, superuser=True) b.wait_present("#app") + # Test registries + b.click("header button:contains(Get new image)") + b.wait_present('div.modal-dialog div.modal-header h4.modal-title:contains("Search Image")') + # HACK: Sometimes the value is not shown fully. FIXME + b.set_input_text("#search-image-dialog-name", "my-busybox", value_check=False) + + b.wait_present("div.list-group .image-list-item label:contains('localhost:5000/my-busybox')") + b.wait_present("div.list-group .image-list-item label:contains('localhost:6000/my-busybox')") + + b.set_val('#registry-select', "localhost:6000") + b.wait_not_present("div.list-group .image-list-item label:contains('localhost:5000/my-busybox')") + b.wait_present("div.list-group .image-list-item label:contains('localhost:6000/my-busybox')") + dialog0 = DownloadImageDialog('my-busybox', user="system") dialog0.openDialog() \ .fillDialog() \ @@ -518,7 +534,7 @@ class TestApplication(testlib.MachineCase): .expectDownloadSuccess() dialog1.deleteImage(True) - dialog0.deleteImage() + dialog0.deleteImage(True) dialog = DownloadImageDialog('my-busybox', 'latest', user="system") dialog.openDialog() \