Issue#517 connector task status bug (#747)

* kafka-ui-connectors.yaml fix

* adding tasks status check

* adding new Connector Status

* adding new Connector Status

* [issue-517] connector task status bug #747

Co-authored-by: marselakhmetov <makhmetov@provectus.com>
Co-authored-by: mbovtryuk <mbovtryuk@provectus.com>
This commit is contained in:
Marsel 2021-08-18 14:03:41 +05:00 committed by GitHub
parent 128c67c1cf
commit 7f66f00008
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 93 additions and 49 deletions

View file

@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.provectus.kafka.ui.client.KafkaConnectClients; import com.provectus.kafka.ui.client.KafkaConnectClients;
import com.provectus.kafka.ui.connect.model.ConnectorTopics; import com.provectus.kafka.ui.connect.model.ConnectorTopics;
import com.provectus.kafka.ui.connect.model.TaskStatus;
import com.provectus.kafka.ui.exception.ClusterNotFoundException; import com.provectus.kafka.ui.exception.ClusterNotFoundException;
import com.provectus.kafka.ui.exception.ConnectNotFoundException; import com.provectus.kafka.ui.exception.ConnectNotFoundException;
import com.provectus.kafka.ui.mapper.ClusterMapper; import com.provectus.kafka.ui.mapper.ClusterMapper;
@ -13,6 +14,7 @@ import com.provectus.kafka.ui.model.Connector;
import com.provectus.kafka.ui.model.ConnectorAction; import com.provectus.kafka.ui.model.ConnectorAction;
import com.provectus.kafka.ui.model.ConnectorPlugin; import com.provectus.kafka.ui.model.ConnectorPlugin;
import com.provectus.kafka.ui.model.ConnectorPluginConfigValidationResponse; import com.provectus.kafka.ui.model.ConnectorPluginConfigValidationResponse;
import com.provectus.kafka.ui.model.ConnectorState;
import com.provectus.kafka.ui.model.FullConnectorInfo; import com.provectus.kafka.ui.model.FullConnectorInfo;
import com.provectus.kafka.ui.model.KafkaCluster; import com.provectus.kafka.ui.model.KafkaCluster;
import com.provectus.kafka.ui.model.KafkaConnectCluster; import com.provectus.kafka.ui.model.KafkaConnectCluster;
@ -161,23 +163,32 @@ public class KafkaConnectService {
public Mono<Connector> getConnector(String clusterName, String connectName, public Mono<Connector> getConnector(String clusterName, String connectName,
String connectorName) { String connectorName) {
return getConnectAddress(clusterName, connectName) return getConnectAddress(clusterName, connectName)
.flatMap(connect -> .flatMap(connect -> KafkaConnectClients.withBaseUrl(connect).getConnector(connectorName)
KafkaConnectClients.withBaseUrl(connect).getConnector(connectorName) .map(kafkaConnectMapper::fromClient)
.map(kafkaConnectMapper::fromClient) .flatMap(connector ->
.flatMap(connector -> KafkaConnectClients.withBaseUrl(connect).getConnectorStatus(connector.getName())
KafkaConnectClients.withBaseUrl(connect).getConnectorStatus(connector.getName()) .map(connectorStatus -> {
.map(connectorStatus -> { var status = connectorStatus.getConnector();
var status = connectorStatus.getConnector(); Connector result = (Connector) new Connector()
connector.status(kafkaConnectMapper.fromClient(status)); .connect(connectName)
return (Connector) new Connector() .status(kafkaConnectMapper.fromClient(status))
.connect(connectName) .type(connector.getType())
.status(kafkaConnectMapper.fromClient(status)) .tasks(connector.getTasks())
.type(connector.getType()) .name(connector.getName())
.tasks(connector.getTasks()) .config(connector.getConfig());
.name(connector.getName())
.config(connector.getConfig()); if (connectorStatus.getTasks() != null) {
}) boolean isAnyTaskFailed = connectorStatus.getTasks().stream()
) .map(TaskStatus::getState)
.anyMatch(TaskStatus.StateEnum.FAILED::equals);
if (isAnyTaskFailed) {
result.getStatus().state(ConnectorState.TASK_FAILED);
}
}
return result;
})
)
); );
} }

View file

@ -8,8 +8,8 @@ import com.provectus.kafka.ui.model.ConnectorPlugin;
import com.provectus.kafka.ui.model.ConnectorPluginConfig; import com.provectus.kafka.ui.model.ConnectorPluginConfig;
import com.provectus.kafka.ui.model.ConnectorPluginConfigValidationResponse; import com.provectus.kafka.ui.model.ConnectorPluginConfigValidationResponse;
import com.provectus.kafka.ui.model.ConnectorPluginConfigValue; import com.provectus.kafka.ui.model.ConnectorPluginConfigValue;
import com.provectus.kafka.ui.model.ConnectorState;
import com.provectus.kafka.ui.model.ConnectorStatus; import com.provectus.kafka.ui.model.ConnectorStatus;
import com.provectus.kafka.ui.model.ConnectorTaskStatus;
import com.provectus.kafka.ui.model.ConnectorType; import com.provectus.kafka.ui.model.ConnectorType;
import com.provectus.kafka.ui.model.NewConnector; import com.provectus.kafka.ui.model.NewConnector;
import com.provectus.kafka.ui.model.TaskId; import com.provectus.kafka.ui.model.TaskId;
@ -171,7 +171,7 @@ public class KafkaConnectServiceTests extends AbstractBaseTest {
Connector expected = (Connector) new Connector() Connector expected = (Connector) new Connector()
.connect(connectName) .connect(connectName)
.status(new ConnectorStatus() .status(new ConnectorStatus()
.state(ConnectorTaskStatus.RUNNING) .state(ConnectorState.RUNNING)
.workerId("kafka-connect:8083")) .workerId("kafka-connect:8083"))
.tasks(List.of(new TaskId() .tasks(List.of(new TaskId()
.connector(connectorName) .connector(connectorName)

View file

@ -2285,7 +2285,7 @@ components:
type: object type: object
properties: properties:
state: state:
$ref: '#/components/schemas/ConnectorTaskStatus' $ref: '#/components/schemas/ConnectorState'
worker_id: worker_id:
type: string type: string
required: required:
@ -2299,6 +2299,15 @@ components:
- PAUSED - PAUSED
- UNASSIGNED - UNASSIGNED
ConnectorState:
type: string
enum:
- RUNNING
- FAILED
- PAUSED
- UNASSIGNED
- TASK_FAILED
ConnectorAction: ConnectorAction:
type: string type: string
enum: enum:

View file

@ -0,0 +1,21 @@
import cx from 'classnames';
import { ConnectorState } from 'generated-sources';
import React from 'react';
export interface StatusTagProps {
status: ConnectorState;
}
const ConnectorStatusTag: React.FC<StatusTagProps> = ({ status }) => {
const classNames = cx('tag', {
'is-success': status === ConnectorState.RUNNING,
'is-light': status === ConnectorState.PAUSED,
'is-warning': status === ConnectorState.UNASSIGNED,
'is-danger':
status === ConnectorState.FAILED || status === ConnectorState.TASK_FAILED,
});
return <span className={classNames}>{status}</span>;
};
export default ConnectorStatusTag;

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { Link, useHistory, useParams } from 'react-router-dom'; import { Link, useHistory, useParams } from 'react-router-dom';
import { ConnectorTaskStatus } from 'generated-sources'; import { ConnectorState } from 'generated-sources';
import { ClusterName, ConnectName, ConnectorName } from 'redux/interfaces'; import { ClusterName, ConnectName, ConnectorName } from 'redux/interfaces';
import { import {
clusterConnectConnectorEditPath, clusterConnectConnectorEditPath,
@ -21,7 +21,7 @@ export interface ActionsProps {
connectorName: ConnectorName connectorName: ConnectorName
): Promise<void>; ): Promise<void>;
isConnectorDeleting: boolean; isConnectorDeleting: boolean;
connectorStatus?: ConnectorTaskStatus; connectorStatus?: ConnectorState;
restartConnector( restartConnector(
clusterName: ClusterName, clusterName: ClusterName,
connectName: ConnectName, connectName: ConnectName,
@ -79,7 +79,7 @@ const Actions: React.FC<ActionsProps> = ({
return ( return (
<div className="buttons"> <div className="buttons">
{connectorStatus === ConnectorTaskStatus.RUNNING && ( {connectorStatus === ConnectorState.RUNNING && (
<button <button
type="button" type="button"
className="button" className="button"
@ -93,7 +93,7 @@ const Actions: React.FC<ActionsProps> = ({
</button> </button>
)} )}
{connectorStatus === ConnectorTaskStatus.PAUSED && ( {connectorStatus === ConnectorState.PAUSED && (
<button <button
type="button" type="button"
className="button" className="button"

View file

@ -8,7 +8,7 @@ import ActionsContainer from 'components/Connect/Details/Actions/ActionsContaine
import Actions, { import Actions, {
ActionsProps, ActionsProps,
} from 'components/Connect/Details/Actions/Actions'; } from 'components/Connect/Details/Actions/Actions';
import { ConnectorTaskStatus } from 'generated-sources'; import { ConnectorState } from 'generated-sources';
import { ConfirmationModalProps } from 'components/common/ConfirmationModal/ConfirmationModal'; import { ConfirmationModalProps } from 'components/common/ConfirmationModal/ConfirmationModal';
const mockHistoryPush = jest.fn(); const mockHistoryPush = jest.fn();
@ -45,7 +45,7 @@ describe('Actions', () => {
<Actions <Actions
deleteConnector={jest.fn()} deleteConnector={jest.fn()}
isConnectorDeleting={false} isConnectorDeleting={false}
connectorStatus={ConnectorTaskStatus.RUNNING} connectorStatus={ConnectorState.RUNNING}
restartConnector={jest.fn()} restartConnector={jest.fn()}
pauseConnector={jest.fn()} pauseConnector={jest.fn()}
resumeConnector={jest.fn()} resumeConnector={jest.fn()}
@ -62,21 +62,21 @@ describe('Actions', () => {
it('matches snapshot when paused', () => { it('matches snapshot when paused', () => {
const wrapper = create( const wrapper = create(
setupWrapper({ connectorStatus: ConnectorTaskStatus.PAUSED }) setupWrapper({ connectorStatus: ConnectorState.PAUSED })
); );
expect(wrapper.toJSON()).toMatchSnapshot(); expect(wrapper.toJSON()).toMatchSnapshot();
}); });
it('matches snapshot when failed', () => { it('matches snapshot when failed', () => {
const wrapper = create( const wrapper = create(
setupWrapper({ connectorStatus: ConnectorTaskStatus.FAILED }) setupWrapper({ connectorStatus: ConnectorState.FAILED })
); );
expect(wrapper.toJSON()).toMatchSnapshot(); expect(wrapper.toJSON()).toMatchSnapshot();
}); });
it('matches snapshot when unassigned', () => { it('matches snapshot when unassigned', () => {
const wrapper = create( const wrapper = create(
setupWrapper({ connectorStatus: ConnectorTaskStatus.UNASSIGNED }) setupWrapper({ connectorStatus: ConnectorState.UNASSIGNED })
); );
expect(wrapper.toJSON()).toMatchSnapshot(); expect(wrapper.toJSON()).toMatchSnapshot();
}); });
@ -157,7 +157,7 @@ describe('Actions', () => {
const pauseConnector = jest.fn(); const pauseConnector = jest.fn();
const wrapper = mount( const wrapper = mount(
setupWrapper({ setupWrapper({
connectorStatus: ConnectorTaskStatus.RUNNING, connectorStatus: ConnectorState.RUNNING,
pauseConnector, pauseConnector,
}) })
); );
@ -174,7 +174,7 @@ describe('Actions', () => {
const resumeConnector = jest.fn(); const resumeConnector = jest.fn();
const wrapper = mount( const wrapper = mount(
setupWrapper({ setupWrapper({
connectorStatus: ConnectorTaskStatus.PAUSED, connectorStatus: ConnectorState.PAUSED,
resumeConnector, resumeConnector,
}) })
); );

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { Connector } from 'generated-sources'; import { Connector } from 'generated-sources';
import StatusTag from 'components/Connect/StatusTag'; import ConnectorStatusTag from 'components/Connect/ConnectorStatusTag';
export interface OverviewProps { export interface OverviewProps {
connector: Connector | null; connector: Connector | null;
@ -38,7 +38,7 @@ const Overview: React.FC<OverviewProps> = ({
<tr> <tr>
<th>State</th> <th>State</th>
<td> <td>
<StatusTag status={connector.status.state} /> <ConnectorStatusTag status={connector.status.state} />
</td> </td>
</tr> </tr>
<tr> <tr>

View file

@ -37,9 +37,11 @@ exports[`Overview view matches snapshot 1`] = `
State State
</th> </th>
<td> <td>
<mock-StatusTag <span
status="RUNNING" className="tag is-success"
/> >
RUNNING
</span>
</td> </td>
</tr> </tr>
<tr> <tr>

View file

@ -10,7 +10,7 @@ import Dropdown from 'components/common/Dropdown/Dropdown';
import DropdownDivider from 'components/common/Dropdown/DropdownDivider'; import DropdownDivider from 'components/common/Dropdown/DropdownDivider';
import DropdownItem from 'components/common/Dropdown/DropdownItem'; import DropdownItem from 'components/common/Dropdown/DropdownItem';
import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal'; import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal';
import StatusTag from 'components/Connect/StatusTag'; import ConnectorStatusTag from 'components/Connect/ConnectorStatusTag';
export interface ListItemProps { export interface ListItemProps {
clusterName: ClusterName; clusterName: ClusterName;
@ -72,7 +72,7 @@ const ListItem: React.FC<ListItemProps> = ({
))} ))}
</div> </div>
</td> </td>
<td>{status && <StatusTag status={status.state} />}</td> <td>{status && <ConnectorStatusTag status={status.state} />}</td>
<td> <td>
{runningTasks && ( {runningTasks && (
<span <span

View file

@ -132,7 +132,7 @@ exports[`Connectors ListItem matches snapshot 1`] = `
</div> </div>
</td> </td>
<td> <td>
<StatusTag <ConnectorStatusTag
status="RUNNING" status="RUNNING"
> >
<span <span
@ -140,7 +140,7 @@ exports[`Connectors ListItem matches snapshot 1`] = `
> >
RUNNING RUNNING
</span> </span>
</StatusTag> </ConnectorStatusTag>
</td> </td>
<td> <td>
<span <span

View file

@ -1,6 +1,7 @@
import { import {
Connect, Connect,
Connector, Connector,
ConnectorState,
ConnectorTaskStatus, ConnectorTaskStatus,
ConnectorType, ConnectorType,
FullConnectorInfo, FullConnectorInfo,
@ -49,7 +50,7 @@ export const connectors: FullConnectorInfo[] = [
type: ConnectorType.SOURCE, type: ConnectorType.SOURCE,
topics: ['test-topic'], topics: ['test-topic'],
status: { status: {
state: ConnectorTaskStatus.RUNNING, state: ConnectorState.RUNNING,
}, },
tasksCount: 2, tasksCount: 2,
failedTasksCount: 0, failedTasksCount: 0,
@ -61,7 +62,7 @@ export const connectors: FullConnectorInfo[] = [
type: ConnectorType.SINK, type: ConnectorType.SINK,
topics: ['test-topic'], topics: ['test-topic'],
status: { status: {
state: ConnectorTaskStatus.FAILED, state: ConnectorState.FAILED,
}, },
tasksCount: 3, tasksCount: 3,
failedTasksCount: 1, failedTasksCount: 1,
@ -90,7 +91,7 @@ export const connector: Connector = {
name: 'hdfs-source-connector', name: 'hdfs-source-connector',
type: ConnectorType.SOURCE, type: ConnectorType.SOURCE,
status: { status: {
state: ConnectorTaskStatus.RUNNING, state: ConnectorState.RUNNING,
workerId: 'kafka-connect0:8083', workerId: 'kafka-connect0:8083',
}, },
config: { config: {

View file

@ -1,4 +1,4 @@
import { ConnectorTaskStatus } from 'generated-sources'; import { ConnectorState, ConnectorTaskStatus } from 'generated-sources';
import { import {
fetchConnectorsAction, fetchConnectorsAction,
fetchConnectorAction, fetchConnectorAction,
@ -23,7 +23,7 @@ const runningConnectorState = {
...connector, ...connector,
status: { status: {
...connector.status, ...connector.status,
state: ConnectorTaskStatus.RUNNING, state: ConnectorState.RUNNING,
}, },
}, },
tasks: tasks.map((task) => ({ tasks: tasks.map((task) => ({
@ -44,7 +44,7 @@ const pausedConnectorState = {
...connector, ...connector,
status: { status: {
...connector.status, ...connector.status,
state: ConnectorTaskStatus.PAUSED, state: ConnectorState.PAUSED,
}, },
}, },
tasks: tasks.map((task) => ({ tasks: tasks.map((task) => ({

View file

@ -2,7 +2,7 @@ import { getType } from 'typesafe-actions';
import * as actions from 'redux/actions'; import * as actions from 'redux/actions';
import { ConnectState } from 'redux/interfaces/connect'; import { ConnectState } from 'redux/interfaces/connect';
import { Action } from 'redux/interfaces'; import { Action } from 'redux/interfaces';
import { ConnectorTaskStatus } from 'generated-sources'; import { ConnectorState, ConnectorTaskStatus } from 'generated-sources';
export const initialState: ConnectState = { export const initialState: ConnectState = {
connects: [], connects: [],
@ -53,7 +53,7 @@ const reducer = (state = initialState, action: Action): ConnectState => {
...state.currentConnector.connector, ...state.currentConnector.connector,
status: { status: {
...state.currentConnector.connector?.status, ...state.currentConnector.connector?.status,
state: ConnectorTaskStatus.PAUSED, state: ConnectorState.PAUSED,
}, },
} }
: null, : null,
@ -76,7 +76,7 @@ const reducer = (state = initialState, action: Action): ConnectState => {
...state.currentConnector.connector, ...state.currentConnector.connector,
status: { status: {
...state.currentConnector.connector?.status, ...state.currentConnector.connector?.status,
state: ConnectorTaskStatus.RUNNING, state: ConnectorState.RUNNING,
}, },
} }
: null, : null,