This is an automated email from the ASF dual-hosted git repository. cwylie pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-druid.git
The following commit(s) were added to refs/heads/master by this push: new 494c1a2 Add overlord edit dialog to allow user to change overlord dynamic config on the fly (#7308) 494c1a2 is described below commit 494c1a2ef84c7538780a6ebdbdc74e18fe668af3 Author: Qi Shu <shuqi...@gmail.com> AuthorDate: Thu Mar 21 03:38:35 2019 -0700 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 --- web-console/src/components/filler.tsx | 3 +- web-console/src/components/header-bar.tsx | 11 +- .../src/dialogs/overlord-dynamic-config.scss | 53 +++++++++ .../src/dialogs/overlord-dynamic-config.tsx | 130 +++++++++++++++++++++ 4 files changed, 194 insertions(+), 3 deletions(-) diff --git a/web-console/src/components/filler.tsx b/web-console/src/components/filler.tsx index 11dcf93..decea1f 100644 --- a/web-console/src/components/filler.tsx +++ b/web-console/src/components/filler.tsx @@ -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]; diff --git a/web-console/src/components/header-bar.tsx b/web-console/src/components/header-bar.tsx index 9eea252..ec3e283 100644 --- a/web-console/src/components/header-bar.tsx +++ b/web-console/src/components/header-bar.tsx @@ -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>; } } diff --git a/web-console/src/dialogs/overlord-dynamic-config.scss b/web-console/src/dialogs/overlord-dynamic-config.scss new file mode 100644 index 0000000..0a8dcfc --- /dev/null +++ b/web-console/src/dialogs/overlord-dynamic-config.scss @@ -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; + } + } + } +} diff --git a/web-console/src/dialogs/overlord-dynamic-config.tsx b/web-console/src/dialogs/overlord-dynamic-config.tsx new file mode 100644 index 0000000..f3be789 --- /dev/null +++ b/web-console/src/dialogs/overlord-dynamic-config.tsx @@ -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>; + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@druid.apache.org For additional commands, e-mail: commits-h...@druid.apache.org