Browse Source

feat: integrate compliance tab with cluster contract data

Jose Diaz-Gonzalez 2 years ago
parent
commit
76bb1714c2

+ 129 - 92
dashboard/src/components/ProvisionerSettings.tsx

@@ -1,6 +1,7 @@
 import React, { useContext, useEffect, useState } from "react";
 import {
   AWSClusterNetwork,
+  CloudwatchAlarm,
   Cluster,
   Contract,
   EKS,
@@ -27,11 +28,12 @@ import { useIntercom } from "lib/hooks/useIntercom";
 import api from "shared/api";
 import { Context } from "shared/Context";
 import { pushFiltered } from "shared/routing";
-import { type ClusterType, type ClusterState } from "shared/types";
+import { type ClusterState, type ClusterType } from "shared/types";
 import { PREFLIGHT_TO_ENUM } from "shared/util";
 import info from "assets/info-outlined.svg";
 import healthy from "assets/status-healthy.png";
 
+import GPUProvisionSettings from "./GPUProvisionSettings";
 import Loading from "./Loading";
 import Button from "./porter/Button";
 import Checkbox from "./porter/Checkbox";
@@ -43,9 +45,6 @@ import Text from "./porter/Text";
 import Tooltip from "./porter/Tooltip";
 import VerticalSteps from "./porter/VerticalSteps";
 import PreflightChecks from "./PreflightChecks";
-import { Integer } from "type-fest";
-import InputSlider from "./porter/InputSlider";
-import GPUProvisionSettings from "./GPUProvisionSettings";
 
 const regionOptions = [
   { value: "us-east-1", label: "US East (N. Virginia) us-east-1" },
@@ -169,6 +168,9 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
   const [controlPlaneLogs, setControlPlaneLogs] = useState<EKSLogging>(
     new EKSLogging()
   );
+  const [cloudwatchAlarm, setCloudwatchAlarm] = useState<CloudwatchAlarm>(
+    new CloudwatchAlarm()
+  );
 
   const markStepStarted = async (
     step: string,
@@ -187,7 +189,7 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
           project_id: currentProject ? currentProject.id : 0,
         }
       );
-    } catch (err) { }
+    } catch (err) {}
   };
 
   const getStatus = (): React.ReactNode => {
@@ -264,17 +266,19 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
     }
 
     // Split the input string by comma, then reduce the resulting array to an object
-    const tags = tagString.split(",").reduce<Record<string, string>>((obj, item) => {
-      // Split each item by "=", and trim whitespace from both key and value
-      const [key, value] = item.split("=").map(part => part.trim());
-
-      // Only add the key-value pair to the object if both key and value are present
-      if (key && value) {
-        obj[key] = value;
-      }
+    const tags = tagString
+      .split(",")
+      .reduce<Record<string, string>>((obj, item) => {
+        // Split each item by "=", and trim whitespace from both key and value
+        const [key, value] = item.split("=").map((part) => part.trim());
+
+        // Only add the key-value pair to the object if both key and value are present
+        if (key && value) {
+          obj[key] = value;
+        }
 
-      return obj;
-    }, {});
+        return obj;
+      }, {});
 
     return tags;
   }
@@ -314,7 +318,6 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
       }
     }
 
