= {
+ RUNNING: 4,
+ PENDING: 3,
+ WAITING: 2,
+ SUCCESS: 1,
+ FAILED: 1,
+ };
+
+ static SUPERVISOR_SQL = `SELECT
+ "supervisor_id", "type", "source", "state", "detailed_state", "suspended" = 1 AS "suspended"
+FROM sys.supervisors
+ORDER BY "supervisor_id"`;
+
+ static TASK_SQL = `WITH tasks AS (SELECT
+ "task_id", "group_id", "type", "datasource", "created_time", "location", "duration", "error_msg",
+ CASE WHEN "error_msg" = '${CANCELED_ERROR_MSG}' THEN 'CANCELED' WHEN "status" = 'RUNNING' THEN "runner_status" ELSE "status" END AS "status"
+ FROM sys.tasks
+)
+SELECT "task_id", "group_id", "type", "datasource", "created_time", "location", "duration", "error_msg", "status"
+FROM tasks
+ORDER BY
+ (
+ CASE "status"
+ WHEN 'RUNNING' THEN 4
+ WHEN 'PENDING' THEN 3
+ WHEN 'WAITING' THEN 2
+ ELSE 1
+ END
+ ) DESC,
+ "created_time" DESC`;
+
+ constructor(props: IngestionViewProps, context: any) {
+ super(props, context);
+
+ const taskFilter: Filter[] = [];
+ if (props.taskId) taskFilter.push({ id: 'task_id', value: `=${props.taskId}` });
+ if (props.taskGroupId) taskFilter.push({ id: 'group_id', value: `=${props.taskGroupId}` });
+ if (props.datasourceId) taskFilter.push({ id: 'datasource', value: `=${props.datasourceId}` });
+
+ const supervisorFilter: Filter[] = [];
+ if (props.datasourceId)
+ supervisorFilter.push({ id: 'datasource', value: `=${props.datasourceId}` });
+
+ this.state = {
+ supervisorsState: QueryState.INIT,
+
+ showResumeAllSupervisors: false,
+ showSuspendAllSupervisors: false,
+ showTerminateAllSupervisors: false,
+
+ tasksState: QueryState.INIT,
+ taskFilter: taskFilter,
+ supervisorFilter: supervisorFilter,
+
+ supervisorSpecDialogOpen: props.openDialog === 'supervisor',
+ taskSpecDialogOpen: props.openDialog === 'task',
+
+ taskTableActionDialogActions: [],
+ supervisorTableActionDialogActions: [],
+
+ hiddenTaskColumns: new LocalStorageBackedVisibility(
+ LocalStorageKeys.TASK_TABLE_COLUMN_SELECTION,
+ ),
+ hiddenSupervisorColumns: new LocalStorageBackedVisibility(
+ LocalStorageKeys.SUPERVISOR_TABLE_COLUMN_SELECTION,
+ ),
+ };
+
+ this.supervisorQueryManager = new QueryManager({
+ processQuery: async capabilities => {
+ if (capabilities.hasSql()) {
+ return await queryDruidSql({
+ query: IngestionView.SUPERVISOR_SQL,
+ });
+ } else if (capabilities.hasOverlordAccess()) {
+ const supervisors = (await Api.instance.get('/druid/indexer/v1/supervisor?full')).data;
+ if (!Array.isArray(supervisors)) throw new Error(`Unexpected results`);
+ return supervisors.map((sup: any) => {
+ return {
+ supervisor_id: deepGet(sup, 'id'),
+ type: deepGet(sup, 'spec.tuningConfig.type'),
+ source:
+ deepGet(sup, 'spec.ioConfig.topic') ||
+ deepGet(sup, 'spec.ioConfig.stream') ||
+ 'n/a',
+ state: deepGet(sup, 'state'),
+ detailed_state: deepGet(sup, 'detailedState'),
+ suspended: Boolean(deepGet(sup, 'suspended')),
+ };
+ });
+ } else {
+ throw new Error(`must have SQL or overlord access`);
+ }
+ },
+ onStateChange: supervisorsState => {
+ this.setState({
+ supervisorsState,
+ });
+ },
+ });
+
+ this.taskQueryManager = new QueryManager({
+ processQuery: async capabilities => {
+ if (capabilities.hasSql()) {
+ return await queryDruidSql({
+ query: IngestionView.TASK_SQL,
+ });
+ } else if (capabilities.hasOverlordAccess()) {
+ const resp = await Api.instance.get(`/druid/indexer/v1/tasks`);
+ return IngestionView.parseTasks(resp.data);
+ } else {
+ throw new Error(`must have SQL or overlord access`);
+ }
+ },
+ onStateChange: tasksState => {
+ this.setState({
+ tasksState,
+ });
+ },
+ });
+ }
+
+ static parseTasks = (data: any[]): TaskQueryResultRow[] => {
+ return data.map(d => {
+ return {
+ task_id: d.id,
+ group_id: d.groupId,
+ type: d.type,
+ created_time: d.createdTime,
+ datasource: d.dataSource,
+ duration: d.duration ? d.duration : 0,
+ error_msg: d.errorMsg,
+ location: d.location.host ? `${d.location.host}:${d.location.port}` : null,
+ status: d.statusCode === 'RUNNING' ? d.runnerStatusCode : d.statusCode,
+ };
+ });
+ };
+
+ private static onSecondaryPaneSizeChange(secondaryPaneSize: number) {
+ localStorageSet(LocalStorageKeys.INGESTION_VIEW_PANE_SIZE, String(secondaryPaneSize));
+ }
+
+ componentDidMount(): void {
+ const { capabilities } = this.props;
+
+ this.supervisorQueryManager.runQuery(capabilities);
+ this.taskQueryManager.runQuery(capabilities);
+ }
+
+ componentWillUnmount(): void {
+ this.supervisorQueryManager.terminate();
+ this.taskQueryManager.terminate();
+ }
+
+ private readonly closeSpecDialogs = () => {
+ this.setState({
+ supervisorSpecDialogOpen: false,
+ taskSpecDialogOpen: false,
+ });
+ };
+
+ private readonly submitSupervisor = async (spec: JSON) => {
+ try {
+ await Api.instance.post('/druid/indexer/v1/supervisor', spec);
+ } catch (e) {
+ AppToaster.show({
+ message: `Failed to submit supervisor: ${getDruidErrorMessage(e)}`,
+ intent: Intent.DANGER,
+ });
+ return;
+ }
+
+ AppToaster.show({
+ message: 'Supervisor submitted successfully',
+ intent: Intent.SUCCESS,
+ });
+ this.supervisorQueryManager.rerunLastQuery();
+ };
+
+ private readonly submitTask = async (spec: JSON) => {
+ try {
+ await Api.instance.post('/druid/indexer/v1/task', spec);
+ } catch (e) {
+ AppToaster.show({
+ message: `Failed to submit task: ${getDruidErrorMessage(e)}`,
+ intent: Intent.DANGER,
+ });
+ return;
+ }
+
+ AppToaster.show({
+ message: 'Task submitted successfully',
+ intent: Intent.SUCCESS,
+ });
+ this.taskQueryManager.rerunLastQuery();
+ };
+
+ private getSupervisorActions(
+ id: string,
+ supervisorSuspended: boolean,
+ type: string,
+ ): BasicAction[] {
+ const { goToDatasource, goToStreamingDataLoader } = this.props;
+
+ const actions: BasicAction[] = [];
+ if (oneOf(type, 'kafka', 'kinesis')) {
+ actions.push(
+ {
+ icon: IconNames.MULTI_SELECT,
+ title: 'Go to datasource',
+ onAction: () => goToDatasource(id),
+ },
+ {
+ icon: IconNames.CLOUD_UPLOAD,
+ title: 'Open in data loader',
+ onAction: () => goToStreamingDataLoader(id),
+ },
+ );
+ }
+ actions.push(
+ {
+ icon: supervisorSuspended ? IconNames.PLAY : IconNames.PAUSE,
+ title: supervisorSuspended ? 'Resume' : 'Suspend',
+ onAction: () =>
+ supervisorSuspended
+ ? this.setState({ resumeSupervisorId: id })
+ : this.setState({ suspendSupervisorId: id }),
+ },
+ {
+ icon: IconNames.STEP_BACKWARD,
+ title: 'Hard reset',
+ intent: Intent.DANGER,
+ onAction: () => this.setState({ resetSupervisorId: id }),
+ },
+ {
+ icon: IconNames.CROSS,
+ title: 'Terminate',
+ intent: Intent.DANGER,
+ onAction: () => this.setState({ terminateSupervisorId: id }),
+ },
+ );
+ return actions;
+ }
+
+ renderResumeSupervisorAction() {
+ const { resumeSupervisorId } = this.state;
+ if (!resumeSupervisorId) return;
+
+ return (
+ {
+ const resp = await Api.instance.post(
+ `/druid/indexer/v1/supervisor/${Api.encodePath(resumeSupervisorId)}/resume`,
+ {},
+ );
+ return resp.data;
+ }}
+ confirmButtonText="Resume supervisor"
+ successText="Supervisor has been resumed"
+ failText="Could not resume supervisor"
+ intent={Intent.PRIMARY}
+ onClose={() => {
+ this.setState({ resumeSupervisorId: undefined });
+ }}
+ onSuccess={() => {
+ this.supervisorQueryManager.rerunLastQuery();
+ }}
+ >
+ {`Are you sure you want to resume supervisor '${resumeSupervisorId}'?`}
+
+ );
+ }
+
+ renderSuspendSupervisorAction() {
+ const { suspendSupervisorId } = this.state;
+ if (!suspendSupervisorId) return;
+
+ return (
+ {
+ const resp = await Api.instance.post(
+ `/druid/indexer/v1/supervisor/${Api.encodePath(suspendSupervisorId)}/suspend`,
+ {},
+ );
+ return resp.data;
+ }}
+ confirmButtonText="Suspend supervisor"
+ successText="Supervisor has been suspended"
+ failText="Could not suspend supervisor"
+ intent={Intent.DANGER}
+ onClose={() => {
+ this.setState({ suspendSupervisorId: undefined });
+ }}
+ onSuccess={() => {
+ this.supervisorQueryManager.rerunLastQuery();
+ }}
+ >
+ {`Are you sure you want to suspend supervisor '${suspendSupervisorId}'?`}
+
+ );
+ }
+
+ renderResetSupervisorAction() {
+ const { resetSupervisorId } = this.state;
+ if (!resetSupervisorId) return;
+
+ return (
+ {
+ const resp = await Api.instance.post(
+ `/druid/indexer/v1/supervisor/${Api.encodePath(resetSupervisorId)}/reset`,
+ {},
+ );
+ return resp.data;
+ }}
+ confirmButtonText="Hard reset supervisor"
+ successText="Supervisor has been hard reset"
+ failText="Could not hard reset supervisor"
+ intent={Intent.DANGER}
+ onClose={() => {
+ this.setState({ resetSupervisorId: undefined });
+ }}
+ onSuccess={() => {
+ this.supervisorQueryManager.rerunLastQuery();
+ }}
+ warningChecks={[
+ `I understand that resetting ${resetSupervisorId} will clear checkpoints and therefore lead to data loss or duplication.`,
+ 'I understand that this operation cannot be undone.',
+ ]}
+ >
+ {`Are you sure you want to hard reset supervisor '${resetSupervisorId}'?`}
+ Hard resetting a supervisor will lead to data loss or data duplication.
+
+ The reason for using this operation is to recover from a state in which the supervisor
+ ceases operating due to missing offsets.
+
+
+ );
+ }
+
+ renderTerminateSupervisorAction() {
+ const { terminateSupervisorId } = this.state;
+ if (!terminateSupervisorId) return;
+
+ return (
+ {
+ const resp = await Api.instance.post(
+ `/druid/indexer/v1/supervisor/${Api.encodePath(terminateSupervisorId)}/terminate`,
+ {},
+ );
+ return resp.data;
+ }}
+ confirmButtonText="Terminate supervisor"
+ successText="Supervisor has been terminated"
+ failText="Could not terminate supervisor"
+ intent={Intent.DANGER}
+ onClose={() => {
+ this.setState({ terminateSupervisorId: undefined });
+ }}
+ onSuccess={() => {
+ this.supervisorQueryManager.rerunLastQuery();
+ }}
+ >
+ {`Are you sure you want to terminate supervisor '${terminateSupervisorId}'?`}
+ This action is not reversible.
+
+ );
+ }
+
+ private renderSupervisorFilterableCell(field: string) {
+ const { supervisorFilter } = this.state;
+
+ return (row: { value: any }) => (
+ this.setState({ supervisorFilter: filters })}
+ >
+ {row.value}
+
+ );
+ }
+
+ private onSupervisorDetail(supervisor: SupervisorQueryResultRow) {
+ this.setState({
+ supervisorTableActionDialogId: supervisor.supervisor_id,
+ supervisorTableActionDialogActions: this.getSupervisorActions(
+ supervisor.supervisor_id,
+ supervisor.suspended,
+ supervisor.type,
+ ),
+ });
+ }
+
+ private renderSupervisorTable() {
+ const { supervisorsState, hiddenSupervisorColumns, taskFilter, supervisorFilter } = this.state;
+
+ const supervisors = supervisorsState.data || [];
+ return (
+ {
+ this.setState({
+ supervisorFilter: filtered,
+ taskFilter:
+ column.id === 'datasource'
+ ? syncFilterClauseById(taskFilter, filtered, 'datasource')
+ : taskFilter,
+ });
+ }}
+ filterable
+ defaultPageSize={SMALL_TABLE_PAGE_SIZE}
+ pageSizeOptions={SMALL_TABLE_PAGE_SIZE_OPTIONS}
+ showPagination={supervisors.length > SMALL_TABLE_PAGE_SIZE}
+ columns={[
+ {
+ Header: 'Datasource',
+ id: 'datasource',
+ accessor: 'supervisor_id',
+ width: 300,
+ show: hiddenSupervisorColumns.shown('Datasource'),
+ Cell: ({ value, original }) => (
+ this.onSupervisorDetail(original)}
+ hoverIcon={IconNames.EDIT}
+ >
+ {value}
+
+ ),
+ },
+ {
+ Header: 'Type',
+ accessor: 'type',
+ width: 100,
+ Cell: this.renderSupervisorFilterableCell('type'),
+ show: hiddenSupervisorColumns.shown('Type'),
+ },
+ {
+ Header: 'Topic/Stream',
+ accessor: 'source',
+ width: 300,
+ Cell: this.renderSupervisorFilterableCell('source'),
+ show: hiddenSupervisorColumns.shown('Topic/Stream'),
+ },
+ {
+ Header: 'Status',
+ id: 'status',
+ width: 300,
+ accessor: 'detailed_state',
+ Cell: row => (
+ this.setState({ supervisorFilter: filters })}
+ >
+
+ ●
+ {row.value}
+
+
+ ),
+ show: hiddenSupervisorColumns.shown('Status'),
+ },
+ {
+ Header: ACTION_COLUMN_LABEL,
+ id: ACTION_COLUMN_ID,
+ accessor: 'supervisor_id',
+ width: ACTION_COLUMN_WIDTH,
+ filterable: false,
+ Cell: row => {
+ const id = row.value;
+ const type = row.original.type;
+ const supervisorSuspended = row.original.suspended;
+ const supervisorActions = this.getSupervisorActions(id, supervisorSuspended, type);
+ return (
+ this.onSupervisorDetail(row.original)}
+ actions={supervisorActions}
+ />
+ );
+ },
+ show: hiddenSupervisorColumns.shown(ACTION_COLUMN_LABEL),
+ },
+ ]}
+ />
+ );
+ }
+
+ private getTaskActions(
+ id: string,
+ datasource: string,
+ status: string,
+ type: string,
+ ): BasicAction[] {
+ const { goToDatasource, goToClassicBatchDataLoader } = this.props;
+
+ const actions: BasicAction[] = [];
+ if (datasource && status === 'SUCCESS') {
+ actions.push({
+ icon: IconNames.MULTI_SELECT,
+ title: 'Go to datasource',
+ onAction: () => goToDatasource(datasource),
+ });
+ }
+ if (oneOf(type, 'index', 'index_parallel')) {
+ actions.push({
+ icon: IconNames.CLOUD_UPLOAD,
+ title: 'Open in data loader',
+ onAction: () => goToClassicBatchDataLoader(id),
+ });
+ }
+ if (oneOf(status, 'RUNNING', 'WAITING', 'PENDING')) {
+ actions.push({
+ icon: IconNames.CROSS,
+ title: 'Kill',
+ intent: Intent.DANGER,
+ onAction: () => this.setState({ killTaskId: id }),
+ });
+ }
+ return actions;
+ }
+
+ renderKillTaskAction() {
+ const { killTaskId } = this.state;
+ if (!killTaskId) return;
+
+ return (
+ {
+ const resp = await Api.instance.post(
+ `/druid/indexer/v1/task/${Api.encodePath(killTaskId)}/shutdown`,
+ {},
+ );
+ return resp.data;
+ }}
+ confirmButtonText="Kill task"
+ successText="Task was killed"
+ failText="Could not kill task"
+ intent={Intent.DANGER}
+ onClose={() => {
+ this.setState({ killTaskId: undefined });
+ }}
+ onSuccess={() => {
+ this.taskQueryManager.rerunLastQuery();
+ }}
+ >
+ {`Are you sure you want to kill task '${killTaskId}'?`}
+
+ );
+ }
+
+ private renderTaskFilterableCell(field: string) {
+ const { taskFilter } = this.state;
+
+ return (row: { value: any }) => (
+ this.setState({ taskFilter: filters })}
+ >
+ {row.value}
+
+ );
+ }
+
+ private onTaskDetail(task: TaskQueryResultRow) {
+ this.setState({
+ taskTableActionDialogId: task.task_id,
+ taskTableActionDialogStatus: task.status,
+ taskTableActionDialogActions: this.getTaskActions(
+ task.task_id,
+ task.datasource,
+ task.status,
+ task.type,
+ ),
+ });
+ }
+
+ private renderTaskTable() {
+ const { tasksState, taskFilter, groupTasksBy, hiddenTaskColumns, supervisorFilter } =
+ this.state;
+
+ const tasks = tasksState.data || [];
+ return (
+ {
+ this.setState({
+ supervisorFilter:
+ column.id === 'datasource'
+ ? syncFilterClauseById(supervisorFilter, filtered, 'datasource')
+ : supervisorFilter,
+ taskFilter: filtered,
+ });
+ }}
+ defaultSorted={[{ id: 'status', desc: true }]}
+ pivotBy={groupTasksBy ? [groupTasksBy] : []}
+ defaultPageSize={SMALL_TABLE_PAGE_SIZE}
+ pageSizeOptions={SMALL_TABLE_PAGE_SIZE_OPTIONS}
+ showPagination={tasks.length > SMALL_TABLE_PAGE_SIZE}
+ columns={[
+ {
+ Header: 'Task ID',
+ accessor: 'task_id',
+ width: 440,
+ Cell: ({ value, original }) => (
+ this.onTaskDetail(original)}
+ hoverIcon={IconNames.EDIT}
+ >
+ {value}
+
+ ),
+ Aggregated: () => '',
+ show: hiddenTaskColumns.shown('Task ID'),
+ },
+ {
+ Header: 'Group ID',
+ accessor: 'group_id',
+ width: 300,
+ Cell: this.renderTaskFilterableCell('group_id'),
+ Aggregated: () => '',
+ show: hiddenTaskColumns.shown('Group ID'),
+ },
+ {
+ Header: 'Type',
+ accessor: 'type',
+ width: 140,
+ Cell: this.renderTaskFilterableCell('type'),
+ show: hiddenTaskColumns.shown('Type'),
+ },
+ {
+ Header: 'Datasource',
+ accessor: 'datasource',
+ width: 200,
+ Cell: this.renderTaskFilterableCell('datasource'),
+ show: hiddenTaskColumns.shown('Datasource'),
+ },
+ {
+ Header: 'Status',
+ id: 'status',
+ width: 110,
+ accessor: row => ({
+ status: row.status,
+ created_time: row.created_time,
+ toString: () => row.status,
+ }),
+ Cell: row => {
+ if (row.aggregated) return '';
+ const { status } = row.original;
+ const errorMsg = row.original.error_msg;
+ return (
+ this.setState({ taskFilter: filters })}
+ >
+
+ ●
+ {status}
+ {errorMsg && errorMsg !== CANCELED_ERROR_MSG && (
+ this.setState({ alertErrorMsg: errorMsg })}
+ title={errorMsg}
+ >
+ ?
+
+ )}
+
+
+ );
+ },
+ sortMethod: (d1, d2) => {
+ const typeofD1 = typeof d1;
+ const typeofD2 = typeof d2;
+ if (typeofD1 !== typeofD2) return 0;
+ switch (typeofD1) {
+ case 'string':
+ return IngestionView.statusRanking[d1] - IngestionView.statusRanking[d2];
+
+ case 'object':
+ return (
+ IngestionView.statusRanking[d1.status] -
+ IngestionView.statusRanking[d2.status] ||
+ d1.created_time.localeCompare(d2.created_time)
+ );
+
+ default:
+ return 0;
+ }
+ },
+ show: hiddenTaskColumns.shown('Status'),
+ },
+ {
+ Header: 'Created time',
+ accessor: 'created_time',
+ width: 190,
+ Cell: this.renderTaskFilterableCell('created_time'),
+ Aggregated: () => '',
+ show: hiddenTaskColumns.shown('Created time'),
+ },
+ {
+ Header: 'Duration',
+ accessor: 'duration',
+ width: 80,
+ filterable: false,
+ className: 'padded',
+ Cell({ value, original, aggregated }) {
+ if (aggregated) return '';
+ if (value > 0) {
+ return formatDuration(value);
+ }
+ if (oneOf(original.status, 'RUNNING', 'PENDING') && original.created_time) {
+ // Compute running duration from the created time if it exists
+ return formatDuration(Date.now() - Date.parse(original.created_time));
+ }
+ return '';
+ },
+ Aggregated: () => '',
+ show: hiddenTaskColumns.shown('Duration'),
+ },
+ {
+ Header: 'Location',
+ accessor: 'location',
+ width: 200,
+ Cell: this.renderTaskFilterableCell('location'),
+ Aggregated: () => '',
+ show: hiddenTaskColumns.shown('Location'),
+ },
+ {
+ Header: ACTION_COLUMN_LABEL,
+ id: ACTION_COLUMN_ID,
+ accessor: 'task_id',
+ width: ACTION_COLUMN_WIDTH,
+ filterable: false,
+ Cell: row => {
+ if (row.aggregated) return '';
+ const id = row.value;
+ const type = row.row.type;
+ const { datasource, status } = row.original;
+ const taskActions = this.getTaskActions(id, datasource, status, type);
+ return (
+ this.onTaskDetail(row.original)}
+ actions={taskActions}
+ />
+ );
+ },
+ Aggregated: () => '',
+ show: hiddenTaskColumns.shown(ACTION_COLUMN_LABEL),
+ },
+ ]}
+ />
+ );
+ }
+
+ renderBulkSupervisorActions() {
+ const { capabilities, goToQuery } = this.props;
+
+ return (
+ <>
+
+ {capabilities.hasSql() && (
+
+ {this.renderResumeAllSupervisorAction()}
+ {this.renderSuspendAllSupervisorAction()}
+ {this.renderTerminateAllSupervisorAction()}
+ >
+ );
+ }
+
+ renderResumeAllSupervisorAction() {
+ const { showResumeAllSupervisors } = this.state;
+ if (!showResumeAllSupervisors) return;
+
+ return (
+ {
+ const resp = await Api.instance.post(`/druid/indexer/v1/supervisor/resumeAll`, {});
+ return resp.data;
+ }}
+ confirmButtonText="Resume all supervisors"
+ successText="All supervisors have been resumed"
+ failText="Could not resume all supervisors"
+ intent={Intent.PRIMARY}
+ onClose={() => {
+ this.setState({ showResumeAllSupervisors: false });
+ }}
+ onSuccess={() => {
+ this.supervisorQueryManager.rerunLastQuery();
+ }}
+ >
+ Are you sure you want to resume all the supervisors?
+
+ );
+ }
+
+ renderSuspendAllSupervisorAction() {
+ const { showSuspendAllSupervisors } = this.state;
+ if (!showSuspendAllSupervisors) return;
+
+ return (
+ {
+ const resp = await Api.instance.post(`/druid/indexer/v1/supervisor/suspendAll`, {});
+ return resp.data;
+ }}
+ confirmButtonText="Suspend all supervisors"
+ successText="All supervisors have been suspended"
+ failText="Could not suspend all supervisors"
+ intent={Intent.DANGER}
+ onClose={() => {
+ this.setState({ showSuspendAllSupervisors: false });
+ }}
+ onSuccess={() => {
+ this.supervisorQueryManager.rerunLastQuery();
+ }}
+ >
+ Are you sure you want to suspend all the supervisors?
+
+ );
+ }
+
+ renderTerminateAllSupervisorAction() {
+ const { showTerminateAllSupervisors } = this.state;
+ if (!showTerminateAllSupervisors) return;
+
+ return (
+ {
+ const resp = await Api.instance.post(`/druid/indexer/v1/supervisor/terminateAll`, {});
+ return resp.data;
+ }}
+ confirmButtonText="Terminate all supervisors"
+ successText="All supervisors have been terminated"
+ failText="Could not terminate all supervisors"
+ intent={Intent.DANGER}
+ onClose={() => {
+ this.setState({ showTerminateAllSupervisors: false });
+ }}
+ onSuccess={() => {
+ this.supervisorQueryManager.rerunLastQuery();
+ }}
+ >
+ Are you sure you want to terminate all the supervisors?
+
+ );
+ }
+
+ renderBulkTasksActions() {
+ const { goToQuery, capabilities } = this.props;
+
+ return (
+
+ {capabilities.hasSql() && (
+ goToQuery({ queryString: IngestionView.TASK_SQL })}
+ />
+ )}
+ this.setState({ taskSpecDialogOpen: true })}
+ />
+
+ );
+ }
+
+ render(): JSX.Element {
+ const {
+ groupTasksBy,
+ supervisorSpecDialogOpen,
+ taskSpecDialogOpen,
+ alertErrorMsg,
+ taskTableActionDialogId,
+ taskTableActionDialogActions,
+ supervisorTableActionDialogId,
+ supervisorTableActionDialogActions,
+ taskTableActionDialogStatus,
+ hiddenSupervisorColumns,
+ hiddenTaskColumns,
+ } = this.state;
+
+ return (
+ <>
+
+
+
+ {
+ if (auto && hasPopoverOpen()) return;
+ this.supervisorQueryManager.rerunLastQuery(auto);
+ }}
+ />
+ {this.renderBulkSupervisorActions()}
+
+ this.setState(prevState => ({
+ hiddenSupervisorColumns: prevState.hiddenSupervisorColumns.toggle(column),
+ }))
+ }
+ tableColumnsHidden={hiddenSupervisorColumns.getHiddenColumns()}
+ />
+
+ {this.renderSupervisorTable()}
+
+
+
+
+
+
+
+
+
+
+
+ {
+ if (auto && hasPopoverOpen()) return;
+ this.taskQueryManager.rerunLastQuery(auto);
+ }}
+ />
+ {this.renderBulkTasksActions()}
+
+ this.setState(prevState => ({
+ hiddenTaskColumns: prevState.hiddenTaskColumns.toggle(column),
+ }))
+ }
+ tableColumnsHidden={hiddenTaskColumns.getHiddenColumns()}
+ />
+
+ {this.renderTaskTable()}
+
+
+ {this.renderResumeSupervisorAction()}
+ {this.renderSuspendSupervisorAction()}
+ {this.renderResetSupervisorAction()}
+ {this.renderTerminateSupervisorAction()}
+ {this.renderKillTaskAction()}
+ {supervisorSpecDialogOpen && (
+
+ )}
+ {taskSpecDialogOpen && (
+
+ )}
+ this.setState({ alertErrorMsg: undefined })}
+ >
+ {alertErrorMsg}
+
+ {supervisorTableActionDialogId && (
+ this.setState({ supervisorTableActionDialogId: undefined })}
+ />
+ )}
+ {taskTableActionDialogId && taskTableActionDialogStatus && (
+ this.setState({ taskTableActionDialogId: undefined })}
+ />
+ )}
+ >
+ );
+ }
+}