Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions frontend/integration-tests/tests/deploy-image.scenario.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { browser, $, element, by, ExpectedConditions as until } from 'protractor';
import { browser, element, by, ExpectedConditions as until } from 'protractor';

import { appHost, checkLogs, checkErrors, testName } from '../protractor.conf';

Expand Down Expand Up @@ -35,15 +35,16 @@ describe('Deploy Image', () => {

it('can be used to search for an image', async () => {
// Put the search term in the search field
await element(by.css('[data-test-id="deploy-image-search-term"]')).sendKeys(imageName);
// Click the search button
await element(by.css('[data-test-id="input-search-field-btn"]')).click();
// Wait for the results section to appear
await browser.wait(until.presenceOf($('.co-image-name-results__details')));
await element(by.css('[data-test-id=deploy-image-search-term]')).sendKeys(imageName);

//remove focus form image search field
await element(by.css('[data-test-id=application-form-app-name]')).click();

const helperText = 'form-input-searchTerm-field-helper';
// Wait for the validation
await browser.wait(until.presenceOf(element(by.id(helperText))));
// Confirm the results appeared
expect(
element(by.cssContainingText('.co-image-name-results__heading', imageName)).isPresent(),
).toBe(true);
expect(element(by.id(helperText)).isPresent()).toBe(true);
});

it('should auto fill in the application', async () => {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ export { default as DropdownField } from './DropdownField';
export { default as DroppableFileInputField } from './DroppableFileInputField';
export { default as EnvironmentField } from './EnvironmentField';
export { default as InputField } from './InputField';
export { default as InputSearchField } from './InputSearchField';
export { default as MultiColumnField } from './multi-column-field/MultiColumnField';
export { default as NSDropdownField } from './NSDropdownField';
export { default as NumberSpinnerField } from './NumberSpinnerField';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('Application Launcher Menu', () => {
expect(pageSidebar.getText()).toContain('Topology');
expect(pageSidebar.getText()).toContain('+Add');
expect(pageSidebar.getText()).toContain('Builds');
expect(pageSidebar.getText()).toContain('Advanced');
expect(pageSidebar.getText()).toContain('More');
expect(pageSidebar.getText()).toContain('Project Details');
expect(pageSidebar.getText()).toContain('Project Access');
expect(pageSidebar.getText()).toContain('Search');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,97 +3,124 @@ import * as _ from 'lodash';
import { k8sCreate } from '@console/internal/module/k8s';
import { ImageStreamImportsModel } from '@console/internal/models';
import { useFormikContext, FormikValues } from 'formik';
import { TextInputTypes, Alert, AlertActionCloseButton, Button } from '@patternfly/react-core';
import {
TextInputTypes,
Alert,
AlertActionCloseButton,
Button,
ValidatedOptions,
} from '@patternfly/react-core';
import { SecretTypeAbstraction } from '@console/internal/components/secrets/create-secret';
import { InputSearchField } from '@console/shared';
import { InputField } from '@console/shared';
import { getSuggestedName, getPorts, makePortName } from '../../../utils/imagestream-utils';
import { secretModalLauncher } from '../CreateSecretModal';

const ImageSearch: React.FC = () => {
const { values, setFieldValue, dirty } = useFormikContext<FormikValues>();
const [newImageSecret, setNewImageSecret] = React.useState('');
const [alertVisible, shouldHideAlert] = React.useState(true);
const [validated, setValidated] = React.useState<ValidatedOptions>(ValidatedOptions.default);
const namespace = values.project.name;

const handleSearch = React.useCallback(
(searchTerm: string) => {
const importImage = {
kind: 'ImageStreamImport',
apiVersion: 'image.openshift.io/v1',
metadata: {
name: 'newapp',
namespace: values.project.name,
},
spec: {
import: false,
images: [
{
from: {
kind: 'DockerImage',
name: _.trim(searchTerm),
},
const handleSearch = React.useCallback(() => {
const searchTermImage = values.searchTerm;
setFieldValue('isSearchingForImage', true);
setValidated(ValidatedOptions.default);
const importImage = {
kind: 'ImageStreamImport',
apiVersion: 'image.openshift.io/v1',
metadata: {
name: 'newapp',
namespace: values.project.name,
},
spec: {
import: false,
images: [
{
from: {
kind: 'DockerImage',
name: _.trim(searchTermImage),
},
],
},
status: {},
};
},
],
},
status: {},
};

k8sCreate(ImageStreamImportsModel, importImage)
.then((imageStreamImport) => {
const status = _.get(imageStreamImport, 'status.images[0].status');
if (status.status === 'Success') {
const name = _.get(imageStreamImport, 'spec.images[0].from.name');
const image = _.get(imageStreamImport, 'status.images[0].image');
const tag = _.get(imageStreamImport, 'status.images[0].tag');
const isi = { name, image, tag, status };
const ports = getPorts(isi);
setFieldValue('isSearchingForImage', false);
setFieldValue('isi.name', name);
setFieldValue('isi.image', image);
setFieldValue('isi.tag', tag);
setFieldValue('isi.status', status);
setFieldValue('isi.ports', ports);
setFieldValue('image.ports', ports);
setFieldValue('image.tag', tag);
!values.name && setFieldValue('name', getSuggestedName(name));
!values.application.name &&
setFieldValue('application.name', `${getSuggestedName(name)}-app`);
// set default port value
const targetPort = _.head(ports);
targetPort && setFieldValue('route.targetPort', makePortName(targetPort));
} else {
setFieldValue('isSearchingForImage', false);
setFieldValue('isi', {});
setFieldValue('isi.status', status.message);
setFieldValue('route.targetPort', null);
}
})
.catch((error) => {
setFieldValue('isi', {});
setFieldValue('isi.status', error.message);
k8sCreate(ImageStreamImportsModel, importImage)
.then((imageStreamImport) => {
const status = _.get(imageStreamImport, 'status.images[0].status');
if (status.status === 'Success') {
const name = _.get(imageStreamImport, 'spec.images[0].from.name');
const image = _.get(imageStreamImport, 'status.images[0].image');
const tag = _.get(imageStreamImport, 'status.images[0].tag');
const isi = { name, image, tag, status };
const ports = getPorts(isi);
setFieldValue('isSearchingForImage', false);
});
},
[setFieldValue, values.application.name, values.name, values.project.name],
);
setFieldValue('isi.name', name);
setFieldValue('isi.image', image);
setFieldValue('isi.tag', tag);
setFieldValue('isi.status', status);
setFieldValue('isi.ports', ports);
setFieldValue('image.ports', ports);
setFieldValue('image.tag', tag);
!values.name && setFieldValue('name', getSuggestedName(name));
!values.application.name &&
setFieldValue('application.name', `${getSuggestedName(name)}-app`);
// set default port value
const targetPort = _.head(ports);
targetPort && setFieldValue('route.targetPort', makePortName(targetPort));
setValidated(ValidatedOptions.success);
} else {
setFieldValue('isSearchingForImage', false);
setFieldValue('isi', {});
setFieldValue('isi.status', status.message);
setFieldValue('route.targetPort', null);
setValidated(ValidatedOptions.error);
}
})
.catch((error) => {
setFieldValue('isi', {});
setFieldValue('isi.status', error.message);
setFieldValue('isSearchingForImage', false);
setValidated(ValidatedOptions.error);
});
}, [setFieldValue, values.application.name, values.name, values.project.name, values.searchTerm]);

const handleSave = (name: string) => {
setNewImageSecret(name);
values.searchTerm && handleSearch(values.searchTerm);
values.searchTerm && handleSearch();
};

const getHelpText = () => {
if (values.isSearchingForImage) {
return 'Validating...';
}
if (!values.isSearchingForImage && validated === ValidatedOptions.success) {
return 'Validated';
}
return '';
};

const helpTextInvalid = validated === ValidatedOptions.error && (
<span>{values.searchTerm === '' ? 'Required' : values.isi.status}</span>
);

React.useEffect(() => {
!dirty && values.searchTerm && handleSearch(values.searchTerm);
!dirty && values.searchTerm && handleSearch();
}, [dirty, handleSearch, values.searchTerm]);

return (
<>
<InputSearchField
<InputField
type={TextInputTypes.text}
data-test-id="deploy-image-search-term"
name="searchTerm"
label="Image Name"
onSearch={handleSearch}
placeholder="Enter an image name"
helpText={getHelpText()}
helpTextInvalid={helpTextInvalid}
validated={validated}
onBlur={handleSearch}
data-test-id="deploy-image-search-term"
required
/>
<div className="help-block" id="image-name-help">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import FormSection from '../section/FormSection';
import { imageRegistryType } from '../../../utils/imagestream-utils';
import ImageStream from './ImageStream';
import ImageSearch from './ImageSearch';
import SearchStatus from './SearchStatus';
import SearchResults from './SearchResults';

const ImageSearchSection: React.FC = () => {
const { values, setFieldValue, initialValues } = useFormikContext<FormikValues>();
Expand Down Expand Up @@ -49,8 +47,6 @@ const ImageSearchSection: React.FC = () => {
},
]}
/>
<SearchStatus />
<SearchResults />
</FormSection>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import * as _ from 'lodash';
import { Alert } from '@patternfly/react-core';
import { Alert, FormGroup, ValidatedOptions } from '@patternfly/react-core';
import { ExclamationCircleIcon } from '@patternfly/react-icons';
import { useFormikContext, FormikValues } from 'formik';
import { CheckboxField } from '@console/shared';
import { K8sResourceKind } from '@console/internal/module/k8s';
Expand Down Expand Up @@ -45,9 +46,10 @@ export const ImageStreamReducer = (state: ImageStreamState, action: ImageStreamA

const ImageStream: React.FC = () => {
const {
values: { imageStream, project, registry },
values: { imageStream, project, registry, isi },
setFieldValue,
} = useFormikContext<FormikValues>();
const [validated, setValidated] = React.useState<ValidatedOptions>(ValidatedOptions.default);
const [state, dispatch] = React.useReducer(ImageStreamReducer, initialState);
const [hasImageStreams, setHasImageStreams] = React.useState(false);
const {
Expand Down Expand Up @@ -75,23 +77,37 @@ const ImageStream: React.FC = () => {
registry === RegistryType.Internal &&
imageStream.namespace !== BuilderImagesNamespace.Openshift &&
project.name !== imageStream.namespace;
const helperTextInvalid = validated === ValidatedOptions.error && (
<>
<ExclamationCircleIcon />
&nbsp;{isi.status}
</>
);

return (
<>
<ImageStreamContext.Provider value={{ state, dispatch, hasImageStreams, setHasImageStreams }}>
<div className="row">
<div className="col-lg-4 col-md-4 col-sm-4 col-xs-12">
<ImageStreamNsDropdown />
</div>
<div className="col-lg-4 col-md-4 col-sm-4 col-xs-12">
<ImageStreamDropdown />
<div className="odc-imagestream-separator">/</div>
</div>
<div className="col-lg-4 col-md-4 col-sm-4 col-xs-12">
<ImageStreamTagDropdown />
<div className="odc-imagestream-separator">:</div>
<ImageStreamContext.Provider
value={{ state, dispatch, hasImageStreams, setHasImageStreams, setValidated }}
>
<FormGroup
fieldId="image-stream-dropdowns"
validated={validated}
helperTextInvalid={helperTextInvalid}
>
<div className="row">
<div className="col-lg-4 col-md-4 col-sm-4 col-xs-12">
<ImageStreamNsDropdown />
</div>
<div className="col-lg-4 col-md-4 col-sm-4 col-xs-12">
<ImageStreamDropdown />
<div className="odc-imagestream-separator">/</div>
</div>
<div className="col-lg-4 col-md-4 col-sm-4 col-xs-12">
<ImageStreamTagDropdown />
<div className="odc-imagestream-separator">:</div>
</div>
</div>
</div>
</FormGroup>
{isNamespaceSelected && isImageStreamSelected && !isTagsAvailable && hasCreateAccess && (
<div className="odc-imagestream-alert">
<Alert variant="warning" title="No Image streams tags found" isInline>
Expand Down
Loading