-
     const nodeGroups = [
       new EKSNodeGroup({
         instanceType: "t3.medium",
@@ -344,17 +347,18 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
 
     // Conditionally add the last EKSNodeGroup if gpuModal is enabled
     if (props.gpuModal) {
-      nodeGroups.push(new EKSNodeGroup({
-        instanceType: clusterState.gpuInstanceType,
-        minInstances: clusterState.gpuMinInstances || 0,
-        maxInstances: clusterState.gpuMaxInstances || 5,
-        nodeGroupType: NodeGroupType.CUSTOM,
-        isStateful: false,
-        additionalPolicies: clusterState.additionalNodePolicies,
-      }));
+      nodeGroups.push(
+        new EKSNodeGroup({
+          instanceType: clusterState.gpuInstanceType,
+          minInstances: clusterState.gpuMinInstances || 0,
+          maxInstances: clusterState.gpuMaxInstances || 5,
+          nodeGroupType: NodeGroupType.CUSTOM,
+          isStateful: false,
+          additionalPolicies: clusterState.additionalNodePolicies,
+        })
+      );
     }
 
-
     const data = new Contract({
       cluster: new Cluster({
         projectId: currentProject.id,
@@ -365,17 +369,20 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
           case: "eksKind",
           value: new EKS({
             clusterName: clusterState.clusterName,
-            clusterVersion: clusterState.clusterVersion || defaultClusterVersion,
+            clusterVersion:
+              clusterState.clusterVersion || defaultClusterVersion,
             cidrRange: clusterState.cidrRangeVPC || defaultCidrVpc, // deprecated in favour of network.cidrRangeVPC: can be removed after december 2023
             region: clusterState.awsRegion,
             loadBalancer: loadBalancerObj,
             logging: controlPlaneLogs,
+            cloudwatchAlarm,
             enableGuardDuty: clusterState.guardDutyEnabled,
             enableKmsEncryption: clusterState.kmsEncryptionEnabled,
             enableEcrScanning: clusterState.ecrScanningEnabled,
             network: new AWSClusterNetwork({
               vpcCidr: clusterState.cidrRangeVPC || defaultCidrVpc,
-              serviceCidr: clusterState.cidrRangeServices || defaultCidrServices,
+              serviceCidr:
+                clusterState.cidrRangeServices || defaultCidrServices,
             }),
             nodeGroups,
           }),
@@ -422,15 +429,13 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
                 pushFiltered(props, "/cluster-dashboard", ["project_id"], {
                   cluster: cluster.name,
                 });
-              }
-              else {
+              } else {
                 if (props.closeModal) {
                   props.closeModal();
                 }
               }
             }
           });
-
         })
         .catch((err) => {
           if (err) {
@@ -461,8 +466,8 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
   useEffect(() => {
     setIsReadOnly(
       props.clusterId &&
-      (currentCluster.status === "UPDATING" ||
-        currentCluster.status === "UPDATING_UNAVAILABLE")
+        (currentCluster.status === "UPDATING" ||
+          currentCluster.status === "UPDATING_UNAVAILABLE")
     );
     handleClusterStateChange(
       "clusterName",
@@ -523,8 +528,8 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
 
         const awsTags = eksValues.loadBalancer.tags
           ? Object.entries(eksValues.loadBalancer.tags)
-            .map(([key, value]) => `${key}=${value}`)
-            .join(",")
+              .map(([key, value]) => `${key}=${value}`)
+              .join(",")
           : "";
         handleClusterStateChange("awsTags", awsTags);
 
@@ -551,6 +556,14 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
         setControlPlaneLogs(logging);
       }
 
+      if (eksValues.cloudwatchAlarm != null) {
+        const cloudwatchAlarm = new CloudwatchAlarm({
+          emails: eksValues.cloudwatchAlarm.emails,
+          enable: eksValues.cloudwatchAlarm.enable,
+        });
+        setCloudwatchAlarm(cloudwatchAlarm);
+      }
+
       handleClusterStateChange("guardDutyEnabled", eksValues.enableGuardDuty);
       handleClusterStateChange(
         "kmsEncryptionEnabled",
@@ -580,7 +593,6 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
             setStep(0);
           }
         }
-
       }
     }
   }, [clusterState]);
@@ -1072,11 +1084,11 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
 
                         {(clusterState.wafV2ARN === undefined ||
                           clusterState.wafV2ARN?.length === 0) && (
-                            <ErrorInLine>
-                              <i className="material-icons">error</i>
-                              {"Required if WafV2 is enabled"}
-                            </ErrorInLine>
-                          )}
+                          <ErrorInLine>
+                            <i className="material-icons">error</i>
+                            {"Required if WafV2 is enabled"}
+                          </ErrorInLine>
+                        )}
                       </>
                     )}
                     <Spacer y={1} />
@@ -1185,10 +1197,9 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
     setShowHelpMessage(false);
     try {
       await preflightChecks();
-    } catch (err) { }
+    } catch (err) {}
   };
 
