mirror of https://github.com/apache/druid.git
Add overlord edit dialog to allow user to change overlord dynamic config on the fly (#7308)
* Add overlord edit dialog to allow user to edit overlord dynmamic config on the fly * Set config to {} if druid return nothing for overlord config
This commit is contained in:
parent
b0271b4f4d
commit
494c1a2ef8
|
@ -53,7 +53,8 @@ export const IconNames = {
|
|||
ARROW_UP: "arrow-up" as "arrow-up",
|
||||
ARROW_DOWN: "arrow-down" as "arrow-down",
|
||||
PROPERTIES: "properties" as "properties",
|
||||
BUILD: "build" as "build"
|
||||
BUILD: "build" as "build",
|
||||
WRENCH: "wrench" as "wrench"
|
||||
};
|
||||
export type IconNames = typeof IconNames[keyof typeof IconNames];
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import * as React from 'react';
|
|||
import { Alignment, IconNames, Navbar, NavbarDivider, NavbarGroup } from "../components/filler";
|
||||
import { AboutDialog } from "../dialogs/about-dialog";
|
||||
import { CoordinatorDynamicConfigDialog } from '../dialogs/coordinator-dynamic-config';
|
||||
import { OverlordDynamicConfigDialog } from "../dialogs/overlord-dynamic-config";
|
||||
import {
|
||||
DRUID_DOCS,
|
||||
DRUID_GITHUB,
|
||||
|
@ -42,6 +43,7 @@ export interface HeaderBarProps extends React.Props<any> {
|
|||
export interface HeaderBarState {
|
||||
aboutDialogOpen: boolean;
|
||||
coordinatorDynamicConfigDialogOpen: boolean;
|
||||
overlordDynamicConfigDialogOpen: boolean;
|
||||
}
|
||||
|
||||
export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
|
||||
|
@ -49,7 +51,8 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
|
|||
super(props);
|
||||
this.state = {
|
||||
aboutDialogOpen: false,
|
||||
coordinatorDynamicConfigDialogOpen: false
|
||||
coordinatorDynamicConfigDialogOpen: false,
|
||||
overlordDynamicConfigDialogOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -104,7 +107,7 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
|
|||
|
||||
render() {
|
||||
const { active } = this.props;
|
||||
const { aboutDialogOpen, coordinatorDynamicConfigDialogOpen } = this.state;
|
||||
const { aboutDialogOpen, coordinatorDynamicConfigDialogOpen, overlordDynamicConfigDialogOpen } = this.state;
|
||||
|
||||
const legacyMenu = <Menu>
|
||||
<MenuItem iconName={IconNames.GRAPH} text="Legacy coordinator console" href={LEGACY_COORDINATOR_CONSOLE} target="_blank" />
|
||||
|
@ -120,6 +123,7 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
|
|||
|
||||
const configMenu = <Menu>
|
||||
<MenuItem iconName={IconNames.COG} text="Coordinator dynamic config" onClick={() => this.setState({ coordinatorDynamicConfigDialogOpen: true })}/>
|
||||
<MenuItem iconName={IconNames.WRENCH} text="Overlord dynamic config" onClick={() => this.setState({ overlordDynamicConfigDialogOpen: true })}/>
|
||||
<MenuItem iconName={IconNames.PROPERTIES} className={classNames(Classes.MINIMAL, { 'pt-active': active === 'lookups' })} text="Lookups" href="#lookups"/>
|
||||
</Menu>;
|
||||
|
||||
|
@ -153,6 +157,9 @@ export class HeaderBar extends React.Component<HeaderBarProps, HeaderBarState> {
|
|||
{ coordinatorDynamicConfigDialogOpen ? <CoordinatorDynamicConfigDialog
|
||||
onClose={() => this.setState({ coordinatorDynamicConfigDialogOpen: false })}
|
||||
/> : null }
|
||||
{ overlordDynamicConfigDialogOpen ? <OverlordDynamicConfigDialog
|
||||
onClose={() => this.setState({ overlordDynamicConfigDialogOpen: false })}
|
||||
/> : null }
|
||||
</Navbar>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.overlord-dynamic-config {
|
||||
|
||||
&.pt-dialog {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
margin-top: 5vh;
|
||||
|
||||
.pt-dialog-body {
|
||||
max-height: 70vh;
|
||||
|
||||
.auto-form {
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
|
||||
.ace_editor {
|
||||
height: 25vh !important;
|
||||
}
|
||||
}
|
||||
|
||||
.html-select {
|
||||
width: 195px;
|
||||
}
|
||||
|
||||
.config-comment {
|
||||
margin-top: 10px;
|
||||
padding: 0 15px;
|
||||
|
||||
textarea {
|
||||
max-width: 200px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Intent } from "@blueprintjs/core";
|
||||
import axios from "axios";
|
||||
import * as React from "react";
|
||||
|
||||
import { AutoForm } from "../components/auto-form";
|
||||
import { IconNames } from "../components/filler";
|
||||
import { AppToaster } from "../singletons/toaster";
|
||||
import { getDruidErrorMessage } from "../utils";
|
||||
|
||||
import { SnitchDialog } from "./snitch-dialog";
|
||||
|
||||
import "./overlord-dynamic-config.scss";
|
||||
|
||||
export interface OverlordDynamicConfigDialogProps extends React.Props<any> {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export interface OverlordDynamicConfigDialogState {
|
||||
dynamicConfig: Record<string, any> | null;
|
||||
allJSONValid: boolean;
|
||||
}
|
||||
|
||||
export class OverlordDynamicConfigDialog extends React.Component<OverlordDynamicConfigDialogProps, OverlordDynamicConfigDialogState> {
|
||||
constructor(props: OverlordDynamicConfigDialogProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
dynamicConfig: null,
|
||||
allJSONValid: true
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.getConfig();
|
||||
}
|
||||
|
||||
async getConfig() {
|
||||
let config: Record<string, any> | null = null;
|
||||
try {
|
||||
const configResp = await axios.get("/druid/indexer/v1/worker");
|
||||
config = configResp.data || {};
|
||||
} catch (e) {
|
||||
AppToaster.show({
|
||||
iconName: IconNames.ERROR,
|
||||
intent: Intent.DANGER,
|
||||
message: `Could not load overlord dynamic config: ${getDruidErrorMessage(e)}`
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
dynamicConfig: config
|
||||
});
|
||||
}
|
||||
|
||||
private saveConfig = async (author: string, comment: string) => {
|
||||
const { onClose } = this.props;
|
||||
const newState: any = this.state.dynamicConfig;
|
||||
try {
|
||||
await axios.post("/druid/indexer/v1/worker", newState, {
|
||||
headers: {
|
||||
"X-Druid-Author": author,
|
||||
"X-Druid-Comment": comment
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
AppToaster.show({
|
||||
iconName: IconNames.ERROR,
|
||||
intent: Intent.DANGER,
|
||||
message: `Could not save overlord dynamic config: ${getDruidErrorMessage(e)}`
|
||||
});
|
||||
}
|
||||
|
||||
AppToaster.show({
|
||||
message: 'Saved overlord dynamic config',
|
||||
intent: Intent.SUCCESS
|
||||
});
|
||||
onClose();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onClose } = this.props;
|
||||
const { dynamicConfig, allJSONValid } = this.state;
|
||||
|
||||
return <SnitchDialog
|
||||
className="overlord-dynamic-config"
|
||||
isOpen
|
||||
onSave={this.saveConfig}
|
||||
onClose={onClose}
|
||||
title="Overlord dynamic config"
|
||||
saveDisabled={!allJSONValid}
|
||||
>
|
||||
<p>
|
||||
Edit the overlord dynamic configuration on the fly.
|
||||
For more information please refer to the <a href="http://druid.io/docs/latest/configuration/index.html#overlord-dynamic-configuration" target="_blank">documentation</a>.
|
||||
</p>
|
||||
<AutoForm
|
||||
fields={[
|
||||
{
|
||||
name: "selectStrategy",
|
||||
type: "json"
|
||||
},
|
||||
{
|
||||
name: "autoScaler",
|
||||
type: "json"
|
||||
}
|
||||
]}
|
||||
model={dynamicConfig}
|
||||
onChange={m => this.setState({ dynamicConfig: m })}
|
||||
updateJSONValidity={e => this.setState({allJSONValid: e})}
|
||||
/>
|
||||
</SnitchDialog>;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue