source/idea/idea-cluster-manager/webapp/src/pages/cluster-admin/cluster-settings.tsx (382 lines of code) (raw):
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
* with the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
import React, { Component, RefObject } from "react";
import { IdeaSideNavigationProps } from "../../components/side-navigation";
import IdeaAppLayout, { IdeaAppLayoutProps } from "../../components/app-layout";
import { Box, Button, ColumnLayout, Container, Header, SpaceBetween, Tabs } from "@cloudscape-design/components";
import { KeyValue } from "../../components/key-value";
import IdeaForm from "../../components/form";
import { AppContext } from "../../common";
import dot from "dot-object";
import Utils from "../../common/utils";
import { EnabledDisabledStatusIndicator } from "../../components/common";
import { withRouter } from "../../navigation/navigation-utils";
import ConfigUtils from "../../common/config-utils";
import { UpdateModuleSettingsRequestWebPortal, UpdateModuleSettingsValuesWebPortal } from "../../client/data-model";
export interface ClusterSettingsProps extends IdeaAppLayoutProps, IdeaSideNavigationProps {}
export interface ClusterSettingsState {
cluster: any;
identityProvider: any;
directoryservice: any;
clusterManager: any;
activeTabId: string;
}
const DEFAULT_ACTIVE_TAB_ID = "general";
class ClusterSettings extends Component<ClusterSettingsProps, ClusterSettingsState> {
updateWebPortalSettingsForm: RefObject<IdeaForm>;
enableSSOConfigForm: RefObject<IdeaForm>
constructor(props: ClusterSettingsProps) {
super(props);
this.enableSSOConfigForm = React.createRef()
this.state = {
cluster: {},
identityProvider: {},
directoryservice: {},
clusterManager: {},
activeTabId: DEFAULT_ACTIVE_TAB_ID,
};
this.updateWebPortalSettingsForm = React.createRef();
}
buildUpdateWebPortalSettingsForm() {
return (
<IdeaForm
ref={this.updateWebPortalSettingsForm}
name="update-web-portal-settings"
modal={true}
title="Update Web Portal Settings"
onSubmit={() => {
if (!this.updateWebPortalSettingsForm.current?.validate()) {
return;
}
const values = this.updateWebPortalSettingsForm.current?.getValues();
const updateSettings: UpdateModuleSettingsRequestWebPortal = {
module_id: 'cluster-manager',
settings : {
web_portal: {
}
}
}
if (values.title !== dot.pick("web_portal.title", this.state.clusterManager)) {
updateSettings.settings.web_portal[UpdateModuleSettingsValuesWebPortal.TITLE] = values.title;
}
if (values.subtitle !== dot.pick("web_portal.subtitle", this.state.clusterManager)) {
updateSettings.settings.web_portal[UpdateModuleSettingsValuesWebPortal.SUBTITLE] = values.subtitle;
}
if (Object.keys(updateSettings.settings.web_portal).length > 0) {
AppContext.get()
.client()
.clusterSettings()
.updateModuleSettings(updateSettings)
.then(() => {
this.props.onFlashbarChange({
items: [
{
type: "success",
content: "Web portal settings updated successfully.",
dismissible: true,
},
],
});
this.loadSettings();
this.updateWebPortalSettingsForm.current?.hideModal();
})
.catch((error) => {
this.updateWebPortalSettingsForm.current?.setError(error.errorCode, error.message);
});
} else {
this.updateWebPortalSettingsForm.current?.setError("400", "No settings updated.");
}
}}
onCancel={() => {
this.updateWebPortalSettingsForm.current?.hideModal();
}}
params={[
{
name: "title",
title: "Title",
validate: {
required: true,
},
default: dot.pick("web_portal.title", this.state.clusterManager),
},
{
name: "subtitle",
title: "Subtitle",
validate: {
required: true,
},
default: dot.pick("web_portal.subtitle", this.state.clusterManager),
},
]}
/>
);
}
componentDidMount() {
this.loadSettings();
}
loadSettings() {
let promises: Promise<any>[] = [];
const clusterSettingsService = AppContext.get().getClusterSettingsService();
// 0
promises.push(clusterSettingsService.getClusterSettings());
// 1
promises.push(clusterSettingsService.getIdentityProviderSettings());
// 2
promises.push(clusterSettingsService.getDirectoryServiceSettings());
// 3
promises.push(clusterSettingsService.getClusterManagerSettings(false));
const queryParams = new URLSearchParams(this.props.location.search);
const activeTabId = Utils.asString(queryParams.get("tab"), DEFAULT_ACTIVE_TAB_ID);
Promise.all(promises).then((result) => {
this.setState({
cluster: result[0],
identityProvider: result[1],
directoryservice: result[2],
clusterManager: result[3],
activeTabId: activeTabId,
}, () => {
this.updateWebPortalSettingsForm.current?.registry.list().map((field) => {
field.setState({default: field.props.param.default});
});
this.updateWebPortalSettingsForm.current?.reset();
});
});
}
render() {
const isExternalAlbCertSelfSigned = (): boolean => {
return !Utils.asBoolean(dot.pick("load_balancers.external_alb.certificates.provided", this.state.cluster), false);
};
return (
<IdeaAppLayout
ideaPageId={this.props.ideaPageId}
toolsOpen={this.props.toolsOpen}
tools={this.props.tools}
onToolsChange={this.props.onToolsChange}
onPageChange={this.props.onPageChange}
sideNavHeader={this.props.sideNavHeader}
sideNavItems={this.props.sideNavItems}
onSideNavChange={this.props.onSideNavChange}
onFlashbarChange={this.props.onFlashbarChange}
flashbarItems={this.props.flashbarItems}
breadcrumbItems={[
{
text: "RES",
href: "#/",
},
{
text: "Environment Management",
href: "#/cluster/status",
},
{
text: "Environment settings",
href: "",
},
]}
header={
<Header
variant={"h1"}
description={"View and manage environment settings."}
actions={
<SpaceBetween size={"s"}>
<Button variant={"primary"} onClick={() => this.props.navigate("/cluster/status")}>
View Environment Status
</Button>
</SpaceBetween>
}
>
Environment settings
</Header>
}
contentType={"default"}
content={
<SpaceBetween size={"l"}>
{this.buildUpdateWebPortalSettingsForm()}
<Container>
<ColumnLayout variant={"text-grid"} columns={3}>
<KeyValue title="Environment Name" value={dot.pick("cluster_name", this.state.cluster)} clipboard={true} />
<KeyValue title="AWS Region" value={dot.pick("aws.region", this.state.cluster)} />
<KeyValue title="S3 Bucket" value={dot.pick("cluster_s3_bucket", this.state.cluster)} clipboard={true} type={"s3:bucket-name"} />
</ColumnLayout>
</Container>
<Tabs
activeTabId={this.state.activeTabId}
onChange={(event) => {
this.setState(
{
activeTabId: event.detail.activeTabId,
},
() => {
this.props.searchParams.set("tab", event.detail.activeTabId);
this.props.setSearchParams(this.props.searchParams);
}
);
}}
tabs={[
{
label: "General",
id: "general",
content: (
<SpaceBetween size="m">
<Container header={<Header variant={"h2"}>General Settings</Header>}>
<ColumnLayout variant={"text-grid"} columns={3}>
<KeyValue title="Administrator Username" value={dot.pick("administrator_username", this.state.cluster)} />
<KeyValue title="Administrator Email" value={dot.pick("administrator_email", this.state.cluster)} clipboard={true} />
<KeyValue title="Home Directory" value={dot.pick("home_dir", this.state.cluster)} clipboard={true} />
<KeyValue title="Locale" value={dot.pick("locale", this.state.cluster)} />
<KeyValue title="Timezone" value={dot.pick("timezone", this.state.cluster)} />
<KeyValue title="Default Encoding" value={dot.pick("encoding", this.state.cluster)} />
</ColumnLayout>
</Container>
<Container
header={
<Header
variant={"h2"}
actions={
<Button
iconName="edit"
onClick={() => {
this.updateWebPortalSettingsForm.current?.showModal();
}}
/>
}
>
Web Portal
</Header>
}
>
<ColumnLayout variant={"text-grid"} columns={3}>
<KeyValue title="Title" value={dot.pick("web_portal.title", this.state.clusterManager)}/>
<KeyValue title="Subtitle" value={dot.pick("web_portal.subtitle", this.state.clusterManager)}/>
<KeyValue title="Copyright Text" value={dot.pick("web_portal.copyright_text", this.state.clusterManager)}/>
</ColumnLayout>
</Container>
<Container header={<Header variant={"h2"}>AWS Account Settings</Header>}>
<ColumnLayout variant={"text-grid"} columns={3}>
<KeyValue title="AWS Account ID" value={dot.pick("aws.account_id", this.state.cluster)} clipboard={true} />
<KeyValue title="AWS Region" value={dot.pick("aws.region", this.state.cluster)} clipboard={true} />
<KeyValue title="AWS Partition" value={dot.pick("aws.partition", this.state.cluster)} />
<KeyValue title="AWS DNS Suffix" value={dot.pick("aws.dns_suffix", this.state.cluster)} />
</ColumnLayout>
</Container>
</SpaceBetween>
),
},
{
label: "Network",
id: "network",
content: (
<SpaceBetween size={"l"}>
<Container header={<Header variant={"h2"}>VPC</Header>}>
<ColumnLayout variant={"text-grid"} columns={3}>
<KeyValue title="VPC Id" value={dot.pick("network.vpc_id", this.state.cluster)} clipboard={true} />
<KeyValue title="Private Subnets" value={dot.pick("network.private_subnets", this.state.cluster)} clipboard={true} />
<KeyValue title="Public Subnets" value={dot.pick("network.public_subnets", this.state.cluster)} clipboard={true} />
<KeyValue title="Cluster Prefix List Id" value={dot.pick("network.cluster_prefix_list_id", this.state.cluster)} clipboard={true} />
<KeyValue title="Existing VPC?" value={dot.pick("network.use_existing_vpc", this.state.cluster)} type={"boolean"} />
</ColumnLayout>
</Container>
<Container header={<Header variant={"h2"}>Security Groups</Header>}>
<ColumnLayout variant={"text-grid"} columns={3}>
<KeyValue title="Bastion Host" value={dot.pick("network.security_groups.bastion-host", this.state.cluster)} clipboard={true} type={"ec2:security-group-id"} />
<KeyValue title="External Load Balancer" value={dot.pick("network.security_groups.external-load-balancer", this.state.cluster)} clipboard={true} type={"ec2:security-group-id"} />
<KeyValue title="Internal Load Balancer" value={dot.pick("network.security_groups.internal-load-balancer", this.state.cluster)} clipboard={true} type={"ec2:security-group-id"} />
<KeyValue title="Default Security Group" value={dot.pick("network.security_groups.cluster", this.state.cluster)} clipboard={true} type={"ec2:security-group-id"} />
</ColumnLayout>
</Container>
<Container header={<Header variant={"h2"}>External Load Balancer</Header>}>
<SpaceBetween size={"m"}>
<ColumnLayout variant={"text-grid"} columns={2}>
<KeyValue title="Load Balancer DNS Name" value={ConfigUtils.getExternalAlbDnsName(this.state.cluster)} clipboard={true} />
<KeyValue title="Custom DNS Name" value={ConfigUtils.getExternalAlbCustomDnsName(this.state.cluster)} clipboard={true} />
<KeyValue title="Load Balancer ARN" value={ConfigUtils.getExternalAlbArn(this.state.cluster)} clipboard={true} />
<KeyValue title="Deploy in Public Subnets?" value={dot.pick("load_balancers.external_alb.public", this.state.cluster)} type={"boolean"} />
</ColumnLayout>
<Box>
<h3>SSL/TLS Settings</h3>
<ColumnLayout variant={"text-grid"} columns={2}>
<KeyValue title="Certificates" value={isExternalAlbCertSelfSigned() ? "Self-Signed" : "ACM"} />
{isExternalAlbCertSelfSigned() && <KeyValue title="Certificate Secret ARN" value={ConfigUtils.getExternalAlbCertificateSecretArn(this.state.cluster)} clipboard={true} />}
{isExternalAlbCertSelfSigned() && <KeyValue title="Certificate Private Key Secret ARN" value={ConfigUtils.getExternalAlbPrivateKeySecretArn(this.state.cluster)} clipboard={true} />}
<KeyValue title="ACM Certificate ARN" value={ConfigUtils.getExternalAlbAcmCertificateArn(this.state.cluster)} clipboard={true} />
</ColumnLayout>
</Box>
</SpaceBetween>
</Container>
<Container header={<Header variant={"h2"}>Internal Load Balancer</Header>}>
<SpaceBetween size={"m"}>
<ColumnLayout variant={"text-grid"} columns={2}>
<KeyValue title="Load Balancer DNS Name" value={ConfigUtils.getInternalAlbDnsName(this.state.cluster)} clipboard={true} />
<KeyValue title="Custom DNS Name" value={ConfigUtils.getInternalAlbCustomDnsName(this.state.cluster)} clipboard={true} />
<KeyValue title="Load Balancer ARN" value={ConfigUtils.getInternalAlbArn(this.state.cluster)} clipboard={true} />
</ColumnLayout>
<Box>
<h3>SSL/TLS Settings</h3>
<ColumnLayout variant={"text-grid"} columns={2}>
<KeyValue title="Certificates" value="Self-Signed" />
<KeyValue title="Certificate Secret ARN" value={ConfigUtils.getInternalAlbCertificateSecretArn(this.state.cluster)} clipboard={true} />
<KeyValue title="Certificate Private Key Secret ARN" value={ConfigUtils.getInternalAlbPrivateKeySecretArn(this.state.cluster)} clipboard={true} />
<KeyValue title="ACM Certificate ARN" value={ConfigUtils.getInternalAlbAcmCertificateArn(this.state.cluster)} clipboard={true} />
</ColumnLayout>
</Box>
</SpaceBetween>
</Container>
</SpaceBetween>
),
},
{
label: "CloudWatch Logs",
id: "cloudwatch-logs",
content: (
<Container header={<Header variant={"h2"}>CloudWatch Logs</Header>}>
<ColumnLayout variant={"text-grid"} columns={3}>
<KeyValue title="Status" value={<EnabledDisabledStatusIndicator enabled={Utils.asBoolean(dot.pick("cloudwatch_logs.enabled", this.state.cluster), false)} />} type={"react-node"} />
<KeyValue title="Force Flush Interval" value={dot.pick("cloudwatch_logs.force_flush_interval", this.state.cluster)} suffix={"seconds"} />
<KeyValue title="Log Retention" value={dot.pick("cloudwatch_logs.retention_in_days", this.state.cluster)} suffix={"days"} />
</ColumnLayout>
</Container>
),
},
{
label: "SES",
id: "ses",
content: (
<Container header={<Header variant={"h2"}>Simple Email Service (SES)</Header>}>
<ColumnLayout variant={"text-grid"} columns={3}>
<KeyValue title="Status" value={<EnabledDisabledStatusIndicator enabled={Utils.asBoolean(dot.pick("ses.enabled", this.state.cluster), false)} />} type={"react-node"} />
<KeyValue title="AWS Account ID" value={dot.pick("ses.account_id", this.state.cluster)} clipboard={true} />
<KeyValue title="AWS Region" value={dot.pick("ses.region", this.state.cluster)} />
<KeyValue title="Sender Email" value={dot.pick("ses.sender_email", this.state.cluster)} clipboard={true} />
<KeyValue title="Max Sending Rate" value={dot.pick("ses.max_sending_rate", this.state.cluster)} suffix={" / second"} />
</ColumnLayout>
</Container>
),
},
{
label: "EC2",
id: "ec2",
content: (
<Container header={<Header variant={"h2"}>EC2</Header>}>
<ColumnLayout variant={"text-grid"} columns={1}>
<KeyValue title="SSH Key Pair" value={dot.pick("network.ssh_key_pair", this.state.cluster)} clipboard={true} />
<KeyValue title="Custom EC2 Managed Policy ARNs" value={dot.pick("iam.ec2_managed_policy_arns", this.state.cluster)} clipboard={true} />
</ColumnLayout>
</Container>
),
},
{
label: "Route 53",
id: "route-53",
content: (
<SpaceBetween size={"l"}>
<Container header={<Header variant={"h2"}>Private Hosted Zone</Header>}>
<ColumnLayout variant={"text-grid"} columns={2}>
<KeyValue title="Hosted Zone Name" value={dot.pick("route53.private_hosted_zone_name", this.state.cluster)} clipboard={true} />
<KeyValue title="Hosted Zone ID" value={dot.pick("route53.private_hosted_zone_id", this.state.cluster)} clipboard={true} />
<KeyValue title="Hosted Zone ARN" value={dot.pick("route53.private_hosted_zone_arn", this.state.cluster)} clipboard={true} />
</ColumnLayout>
</Container>
</SpaceBetween>
),
},
]}
/>
</SpaceBetween>
}
/>
);
}
}
export default withRouter(ClusterSettings);