-
   const renderForm = (): JSX.Element => {
     // Render simplified form if initial create
     if (!props.clusterId) {
@@ -1236,8 +1247,8 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
                         </Text>
                       </CheckItemTop>
                     </CheckItemContainer>
-
-                  </>) :
+                  </>
+                ) : (
                   <>
                     <PreflightChecks
                       provider="AWS"
@@ -1247,67 +1258,94 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
                     <Spacer y={0.5} />
                     {preflightFailed && preflightData && (
                       <>
-                        {(showHelpMessage && currentProject?.quota_increase) ? <>
-                          <Text color="helper">
-                            Your account currently is blocked from provisioning in {clusterState.awsRegion} due to a quota limit imposed by AWS. Either change the region or request to increase quotas.
-                          </Text>
-                          <Spacer y={.5} />
-                          <Text color="helper">
-                            Porter can automatically request quota increases on your behalf and email you once the cluster is provisioned.
-                          </Text>
-                          <Spacer y={.5} />
-                          <div style={{ display: 'flex', justifyContent: 'flex-start', alignItems: 'center', gap: '15px' }}>
-                            <Button
-                              disabled={isLoading}
-                              onClick={proceedToProvision}
-
+                        {showHelpMessage && currentProject?.quota_increase ? (
+                          <>
+                            <Text color="helper">
+                              Your account currently is blocked from
+                              provisioning in {clusterState.awsRegion} due to a
+                              quota limit imposed by AWS. Either change the
+                              region or request to increase quotas.
+                            </Text>
+                            <Spacer y={0.5} />
+                            <Text color="helper">
+                              Porter can automatically request quota increases
+                              on your behalf and email you once the cluster is
+                              provisioned.
+                            </Text>
+                            <Spacer y={0.5} />
+                            <div
+                              style={{
+                                display: "flex",
+                                justifyContent: "flex-start",
+                                alignItems: "center",
+                                gap: "15px",
+                              }}
                             >
-                              Auto request increase
-                            </Button>
+                              <Button
+                                disabled={isLoading}
+                                onClick={proceedToProvision}
+                              >
+                                Auto request increase
+                              </Button>
+                              <Button
+                                disabled={isLoading}
+                                onClick={dismissPreflight}
+                                color="#313539"
+                              >
+                                I&apos;ll do it myself
+                              </Button>
+                            </div>
+                          </>
+                        ) : (
+                          <>
+                            <Text color="helper">
+                              Your account currently is blocked from
+                              provisioning in {clusterState.awsRegion} due to a
+                              quota limit imposed by AWS. Either change the
+                              region or request to increase quotas.
+                            </Text>
+                            <Spacer y={0.5} />
                             <Button
                               disabled={isLoading}
-                              onClick={dismissPreflight}
-                              color="#313539"
+                              onClick={preflightChecks}
                             >
-                              I'll do it myself
-                            </Button>
-                          </div>
-
-                        </> : (
-                          <><Text color="helper">
-                            Your account currently is blocked from provisioning in {clusterState.awsRegion} due to a quota limit imposed by AWS. Either change the region or request to increase quotas.
-                          </Text><Spacer y={.5} /><Button
-                            disabled={isLoading}
-                            onClick={preflightChecks}
-
-                          >
                               Retry checks
-                            </Button></>)}
-                      </>)}
-                  </>}
-              </>, <>
+                            </Button>
+                          </>
+                        )}
+                      </>
+                    )}
+                  </>
+                )}
+              </>,
+              <>
                 <Text size={16}>Provision your cluster</Text>
                 <Spacer y={1} />
-                {showEmailMessage && <>
-                  <Text color="helper">
-                    After your quota requests have been approved by AWS, Porter will email you when your cluster has been provisioned.
-                  </Text>
-                  <Spacer y={1} />
-                </>}
+                {showEmailMessage && (
+                  <>
+                    <Text color="helper">
+                      After your quota requests have been approved by AWS,
+                      Porter will email you when your cluster has been
+                      provisioned.
+                    </Text>
+                    <Spacer y={1} />
+                  </>
+                )}
                 <Button
                   disabled={(preflightFailed && !showEmailMessage) || isLoading}
-                  onClick={showEmailMessage ? requestQuotasAndProvision : createCluster}
+                  onClick={
+                    showEmailMessage ? requestQuotasAndProvision : createCluster
+                  }
                   status={getStatus()}
                 >
                   Provision
                 </Button>
-                <Spacer y={1} /></>
-              ,
-
+                <Spacer y={1} />
+              </>,
             ].filter((x) => x)}
           />
         </>
-      )
+      );
     }
 
     // If settings, update full form
@@ -1330,10 +1368,9 @@ const ProvisionerSettings: React.FC<Props> = (props) => {
           showEmailMessage={showEmailMessage}
           requestQuotasAndProvision={requestQuotaIncrease}
         />
-      )
+      );
     }
 
