This is an automated email from the ASF dual-hosted git repository.
arshad pushed a commit to branch frontend-refactor
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/frontend-refactor by this push:
new c759443780 AMBARI-26363 : Ambari Web React: Cluster Installation
Wizard - Step7 (#4069)
c759443780 is described below
commit c759443780b71a539e1eae670147cff0a7a30de8
Author: Himanshu Maurya <[email protected]>
AuthorDate: Sun Sep 14 21:47:33 2025 +0530
AMBARI-26363 : Ambari Web React: Cluster Installation Wizard - Step7 (#4069)
---
.../screens/ClusterWizard/Step7/AccountsTab.tsx | 276 +++++++++++++++++++++
.../ClusterWizard/Step7/AllConfigurationsTab.tsx | 27 ++
.../screens/ClusterWizard/Step7/CredentialsTab.tsx | 266 ++++++++++++++++++++
.../screens/ClusterWizard/Step7/DatabasesTab.tsx | 27 ++
.../screens/ClusterWizard/Step7/DirectoriesTab.tsx | 27 ++
.../src/screens/ClusterWizard/types/step7Types.ts | 13 +
.../latest/src/screens/ClusterWizard/utils.ts | 88 +++++++
7 files changed, 724 insertions(+)
diff --git a/ambari-web/latest/src/screens/ClusterWizard/Step7/AccountsTab.tsx
b/ambari-web/latest/src/screens/ClusterWizard/Step7/AccountsTab.tsx
new file mode 100644
index 0000000000..42e5c928c3
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/AccountsTab.tsx
@@ -0,0 +1,276 @@
+/**
+ * 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 { cloneDeep, get, isEmpty, set } from "lodash";
+import { useEffect, useRef, useState } from "react";
+import { Card, Col, Container, Row } from "react-bootstrap";
+import { getDependentConfigChanges, isValidUserName } from "../utils";
+import Table from "../../../components/Table";
+import Modal from "../../../components/Modal";
+import TooltipInput from "../../../components/TooltipInput";
+import Spinner from "../../../components/Spinner";
+
+interface AccountsTabProps {
+ configProperties:any,
+ setConfigProperties:any,
+ services: string[];
+}
+
+enum UserGroupsProperties {
+ USER = "USER",
+ GROUP = "GROUP",
+ ADDITIONAL_USER_PROPERTY = "ADDITIONAL_USER_PROPERTY",
+ ADDITIONAL_GROUP_PROPERTY = "ADDITIONAL_GROUP_PROPERTY",
+}
+
+export default function AccountsTab({
+ configProperties,
+ setConfigProperties,
+ services,
+}: AccountsTabProps) {
+ const [accountConfigs, setAccountConfigs] = useState(configProperties);
+ const [showWarning, setShowWarning] = useState<boolean>(false);
+ const affectedProperties = useRef<any[]>([]);
+ const allConfigs = useRef<any[]>([]);
+
+ const serviceName = "MISC";
+ const configType = "Users and Groups";
+
+ useEffect(() => {
+ setConfigProperties(accountConfigs);
+ }, [accountConfigs]);
+
+
+ useEffect(() => {
+ allConfigs.current = getAllConfigs();
+ }, [accountConfigs]);
+
+ const getAllConfigs = () => {
+ let allConfigs: any[] = [];
+ Object.keys(accountConfigs).forEach((serviceName) => {
+ Object.keys(accountConfigs[serviceName]).forEach((configType) => {
+
Object.keys(accountConfigs[serviceName][configType]?.properties).forEach((property)
=> {
+ const configuration =
accountConfigs[serviceName][configType]?.properties[property];
+ allConfigs.push(configuration);
+ });
+ });
+ });
+
+ return allConfigs;
+ };
+
+ const columnInWarningTable = [
+ {
+ header: "Service",
+ accessorKey: "serviceName",
+ },
+ {
+ header: "Property",
+ accessorKey: "propertyName",
+ },
+ {
+ header: "Current Value",
+ accessorKey: "propertyValue",
+ },
+ {
+ header: "Adjusted Value",
+ accessorKey: "new_value",
+ },
+ ];
+
+ const updateAllConfigs = (affectedProperties: any[]) => {
+ const configPropertiesCopy = cloneDeep(accountConfigs);
+
+ affectedProperties.forEach((affectedProperty) => {
+
+
configPropertiesCopy[affectedProperty.serviceName][affectedProperty.type].properties[affectedProperty.propertyName].value
= affectedProperty.new_value;
+ });
+ setAccountConfigs(configPropertiesCopy);
+ };
+
+ const applyConfigChanges = () => {
+ setShowWarning(false);
+ let affectedPropertiesCopy = cloneDeep(affectedProperties.current);
+ affectedPropertiesCopy.forEach((affectedProperty) => {
+ set(
+ affectedProperty,
+ "value",
+ get(affectedProperty, "new_value")
+ );
+ });
+ affectedProperties.current = affectedPropertiesCopy;
+ updateAllConfigs(affectedProperties.current);
+ //Get the configs for config tab from context then update it using
affectedProperties and push it back
+ };
+
+ if (isEmpty(accountConfigs)) {
+ return <Spinner/>;
+ }
+
+
+
+ return (
+ <div>
+ {showWarning ? (
+ <Modal
+ isOpen={showWarning}
+ onClose={() => setShowWarning(false)}
+ modalTitle="Warning: you must also change these Service properties"
+ modalBody={
+ <Table
+ data={affectedProperties.current}
+ columns={columnInWarningTable}
+ />
+ }
+ successCallback={applyConfigChanges}
+ options={{
+ modalSize: "modal-lg",
+ buttonSize: "sm",
+ okButtonText: "APPLY",
+ cancelButtonText: "CANCEL",
+ cancelableViaIcon: false,
+ cancelableViaBtn: true,
+ okButtonVariant: "success",
+ extraButtons: [
+ {
+ text: "IGNORE",
+ onClick: () => setShowWarning(false),
+ variant: "warning",
+ order: 1,
+ },
+ ],
+ }}
+ />
+ ) : null}
+ <Card>
+ <Card.Body>
+ <div className="mb-4 text-muted ps-3">
+ Please review these settings for Service Accounts
+ </div>
+ <div className="p-2">
+ {accountConfigs[serviceName][configType] &&
Object.keys(accountConfigs[serviceName][configType].properties).map((propertyName:any)
=> {
+ const config =
accountConfigs[serviceName][configType]?.properties[propertyName];
+ return (
+
config.propertyType.includes(UserGroupsProperties.ADDITIONAL_USER_PROPERTY) ||
config.propertyType.includes(UserGroupsProperties.ADDITIONAL_GROUP_PROPERTY) ? (
+ <div key={config.propertyName} className="ps-3">
+ <TooltipInput
+ tooltipProps={{
+ heading: config.propertyDisplayname + " - " +
get(config,"propertyName"),
+ message: config.propertyDescription,
+ placement: "right",
+ }}
+ formControlProps={{
+ type: "checkbox",
+ label: config.propertyDisplayname,
+ checked: config.value === "true",
+ onChange: (e) => {
+ let tempConfig = config;
+ tempConfig.value = e.target.checked ? "true" :
"false";
+ setAccountConfigs({
+ ...accountConfigs,
+ [serviceName]: {
+ ...accountConfigs[serviceName],
+ [configType]: {
+ ...accountConfigs[serviceName][configType],
+ properties: {
+
...accountConfigs[serviceName][configType].properties,
+ [propertyName]: tempConfig,
+ },
+ },
+ },
+ });
+ },
+ className: "custom-checkbox",
+ }}
+ />
+ </div>
+ ):null);
+
+ })}
+ </div>
+ <div className="p-2 w-50">
+ <Container>
+ <Row className="p-2">
+ <Col className="fw-bolder">Users/Groups</Col>
+ <Col className="fw-bolder">Username</Col>
+ </Row>
+ {accountConfigs[serviceName][configType] &&
Object.keys(accountConfigs[serviceName][configType].properties).map((propertyName:any)
=> {
+ const config =
accountConfigs[serviceName][configType].properties[propertyName];
+ if(propertyName==="dfs.permissions.superusergroup") return;
+ return (
+ config.propertyType.includes(UserGroupsProperties.USER)
|| config.propertyType.includes(UserGroupsProperties.GROUP) ? (
+ <Row key={propertyName} className="p-2 text-muted">
+ <Col
className="pt-2">{config.propertyDisplayname?config.propertyDisplayname :
config.propertyName}</Col>
+ <Col>
+ <TooltipInput
+ tooltipProps={{
+ heading: config.propertyDisplayname + " - " +
get(config,"propertyName"),
+ message: config.propertyDescription,
+ placement: "right",
+ }}
+ formControlProps={{
+ type: "text",
+ value: config.value,
+ onChange: (e) => {
+ let tempConfig = config;
+ tempConfig.value = e.target.value;
+ setAccountConfigs({
+ ...accountConfigs,
+ [serviceName]: {
+ ...accountConfigs[serviceName],
+ [configType]: {
+
...accountConfigs[serviceName][configType],
+ properties: {
+
...accountConfigs[serviceName][configType].properties,
+ [propertyName]: tempConfig,
+ },
+ },
+ },
+ });
+ },
+ onBlur: () => {
+ const allAffectedProperties =
+ getDependentConfigChanges(
+ config,
+ services,
+ allConfigs.current
+ ) || [];
+ if (allAffectedProperties.length > 0) {
+ affectedProperties.current =
+ allAffectedProperties;
+ setShowWarning(true);
+ }
+ },
+ className: isValidUserName(config.value)
+ ? "rounded-0"
+ : "rounded-0 border-danger",
+ }}
+ />
+ </Col>
+ </Row>
+ ):null)
+
+
+ })}
+ </Container>
+ </div>
+ </Card.Body>
+ </Card>
+ </div>
+ );
+}
diff --git
a/ambari-web/latest/src/screens/ClusterWizard/Step7/AllConfigurationsTab.tsx
b/ambari-web/latest/src/screens/ClusterWizard/Step7/AllConfigurationsTab.tsx
new file mode 100644
index 0000000000..89c82afe2a
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/AllConfigurationsTab.tsx
@@ -0,0 +1,27 @@
+/**
+ * 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 { Card } from "react-bootstrap";
+
+export default function AllConfigurationsTab() {
+ return (
+ <Card>
+ <h5 className="p-4">All Configurations Tab</h5>
+ </Card>
+ );
+}
\ No newline at end of file
diff --git
a/ambari-web/latest/src/screens/ClusterWizard/Step7/CredentialsTab.tsx
b/ambari-web/latest/src/screens/ClusterWizard/Step7/CredentialsTab.tsx
new file mode 100644
index 0000000000..947dcdce0d
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/CredentialsTab.tsx
@@ -0,0 +1,266 @@
+/**
+ * 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 { cloneDeep, get, set } from "lodash";
+import { useEffect, useState } from "react";
+import {Card, Form } from "react-bootstrap";
+import { CredentialConfigType } from "../types/step7Types";
+import { isValidUserName } from "../utils";
+import TooltipInput from "../../../components/TooltipInput";
+
+interface CredentialsTabProps {
+ themes: Object;
+ configs: Object;
+ setIsNextEnabled:Function;
+}
+export default function CredentialsTab({
+ themes,
+ configs,
+ setIsNextEnabled
+}: CredentialsTabProps) {
+ const [allCredentials, setAllCredentials] = useState<CredentialConfigType[]>(
+ []
+ );
+
+ useEffect(() => {
+ setAllCredentials(processDataForCredentialsTab(configs, themes));
+ }, [themes, configs]);
+
+ const updateConfig = (key: string, value: string, config: any) => {
+ let newConfig = { ...config };
+ set(newConfig, key, value);
+ setAllCredentials(
+ allCredentials.map((c) => (c === config ? newConfig : c))
+ );
+ };
+
+ const isValidPassword = (config: Object) => {
+ return (
+ get(config, "passwordProperty.property_value", "").length > 0 &&
+ get(config, "passwordProperty.property_value", "") ===
+ get(config, "confirmPasswordProperty.property_value", "")
+ );
+ };
+
+ useEffect(()=>{
+ const allCredentialsValid=allCredentials.every((config) => {
+ return (
+ (get(config, "usernameProperty")
+ ? isValidUserName(get(config, "usernameProperty.property_value", ""))
+ : true) && isValidPassword(config)
+ );
+ });
+ console.log("All Credentials Valid",allCredentialsValid)
+ if(allCredentialsValid){
+ setIsNextEnabled(true);
+ }
+ else{
+ setIsNextEnabled(false);
+ }
+ },[allCredentials])
+
+
+ const getTooltipInput = (config: any, propertyType: string) => {
+ const property_display_name = get(
+ config,
+ propertyType + ".property_display_name",
+ ""
+ );
+ const property_name = get(config, propertyType + ".property_name", "");
+ const property_description = get(
+ config,
+ propertyType + ".property_description",
+ ""
+ );
+ const property_value = get(config, propertyType + ".property_value", "");
+ const isPasswordProperty = propertyType.toLowerCase().includes("password");
+ const isInputValid = isPasswordProperty
+ ? isValidPassword(config)
+ : isValidUserName(property_value);
+
+ let tooltipHeading = "";
+ if (property_display_name && property_name) {
+ tooltipHeading = property_display_name + " " + property_name;
+ } else {
+ if (isPasswordProperty) {
+ tooltipHeading = "password";
+ } else {
+ tooltipHeading = "username";
+ }
+ }
+
+ let tooltipMessage = property_description;
+ if (isPasswordProperty) {
+ tooltipMessage +=
+ " For security purposes, password changes will not be shown in
configuration version comparisons";
+ }
+
+ const formControlProps = {
+ type: isPasswordProperty ? "password" : "text",
+ value: property_value,
+ onChange: (e: any) =>
+ updateConfig(propertyType + ".property_value", e.target.value, config),
+ className: isInputValid ? "" : "border-danger",
+ placeholder: propertyType === "passwordProperty" ? "Type password" : "",
+ };
+
+ return (
+ <TooltipInput
+ tooltipProps={{
+ message: tooltipMessage,
+ heading: tooltipHeading,
+ placement: "left",
+ }}
+ formControlProps={formControlProps as any}
+ />
+ );
+ };
+
+ return (
+ <div>
+ <Card>
+ <Card.Body>
+ <div className="mb-4 text-muted">
+ Please provide credentials for these services
+ </div>
+ <div className="d-flex text-muted">
+ <div className="w-25 ps-3"></div>
+ <div className="w-25 ps-3">Username*</div>
+ <div className="w-25 ps-3">Password*</div>
+ <div className="w-25 ps-3">Confirm Password*</div>
+ </div>
+ <hr className="mb-2" />
+ <Form>
+ {allCredentials.map((config) => {
+ return (
+ <div key={get(config, "name", "")}>
+ <div className="d-flex text-muted">
+ <div className="w-25 pt-3 ps-2">
+ {get(config, "display-name", "")}
+ </div>
+ <Form.Group className="w-25 p-2">
+ {get(config, "usernameProperty") ? (
+ getTooltipInput(config, "usernameProperty")
+ ) : (
+ <div className="pt-2 ps-2">N/A</div>
+ )}
+ </Form.Group>
+ <Form.Group className="w-25 p-2">
+ {getTooltipInput(config, "passwordProperty")}
+ </Form.Group>
+ <Form.Group className="w-25 p-2">
+ {getTooltipInput(config, "confirmPasswordProperty")}
+ </Form.Group>
+ </div>
+ <hr className="m-1" />
+ </div>
+ );
+ })}
+ </Form>
+ </Card.Body>
+ </Card>
+ </div>
+ );
+}
+
+export const processDataForCredentialsTab = (
+ configsData: Object,
+ themesData: Object
+) => {
+ let allConfigs: any[] = [];
+ get(configsData, "items", []).forEach((item) => {
+ get(item, "configurations", []).forEach((configuration) => {
+ allConfigs.push(get(configuration, "StackConfigurations", {}));
+ });
+ });
+
+ let tempCredentials: CredentialConfigType[] = [];
+ get(themesData, "items", []).forEach((item) => {
+ get(item, "themes", [])
+ .filter(
+ (theme) =>
+ get(theme, "ThemeInfo.theme_data.Theme.name") === "credentials"
+ )
+ .forEach((theme) => {
+ let currCredLayout: CredentialConfigType[] = [];
+
+ get(
+ theme,
+ "ThemeInfo.theme_data.Theme.configuration.layouts",
+ []
+ ).forEach((layout) => {
+ get(layout, "tabs", []).forEach((tab) => {
+ get(tab, "layout.sections", []).forEach((section) => {
+ get(section, "subsections", []).forEach((subsection: any) => {
+ if (subsection) {
+ currCredLayout.push(subsection);
+ }
+ });
+ });
+ });
+ });
+
+ if (currCredLayout.length) {
+ get(
+ theme,
+ "ThemeInfo.theme_data.Theme.configuration.placement.configs",
+ []
+ ).forEach((config) => {
+ const [propertyType, propertyName] = get(
+ config,
+ "config",
+ ""
+ ).split("/");
+ const configMatchingProperty = allConfigs.find(
+ (c) =>
+ get(c, "type", "").startsWith(propertyType) &&
+ get(c, "property_name", "") === propertyName
+ );
+ if (configMatchingProperty) {
+ currCredLayout.forEach((section) => {
+ if (
+ get(section, "name", "") ===
+ get(config, "subsection-name", "")
+ ) {
+ if (
+ get(
+ configMatchingProperty,
+ "property_value_attributes.type",
+ ""
+ ) === "password"
+ ) {
+ set(section, "passwordProperty", configMatchingProperty);
+ set(
+ section,
+ "confirmPasswordProperty",
+ cloneDeep(configMatchingProperty)
+ );
+ } else {
+ set(section, "usernameProperty", configMatchingProperty);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ tempCredentials = [...tempCredentials, ...currCredLayout];
+ });
+ });
+ return tempCredentials;
+};
diff --git a/ambari-web/latest/src/screens/ClusterWizard/Step7/DatabasesTab.tsx
b/ambari-web/latest/src/screens/ClusterWizard/Step7/DatabasesTab.tsx
new file mode 100644
index 0000000000..bd01572932
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/DatabasesTab.tsx
@@ -0,0 +1,27 @@
+/**
+ * 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 { Card } from "react-bootstrap";
+
+export default function DatabasesTab() {
+ return (
+ <Card>
+ <h5 className="p-4">Databases Tab</h5>
+ </Card>
+ );
+}
\ No newline at end of file
diff --git
a/ambari-web/latest/src/screens/ClusterWizard/Step7/DirectoriesTab.tsx
b/ambari-web/latest/src/screens/ClusterWizard/Step7/DirectoriesTab.tsx
new file mode 100644
index 0000000000..b5dd7572a8
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/DirectoriesTab.tsx
@@ -0,0 +1,27 @@
+/**
+ * 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 { Card } from "react-bootstrap";
+
+export default function DirectoriesTab() {
+ return (
+ <Card>
+ <h5 className="p-4">Directories Tab</h5>
+ </Card>
+ );
+}
\ No newline at end of file
diff --git a/ambari-web/latest/src/screens/ClusterWizard/types/step7Types.ts
b/ambari-web/latest/src/screens/ClusterWizard/types/step7Types.ts
new file mode 100644
index 0000000000..7c49aa4935
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/types/step7Types.ts
@@ -0,0 +1,13 @@
+export interface CredentialConfigType {
+ final: string;
+ property_name: string;
+ property_type: string[];
+ property_value: string;
+ service_name: string;
+ stack_name: string;
+ stack_version: string;
+ type: string;
+ passwordProperty?: Object;
+ usernameProperty?: Object;
+ [key: string]: any;
+ }
\ No newline at end of file
diff --git a/ambari-web/latest/src/screens/ClusterWizard/utils.ts
b/ambari-web/latest/src/screens/ClusterWizard/utils.ts
index 8503741c12..f3fb6793c0 100644
--- a/ambari-web/latest/src/screens/ClusterWizard/utils.ts
+++ b/ambari-web/latest/src/screens/ClusterWizard/utils.ts
@@ -1,3 +1,23 @@
+/**
+ * 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 { get } from "lodash";
+
export const isHostname = (hostname: string): boolean => {
const regex = new RegExp(
/(?=^.{3,254}$)(^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*(\.[a-zA-Z]{1,62})$)/
@@ -9,3 +29,71 @@ export const isValidUserName = (username: string): boolean =>
{
const regex = new RegExp(/^[a-z]([-a-z0-9]{0,30})$/);
return regex.test(username);
};
+
+export const getDependentConfigChanges = (
+ changedConfig: any,
+ selectedServices: string[],
+ allConfigs: any
+) => {
+ const propertyName = get(changedConfig, "propertyName");
+ const propertyValue = get(changedConfig, "value");
+ let allAffectedProperties: any[] = [];
+ if (propertyName === "hdfs_user") {
+ const affectedPropertyNames = [
+ // "dfs.permissions.superusergroup",
+ "dfs.cluster.administrators",
+ ];
+ allConfigs.forEach((config: any) => {
+ if (
+ get(config, "serviceName") === "HDFS" &&
+ affectedPropertyNames.includes(get(config, "propertyName")) &&
+ propertyValue.trim() !== get(config, "value", "").trim()
+ ) {
+ allAffectedProperties.push({ ...config, new_value: propertyValue });
+ }
+ });
+ } else if (propertyName === "yarn_user") {
+ const affectedPropertyNames = ["yarn.admin.acl"];
+ allConfigs.forEach((config: any) => {
+ if (
+ get(config, "serviceName") === "YARN" &&
+ affectedPropertyNames.includes(get(config, "propertyName")) &&
+ propertyValue.trim() !== get(config, "value").trim()
+ ) {
+ allAffectedProperties.push({ ...config, new_value: propertyValue });
+ }
+ });
+ } else if (propertyName === "user_group") {
+ if (!selectedServices.includes("YARN")) {
+ return;
+ }
+ if (selectedServices.includes("MAPREDUCE2")) {
+ const affectedPropertyNames = ["mapreduce.cluster.administrators"];
+ allConfigs.forEach((config: any) => {
+ if (
+ get(config, "serviceName") === "MAPREDUCE2" &&
+ affectedPropertyNames.includes(get(config, "propertyName")) &&
+ propertyValue.trim() !== get(config, "value").trim()
+ ) {
+ allAffectedProperties.push({ ...config, new_value: propertyValue });
+ }
+ });
+ }
+ if (selectedServices.includes("YARN")) {
+ const affectedPropertyNames = [
+ "yarn.nodemanager.linux-container-executor.group",
+ ];
+ allConfigs.forEach((config: any) => {
+ if (
+ get(config, "serviceName") === "YARN" &&
+ affectedPropertyNames.includes(get(config, "propertyName")) &&
+ propertyValue.trim() !== get(config, "value").trim()
+ ) {
+ allAffectedProperties.push({ ...config, new_value: propertyValue });
+ }
+ });
+ }
+ }
+
+ return allAffectedProperties;
+};
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]