Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug 1834805: Support new spec changes in kafkasource #5407

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -68,7 +68,7 @@ describe('Event Source ValidationUtils', () => {
it('should throw an error for required fields if empty', async () => {
const defaultEventingData = getDefaultEventingData(EventSources.KafkaSource);
const mockData = _.cloneDeep(defaultEventingData);
mockData.data.kafkasource.bootstrapServers = '';
mockData.data.kafkasource.bootstrapServers = [''];
await eventSourceValidationSchema
.resolve({ value: mockData })
.isValid(mockData)
Expand All @@ -95,7 +95,7 @@ describe('Event Source ValidationUtils', () => {
const ContainerSourceData = {
...getDefaultEventingData(EventSources.ContainerSource),
};
ContainerSourceData.data.containersource.containers[0].image = '';
ContainerSourceData.data.containersource.template.spec.containers[0].image = '';
await eventSourceValidationSchema
.resolve({ value: ContainerSourceData })
.isValid(ContainerSourceData)
Expand Down
Expand Up @@ -2,24 +2,28 @@ import * as React from 'react';
import * as _ from 'lodash';
import { useFormikContext, FormikValues } from 'formik';
import { TextInputTypes, FormGroup } from '@patternfly/react-core';
import { InputField, MultiColumnField } from '@console/shared';
import { InputField, TextColumnField } from '@console/shared';
import { AsyncComponent } from '@console/internal/components/utils';
import FormSection from '@console/dev-console/src/components/import/section/FormSection';
import { getSuggestedName } from '@console/dev-console/src/utils/imagestream-utils';

const containerPaths = {
Image: 'data.containersource.containers[0].image',
Name: 'data.containersource.containers[0].name',
Env: 'data.containersource.containers[0].env',
Args: 'data.containersource.containers[0].args',
Image: 'data.containersource.template.spec.containers[0].image',
Name: 'data.containersource.template.spec.containers[0].name',
Env: 'data.containersource.template.spec.containers[0].env',
Args: 'data.containersource.template.spec.containers[0].args',
};

const ContainerSourceSection: React.FC = () => {
const { values, setFieldValue } = useFormikContext<FormikValues>();
const {
data: {
containersource: {
containers: [{ env: envs, args }],
template: {
spec: {
containers: [{ env: envs, args }],
},
},
},
},
} = values;
Expand Down Expand Up @@ -54,18 +58,15 @@ const ContainerSourceSection: React.FC = () => {
name={containerPaths.Name}
label="Name"
/>
<MultiColumnField
<TextColumnField
data-test-id="container-arg-field"
name={containerPaths.Args}
addLabel="Add args"
label="Arguments"
headers={[]}
emptyValues={{ name: '' }}
addLabel="Add args"
placeholder="argument"
helpText="The command to run inside the container."
disableDeleteRow={args?.length === 1}
emptyMessage="No args are associated with the container."
>
<InputField name="name" type={TextInputTypes.text} placeholder="args" />
</MultiColumnField>
/>
<FormGroup fieldId="containersource-env" label="Environment variables">
<AsyncComponent
loader={() =>
Expand Down
@@ -1,36 +1,47 @@
import * as React from 'react';
import { useFormikContext, FormikValues } from 'formik';
import FormSection from '@console/dev-console/src/components/import/section/FormSection';
import { InputField } from '@console/shared';
import { InputField, TextColumnField } from '@console/shared';
import { TextInputTypes } from '@patternfly/react-core';
import KafkaSourceNetSection from './KafkaSourceNetSection';
import ServiceAccountDropdown from '../../dropdowns/ServiceAccountDropdown';

const KafkaSourceSection: React.FC = () => (
<FormSection title="KafkaSource" extraMargin>
<InputField
data-test-id="kafkasource-bootstrapservers-field"
type={TextInputTypes.text}
name="data.kafkasource.bootstrapServers"
label="BootstrapServers"
required
/>
<InputField
data-test-id="kafkasource-topics-field"
type={TextInputTypes.text}
name="data.kafkasource.topics"
label="Topics"
required
/>
<InputField
data-test-id="kafkasource-consumergoup-field"
type={TextInputTypes.text}
name="data.kafkasource.consumerGroup"
label="ConsumerGroup"
required
/>
<KafkaSourceNetSection />
<ServiceAccountDropdown name="data.kafkasource.serviceAccountName" />
</FormSection>
);
const KafkaSourceSection: React.FC = () => {
const { values } = useFormikContext<FormikValues>();
const {
data: {
kafkasource: { bootstrapServers, topics },
},
} = values;
return (
<FormSection title="KafkaSource" extraMargin>
<TextColumnField
data-test-id="kafkasource-bootstrapservers-field"
name="data.kafkasource.bootstrapServers"
label="BootstrapServers"
addLabel="Add Bootstrapservers"
required
disableDeleteRow={bootstrapServers?.length === 1}
/>
<TextColumnField
data-test-id="kafkasource-topics-field"
name="data.kafkasource.topics"
label="Topics"
addLabel="Add Topics"
required
disableDeleteRow={topics?.length === 1}
/>
<InputField
data-test-id="kafkasource-consumergroup-field"
type={TextInputTypes.text}
name="data.kafkasource.consumerGroup"
label="ConsumerGroup"
required
/>
<KafkaSourceNetSection />
<ServiceAccountDropdown name="data.kafkasource.serviceAccountName" />
</FormSection>
);
};

export default KafkaSourceSection;
@@ -1,6 +1,6 @@
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { MultiColumnField } from '@console/shared';
import { TextColumnField } from '@console/shared';
import FormSection from '@console/dev-console/src/components/import/section/FormSection';
import { AsyncComponent } from '@console/internal/components/utils/async';
import ContainerSourceSection from '../ContainerSourceSection';
Expand All @@ -18,11 +18,15 @@ jest.mock('formik', () => ({
type: 'ContainerSource',
data: {
containersource: {
containers: [
{
args: [],
template: {
spec: {
containers: [
{
args: [],
},
],
},
],
},
},
},
},
Expand All @@ -47,7 +51,7 @@ describe('ContainerSourceSection', () => {
});

it('should render Container args field', () => {
const argsField = wrapper.find(MultiColumnField);
const argsField = wrapper.find(TextColumnField);
expect(argsField).toHaveLength(1);
});

Expand Down
@@ -0,0 +1,59 @@
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import FormSection from '@console/dev-console/src/components/import/section/FormSection';
import KafkaSourceSection from '../KafkaSourceSection';
import { EventSources } from '../../import-types';
import KafkaSourceNetSection from '../KafkaSourceNetSection';
import ServiceAccountDropdown from '../../../dropdowns/ServiceAccountDropdown';

type KafkaSourceSectionProps = React.ComponentProps<typeof KafkaSourceSection>;

jest.mock('formik', () => ({
useField: jest.fn(() => [{}, {}]),
useFormikContext: jest.fn(() => ({
setFieldValue: jest.fn(),
setFieldTouched: jest.fn(),
validateForm: jest.fn(),
values: {
type: 'KafkaSource',
data: {
kafkasource: {
bootstrapServers: [''],
topics: [''],
},
},
},
})),
}));
describe('KafkaSourceSection', () => {
let wrapper: ShallowWrapper<KafkaSourceSectionProps>;
beforeEach(() => {
wrapper = shallow(<KafkaSourceSection />);
});

it('should render KafkaSource FormSection with proper title', () => {
expect(wrapper.find(FormSection)).toHaveLength(1);
expect(wrapper.find(FormSection).props().title).toBe(EventSources.KafkaSource);
});

it('should render BootstrapServers and Topics fields', () => {
const bootstrapServersField = wrapper.find(
'[data-test-id="kafkasource-bootstrapservers-field"]',
);
expect(bootstrapServersField).toHaveLength(1);
expect(bootstrapServersField.props().required).toBeTruthy();

const topicsField = wrapper.find('[data-test-id="kafkasource-topics-field"]');
expect(topicsField).toHaveLength(1);
expect(topicsField.props().required).toBeTruthy();
});

it('should render consumergroup, netsection and service dropdown', () => {
const consumerGroupField = wrapper.find('[data-test-id="kafkasource-consumergroup-field"]');
expect(consumerGroupField).toHaveLength(1);
expect(consumerGroupField.props().required).toBeTruthy();

expect(wrapper.find(KafkaSourceNetSection)).toHaveLength(1);
expect(wrapper.find(ServiceAccountDropdown)).toHaveLength(1);
});
});
Expand Up @@ -77,9 +77,9 @@ export const sourceDataSpecSchema = yup
is: EventSources.KafkaSource,
then: yup.object().shape({
kafkasource: yup.object().shape({
bootstrapServers: yup.string().required('Required'),
bootstrapServers: yup.array().of(yup.string().required('Required')),
consumerGroup: yup.string().required('Required'),
topics: yup.string().required('Required'),
topics: yup.array().of(yup.string().required('Required')),
net: yup.object().shape({
sasl: yup.object().shape({
enable: yup.boolean(),
Expand Down Expand Up @@ -140,11 +140,15 @@ export const sourceDataSpecSchema = yup
is: EventSources.ContainerSource,
then: yup.object().shape({
containersource: yup.object().shape({
containers: yup.array().of(
yup.object({
image: yup.string().required('Required'),
template: yup.object({
spec: yup.object({
containers: yup.array().of(
yup.object({
image: yup.string().required('Required'),
}),
),
}),
),
}),
}),
}),
});
Expand Down
Expand Up @@ -284,8 +284,8 @@ const eventSourceData = {
],
},
kafkasource: {
bootstrapServers: 'my-cluster-kafka-bootstrap.kafka:9092',
topics: 'knative-demo-topic',
bootstrapServers: ['my-cluster-kafka-bootstrap.kafka:9092'],
topics: ['knative-demo-topic'],
consumerGroup: 'knative-group',
net: {
sasl: {
Expand All @@ -303,14 +303,18 @@ const eventSourceData = {
serviceAccountName: '',
},
containersource: {
containers: [
{
image: 'test-knative-image',
name: '',
args: [{ name: '' }],
env: [],
template: {
spec: {
containers: [
{
image: 'test-knative-image',
name: '',
args: [''],
env: [],
},
],
},
],
},
},
};

Expand Down
Expand Up @@ -91,33 +91,11 @@ export const getKafkaSourceResource = (formData: EventSourceFormData): K8sResour
return _.merge({}, baseResource, kafkaSource);
};

export const getContainerSourceResource = (formData: EventSourceFormData): K8sResourceKind => {
const baseResource = _.omit(getEventSourcesDepResource(formData), ['spec.containers']);
const containersourceData = {
spec: {
template: {
spec: {
containers: _.map(formData.data.containersource?.containers, (container) => {
return {
image: container.image,
name: container.name,
args: container.args.map((arg) => arg.name),
env: container.env,
};
}),
},
},
},
};

return _.merge({}, baseResource, containersourceData);
};
export const getEventSourceResource = (formData: EventSourceFormData): K8sResourceKind => {
switch (formData.type) {
case EventSources.KafkaSource:
return getKafkaSourceResource(formData);
case EventSources.ContainerSource:
return getContainerSourceResource(formData);
case EventSources.CronJobSource:
case EventSources.ApiServerSource:
case EventSources.SinkBinding:
Expand Down Expand Up @@ -158,8 +136,8 @@ export const getEventSourceData = (source: string) => {
],
},
kafkasource: {
bootstrapServers: '',
topics: '',
bootstrapServers: [''],
topics: [''],
consumerGroup: '',
net: {
sasl: {
Expand All @@ -177,14 +155,18 @@ export const getEventSourceData = (source: string) => {
serviceAccountName: '',
},
containersource: {
containers: [
{
image: '',
name: '',
args: [{ name: '' }],
env: [],
template: {
spec: {
containers: [
{
image: '',
name: '',
args: [''],
env: [],
},
],
},
],
},
},
};
return eventSourceData[source];
Expand Down