From 3299ec0e11ae72791cd55a7d89e1265c80a5bc35 Mon Sep 17 00:00:00 2001
From: Vadim Ogievetsky
Date: Thu, 5 May 2022 10:10:30 -0700
Subject: [PATCH 1/2] allow unrestrict
---
.../components/header-bar/header-bar.spec.tsx | 4 +-
.../src/components/header-bar/header-bar.tsx | 39 ++++++++++++---
web-console/src/console-application.tsx | 14 ++++--
web-console/src/utils/capabilities.ts | 48 ++++++++++++++-----
4 files changed, 83 insertions(+), 22 deletions(-)
diff --git a/web-console/src/components/header-bar/header-bar.spec.tsx b/web-console/src/components/header-bar/header-bar.spec.tsx
index cac4febb262b..61650aa7aa9c 100644
--- a/web-console/src/components/header-bar/header-bar.spec.tsx
+++ b/web-console/src/components/header-bar/header-bar.spec.tsx
@@ -25,7 +25,9 @@ import { HeaderBar } from './header-bar';
describe('HeaderBar', () => {
it('matches snapshot', () => {
- const headerBar = shallow( );
+ const headerBar = shallow(
+ {}} />,
+ );
expect(headerBar).toMatchSnapshot();
});
});
diff --git a/web-console/src/components/header-bar/header-bar.tsx b/web-console/src/components/header-bar/header-bar.tsx
index 217ff807571f..53042d91a22d 100644
--- a/web-console/src/components/header-bar/header-bar.tsx
+++ b/web-console/src/components/header-bar/header-bar.tsx
@@ -85,10 +85,11 @@ const DruidLogo = React.memo(function DruidLogo() {
interface RestrictedModeProps {
capabilities: Capabilities;
+ onUnrestrict(capabilities: Capabilities): void;
}
const RestrictedMode = React.memo(function RestrictedMode(props: RestrictedModeProps) {
- const { capabilities } = props;
+ const { capabilities, onUnrestrict } = props;
const mode = capabilities.getModeExtended();
let label: string;
@@ -136,7 +137,8 @@ const RestrictedMode = React.memo(function RestrictedMode(props: RestrictedModeP
It appears that you are accessing the console on the Coordinator/Overlord shared service.
Due to the lack of access to some APIs on this service the console will operate in a
- limited mode. The full version of the console can be accessed on the Router service.
+ limited mode. The unrestricted version of the console can be accessed on the Router
+ service.
);
break;
@@ -157,8 +159,8 @@ const RestrictedMode = React.memo(function RestrictedMode(props: RestrictedModeP
message = (
It appears that you are accessing the console on the Overlord service. Due to the lack of
- access to some APIs on this service the console will operate in a limited mode. The full
- version of the console can be accessed on the Router service.
+ access to some APIs on this service the console will operate in a limited mode. The
+ unrestricted version of the console can be accessed on the Router service.
);
break;
@@ -168,7 +170,8 @@ const RestrictedMode = React.memo(function RestrictedMode(props: RestrictedModeP
message = (
Due to the lack of access to some APIs on this service the console will operate in a
- limited mode. The full version of the console can be accessed on the Router service.
+ limited mode. The unrestricted version of the console can be accessed on the Router
+ service.
);
break;
@@ -187,6 +190,27 @@ const RestrictedMode = React.memo(function RestrictedMode(props: RestrictedModeP
.
+
+ It is possible that there is an issue with the capability detection. You can enable the
+ unrestricted console but certain features might not work if the underlying APIs are not
+ available.
+
+
+ onUnrestrict(Capabilities.FULL)}
+ />
+
+ {!capabilities.hasSql() && (
+
+ onUnrestrict(Capabilities.NO_SQL)}
+ />
+
+ )}
}
position={Position.BOTTOM_RIGHT}
@@ -199,10 +223,11 @@ const RestrictedMode = React.memo(function RestrictedMode(props: RestrictedModeP
export interface HeaderBarProps {
active: HeaderActiveTab;
capabilities: Capabilities;
+ onUnrestrict(capabilities: Capabilities): void;
}
export const HeaderBar = React.memo(function HeaderBar(props: HeaderBarProps) {
- const { active, capabilities } = props;
+ const { active, capabilities, onUnrestrict } = props;
const [aboutDialogOpen, setAboutDialogOpen] = useState(false);
const [doctorDialogOpen, setDoctorDialogOpen] = useState(false);
const [coordinatorDynamicConfigDialogOpen, setCoordinatorDynamicConfigDialogOpen] =
@@ -371,7 +396,7 @@ export const HeaderBar = React.memo(function HeaderBar(props: HeaderBarProps) {
/>
-
+
diff --git a/web-console/src/console-application.tsx b/web-console/src/console-application.tsx
index 2283deac68d6..731002632c03 100644
--- a/web-console/src/console-application.tsx
+++ b/web-console/src/console-application.tsx
@@ -55,7 +55,7 @@ export class ConsoleApplication extends React.PureComponent<
> {
private readonly capabilitiesQueryManager: QueryManager;
- static shownNotifications() {
+ static shownServiceNotification() {
AppToaster.show({
icon: IconNames.ERROR,
intent: Intent.DANGER,
@@ -87,7 +87,7 @@ export class ConsoleApplication extends React.PureComponent<
this.capabilitiesQueryManager = new QueryManager({
processQuery: async () => {
const capabilities = await Capabilities.detectCapabilities();
- if (!capabilities) ConsoleApplication.shownNotifications();
+ if (!capabilities) ConsoleApplication.shownServiceNotification();
return capabilities || Capabilities.FULL;
},
onStateChange: ({ data, loading }) => {
@@ -107,6 +107,10 @@ export class ConsoleApplication extends React.PureComponent<
this.capabilitiesQueryManager.terminate();
}
+ private readonly handleUnrestrict = (capabilities: Capabilities) => {
+ this.setState({ capabilities });
+ };
+
private resetInitialsWithDelay() {
setTimeout(() => {
this.taskId = undefined;
@@ -168,7 +172,11 @@ export class ConsoleApplication extends React.PureComponent<
return (
<>
-
+
{el}
>
);
diff --git a/web-console/src/utils/capabilities.ts b/web-console/src/utils/capabilities.ts
index 406115dfc996..033b39842525 100644
--- a/web-console/src/utils/capabilities.ts
+++ b/web-console/src/utils/capabilities.ts
@@ -40,8 +40,9 @@ export interface CapabilitiesOptions {
}
export class Capabilities {
- static STATUS_TIMEOUT = 2000;
+ static STATUS_TIMEOUT = 15000;
static FULL: Capabilities;
+ static NO_SQL: Capabilities;
static COORDINATOR_OVERLORD: Capabilities;
static COORDINATOR: Capabilities;
static OVERLORD: Capabilities;
@@ -55,7 +56,7 @@ export class Capabilities {
// Check SQL endpoint
try {
await Api.instance.post(
- '/druid/v2/sql',
+ '/druid/v2/sql?capabilities',
{ query: 'SELECT 1337', context: { timeout: Capabilities.STATUS_TIMEOUT } },
{ timeout: Capabilities.STATUS_TIMEOUT },
);
@@ -65,7 +66,7 @@ export class Capabilities {
return; // other failure
}
try {
- await Api.instance.get('/status', { timeout: Capabilities.STATUS_TIMEOUT });
+ await Api.instance.get('/status?capabilities', { timeout: Capabilities.STATUS_TIMEOUT });
} catch (e) {
return; // total failure
}
@@ -73,7 +74,7 @@ export class Capabilities {
try {
await Api.instance.post(
- '/druid/v2',
+ '/druid/v2?capabilities',
{
queryType: 'dataSourceMetadata',
dataSource: '__web_console_probe__',
@@ -95,9 +96,9 @@ export class Capabilities {
return 'nativeAndSql';
}
- static async detectNode(node: 'coordinator' | 'overlord'): Promise {
+ static async detectManagementProxy(): Promise {
try {
- await Api.instance.get(`/proxy/${node}/status`, {
+ await Api.instance.get(`/proxy/coordinator/status?capabilities`, {
timeout: Capabilities.STATUS_TIMEOUT,
});
} catch (e) {
@@ -107,6 +108,21 @@ export class Capabilities {
return true;
}
+ static async detectNode(node: 'coordinator' | 'overlord'): Promise {
+ try {
+ await Api.instance.get(
+ `/druid/${node === 'overlord' ? 'indexer' : node}/v1/isLeader?capabilities`,
+ {
+ timeout: Capabilities.STATUS_TIMEOUT,
+ },
+ );
+ } catch (e) {
+ return false;
+ }
+
+ return true;
+ }
+
static async detectCapabilities(): Promise {
const capabilitiesOverride = localStorageGetJson(LocalStorageKeys.CAPABILITIES_OVERRIDE);
if (capabilitiesOverride) return new Capabilities(capabilitiesOverride);
@@ -114,11 +130,16 @@ export class Capabilities {
const queryType = await Capabilities.detectQueryType();
if (typeof queryType === 'undefined') return;
- const coordinator = await Capabilities.detectNode('coordinator');
- if (typeof coordinator === 'undefined') return;
-
- const overlord = await Capabilities.detectNode('overlord');
- if (typeof overlord === 'undefined') return;
+ let coordinator: boolean;
+ let overlord: boolean;
+ if (queryType === 'none') {
+ // must not be running on the router, figure out what node the console is on (or both?)
+ coordinator = await Capabilities.detectNode('coordinator');
+ overlord = await Capabilities.detectNode('overlord');
+ } else {
+ // must be running on the router, figure out if the management proxy is working
+ coordinator = overlord = await Capabilities.detectManagementProxy();
+ }
return new Capabilities({
queryType,
@@ -204,6 +225,11 @@ Capabilities.FULL = new Capabilities({
coordinator: true,
overlord: true,
});
+Capabilities.NO_SQL = new Capabilities({
+ queryType: 'nativeOnly',
+ coordinator: true,
+ overlord: true,
+});
Capabilities.COORDINATOR_OVERLORD = new Capabilities({
queryType: 'none',
coordinator: true,
From 88c14666f1baf1aaeae32051425731df6acadfe7 Mon Sep 17 00:00:00 2001
From: Vadim Ogievetsky
Date: Thu, 5 May 2022 10:45:42 -0700
Subject: [PATCH 2/2] update tests
---
.../components/header-bar/__snapshots__/header-bar.spec.tsx.snap | 1 +
1 file changed, 1 insertion(+)
diff --git a/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap b/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap
index bfd6aa4c3daa..b3943e556a92 100644
--- a/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap
+++ b/web-console/src/components/header-bar/__snapshots__/header-bar.spec.tsx.snap
@@ -76,6 +76,7 @@ exports[`HeaderBar matches snapshot 1`] = `
"queryType": "nativeAndSql",
}
}
+ onUnrestrict={[Function]}
/>