-
     return (
       <>
         <StyledForm>
@@ -1390,7 +1427,7 @@ const ExpandHeader = styled.div<{ isExpanded: boolean }>`
         margin - right: 7px;
       margin-left: -7px;
       transform: ${(props) =>
-    props.isExpanded ? "rotate(0deg)" : "rotate(-90deg)"};
+        props.isExpanded ? "rotate(0deg)" : "rotate(-90deg)"};
       transition: transform 0.1s ease;
   }
       `;

+ 8 - 10
dashboard/src/components/SOC2Checks.tsx

@@ -4,6 +4,7 @@ import styled from "styled-components";
 
 import Container from "components/porter/Container";
 
+import { type Soc2Check, type Soc2Data } from "shared/types";
 import external_link from "assets/external-link.svg";
 import failure from "assets/failure.svg";
 import pending from "assets/pending.svg";
@@ -14,10 +15,8 @@ import Link from "./porter/Link";
 import Spacer from "./porter/Spacer";
 import Text from "./porter/Text";
 import ToggleRow from "./porter/ToggleRow";
-import { type Soc2Data, type Soc2Check } from "shared/types";
 import SOC2EmailComponent from "./SOC2EmailComponent";
 
-
 type Props = RouteComponentProps & {
   soc2Data: Soc2Data;
   error?: string;
@@ -65,8 +64,8 @@ const SOC2Checks: React.FC<Props> = ({
           status: !soc2Checks[key].enabled
             ? ""
             : soc2Checks[key].status === "PENDING_ENABLED"
-              ? "PENDING_ENABLED"
-              : "ENABLED",
+            ? "PENDING_ENABLED"
+            : "ENABLED",
         };
         return acc;
       }, {});
@@ -176,8 +175,8 @@ const SOC2Checks: React.FC<Props> = ({
                       readOnly
                         ? "Wait for provisioning to complete before editing this field."
                         : enableAll
-                          ? "Global SOC 2 setting must be disabled to toggle this"
-                          : checkData?.disabledTooltip
+                        ? "Global SOC 2 setting must be disabled to toggle this"
+                        : checkData?.disabledTooltip
                     }
                   >
                     <Container row>
@@ -198,8 +197,7 @@ const SOC2Checks: React.FC<Props> = ({
                     </Link>
                   )}
                 </Container>
-                {
-                  (checkData.email && (checkData.enabled || enableAll)) &&
+                {checkData.email && (checkData.enabled || enableAll) && (
                   <>
                     <Spacer y={1} />
                     <SOC2EmailComponent
@@ -209,7 +207,7 @@ const SOC2Checks: React.FC<Props> = ({
                       soc2CheckKey={checkKey}
                     />
                   </>
-                }
+                )}
                 <Spacer y={0.5} />
               </>
             )}
@@ -267,7 +265,7 @@ const CheckItemContainer = styled.div`
     props.isExpanded
       ? "2px solid #3a48ca"
       : "1px solid " +
-      props.theme.border}; // Thicker and blue border if expanded
+        props.theme.border}; // Thicker and blue border if expanded
   border-radius: 5px;
   font-size: 13px;
   width: 100%;

+ 5 - 5
dashboard/src/main/home/cluster-dashboard/dashboard/Compliance.tsx

@@ -139,7 +139,6 @@ const Compliance: React.FC<Props> = (props) => {
       );
 
       const contract = createContract(result.base64_contract);
-
       await api.createContract("<token>", contract, {
         project_id: currentProject.id,
       });
@@ -207,7 +206,7 @@ const Compliance: React.FC<Props> = (props) => {
           }),
           cloudwatchAlarm: new CloudwatchAlarm({
             enable: soc2Enabled || snsMonitoringEnabled || false,
-            emails: snsMonitoringEmails || [""],
+            emails: snsMonitoringEmails || [],
           }),
         }),
         case: "eksKind" as const,
@@ -360,7 +359,7 @@ const Compliance: React.FC<Props> = (props) => {
               status: determineStatus(
                 eksValues.cloudwatchAlarm?.enable || false
               ),
-              email: eksValues.cloudwatchAlarm?.emails,
+              email: eksValues.cloudwatchAlarm?.emails || [],
             },
           },
         };
@@ -368,8 +367,9 @@ const Compliance: React.FC<Props> = (props) => {
 
       setSoc2Enabled(
         cloudTrailEnabled &&
-        eksValues.enableKmsEncryption &&
-        eksValues.enableEcrScanning
+          eksValues.enableKmsEncryption &&
+          eksValues.enableEcrScanning &&
+          (eksValues.cloudwatchAlarm?.enable || false)
       );
     }
   }, [props.selectedClusterVersion]);